Got tests compiling

This commit is contained in:
Richard Feldman 2020-01-17 21:39:08 -05:00
parent 534d64abda
commit 99f99380da
14 changed files with 588 additions and 193 deletions

7
Cargo.lock generated
View File

@ -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"

View File

@ -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!

View File

@ -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

View File

@ -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,

View File

@ -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)
}

View File

@ -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;

View File

@ -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,

View File

@ -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)
}
}

View File

@ -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
View 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() })
}
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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)"