diff --git a/src/can/ident.rs b/src/can/ident.rs new file mode 100644 index 0000000000..a3b45eb256 --- /dev/null +++ b/src/can/ident.rs @@ -0,0 +1,51 @@ +use crate::ident::UnqualifiedIdent; +use std::fmt; + +#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct ModuleName(Box); + +/// An uncapitalized identifier, such as a field name or local variable +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Lowercase(Box); + +/// A capitalized identifier, such as a tag name or module name +#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct Uppercase(Box); + +impl Lowercase { + pub fn from_unqualified_ident(ident: &UnqualifiedIdent<'_>) -> Self { + Self(ident.as_str().into()) + } +} + +impl Into> for Lowercase { + fn into(self) -> Box { + self.0 + } +} + +/// Rather than displaying as this: +/// +/// Lowercase("foo") +/// +/// ...instead display as this: +/// +/// 'foo' +impl fmt::Debug for Lowercase { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "'{}'", self.0) + } +} + +/// Rather than displaying as this: +/// +/// Uppercase("Foo") +/// +/// ...instead display as this: +/// +/// 'Foo' +impl fmt::Debug for Uppercase { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "'{}'", self.0) + } +} diff --git a/src/solve.rs b/src/solve.rs index 17ab62db6f..ed3460c86b 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -3,6 +3,7 @@ use crate::collections::ImMap; use crate::subs::{Content, Descriptor, FlatType, Subs, Variable}; use crate::types::Constraint::{self, *}; use crate::types::Type::{self, *}; +use crate::unify::unify; type Env = ImMap; @@ -11,10 +12,10 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) { True => (), Eq(typ, expected_type, _region) => { // TODO use region? - let actual = type_to_variable(subs, typ.clone()); - let expected = type_to_variable(subs, expected_type.clone().get_type()); + let actual = type_to_var(subs, typ.clone()); + let expected = type_to_var(subs, expected_type.clone().get_type()); - subs.union(actual, expected); + unify(subs, actual, expected); } Lookup(symbol, expected_type, _region) => { // TODO use region? @@ -22,9 +23,9 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) { subs.copy_var(*env.get(&symbol).unwrap_or_else(|| { panic!("Could not find symbol {:?} in env {:?}", symbol, env) })); - let expected = type_to_variable(subs, expected_type.clone().get_type()); + let expected = type_to_var(subs, expected_type.clone().get_type()); - subs.union(actual, expected); + unify(subs, actual, expected); } And(sub_constraints) => { for sub_constraint in sub_constraints.iter() { @@ -33,10 +34,10 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) { } Pattern(_region, _category, typ, expected) => { // TODO use region? - let actual = type_to_variable(subs, typ.clone()); - let expected = type_to_variable(subs, expected.clone().get_type()); + let actual = type_to_var(subs, typ.clone()); + let expected = type_to_var(subs, expected.clone().get_type()); - subs.union(actual, expected); + unify(subs, actual, expected); } Let(let_con) => { match &let_con.ret_constraint { @@ -58,7 +59,7 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) { // inserted earlier in solving. (If we allowed // shadowing, we'd need to do something fancier here.) if !new_env.contains_key(&symbol) { - let var = type_to_variable(subs, loc_type.value.clone()); + let var = type_to_var(subs, loc_type.value.clone()); new_env.insert(symbol.clone(), var); } @@ -75,7 +76,11 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) { } } -fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable { +fn type_to_var(subs: &mut Subs, typ: Type) -> Variable { + type_to_variable(subs, &ImMap::default(), typ) +} + +fn type_to_variable(subs: &mut Subs, aliases: &ImMap, Variable>, typ: Type) -> Variable { match typ { Variable(var) => var, Apply { @@ -86,7 +91,7 @@ fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable { let mut arg_vars = Vec::with_capacity(args.len()); for arg in args { - arg_vars.push(type_to_variable(subs, arg.clone())) + arg_vars.push(type_to_variable(subs, aliases, arg.clone())) } let flat_type = FlatType::Apply { @@ -107,11 +112,11 @@ fn type_to_variable(subs: &mut Subs, typ: Type) -> Variable { let mut arg_vars = Vec::with_capacity(args.len()); for arg in args { - arg_vars.push(type_to_variable(subs, arg.clone())) + arg_vars.push(type_to_variable(subs, aliases, arg.clone())) } - let ret_var = type_to_variable(subs, *ret_type); - let content: Content = Content::Structure(FlatType::Func(arg_vars, ret_var)); + let ret_var = type_to_variable(subs, aliases, *ret_type); + let content = Content::Structure(FlatType::Func(arg_vars, ret_var)); subs.fresh(Descriptor::from(content)) } diff --git a/src/subs.rs b/src/subs.rs index 638dd9a9be..8fcf868002 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -1,9 +1,47 @@ use crate::ena::unify::{InPlace, UnificationTable, UnifyKey}; use crate::types::Problem; -use crate::unify; use std::fmt; use std::sync::atomic::{AtomicUsize, Ordering}; +#[derive(Clone, Copy, Hash, PartialEq, Eq)] +pub struct Mark(u8); + +impl Mark { + #[inline(always)] + pub fn none() -> Mark { + Mark(0) + } + + #[inline(always)] + pub fn occurs() -> Mark { + Mark(1) + } + + #[inline(always)] + pub fn get_var_names() -> Mark { + Mark(2) + } + + #[inline(always)] + pub fn next(self) -> Mark { + Mark(self.0 - 1) + } +} + +impl fmt::Debug for Mark { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self == &Mark::none() { + write!(f, "Mark::none") + } else if self == &Mark::occurs() { + write!(f, "Mark::occurs") + } else if self == &Mark::get_var_names() { + write!(f, "Mark::get_var_names") + } else { + write!(f, "Mark({})", self.0) + } + } +} + #[derive(Debug, Default)] pub struct Subs { utable: UnificationTable>, @@ -91,15 +129,11 @@ impl Subs { } /// Unions two keys without the possibility of failure. - pub fn union(&mut self, left: Variable, right: Variable) { + pub fn union(&mut self, left: Variable, right: Variable, desc: Descriptor) { let l_root = self.utable.get_root_key(left); let r_root = self.utable.get_root_key(right); - if l_root != r_root { - let combined = unify::unify_vars(self, l_root, r_root); - - self.utable.unify_roots(l_root, r_root, combined) - } + self.utable.unify_roots(l_root, r_root, desc) } pub fn get(&mut self, key: Variable) -> Descriptor { @@ -112,9 +146,8 @@ impl Subs { pub fn set(&mut self, key: Variable, r_value: Descriptor) { let l_key = self.utable.get_root_key(key); - let unified = unify::unify_var_val(self, l_key, &r_value); - self.utable.update_value(l_key, |node| node.value = unified); + self.utable.update_value(l_key, |node| node.value = r_value); } pub fn copy_var(&mut self, var: Variable) -> Variable { @@ -123,20 +156,9 @@ impl Subs { var } - // pub fn set_rank(&mut self, key: Variable, rank: usize) { - // let mut descriptor = self.utable.probe_value(key); - - // descriptor.rank = rank; - - // let result = self.utable.unify_var_value(key, descriptor); - - // // Updating the rank should never fail! - // debug_assert_eq!(result, Ok(())); - // } - - // pub fn equivalent(&mut self, left: Variable, right: Variable) -> bool { - // self.utable.unioned(left, right) - // } + pub fn equivalent(&mut self, left: Variable, right: Variable) -> bool { + self.utable.unioned(left, right) + } } #[inline(always)] @@ -152,17 +174,23 @@ fn unnamed_flex_var() -> Content { #[derive(Clone, Debug, PartialEq, Eq)] pub struct Descriptor { pub content: Content, - pub rank: usize, - pub mark: u32, + pub rank: u8, + pub mark: Mark, pub copy: Option, } +impl Default for Descriptor { + fn default() -> Self { + unnamed_flex_var().into() + } +} + impl From for Descriptor { fn from(content: Content) -> Descriptor { Descriptor { content, rank: 0, - mark: 2, // no mark + mark: Mark::none(), copy: None, } } diff --git a/src/unify.rs b/src/unify.rs index f5a4de454c..1281665b68 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -1,61 +1,68 @@ use crate::subs::Content::{self, *}; -use crate::subs::{Descriptor, FlatType, Subs, Variable}; +use crate::subs::{Descriptor, FlatType, Mark, Subs, Variable}; use crate::types::Problem; -#[inline(always)] -pub fn unify_vars(subs: &mut Subs, left_key: Variable, right_key: Variable) -> Descriptor { - let right = subs.get(right_key); - - unify_var_val(subs, left_key, &right) +struct Context { + first: Variable, + first_desc: Descriptor, + second: Variable, + second_desc: Descriptor, } #[inline(always)] -pub fn unify_var_val(subs: &mut Subs, left_key: Variable, right: &Descriptor) -> Descriptor { - let left = subs.get(left_key); +pub fn unify(subs: &mut Subs, var1: Variable, var2: Variable) { + if !subs.equivalent(var1, var2) { + let ctx = Context { + first: var1, + first_desc: subs.get(var1), + second: var2, + second_desc: subs.get(var2), + }; - unify(subs, &left, right) + unify_context(subs, &ctx) + } } -fn unify(subs: &mut Subs, left: &Descriptor, right: &Descriptor) -> Descriptor { - match left.content { - FlexVar(ref opt_name) => unify_flex(opt_name, &right.content), - RigidVar(ref name) => unify_rigid(name, &right.content), - Structure(ref flat_type) => unify_structure(subs, flat_type, &right.content), +fn unify_context(subs: &mut Subs, ctx: &Context) { + match ctx.first_desc.content { + FlexVar(ref opt_name) => unify_flex(subs, ctx, opt_name, &ctx.second_desc.content), + RigidVar(ref name) => unify_rigid(subs, ctx, name, &ctx.second_desc.content), + Structure(ref flat_type) => unify_structure(subs, ctx, flat_type, &ctx.second_desc.content), Error(ref problem) => { // Error propagates. Whatever we're comparing it to doesn't matter! - from_content(Error(problem.clone())) + merge(subs, ctx, Error(problem.clone())) } } } #[inline(always)] -fn unify_structure(subs: &mut Subs, flat_type: &FlatType, other: &Content) -> Descriptor { +fn unify_structure(subs: &mut Subs, ctx: &Context, flat_type: &FlatType, other: &Content) { match other { FlexVar(_) => { // If the other is flex, Structure wins! - from_content(Structure(flat_type.clone())) + merge(subs, ctx, Structure(flat_type.clone())) } RigidVar(_) => { // Type mismatch! Rigid can only unify with flex. - from_content(Error(Problem::GenericMismatch)) + merge(subs, ctx, Error(Problem::GenericMismatch)) } Structure(ref other_flat_type) => { // Unify the two flat types - unify_flat_type(subs, flat_type, other_flat_type) + unify_flat_type(subs, ctx, flat_type, other_flat_type) } Error(problem) => { // Error propagates. - from_content(Error(problem.clone())) + merge(subs, ctx, Error(problem.clone())) } } } #[inline(always)] -fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descriptor { +fn unify_flat_type(subs: &mut Subs, ctx: &Context, left: &FlatType, right: &FlatType) { use crate::subs::FlatType::*; match (left, right) { - (EmptyRecord, EmptyRecord) => from_content(Structure(left.clone())), + (EmptyRecord, EmptyRecord) => merge(subs, ctx, Structure(left.clone())), ( Apply { module_name: l_module_name, @@ -68,103 +75,85 @@ fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descri args: r_args, }, ) if l_module_name == r_module_name && l_type_name == r_type_name => { - let args = unify_args(subs, l_args.iter(), r_args.iter()); - let flat_type = Apply { - module_name: l_module_name.clone(), - name: l_type_name.clone(), - args, - }; + unify_zip(subs, l_args.iter(), r_args.iter()); - from_content(Structure(flat_type)) + merge( + subs, + ctx, + Structure(Apply { + module_name: (*r_module_name).clone(), + name: (*r_type_name).clone(), + args: (*r_args).clone(), + }), + ) } (Func(l_args, l_ret), Func(r_args, r_ret)) => { if l_args.len() == r_args.len() { - let args = unify_args(subs, l_args.iter(), r_args.iter()); - let ret = union_vars(subs, *l_ret, *r_ret); - let flat_type = Func(args, ret); + unify_zip(subs, l_args.iter(), r_args.iter()); + unify(subs, *l_ret, *r_ret); - from_content(Structure(flat_type)) + merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret))) } else if l_args.len() > r_args.len() { - from_content(Error(Problem::ExtraArguments)) + merge(subs, ctx, Error(Problem::ExtraArguments)) } else { - from_content(Error(Problem::MissingArguments)) + merge(subs, ctx, Error(Problem::MissingArguments)) } } - _ => from_content(Error(Problem::GenericMismatch)), + _ => merge(subs, ctx, Error(Problem::GenericMismatch)), } } -fn unify_args<'a, I>(subs: &mut Subs, left_iter: I, right_iter: I) -> Vec +fn unify_zip<'a, I>(subs: &mut Subs, left_iter: I, right_iter: I) where I: Iterator, { - left_iter - .zip(right_iter) - .map(|(&l_var, &r_var)| { - // Look up the descriptors we have for these variables, and unify them. - let descriptor = unify_vars(subs, l_var, r_var); - - // set r_var to be the unioned value, then union l_var to r_var - subs.set(r_var, descriptor); - subs.union(l_var, r_var); - - r_var - }) - .collect() -} - -fn union_vars(subs: &mut Subs, l_var: Variable, r_var: Variable) -> Variable { - // Look up the descriptors we have for these variables, and unify them. - let descriptor = unify_vars(subs, l_var, r_var); - - // set r_var to be the unioned value, then union l_var to r_var - subs.set(r_var, descriptor); - subs.union(l_var, r_var); - - r_var + for (&l_var, &r_var) in left_iter.zip(right_iter) { + unify(subs, l_var, r_var); + } } #[inline(always)] -fn unify_rigid(name: &str, other: &Content) -> Descriptor { +fn unify_rigid(subs: &mut Subs, ctx: &Context, name: &str, other: &Content) { match other { FlexVar(_) => { // If the other is flex, rigid wins! - from_content(RigidVar(name.into())) + merge(subs, ctx, RigidVar(name.into())) } RigidVar(_) | Structure(_) => { // Type mismatch! Rigid can only unify with flex, even if the // rigid names are the same. - from_content(Error(Problem::GenericMismatch)) + merge(subs, ctx, Error(Problem::GenericMismatch)) } Error(problem) => { // Error propagates. - from_content(Error(problem.clone())) + merge(subs, ctx, Error(problem.clone())) } } } #[inline(always)] -fn unify_flex(opt_name: &Option>, other: &Content) -> Descriptor { +fn unify_flex(subs: &mut Subs, ctx: &Context, opt_name: &Option>, other: &Content) { match other { FlexVar(None) => { // If both are flex, and only left has a name, keep the name around. - from_content(FlexVar(opt_name.clone())) + merge(subs, ctx, FlexVar(opt_name.clone())) } FlexVar(Some(_)) | RigidVar(_) | Structure(_) | Error(_) => { // In all other cases, if left is flex, defer to right. // (This includes using right's name if both are flex and named.) - from_content(other.clone()) + merge(subs, ctx, other.clone()) } } } -/// TODO this was f/k/a merge() - got rid of the rank stuff...good idea? Bad? -/// TODO it used to be { rank: std::cmp::min(left_rank, right_rank), ... } -fn from_content(content: Content) -> Descriptor { - Descriptor { +fn merge(subs: &mut Subs, ctx: &Context, content: Content) { + let rank = ctx.first_desc.rank.min(ctx.second_desc.rank); + let desc = Descriptor { content, - rank: 0, - mark: 2, // no mark + rank, + mark: Mark::none(), copy: None, - } + }; + + subs.union(ctx.first, ctx.second, desc); }