mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-19 14:57:15 +03:00
Got tests compiling
This commit is contained in:
parent
534d64abda
commit
99f99380da
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -764,6 +764,7 @@ dependencies = [
|
||||
"pretty_assertions 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quickcheck 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quickcheck_macros 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"symbol-map 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wyhash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -814,6 +815,11 @@ name = "smallvec"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "symbol-map"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
@ -1026,6 +1032,7 @@ dependencies = [
|
||||
"checksum sized-chunks 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6f59f81ec9833a580d2448e958d16bd872637798f3ab300b693c48f136fb76ff"
|
||||
"checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4"
|
||||
"checksum symbol-map 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61d9cc91822af02f09aa1244db257390074da43541d18f699246a8665de3592c"
|
||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||
"checksum syn 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ddc157159e2a7df58cd67b1cace10b8ed256a404fb0070593f137d8ba6bef4de"
|
||||
"checksum target-lexicon 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4c118a7a38378f305a9e111fcb2f7f838c0be324bfb31a77ea04f7f6e684b4"
|
||||
|
@ -21,6 +21,7 @@ inlinable_string = "0.1.0"
|
||||
# but after several hours of trying unsuccessfully to fix it, `branch` is it.)
|
||||
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "llvm8-0" }
|
||||
futures = "0.3"
|
||||
symbol-map = "1"
|
||||
lazy_static = "1.4"
|
||||
target-lexicon = "0.9" # NOTE: we must use the same version of target-lexicon as cranelift!
|
||||
cranelift = "0.52" # All cranelift crates should have the same version!
|
||||
|
@ -28,18 +28,24 @@ impl<'a> From<&'a str> for ModuleName {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Box<str>> for ModuleName {
|
||||
impl From<Box<str>> for ModuleName {
|
||||
fn from(string: Box<str>) -> Self {
|
||||
Self((string.as_ref()).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<String> for ModuleName {
|
||||
impl From<String> for ModuleName {
|
||||
fn from(string: String) -> Self {
|
||||
Self(string.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InlinableString> for ModuleName {
|
||||
fn from(string: InlinableString) -> Self {
|
||||
Self(string)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<InlinableString> for ModuleName {
|
||||
fn into(self) -> InlinableString {
|
||||
self.0
|
||||
|
@ -48,7 +48,7 @@ where
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn insert_all<K, V, I>(map: &mut ImMap<K, V>, elems: I)
|
||||
pub fn insert_all<K, V, I>(map: &mut MutMap<K, V>, elems: I)
|
||||
where
|
||||
K: Clone + Eq + Hash,
|
||||
V: Clone,
|
||||
|
12
src/infer.rs
12
src/infer.rs
@ -1,15 +1,15 @@
|
||||
use crate::collections::{ImMap, MutMap};
|
||||
use crate::solve;
|
||||
use crate::solve::{self, Solved};
|
||||
use crate::subs::{Content, Subs, Variable};
|
||||
use crate::types::{Constraint, Problem};
|
||||
|
||||
pub fn infer_expr(
|
||||
subs: &mut Subs,
|
||||
subs: Subs,
|
||||
problems: &mut Vec<Problem>,
|
||||
constraint: &Constraint,
|
||||
expr_var: Variable,
|
||||
) -> Content {
|
||||
solve::run(
|
||||
) -> (Content, Solved<Subs>) {
|
||||
let solved = solve::run(
|
||||
&ImMap::default(),
|
||||
MutMap::default(),
|
||||
problems,
|
||||
@ -17,5 +17,7 @@ pub fn infer_expr(
|
||||
constraint,
|
||||
);
|
||||
|
||||
subs.get(expr_var).content
|
||||
let content = solved.inner().get_without_compacting(expr_var).content;
|
||||
|
||||
(content, solved)
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ pub mod ident;
|
||||
pub mod operator;
|
||||
pub mod parse;
|
||||
pub mod region;
|
||||
pub mod symbol_map;
|
||||
pub mod uniqueness;
|
||||
|
||||
pub mod string;
|
||||
|
190
src/load/mod.rs
190
src/load/mod.rs
@ -11,7 +11,7 @@ use crate::parse::ast::{self, Attempting, ExposesEntry, ImportsEntry};
|
||||
use crate::parse::module::{self, module_defs};
|
||||
use crate::parse::parser::{Fail, Parser, State};
|
||||
use crate::region::{Located, Region};
|
||||
use crate::solve::{self, ModuleSubs, Solved, SubsByModule};
|
||||
use crate::solve::{self, ModuleSubs, Solved};
|
||||
use crate::subs::{Subs, VarStore, Variable};
|
||||
use crate::types::{Constraint, Problem};
|
||||
use bumpalo::Bump;
|
||||
@ -22,12 +22,23 @@ use std::sync::{Arc, Mutex};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::task::spawn_blocking;
|
||||
|
||||
/// TODO change solve::SubsByModule to be this
|
||||
type SubsByModule = MutMap<ModuleId, ModuleSubs>;
|
||||
|
||||
/// Filename extension for normal Roc modules
|
||||
const ROC_FILE_EXTENSION: &str = "roc";
|
||||
|
||||
/// The . in between module names like Foo.Bar.Baz
|
||||
const MODULE_SEPARATOR: char = '.';
|
||||
|
||||
pub struct LoadedModule {
|
||||
pub module_id: ModuleId,
|
||||
pub module_ids: ModuleIdStore,
|
||||
pub solved: Solved<Subs>,
|
||||
pub problems: Vec<Problem>,
|
||||
pub declarations: Vec<Declaration>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Env {
|
||||
pub src_dir: PathBuf,
|
||||
@ -98,7 +109,7 @@ pub async fn load<'a>(
|
||||
src_dir: PathBuf,
|
||||
filename: PathBuf,
|
||||
subs_by_module: &mut SubsByModule,
|
||||
) -> Result<(ModuleId, ModuleIdStore, Arc<Solved<Subs>>, Vec<Problem>), LoadingProblem> {
|
||||
) -> Result<LoadedModule, LoadingProblem> {
|
||||
use self::MaybeShared::*;
|
||||
use self::Msg::*;
|
||||
|
||||
@ -121,7 +132,10 @@ pub async fn load<'a>(
|
||||
let mut loading_started: MutSet<ModuleName> = MutSet::default();
|
||||
|
||||
// The Result we got back when we tried to solve a given module
|
||||
let mut solved_modules: SubsByModule = SendMap::default();
|
||||
let mut solved_modules: MutMap<ModuleId, SubsByModule> = MutMap::default();
|
||||
|
||||
// The declarations we'll ultimately be returning
|
||||
let mut declarations_by_id: MutMap<ModuleId, Vec<Declaration>> = MutMap::default();
|
||||
|
||||
let mut deps_by_id: MutMap<ModuleId, Vec<ModuleName>> = MutMap::default();
|
||||
|
||||
@ -132,17 +146,21 @@ pub async fn load<'a>(
|
||||
module_id,
|
||||
dep_names,
|
||||
} => {
|
||||
deps_by_id.insert(module_id, dep_names);
|
||||
deps_by_id.insert(module_id, dep_names.clone());
|
||||
|
||||
for dep_name in dep_names {
|
||||
if !loading_started.contains(&dep_name) {
|
||||
// Record that we've started loading the module *before*
|
||||
// we actually start loading it.
|
||||
loading_started.insert(dep_name);
|
||||
loading_started.insert(dep_name.clone());
|
||||
|
||||
let env = env.clone();
|
||||
let msg_tx = msg_tx.clone();
|
||||
let shared_module_ids = Shared(Arc::clone(&module_ids));
|
||||
|
||||
// Start loading this module in the background.
|
||||
spawn_blocking(move || {
|
||||
load_module(&env, dep_name, msg_tx.clone(), Shared(module_ids))
|
||||
load_module(env, dep_name, msg_tx, shared_module_ids)
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -152,13 +170,74 @@ pub async fn load<'a>(
|
||||
constraint,
|
||||
next_var,
|
||||
} => {
|
||||
panic!("TODO detect when we're ready to start solving this module, and ONLY THEN begin solving it! {:?}", module.module_id);
|
||||
use Declaration::*;
|
||||
|
||||
let subs_by_module = im::hashmap::HashMap::clone(subs_by_module);
|
||||
dbg!("TODO detect when we're ready to start solving this module, and ONLY THEN begin solving it! {:?}", module.module_id);
|
||||
|
||||
let subs_by_module = solved_modules
|
||||
.remove(&module.module_id)
|
||||
.unwrap_or_else(|| MutMap::default());
|
||||
|
||||
// TODO change solve::run to expect SubsById to be keyed on ModuleId instead of doing this
|
||||
// conversion!
|
||||
let subs_by_module = {
|
||||
let mut converted = MutMap::default();
|
||||
let unlocked = (*module_ids).lock().unwrap();
|
||||
|
||||
for (module_id, v) in subs_by_module {
|
||||
converted.insert(unlocked.get_name(module_id).unwrap().clone(), v);
|
||||
}
|
||||
|
||||
converted
|
||||
};
|
||||
|
||||
let mut vars_by_symbol: MutMap<Symbol, Variable> = MutMap::default();
|
||||
|
||||
// All the exposed imports should be available in the solver's vars_by_symbol
|
||||
for (symbol, expr_var) in im::HashMap::clone(&module.exposed_imports) {
|
||||
vars_by_symbol.insert(symbol, expr_var);
|
||||
}
|
||||
|
||||
// All the top-level defs should also be available in vars_by_symbol
|
||||
for decl in &module.declarations {
|
||||
match decl {
|
||||
Declare(def) => {
|
||||
insert_all(&mut vars_by_symbol, def.pattern_vars.clone().into_iter());
|
||||
}
|
||||
DeclareRec(defs) => {
|
||||
for def in defs {
|
||||
insert_all(
|
||||
&mut vars_by_symbol,
|
||||
def.pattern_vars.clone().into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
InvalidCycle(_, _) => panic!("TODO handle invalid cycles"),
|
||||
}
|
||||
}
|
||||
|
||||
let module_id = module.module_id;
|
||||
|
||||
declarations_by_id.insert(module.module_id, module.declarations);
|
||||
|
||||
let msg_tx = msg_tx.clone();
|
||||
|
||||
// Start solving this module in the background.
|
||||
spawn_blocking(move || {
|
||||
solve_module(module, constraint, next_var, subs_by_module, msg_tx.clone())
|
||||
let mut im_vars_by_symbol = ImMap::default();
|
||||
|
||||
for (k, v) in vars_by_symbol {
|
||||
im_vars_by_symbol.insert(k, v);
|
||||
}
|
||||
|
||||
solve_module(
|
||||
module_id,
|
||||
im_vars_by_symbol,
|
||||
constraint,
|
||||
next_var,
|
||||
subs_by_module,
|
||||
msg_tx,
|
||||
)
|
||||
});
|
||||
}
|
||||
Solved {
|
||||
@ -179,7 +258,21 @@ pub async fn load<'a>(
|
||||
.into_inner()
|
||||
.expect("Unwrapping mutex for module_ids");
|
||||
|
||||
return Ok((root_id, module_ids, subs, all_problems));
|
||||
let solved = Arc::try_unwrap(subs).unwrap_or_else(|_| {
|
||||
panic!("There were still outstanding Arc references to Solved<Subs>")
|
||||
});
|
||||
|
||||
let declarations = declarations_by_id
|
||||
.remove(&module_id)
|
||||
.expect("declarations_by_id was missing root module_id entry");
|
||||
|
||||
return Ok(LoadedModule {
|
||||
module_id: root_id,
|
||||
module_ids,
|
||||
solved,
|
||||
problems: all_problems,
|
||||
declarations,
|
||||
});
|
||||
} else {
|
||||
// This was a dependency. Write it down and keep processing messaages.
|
||||
subs_by_module.insert(module_id, ModuleSubs::Valid(subs));
|
||||
@ -194,7 +287,7 @@ pub async fn load<'a>(
|
||||
|
||||
/// Load a module by its module name, rather than by its filename
|
||||
fn load_module(
|
||||
env: &Env,
|
||||
env: Env,
|
||||
module_name: ModuleName,
|
||||
msg_tx: MsgSender,
|
||||
module_ids: MaybeShared<'_, ModuleIdStore>,
|
||||
@ -211,7 +304,7 @@ fn load_module(
|
||||
// End with .roc
|
||||
filename.set_extension(ROC_FILE_EXTENSION);
|
||||
|
||||
load_filename(env, filename, msg_tx, module_ids)
|
||||
load_filename(&env, filename, msg_tx, module_ids)
|
||||
}
|
||||
|
||||
/// Load a module by its filename
|
||||
@ -263,7 +356,7 @@ fn load_filename(
|
||||
// it looks something up. We know the module will only
|
||||
// be able to reference modules it has imported, so this
|
||||
// list should cover every valid lookup it performs.
|
||||
let mut can_module_ids = MutMap::default();
|
||||
let mut can_module_ids: MutMap<&ModuleName, ModuleId> = MutMap::default();
|
||||
|
||||
// Make sure the module_ids has ModuleIds for all our deps,
|
||||
// then record those ModuleIds in can_module_ids for later.
|
||||
@ -272,35 +365,35 @@ fn load_filename(
|
||||
// Lock just long enough to perform these operations.
|
||||
let mut unlocked = (*arc).lock().unwrap();
|
||||
|
||||
for dep in deps {
|
||||
let id = unlocked.get(declared_name);
|
||||
for dep in deps.iter() {
|
||||
let id = unlocked.get_id(dep);
|
||||
|
||||
can_module_ids.insert(declared_name, id);
|
||||
can_module_ids.insert(&dep, id);
|
||||
}
|
||||
|
||||
unlocked.get(declared_name)
|
||||
unlocked.get_id(&declared_name)
|
||||
}
|
||||
|
||||
Unique(mut_ref) => {
|
||||
// If this is the original file the user loaded,
|
||||
// then we already have a mutable reference,
|
||||
// and won't need to pay locking costs.
|
||||
let module_id = mut_ref.get(declared_name.into());
|
||||
let module_id = mut_ref.get_id(&declared_name);
|
||||
|
||||
for dep in deps {
|
||||
let id = mut_ref.get(declared_name);
|
||||
for dep in deps.iter() {
|
||||
let id = mut_ref.get_id(dep);
|
||||
|
||||
can_module_ids.insert(declared_name, id);
|
||||
can_module_ids.insert(&dep, id);
|
||||
}
|
||||
|
||||
module_id
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Insert our own module_id into the can_module_ids.
|
||||
// (It's already in the shared module_ids, but we insert
|
||||
// this after the lock has expired.)
|
||||
can_module_ids.insert(declared_name, module_id);
|
||||
can_module_ids.insert(&declared_name, module_id);
|
||||
|
||||
// Send the deps to the coordinator thread for processing,
|
||||
// then continue on to parsing and canonicalizing defs.
|
||||
@ -308,16 +401,18 @@ fn load_filename(
|
||||
// We always need to send these, even if deps is empty,
|
||||
// because the coordinator thread needs to receive this message
|
||||
// to decrement its "pending" count.
|
||||
tokio::spawn(async move {
|
||||
let mut tx = msg_tx;
|
||||
let mut tx = msg_tx.clone();
|
||||
let dep_names = deps.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
// Send the deps to the main thread for processing,
|
||||
// then continue on to parsing and canonicalizing defs.
|
||||
tx.send(Msg::DepsRequested {
|
||||
module_id,
|
||||
dep_names: deps,
|
||||
dep_names,
|
||||
})
|
||||
.await;
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Failed to send DepsRequested message"));
|
||||
});
|
||||
|
||||
let mut scope = Scope::new(
|
||||
@ -326,7 +421,7 @@ fn load_filename(
|
||||
scope_from_imports,
|
||||
);
|
||||
|
||||
let (declarations, mut problems, exposed_imports, constraint) = process_defs(
|
||||
let (declarations, _problems, exposed_imports, constraint) = process_defs(
|
||||
&arena,
|
||||
state,
|
||||
declared_name.clone(),
|
||||
@ -352,7 +447,8 @@ fn load_filename(
|
||||
constraint,
|
||||
next_var,
|
||||
})
|
||||
.await;
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Failed to send Constrained message"));
|
||||
});
|
||||
|
||||
Ok(module_id)
|
||||
@ -373,14 +469,13 @@ fn load_filename(
|
||||
}
|
||||
|
||||
fn solve_module(
|
||||
module: Module,
|
||||
module_id: ModuleId,
|
||||
vars_by_symbol: ImMap<Symbol, Variable>,
|
||||
constraint: Constraint,
|
||||
next_var: Variable,
|
||||
subs_by_module: SubsByModule,
|
||||
subs_by_module: MutMap<ModuleName, ModuleSubs>,
|
||||
msg_tx: MsgSender,
|
||||
) {
|
||||
use Declaration::*;
|
||||
|
||||
// Now that the module is parsed, canonicalized, and constrained,
|
||||
// we just need to type check it.
|
||||
//
|
||||
@ -389,28 +484,6 @@ fn solve_module(
|
||||
// our own canonicalization.
|
||||
let subs = Subs::new(next_var);
|
||||
|
||||
let mut vars_by_symbol: ImMap<Symbol, Variable> = ImMap::default();
|
||||
|
||||
// All the exposed imports should be available in the solver's vars_by_symbol
|
||||
for (symbol, expr_var) in im::HashMap::clone(&module.exposed_imports) {
|
||||
vars_by_symbol.insert(symbol, expr_var);
|
||||
}
|
||||
|
||||
// All the top-level defs should also be available in vars_by_symbol
|
||||
for decl in &module.declarations {
|
||||
match decl {
|
||||
Declare(def) => {
|
||||
insert_all(&mut vars_by_symbol, def.pattern_vars.clone().into_iter());
|
||||
}
|
||||
DeclareRec(defs) => {
|
||||
for def in defs {
|
||||
insert_all(&mut vars_by_symbol, def.pattern_vars.clone().into_iter());
|
||||
}
|
||||
}
|
||||
InvalidCycle(_, _) => panic!("TODO handle invalid cycles"),
|
||||
}
|
||||
}
|
||||
|
||||
let mut problems = Vec::new();
|
||||
|
||||
// Run the solver to populate Subs.
|
||||
@ -427,11 +500,12 @@ fn solve_module(
|
||||
|
||||
// Send the subs to the main thread for processing,
|
||||
tx.send(Msg::Solved {
|
||||
module_id: module.module_id,
|
||||
module_id: module_id,
|
||||
subs: Arc::new(solved),
|
||||
problems,
|
||||
})
|
||||
.await;
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Failed to send Solved message"));
|
||||
});
|
||||
}
|
||||
|
||||
@ -440,7 +514,7 @@ fn process_defs<'a, I>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
home: ModuleName,
|
||||
_module_ids: MutMap<ModuleName, ModuleId>, // TODO use this to canonicalize into ModuleIds
|
||||
_module_ids: MutMap<&ModuleName, ModuleId>, // TODO use this to canonicalize into ModuleIds
|
||||
exposes: I,
|
||||
scope: &mut Scope,
|
||||
var_store: &VarStore,
|
||||
|
@ -1,70 +1,29 @@
|
||||
use crate::can::ident;
|
||||
use crate::collections::MutMap;
|
||||
use crate::parse::ast::CommentOrNewline;
|
||||
use crate::region::Loc;
|
||||
use bumpalo::collections::Vec;
|
||||
use inlinable_string::InlinableString;
|
||||
use symbol_map::indexing::{HashIndexing, Indexing};
|
||||
use symbol_map::Symbol;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct ModuleId(u32);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct ModuleIdStore {
|
||||
next_module_id: u32,
|
||||
store: MutMap<ident::ModuleName, ModuleId>,
|
||||
}
|
||||
|
||||
impl Default for ModuleIdStore {
|
||||
fn default() -> Self {
|
||||
ModuleIdStore {
|
||||
next_module_id: 1,
|
||||
store: MutMap::default(),
|
||||
}
|
||||
}
|
||||
store: HashIndexing<ident::ModuleName, u32>,
|
||||
}
|
||||
|
||||
impl ModuleIdStore {
|
||||
pub fn get(&mut self, module_name: ident::ModuleName) -> ModuleId {
|
||||
match self.store.get(&module_name) {
|
||||
Some(id) => *id,
|
||||
None => {
|
||||
// We've never seen this module name before.
|
||||
// Assign it an ID and store the result, then return it.
|
||||
let module_id = ModuleId(self.next_module_id);
|
||||
|
||||
self.store.insert(module_name, module_id);
|
||||
self.next_module_id = 1 + module_id.0;
|
||||
|
||||
module_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_if_present(&self, module_name: &ident::ModuleName) -> Option<ModuleId> {
|
||||
pub fn get_id(&mut self, module_name: &ident::ModuleName) -> ModuleId {
|
||||
match self.store.get(module_name) {
|
||||
Some(id) => Some(*id),
|
||||
None => None,
|
||||
Some(symbol) => ModuleId(*symbol.id()),
|
||||
None => ModuleId(*self.store.get_or_insert(module_name.clone()).unwrap().id()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Store all the given module names, but don't return any of them.
|
||||
/// This is useful when you have a Mutex<ModuleIdStore>, so you can
|
||||
/// lock it, store a bunch of module names, and then unlock it.
|
||||
pub fn store_all<'a, I>(&mut self, module_names: I)
|
||||
where
|
||||
I: Iterator<Item = &'a ident::ModuleName>,
|
||||
{
|
||||
let next = self.next_module_id;
|
||||
|
||||
for module_name in module_names {
|
||||
if !self.store.contains_key(module_name) {
|
||||
self.store.insert(module_name.clone(), ModuleId(next));
|
||||
|
||||
next += 1;
|
||||
}
|
||||
}
|
||||
|
||||
self.next_module_id = next;
|
||||
pub fn get_name<'a>(&'a self, id: ModuleId) -> Option<&'a ident::ModuleName> {
|
||||
self.store.get_symbol(&id.0).map(Symbol::data)
|
||||
}
|
||||
}
|
||||
|
||||
|
23
src/solve.rs
23
src/solve.rs
@ -1,7 +1,6 @@
|
||||
use crate::can::ident::Lowercase;
|
||||
use crate::can::ident::{Lowercase, ModuleName};
|
||||
use crate::can::symbol::Symbol;
|
||||
use crate::collections::{ImMap, MutMap, SendMap};
|
||||
use crate::module::ModuleId;
|
||||
use crate::collections::{ImMap, MutMap};
|
||||
use crate::region::Located;
|
||||
use crate::subs::{Content, Descriptor, FlatType, Mark, OptVariable, Rank, Subs, Variable};
|
||||
use crate::types::Constraint::{self, *};
|
||||
@ -11,7 +10,7 @@ use crate::unify::{unify, Unified};
|
||||
use crate::uniqueness::boolean_algebra;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type SubsByModule = SendMap<ModuleId, ModuleSubs>;
|
||||
pub type SubsByModule = MutMap<ModuleName, ModuleSubs>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ModuleSubs {
|
||||
@ -83,7 +82,11 @@ struct State {
|
||||
pub struct Solved<T>(T);
|
||||
|
||||
impl<T> Solved<T> {
|
||||
fn get(self) -> T {
|
||||
pub fn inner<'a>(&'a self) -> &'a T {
|
||||
&self.0
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
@ -933,7 +936,7 @@ fn deep_copy_local_var_help(
|
||||
}
|
||||
|
||||
fn deep_copy_foreign_var(
|
||||
source_subs: &Subs,
|
||||
source_subs: &ModuleSubs,
|
||||
dest_subs: &mut Subs,
|
||||
rank: Rank,
|
||||
pools: &mut Pools,
|
||||
@ -947,7 +950,7 @@ fn deep_copy_foreign_var(
|
||||
}
|
||||
|
||||
fn deep_copy_foreign_var_help(
|
||||
source_subs: &Subs,
|
||||
source_subs: &ModuleSubs,
|
||||
dest_subs: &mut Subs,
|
||||
max_rank: Rank,
|
||||
pools: &mut Pools,
|
||||
@ -955,8 +958,12 @@ fn deep_copy_foreign_var_help(
|
||||
) -> Variable {
|
||||
use crate::subs::Content::*;
|
||||
use crate::subs::FlatType::*;
|
||||
use ModuleSubs::*;
|
||||
|
||||
let desc = source_subs.get_without_compacting(var);
|
||||
let desc = match source_subs {
|
||||
Valid(arc_solved) => (&arc_solved).inner().get_without_compacting(var),
|
||||
Invalid => panic!("TODO gracefully handle lookups on invalid modules"),
|
||||
};
|
||||
|
||||
if let Some(copy) = desc.copy.into_variable() {
|
||||
return copy;
|
||||
|
322
src/symbol_map.rs
Normal file
322
src/symbol_map.rs
Normal file
@ -0,0 +1,322 @@
|
||||
// Adapted from the symbol-map crate by Donald S. Black, licensed using the
|
||||
// Apache License, Version 2.0. Thank you, Donald!
|
||||
//
|
||||
// Indexing on top of a `Table`.
|
||||
//
|
||||
// It is anticipated that most uses cases will be covered by
|
||||
// [HashIndexing](struct.HashIndexing.html), which owns a
|
||||
// [Table](../struct.Table.html) and provides bidirectional mappings between
|
||||
// data values and their symbols.
|
||||
//
|
||||
// The [Indexing](trait.Indexing.html) trait is provided in case another lookup
|
||||
// method is needed.
|
||||
|
||||
use crate::collections::{default_hasher, BuildHasher};
|
||||
use std::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd};
|
||||
use std::collections::HashMap;
|
||||
use std::default::Default;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use symbol_map::{Symbol, SymbolId, Table};
|
||||
|
||||
/// Indicates whether the result of a symbol lookup had to create a new table
|
||||
/// entry.
|
||||
#[derive(Clone, Eq, Ord, Hash, PartialEq, PartialOrd)]
|
||||
pub enum Insertion<T> {
|
||||
/// Result came from an item that was already present in table.
|
||||
Present(T),
|
||||
/// Result came from an item that was not present in table, and a new entry
|
||||
/// was created.
|
||||
New(T),
|
||||
}
|
||||
|
||||
impl<T> Insertion<T> {
|
||||
/// Maps over the type returned by an `Insertion` to produce a new value
|
||||
/// that may be of a different type.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use symbol_map::indexing::{HashIndexing, Indexing, Insertion};
|
||||
/// use std::str::FromStr;
|
||||
///
|
||||
/// let mut index = HashIndexing::<String, usize>::default();
|
||||
/// let s1 = String::from_str("value1").unwrap();
|
||||
/// let s2 = String::from_str("value1").unwrap();
|
||||
/// let s3 = String::from_str("value2").unwrap();
|
||||
/// // get_or_insert normally returns an Insertion that borrows the
|
||||
/// // structure on which it was invoked. We map the symbol reference
|
||||
/// // returned after each insertion to a copy of the ID that was mapped to.
|
||||
/// let id1: Insertion<usize> = index.get_or_insert(s1).map(|symbol| *symbol.id());
|
||||
/// let id2: Insertion<usize> = index.get_or_insert(s2).map(|symbol| *symbol.id());
|
||||
/// let id3: Insertion<usize> = index.get_or_insert(s3).map(|symbol| *symbol.id());
|
||||
/// // The Insertion values are not the same because one was an insertion and
|
||||
/// // the other a retrieval.
|
||||
/// assert!(id1 != id2);
|
||||
/// assert!(id1 != id3);
|
||||
/// // But the symbol IDs for identical values are the same.
|
||||
/// assert!(id1.unwrap() == id2.unwrap());
|
||||
/// ```
|
||||
pub fn map<F, X>(&self, f: F) -> Insertion<X>
|
||||
where
|
||||
F: FnOnce(&T) -> X,
|
||||
{
|
||||
match *self {
|
||||
Insertion::Present(ref s) => Insertion::Present(f(s)),
|
||||
Insertion::New(ref s) => Insertion::New(f(s)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unwraps an `Insertion` to produce the value which it wraps.
|
||||
pub fn unwrap(self) -> T {
|
||||
match self {
|
||||
Insertion::Present(s) => s,
|
||||
Insertion::New(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for a raw pointer which lets us treat it like a reference.
|
||||
///
|
||||
/// You are strongly discouraged from exposing this type directly in your data
|
||||
/// structures. This type is essentially a giant footgun. In particular:
|
||||
///
|
||||
/// - No safety checks or lifetimes protect this reference, so a `Ref<T>` may be
|
||||
/// invalidated without warning. (You may use a `Ref<T>` safely by ensuring that
|
||||
/// the references passed to `Ref<T>::new()` will never be dropped before the
|
||||
/// wrappers. A good example of when you'd be able to do this is in in a struct
|
||||
/// that has `Ref<T>` references into a data structure that it also owns.)
|
||||
///
|
||||
/// - The impls for `Debug`, `Eq`, `Hash`, `Ord`, `PartialEq`, and `PartialOrd`
|
||||
/// all dereference the raw pointer that this structure wraps. As a result, a
|
||||
/// `Ref<T>` must be removed from any data structures that make use of any of
|
||||
/// those interfaces *before* it is invalidated.
|
||||
///
|
||||
/// - `Ref<T>` wraps a value of type `*const T`, which is not usually `Send` or
|
||||
/// `Sync`. This restriction is overridden for a `Ref<T>` wrapper so that data
|
||||
/// structures which encapsulate it may themselves be `Send` or `Sync`. This
|
||||
/// makes it the responsibility of data structures using such wrappers to
|
||||
/// satisfy the contracts of those types.
|
||||
pub struct Ref<T> {
|
||||
ptr: *const T,
|
||||
}
|
||||
|
||||
unsafe impl<T> Send for Ref<T> where T: Send {}
|
||||
|
||||
unsafe impl<T> Sync for Ref<T> where T: Sync {}
|
||||
|
||||
impl<T> Ref<T> {
|
||||
/// Casts `data` to `*const T` and retains the pointer for dereferencing at
|
||||
/// some point in the future.
|
||||
fn new(data: &T) -> Self {
|
||||
Ref {
|
||||
ptr: data as *const T,
|
||||
}
|
||||
}
|
||||
|
||||
/// Dereferences the wrapped pointer. The explicit lifetime parameter should
|
||||
/// match the lifetime of the parent `Table` that the wrapped pointer points
|
||||
/// into. Care should be taken not to call this method if the integrity of
|
||||
/// the reference passed to `new()` cannot be verified.
|
||||
unsafe fn deref<'a>(&self) -> &'a T {
|
||||
&*self.ptr
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Ref<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Ref { ptr: self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for Ref<T> {}
|
||||
|
||||
impl<T> fmt::Pointer for Ref<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Pointer::fmt(&self.ptr, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Ref<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Ref({:?})", unsafe { &(*self.ptr) })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Ref<T> where T: Eq {}
|
||||
|
||||
impl<T> Hash for Ref<T>
|
||||
where
|
||||
T: Hash,
|
||||
{
|
||||
fn hash<H>(&self, h: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
unsafe { (*self.ptr).hash(h) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Ord for Ref<T>
|
||||
where
|
||||
T: Ord,
|
||||
{
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
unsafe { (*self.ptr).cmp(&(*other.ptr)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Ref<T>
|
||||
where
|
||||
T: PartialEq,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe { (*self.ptr).eq(&(*other.ptr)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialOrd for Ref<T>
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
unsafe { (*self.ptr).partial_cmp(&(*other.ptr)) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides indexing for a `Table`, so that its elements may be retrieved
|
||||
/// efficiently. Most table lookups should go through an implementation of this
|
||||
/// trait structure instead of a `Table` directly.
|
||||
///
|
||||
/// An `Indexing` should own an underlying `Table<Indexing::Data>`. This table
|
||||
/// provides persistent storage for `Symbol<Indexing::Data>`s, which associate
|
||||
/// instances of `Data` with a `SymbolId`.
|
||||
///
|
||||
/// This trait is provided for extensibility. Realistically speaking, however,
|
||||
/// you should probably just use `HashIndexing`.
|
||||
pub trait Indexing: Default {
|
||||
/// The type `T` of a `Table<T, D>`.
|
||||
type Data;
|
||||
|
||||
/// The type `D` of a `Table<T, D>`.
|
||||
type Id: SymbolId;
|
||||
|
||||
/// Returns a new indexing method that has already indexed the contents of
|
||||
/// `table`.
|
||||
fn from_table(table: Table<Self::Data, Self::Id>) -> Self;
|
||||
|
||||
/// Returns a read-only view of the underlying table.
|
||||
fn table(&self) -> &Table<Self::Data, Self::Id>;
|
||||
|
||||
/// Extracts the underlying table from the index, discarding all pointers
|
||||
/// into the table.
|
||||
fn to_table(self) -> Table<Self::Data, Self::Id>;
|
||||
|
||||
/// Looks up `data` in the index. Returns `Some(&symbol)` if a symbol is
|
||||
/// present, else `None`.
|
||||
fn get(&self, data: &Self::Data) -> Option<&Symbol<Self::Data, Self::Id>>;
|
||||
|
||||
/// Looks up `data` in the index, inserting it into the index and `table` if
|
||||
/// it isn't present. Returns the resulting `&Symbol<T>` wrapped in an
|
||||
/// `Insertion` that indicates whether a new table entry had to be created.
|
||||
fn get_or_insert<'s>(
|
||||
&'s mut self,
|
||||
data: Self::Data,
|
||||
) -> Insertion<&'s Symbol<Self::Data, Self::Id>>;
|
||||
|
||||
/// Looks up the symbol with id `i` in the index. Returns `Some(symbol)` if
|
||||
/// a symbol is present, else `None`.
|
||||
fn get_symbol<'s>(&'s self, id: &Self::Id) -> Option<&'s Symbol<Self::Data, Self::Id>>;
|
||||
}
|
||||
|
||||
/// HashMap-backed table indexing.
|
||||
pub struct HashIndexing<T, D>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
D: SymbolId,
|
||||
{
|
||||
table: Table<T, D>,
|
||||
by_symbol: HashMap<Ref<T>, Ref<Symbol<T, D>>, BuildHasher>,
|
||||
by_id: Vec<Ref<Symbol<T, D>>>,
|
||||
}
|
||||
|
||||
impl<T, D> Default for HashIndexing<T, D>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
D: SymbolId,
|
||||
{
|
||||
fn default() -> Self {
|
||||
HashIndexing {
|
||||
table: Table::new(),
|
||||
by_symbol: HashMap::with_hasher(default_hasher()),
|
||||
by_id: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, D> Indexing for HashIndexing<T, D>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
D: SymbolId,
|
||||
{
|
||||
type Data = T;
|
||||
type Id = D;
|
||||
|
||||
fn from_table(table: Table<T, D>) -> Self {
|
||||
let mut by_symbol = HashMap::with_capacity_and_hasher(table.len(), default_hasher());
|
||||
let mut by_id = match table.iter().next() {
|
||||
Some(symbol) => vec![Ref::new(symbol); table.len()],
|
||||
None => Vec::new(),
|
||||
};
|
||||
for symbol in table.iter() {
|
||||
by_symbol.insert(Ref::new(symbol.data()), Ref::new(symbol));
|
||||
by_id[symbol.id().as_usize()] = Ref::new(symbol);
|
||||
}
|
||||
HashIndexing {
|
||||
table: table,
|
||||
by_symbol: by_symbol,
|
||||
by_id: by_id,
|
||||
}
|
||||
}
|
||||
|
||||
fn table(&self) -> &Table<Self::Data, Self::Id> {
|
||||
&self.table
|
||||
}
|
||||
|
||||
fn to_table(self) -> Table<Self::Data, Self::Id> {
|
||||
self.table
|
||||
}
|
||||
|
||||
fn get<'s>(&'s self, data: &T) -> Option<&'s Symbol<T, D>> {
|
||||
// Unsafe call to Ref::deref(): should be fine as because we own
|
||||
// self.table and the ref refers into that.
|
||||
self.by_symbol
|
||||
.get(&Ref::new(data))
|
||||
.map(|x| unsafe { x.deref() })
|
||||
}
|
||||
|
||||
fn get_or_insert<'s>(&'s mut self, data: T) -> Insertion<&'s Symbol<T, D>> {
|
||||
use std::collections::hash_map::Entry;
|
||||
if let Entry::Occupied(e) = self.by_symbol.entry(Ref::new(&data)) {
|
||||
// Unsafe call to Ref::deref(): should be fine as because we own
|
||||
// self.table and the ref refers into that.
|
||||
return Insertion::Present(unsafe { e.get().deref() });
|
||||
}
|
||||
// TODO: when the HashMap API gets revised, we may be able to do this
|
||||
// without a second hashtable lookup.
|
||||
let symbol = self.table.insert(data);
|
||||
// The Ref that gets inserted has to be backed by data in the table, not
|
||||
// data on the stack (which is how we did the previous lookup).
|
||||
self.by_symbol
|
||||
.insert(Ref::new(symbol.data()), Ref::new(symbol));
|
||||
self.by_id.push(Ref::new(symbol));
|
||||
Insertion::New(symbol)
|
||||
}
|
||||
|
||||
fn get_symbol<'s>(&'s self, id: &D) -> Option<&'s Symbol<T, D>> {
|
||||
self.by_id.get(id.as_usize()).map(|x| unsafe { x.deref() })
|
||||
}
|
||||
}
|
@ -45,9 +45,9 @@ mod test_crane {
|
||||
let mut func_ctx = FunctionBuilderContext::new();
|
||||
|
||||
let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src);
|
||||
let mut subs = Subs::new(var_store.into());
|
||||
let subs = Subs::new(var_store.into());
|
||||
let mut unify_problems = Vec::new();
|
||||
let content = infer_expr(&mut subs, &mut unify_problems, &constraint, variable);
|
||||
let (content, solved) = infer_expr(subs, &mut unify_problems, &constraint, variable);
|
||||
let shared_builder = settings::builder();
|
||||
let shared_flags = settings::Flags::new(shared_builder);
|
||||
let cfg = match isa::lookup(HOST) {
|
||||
@ -67,6 +67,7 @@ mod test_crane {
|
||||
let main_fn_name = "$Test.main";
|
||||
|
||||
// Compute main_fn_ret_type before moving subs to Env
|
||||
let mut subs = solved.into_inner();
|
||||
let main_ret_type = type_from_content(&content, &mut subs, cfg);
|
||||
|
||||
// Compile and add all the Procs before adding main
|
||||
@ -169,9 +170,9 @@ mod test_crane {
|
||||
($src:expr, $expected:expr, $ty:ty) => {
|
||||
let arena = Bump::new();
|
||||
let (expr, _output, _problems, var_store, variable, constraint) = can_expr($src);
|
||||
let mut subs = Subs::new(var_store.into());
|
||||
let subs = Subs::new(var_store.into());
|
||||
let mut unify_problems = Vec::new();
|
||||
let content = infer_expr(&mut subs, &mut unify_problems, &constraint, variable);
|
||||
let (content, solved) = infer_expr(subs, &mut unify_problems, &constraint, variable);
|
||||
|
||||
let context = Context::create();
|
||||
let module = context.create_module("app");
|
||||
@ -195,6 +196,7 @@ mod test_crane {
|
||||
fpm.initialize();
|
||||
|
||||
// Compute main_fn_type before moving subs to Env
|
||||
let mut subs = solved.into_inner();
|
||||
let main_fn_type = content_to_basic_type(&content, &mut subs, &context)
|
||||
.expect("Unable to infer type for test expr")
|
||||
.fn_type(&[], false);
|
||||
|
@ -28,7 +28,8 @@ mod test_infer {
|
||||
}
|
||||
|
||||
let mut unify_problems = Vec::new();
|
||||
let content = infer_expr(&mut subs, &mut unify_problems, &constraint, variable);
|
||||
let (content, solved) = infer_expr(subs, &mut unify_problems, &constraint, variable);
|
||||
let mut subs = solved.into_inner();
|
||||
|
||||
name_all_type_vars(variable, &mut subs);
|
||||
|
||||
@ -1066,8 +1067,8 @@ mod test_infer {
|
||||
fn two_tag_pattern() {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"\x ->
|
||||
when x is
|
||||
r#"\x ->
|
||||
when x is
|
||||
True -> 1
|
||||
False -> 0
|
||||
"#
|
||||
@ -1104,8 +1105,8 @@ mod test_infer {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
f = \x ->
|
||||
when x is
|
||||
f = \x ->
|
||||
when x is
|
||||
{ a, b } -> a
|
||||
|
||||
f
|
||||
|
@ -17,12 +17,17 @@ mod test_load {
|
||||
use roc::can::def::Declaration::*;
|
||||
use roc::can::ident::ModuleName;
|
||||
use roc::can::module::Module;
|
||||
use roc::collections::SendMap;
|
||||
use roc::collections::{MutMap, SendMap};
|
||||
use roc::load::{load, LoadedModule};
|
||||
use roc::module::ModuleId;
|
||||
use roc::pretty_print_types::{content_to_string, name_all_type_vars};
|
||||
use roc::subs::{Subs, Variable};
|
||||
use roc::solve::ModuleSubs;
|
||||
use roc::subs::Subs;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// TODO change solve::SubsByModule to be this
|
||||
type SubsByModule = MutMap<ModuleId, ModuleSubs>;
|
||||
|
||||
// HELPERS
|
||||
|
||||
fn test_async<F: std::future::Future>(future: F) -> F::Output {
|
||||
@ -35,55 +40,52 @@ mod test_load {
|
||||
rt.block_on(future)
|
||||
}
|
||||
|
||||
fn expect_module(loaded_module: LoadedModule) -> (Module, Subs) {
|
||||
match loaded_module {
|
||||
LoadedModule::Valid(module, subs) => (module, subs),
|
||||
LoadedModule::FileProblem { filename, error } => panic!(
|
||||
"{:?} failed to load with FileProblem: {:?}",
|
||||
filename, error
|
||||
),
|
||||
LoadedModule::ParsingFailed { filename, fail } => panic!(
|
||||
"{:?} failed to load with ParsingFailed: {:?}",
|
||||
filename, fail
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_builtins(deps: &mut Vec<LoadedModule>) -> LoadedModule {
|
||||
async fn load_builtins(subs_by_module: &mut SubsByModule) -> LoadedModule {
|
||||
let src_dir = builtins_dir();
|
||||
let filename = src_dir.join("Defaults.roc");
|
||||
load(src_dir, filename, deps).await
|
||||
|
||||
load(src_dir, filename, subs_by_module)
|
||||
.await
|
||||
.expect("Failed to load builtins from Defaults.roc")
|
||||
}
|
||||
|
||||
async fn load_without_builtins(
|
||||
dir_name: &str,
|
||||
module_name: &str,
|
||||
deps: &mut Vec<LoadedModule>,
|
||||
) -> (Module, Subs) {
|
||||
subs_by_module: &mut SubsByModule,
|
||||
) -> LoadedModule {
|
||||
let src_dir = fixtures_dir().join(dir_name);
|
||||
let filename = src_dir.join(format!("{}.roc", module_name));
|
||||
let loaded = load(src_dir, filename, deps).await;
|
||||
let (module, subs) = expect_module(loaded);
|
||||
let loaded = load(src_dir, filename, subs_by_module).await;
|
||||
let loaded_module = loaded.expect("Test module failed to load");
|
||||
let expected_name = loaded_module
|
||||
.module_ids
|
||||
.get_name(loaded_module.module_id)
|
||||
.expect("Test ModuleID not found in module_ids");
|
||||
|
||||
assert_eq!(module.name, module_name.into());
|
||||
assert_eq!(expected_name, &ModuleName::from(module_name));
|
||||
|
||||
(module, subs)
|
||||
loaded_module
|
||||
}
|
||||
|
||||
async fn load_with_builtins(
|
||||
dir_name: &str,
|
||||
module_name: &str,
|
||||
deps: &mut Vec<LoadedModule>,
|
||||
) -> (Module, Subs) {
|
||||
let next_var = load_builtins(deps).await;
|
||||
subs_by_module: &mut SubsByModule,
|
||||
) -> LoadedModule {
|
||||
let next_var = load_builtins(subs_by_module).await;
|
||||
let src_dir = fixtures_dir().join(dir_name);
|
||||
let filename = src_dir.join(format!("{}.roc", module_name));
|
||||
let loaded = load(src_dir, filename, deps).await;
|
||||
let (module, subs) = expect_module(loaded);
|
||||
let loaded = load(src_dir, filename, subs_by_module).await;
|
||||
let loaded_module = loaded.expect("Test module failed to load");
|
||||
let expected_name = loaded_module
|
||||
.module_ids
|
||||
.get_name(loaded_module.module_id)
|
||||
.expect("Test ModuleID not found in module_ids");
|
||||
|
||||
assert_eq!(module.name, module_name.into());
|
||||
assert_eq!(expected_name, &ModuleName::from(module_name));
|
||||
|
||||
(module, subs)
|
||||
loaded_module
|
||||
}
|
||||
|
||||
// fn expect_types(
|
||||
@ -131,30 +133,27 @@ mod test_load {
|
||||
|
||||
#[test]
|
||||
fn interface_with_deps() {
|
||||
let mut deps = Vec::new();
|
||||
let mut subs_by_module = MutMap::default();
|
||||
let src_dir = fixtures_dir().join("interface_with_deps");
|
||||
let filename = src_dir.join("Primary.roc");
|
||||
|
||||
test_async(async {
|
||||
let (module, subs) = expect_module(load(src_dir, filename, &mut deps).await);
|
||||
let loaded = load(src_dir, filename, &mut subs_by_module).await;
|
||||
let loaded_module = loaded.expect("Test module failed to load");
|
||||
|
||||
let def_count: usize = module
|
||||
let def_count: usize = loaded_module
|
||||
.declarations
|
||||
.iter()
|
||||
.map(|decl| decl.def_count())
|
||||
.sum();
|
||||
assert_eq!(module.name, "Primary".into());
|
||||
|
||||
let expected_name = loaded_module
|
||||
.module_ids
|
||||
.get_name(loaded_module.module_id)
|
||||
.expect("Test ModuleID not found in module_ids");
|
||||
|
||||
assert_eq!(expected_name, &ModuleName::from("Primary"));
|
||||
assert_eq!(def_count, 6);
|
||||
|
||||
let module_names: Vec<ModuleName> = deps
|
||||
.into_iter()
|
||||
.map(|dep| dep.into_module().unwrap().name)
|
||||
.collect();
|
||||
|
||||
assert_eq!(
|
||||
module_names,
|
||||
vec!["Dep1".into(), "Dep3.Blah".into(), "Dep2".into()]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -165,7 +164,8 @@ mod test_load {
|
||||
// let filename = src_dir.join("Defaults.roc");
|
||||
|
||||
// test_async(async {
|
||||
// let (module, subs) = expect_module(load(src_dir, filename, &mut deps).await);
|
||||
// let loaded = load(src_dir, filename, deps).await;
|
||||
// let LoadedModule { module_id, module_ids, solved, problems } = loaded.expect("Test module failed to load");
|
||||
|
||||
// let def_count: usize = module
|
||||
// .declarations
|
||||
|
@ -17,12 +17,13 @@ mod test_infer_uniq {
|
||||
// HELPERS
|
||||
|
||||
fn infer_eq_help(src: &str) -> (Vec<roc::types::Problem>, String) {
|
||||
let (_output, _problems, mut subs, variable, constraint) = uniq_expr(src);
|
||||
let (_output, _problems, subs, variable, constraint) = uniq_expr(src);
|
||||
|
||||
assert_correct_variable_usage(&constraint);
|
||||
|
||||
let mut unify_problems = Vec::new();
|
||||
let content = infer_expr(&mut subs, &mut unify_problems, &constraint, variable);
|
||||
let (content, solved) = infer_expr(subs, &mut unify_problems, &constraint, variable);
|
||||
let mut subs = solved.into_inner();
|
||||
|
||||
name_all_type_vars(variable, &mut subs);
|
||||
|
||||
@ -953,8 +954,8 @@ mod test_infer_uniq {
|
||||
fn two_tag_pattern() {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"\x ->
|
||||
when x is
|
||||
r#"\x ->
|
||||
when x is
|
||||
True -> 1
|
||||
False -> 0
|
||||
"#
|
||||
@ -985,6 +986,18 @@ mod test_infer_uniq {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_field_access() {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\rec -> rec.left
|
||||
"#
|
||||
),
|
||||
"Attr.Attr * (Attr.Attr a { left : (Attr.Attr a b) }* -> Attr.Attr a b)",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn record_field_accessor_function() {
|
||||
infer_eq(
|
||||
@ -1002,7 +1015,7 @@ mod test_infer_uniq {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\rec -> rec.left
|
||||
\rec -> rec.left
|
||||
"#
|
||||
),
|
||||
"Attr.Attr * (Attr.Attr (* | a) { left : (Attr.Attr a b) }* -> Attr.Attr a b)",
|
||||
@ -1042,7 +1055,7 @@ mod test_infer_uniq {
|
||||
\Foo x -> Foo x
|
||||
"#
|
||||
),
|
||||
// NOTE: Foo loses the relation to the uniqueness attribute `a`
|
||||
// NOTE: Foo loses the relation to the uniqueness attribute `a`
|
||||
// That is fine. Whenever we try to extract from it, the relation will be enforced
|
||||
"Attr.Attr * (Attr.Attr a [ Foo (Attr.Attr a b) ]* -> Attr.Attr * [ Foo (Attr.Attr a b) ]*)",
|
||||
);
|
||||
@ -1094,7 +1107,7 @@ mod test_infer_uniq {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Int
|
||||
x : Int
|
||||
x = 4
|
||||
|
||||
x
|
||||
@ -1121,7 +1134,7 @@ mod test_infer_uniq {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\{ x } -> x
|
||||
\{ x } -> x
|
||||
"#
|
||||
),
|
||||
"Attr.Attr * (Attr.Attr a { x : (Attr.Attr a b) }* -> Attr.Attr a b)",
|
||||
@ -1148,8 +1161,8 @@ mod test_infer_uniq {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\r ->
|
||||
x = r.x
|
||||
\r ->
|
||||
x = r.x
|
||||
|
||||
x
|
||||
"#
|
||||
@ -1163,8 +1176,8 @@ mod test_infer_uniq {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\r ->
|
||||
x = r.x
|
||||
\r ->
|
||||
x = r.x
|
||||
|
||||
x
|
||||
"#
|
||||
@ -1195,7 +1208,7 @@ mod test_infer_uniq {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\r -> { r & x: r.x, y: r.y }
|
||||
\r -> { r & x: r.x, y: r.y }
|
||||
"#
|
||||
),
|
||||
"Attr.Attr * (Attr.Attr Attr.Shared { x : (Attr.Attr Attr.Shared a), y : (Attr.Attr Attr.Shared b) }c -> Attr.Attr Attr.Shared { x : (Attr.Attr Attr.Shared a), y : (Attr.Attr Attr.Shared b) }c)" ,
|
||||
@ -1207,7 +1220,7 @@ mod test_infer_uniq {
|
||||
infer_eq(
|
||||
indoc!(
|
||||
r#"
|
||||
\r -> { r & x: r.x, y: r.x }
|
||||
\r -> { r & x: r.x, y: r.x }
|
||||
"#
|
||||
),
|
||||
"Attr.Attr * (Attr.Attr Attr.Shared { x : (Attr.Attr Attr.Shared a), y : (Attr.Attr Attr.Shared a) }b -> Attr.Attr Attr.Shared { x : (Attr.Attr Attr.Shared a), y : (Attr.Attr Attr.Shared a) }b)"
|
||||
|
Loading…
Reference in New Issue
Block a user