diff --git a/src/can/mod.rs b/src/can/mod.rs index 4a4dd2266e..40f870643b 100644 --- a/src/can/mod.rs +++ b/src/can/mod.rs @@ -4,6 +4,7 @@ use self::num::{ finish_parsing_bin, finish_parsing_float, finish_parsing_hex, finish_parsing_int, finish_parsing_oct, float_expr_from_result, int_expr_from_result, }; +use self::pattern::PatternState; use self::pattern::PatternType::*; use self::pattern::{canonicalize_pattern, Pattern}; use self::problem::Problem; @@ -235,7 +236,9 @@ fn canonicalize_expr( let fn_region = loc_fn.region; let fn_expected = NoExpectation(fn_type.clone()); // TODO look up the name and use NamedFnArg if possible. - let fn_reason = Reason::AnonymousFnCall(loc_args.len() as u8); + let fn_reason = Reason::AnonymousFnCall { + arity: loc_args.len() as u8, + }; // Canonicalize the function expression and its arguments let (fn_expr, mut output) = canonicalize_expr( @@ -269,7 +272,9 @@ fn canonicalize_expr( let arg_var = subs.mk_flex_var(); let arg_type = Variable(arg_var); // TODO look up the name and use NamedFnArg if possible. - let reason = Reason::AnonymousFnArg(index as u8); + let reason = Reason::AnonymousFnArg { + arg_index: index as u8, + }; let expected_arg = ForReason(reason, arg_type.clone(), region); let (arg_expr, arg_out) = canonicalize_expr( rigids, @@ -471,12 +476,13 @@ fn canonicalize_expr( scope.idents = union_pairs(scope.idents, arg_idents.iter()); let mut state = PatternState { - assignment_types: ImMap::default(), + headers: ImMap::default(), vars: Vec::with_capacity(loc_arg_patterns.len()), constraints: Vec::with_capacity(1), }; - let args = constrain_args(loc_arg_patterns.iter(), &scope, subs, &mut state); let mut can_args: Vec> = Vec::with_capacity(loc_arg_patterns.len()); + let mut vars = Vec::with_capacity(state.vars.capacity()); + let mut pattern_types = Vec::with_capacity(state.vars.capacity()); for loc_pattern in loc_arg_patterns.into_iter() { // Exclude the current ident from shadowable_idents; you can't shadow yourself! @@ -485,10 +491,14 @@ fn canonicalize_expr( remove_idents(&loc_pattern.value, &mut shadowable_idents); let pattern_var = subs.mk_flex_var(); - let pattern_expected = PExpected::NoExpectation(Type::Variable(pattern_var)); + let pattern_type = Type::Variable(pattern_var); + let pattern_expected = PExpected::NoExpectation(pattern_type.clone()); - let (can_arg, _state) = canonicalize_pattern( + pattern_types.push(pattern_type); + + let can_arg = canonicalize_pattern( env, + &mut state, subs, &mut scope, FunctionArg, @@ -498,10 +508,19 @@ fn canonicalize_expr( pattern_expected, ); + vars.push(pattern_var); + can_args.push(can_arg); } - let body_type = NoExpectation(args.ret_type); + let ret_var = subs.mk_flex_var(); + let ret_type = Type::Variable(ret_var); + + state.vars.push(ret_var); + + let fn_typ = Type::Function(pattern_types, Box::new(ret_type.clone())); + + let body_type = NoExpectation(ret_type); let (loc_body_expr, mut output) = canonicalize_expr( rigids, env, @@ -523,17 +542,17 @@ fn canonicalize_expr( let typ = Variable(var); output.constraint = exists( - args.vars, + state.vars.clone(), And(vec![ Let(Box::new(LetConstraint { rigid_vars: Vec::new(), flex_vars: state.vars, - assignment_types: state.assignment_types, + assignment_types: state.headers, assignments_constraint, ret_constraint, })), // "the closure's type is equal to the var we've stored for later use in the proc" - Eq(args.typ, NoExpectation(typ.clone()), region), + Eq(fn_typ, NoExpectation(typ.clone()), region), // "the var we've stored for later is equal to the overall expected type" Eq(typ, expected, region), ]), @@ -566,7 +585,7 @@ fn canonicalize_expr( region, output.references.clone(), var, - args.ret_var, + ret_var, ); // Always return a function pointer, in case that's how the closure is being used (e.g. with Apply). @@ -847,8 +866,15 @@ fn canonicalize_case_branch<'a>( } } - let (loc_can_pattern, state) = canonicalize_pattern( + let mut state = PatternState { + headers: ImMap::default(), + vars: Vec::with_capacity(1), + constraints: Vec::with_capacity(1), + }; + + let loc_can_pattern = canonicalize_pattern( env, + &mut state, subs, &mut scope, CaseBranch, @@ -1258,48 +1284,6 @@ impl Info { /// In elm/compiler this is called RTV - the "Rigid Type Variables" dictionary. // type BoundTypeVars = ImMap, Type>; -struct PatternState { - assignment_types: ImMap>, - vars: Vec, - constraints: Vec, -} - -impl PatternState { - pub fn add_pattern( - &mut self, - scope: &Scope, - loc_pattern: Located, - expected: Expected, - ) { - let region = loc_pattern.region; - - match loc_pattern.value { - ast::Pattern::Identifier(name) => { - let symbol = scope.symbol(&name); - - self.add_to_assignment_types(region, symbol, expected) - } - ast::Pattern::Underscore => (), - _ => panic!("TODO other patterns"), - } - } - - fn add_to_assignment_types( - &mut self, - region: Region, - symbol: Symbol, - expected: Expected, - ) { - self.assignment_types.insert( - symbol, - Located { - region, - value: expected.get_type(), - }, - ); - } -} - fn add_pattern_to_lookup_types<'a>( scope: &Scope, loc_pattern: Located>, @@ -1367,6 +1351,10 @@ fn can_defs<'a>( let iter = defs.iter(); for loc_def in iter { + // Make types for the body expr, even if we won't end up having a body. + let expr_var = subs.mk_flex_var(); + let expr_type = Type::Variable(expr_var); + // Each assignment gets to have all the idents in scope that are assigned in this // block. Order of assignments doesn't matter, thanks to referential transparency! let (opt_loc_pattern, (loc_can_expr, can_output)) = match loc_def.value { @@ -1400,37 +1388,6 @@ fn can_defs<'a>( (None, (loc_expr, Output::new(True))) } Def::Body(ref loc_pattern, loc_expr) => { - // Make types for the pattern and the body expr. - let expr_var = subs.mk_flex_var(); - let expr_type = Type::Variable(expr_var); - let pattern_var = subs.mk_flex_var(); - let pattern_type = Type::Variable(pattern_var); - - flex_info.vars.push(pattern_var); - - let mut state = PatternState { - assignment_types: ImMap::default(), - vars: Vec::with_capacity(1), - constraints: Vec::with_capacity(1), - }; - - state.add_pattern( - &scope, - loc_pattern.clone(), - NoExpectation(pattern_type.clone()), - ); - let def_constraint = And(state.constraints); - - // Any time there's a lookup on this symbol in the outer Let, - // it should result in this expression's type. After all, this - // is the type to which this symbol is assigned! - add_pattern_to_lookup_types( - &scope, - loc_pattern.clone(), - &mut flex_info.assignment_types, - expr_type.clone(), - ); - let (loc_can_expr, output) = canonicalize_expr( rigids, env, @@ -1438,17 +1395,9 @@ fn can_defs<'a>( &mut scope, loc_expr.region, &loc_expr.value, - NoExpectation(expr_type), + NoExpectation(expr_type.clone()), ); - flex_info.constraints.push(Let(Box::new(LetConstraint { - rigid_vars: Vec::new(), - flex_vars: state.vars, - assignment_types: state.assignment_types, - assignments_constraint: def_constraint, - ret_constraint: output.constraint.clone(), - }))); - (Some(loc_pattern), (loc_can_expr, output)) } Def::CustomType(_, _) => { @@ -1470,10 +1419,18 @@ fn can_defs<'a>( remove_idents(&loc_pattern.value, &mut shadowable_idents); let pattern_var = subs.mk_flex_var(); - let pattern_expected = PExpected::NoExpectation(Type::Variable(pattern_var)); + let pattern_type = Type::Variable(pattern_var); + let pattern_expected = PExpected::NoExpectation(pattern_type); - let (loc_can_pattern, _state) = canonicalize_pattern( + let mut state = PatternState { + headers: ImMap::default(), + vars: Vec::with_capacity(1), + constraints: Vec::with_capacity(1), + }; + + let loc_can_pattern = canonicalize_pattern( env, + &mut state, subs, &mut scope, Assignment, @@ -1482,6 +1439,28 @@ fn can_defs<'a>( &mut shadowable_idents, pattern_expected, ); + + flex_info.vars.push(pattern_var); + + // Any time there's a lookup on this symbol in the outer Let, + // it should result in this expression's type. After all, this + // is the type to which this symbol is assigned! + add_pattern_to_lookup_types( + &scope, + // TODO can we we avoid this clone? + loc_pattern.clone(), + &mut flex_info.assignment_types, + expr_type.clone(), + ); + + flex_info.constraints.push(Let(Box::new(LetConstraint { + rigid_vars: Vec::new(), + flex_vars: state.vars, + assignment_types: state.headers, + assignments_constraint: And(state.constraints), + ret_constraint: can_output.constraint.clone(), + }))); + let mut renamed_closure_assignment: Option<&Symbol> = None; // Give closures names (and tail-recursive status) where appropriate. @@ -1679,7 +1658,12 @@ fn can_defs<'a>( // As a bonus, the topological sort also reveals any cycles between the assignments, allowing // us to give a CircularAssignment error. let successors = |symbol: &Symbol| -> ImSet { - let (_, references) = refs_by_assignment.get(symbol).unwrap(); + let (_, references) = refs_by_assignment.get(symbol).unwrap_or_else(|| { + panic!( + "Could not find symbol {:?} in refs_by_assignment, which had: {:?}", + symbol, refs_by_assignment + ) + }); local_successors(&references, &env.procedures) }; @@ -1745,60 +1729,3 @@ fn can_defs<'a>( } } } - -struct Args { - pub vars: Vec, - pub typ: Type, - pub ret_type: Type, - pub ret_var: Variable, -} - -fn constrain_args<'a, I>(args: I, scope: &Scope, subs: &mut Subs, state: &mut PatternState) -> Args -where - I: Iterator>>, -{ - let (mut vars, arg_types) = patterns_to_variables(args, scope, subs, state); - - let ret_var = subs.mk_flex_var(); - let ret_type = Type::Variable(ret_var); - - vars.push(ret_var); - - let typ = Type::Function(arg_types, Box::new(ret_type.clone())); - - Args { - vars, - typ, - ret_type, - ret_var, - } -} - -fn patterns_to_variables<'a, I>( - patterns: I, - scope: &Scope, - subs: &mut Subs, - state: &mut PatternState, -) -> (Vec, Vec) -where - I: Iterator>>, -{ - let mut vars = Vec::with_capacity(state.vars.capacity()); - let mut pattern_types = Vec::with_capacity(state.vars.capacity()); - - for loc_pattern in patterns { - let pattern_var = subs.mk_flex_var(); - let pattern_type = Type::Variable(pattern_var); - - state.add_pattern( - scope, - loc_pattern.clone(), - NoExpectation(pattern_type.clone()), - ); - - vars.push(pattern_var); - pattern_types.push(pattern_type); - } - - (vars, pattern_types) -} diff --git a/src/can/pattern.rs b/src/can/pattern.rs index 3a6b7a355f..97ba0d0585 100644 --- a/src/can/pattern.rs +++ b/src/can/pattern.rs @@ -47,6 +47,7 @@ pub enum PatternType { pub fn canonicalize_pattern<'a>( env: &'a mut Env, + state: &'a mut PatternState, subs: &mut Subs, scope: &mut Scope, pattern_type: PatternType, @@ -54,7 +55,7 @@ pub fn canonicalize_pattern<'a>( region: Region, shadowable_idents: &'a mut ImMap, expected: PExpected, -) -> (Located, State) { +) -> Located { use self::PatternType::*; use can::ast::Pattern::*; @@ -245,6 +246,7 @@ pub fn canonicalize_pattern<'a>( &SpaceBefore(sub_pattern, _) | SpaceAfter(sub_pattern, _) => { return canonicalize_pattern( env, + state, subs, scope, pattern_type, @@ -257,21 +259,12 @@ pub fn canonicalize_pattern<'a>( _ => panic!("TODO finish restoring can_pattern branch for {:?}", pattern), }; - let mut state = State { - headers: ImMap::default(), - vars: Vec::new(), - constraints: Vec::new(), - }; + add_constraints(&pattern, &scope, region, expected, state); - add_constraints(&pattern, region, expected, &mut state); - - ( - Located { - region, - value: can_pattern, - }, - state, - ) + Located { + region, + value: can_pattern, + } } /// When we detect an unsupported pattern type (e.g. 5 = 1 + 2 is unsupported because you can't @@ -284,7 +277,7 @@ fn unsupported_pattern(env: &mut Env, pattern_type: PatternType, region: Region) // CONSTRAIN -pub struct State { +pub struct PatternState { pub headers: ImMap>, pub vars: Vec, pub constraints: Vec, @@ -292,9 +285,10 @@ pub struct State { fn add_constraints<'a>( pattern: &'a ast::Pattern<'a>, + scope: &'a Scope, region: Region, expected: PExpected, - state: &'a mut State, + state: &'a mut PatternState, ) { use parse::ast::Pattern::*; @@ -304,7 +298,7 @@ fn add_constraints<'a>( } Identifier(name) => { state.headers.insert( - Symbol::new("TODO pass home into add_constraints, or improve Symbol to not need it for idents in patterns", name), + scope.symbol(name), Located { region, value: expected.get_type(), @@ -339,7 +333,7 @@ fn add_constraints<'a>( } SpaceBefore(pattern, _) | SpaceAfter(pattern, _) => { - add_constraints(pattern, region, expected, state) + add_constraints(pattern, scope, region, expected, state) } Variant(_, _) | Apply(_, _) | RecordDestructure(_) | EmptyRecordLiteral => { diff --git a/src/ena/snapshot_vec.rs b/src/ena/snapshot_vec.rs index 67a4de1ed8..e8796a9170 100644 --- a/src/ena/snapshot_vec.rs +++ b/src/ena/snapshot_vec.rs @@ -37,8 +37,13 @@ pub enum UndoLog { Other(D::Undo), } +/// A Vec where we have Debug overridden to render the indices like +/// a hashmap, since we really care about those when debugging one of these. +#[derive(Clone)] +struct BackingVec(Vec); + pub struct SnapshotVec { - values: Vec, + values: BackingVec, undo_log: Vec>, num_open_snapshots: usize, } @@ -78,13 +83,32 @@ pub trait SnapshotVecDelegate { impl Default for SnapshotVec { fn default() -> Self { SnapshotVec { - values: Vec::new(), + values: BackingVec(Vec::new()), undo_log: Vec::new(), num_open_snapshots: 0, } } } +impl fmt::Debug for BackingVec +where + T: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{{{{")?; + + for (index, elem) in self.0.iter().enumerate() { + write!(f, "\n {} => {:?},", index, elem)?; + } + + if !self.0.is_empty() { + write!(f, "\n")?; + } + + write!(f, "}}}}") + } +} + impl SnapshotVec { pub fn new() -> Self { Self::default() @@ -92,7 +116,7 @@ impl SnapshotVec { pub fn with_capacity(c: usize) -> SnapshotVec { SnapshotVec { - values: Vec::with_capacity(c), + values: BackingVec(Vec::with_capacity(c)), undo_log: Vec::new(), num_open_snapshots: 0, } @@ -109,16 +133,16 @@ impl SnapshotVec { } pub fn len(&self) -> usize { - self.values.len() + self.values.0.len() } pub fn is_empty(&self) -> bool { - self.values.len() == 0 + self.values.0.len() == 0 } pub fn push(&mut self, elem: D::Value) -> usize { - let len = self.values.len(); - self.values.push(elem); + let len = self.values.0.len(); + self.values.0.push(elem); if self.in_snapshot() { self.undo_log.push(NewElem(len)); @@ -128,26 +152,26 @@ impl SnapshotVec { } pub fn get(&self, index: usize) -> &D::Value { - &self.values[index] + &self.values.0[index] } /// Reserve space for new values, just like an ordinary vec. pub fn reserve(&mut self, additional: usize) { // This is not affected by snapshots or anything. - self.values.reserve(additional); + self.values.0.reserve(additional); } /// Returns a mutable pointer into the vec; whatever changes you make here cannot be undone /// automatically, so you should be sure call `record()` with some sort of suitable undo /// action. pub fn get_mut(&mut self, index: usize) -> &mut D::Value { - &mut self.values[index] + &mut self.values.0[index] } /// Updates the element at the given index. The old value will saved (and perhaps restored) if /// a snapshot is active. pub fn set(&mut self, index: usize, new_elem: D::Value) { - let old_elem = mem::replace(&mut self.values[index], new_elem); + let old_elem = mem::replace(&mut self.values.0[index], new_elem); if self.in_snapshot() { self.undo_log.push(SetElem(index, old_elem)); } @@ -157,11 +181,11 @@ impl SnapshotVec { /// otherwise equivalent to -- invoking `set` for each element. pub fn set_all(&mut self, mut new_elems: impl FnMut(usize) -> D::Value) { if !self.in_snapshot() { - for (index, slot) in self.values.iter_mut().enumerate() { + for (index, slot) in self.values.0.iter_mut().enumerate() { *slot = new_elems(index); } } else { - for i in 0..self.values.len() { + for i in 0..self.values.0.len() { self.set(i, new_elems(i)); } } @@ -173,16 +197,16 @@ impl SnapshotVec { D::Value: Clone, { if self.in_snapshot() { - let old_elem = self.values[index].clone(); + let old_elem = self.values.0[index].clone(); self.undo_log.push(SetElem(index, old_elem)); } - op(&mut self.values[index]); + op(&mut self.values.0[index]); } pub fn start_snapshot(&mut self) -> Snapshot { self.num_open_snapshots += 1; Snapshot { - value_count: self.values.len(), + value_count: self.values.0.len(), undo_len: self.undo_log.len(), } } @@ -205,16 +229,16 @@ impl SnapshotVec { while self.undo_log.len() > snapshot.undo_len { match self.undo_log.pop().unwrap() { NewElem(i) => { - self.values.pop(); - assert!(self.values.len() == i); + self.values.0.pop(); + assert!(self.values.0.len() == i); } SetElem(i, v) => { - self.values[i] = v; + self.values.0[i] = v; } Other(u) => { - D::reverse(&mut self.values, u); + D::reverse(&mut self.values.0, u); } } } @@ -244,13 +268,13 @@ impl SnapshotVec { impl ops::Deref for SnapshotVec { type Target = [D::Value]; fn deref(&self) -> &[D::Value] { - &*self.values + &*self.values.0 } } impl ops::DerefMut for SnapshotVec { fn deref_mut(&mut self) -> &mut [D::Value] { - &mut *self.values + &mut *self.values.0 } } @@ -272,9 +296,9 @@ impl Extend for SnapshotVec { where T: IntoIterator, { - let initial_len = self.values.len(); - self.values.extend(iterable); - let final_len = self.values.len(); + let initial_len = self.values.0.len(); + self.values.0.extend(iterable); + let final_len = self.values.0.len(); if self.in_snapshot() { self.undo_log.extend((initial_len..final_len).map(NewElem)); diff --git a/src/pretty_print_types.rs b/src/pretty_print_types.rs index d4d79f29a7..17820337aa 100644 --- a/src/pretty_print_types.rs +++ b/src/pretty_print_types.rs @@ -1,30 +1,192 @@ +use collections::{MutMap, MutSet}; use subs::{Content, FlatType, Subs, Variable}; use types; static WILDCARD: &str = "*"; static EMPTY_RECORD: &str = "{}"; +static THE_LETTER_A: u32 = 'a' as u32; + +/// Rerquirements for parentheses. +/// +/// If we're inside a function (that is, this is either an argument or a return +/// value), we may need to use parens. Examples: +/// +/// a -> (* -> a) +/// (* -> a) -> a +/// +/// Separately, if we're inside a type parameter, we may need to use parens: +/// +/// List Int +/// List (List Int) +/// +/// Otherwise, parens are unnecessary. +#[derive(Clone, Copy, Debug, PartialEq)] +enum Parens { + InFn, + InTypeParam, + Unnecessary, +} + +/// How many times a root variable appeared in Subs. +/// +/// We only care about whether it was a single time or multiple times, +/// because single appearances get a wildcard (*) and multiple times +/// get a generated letter ("a" etc). +enum Appearances { + Single, + Multiple, +} + +/// Generate names for all type variables, replacing FlexVar(None) with +/// FlexVar(Some(name)) where appropriate. Example: for the identity +/// function, generate a name of "a" for both its argument and return +/// type variables. +/// +/// It's important that we track insertion order, which is why +/// names_needed is a Vec. We also want to count how many times a root +/// appears, because we should only generate a name for it if it appears +/// more than once. +fn find_names_needed( + variable: Variable, + subs: &mut Subs, + roots: &mut Vec, + root_appearances: &mut MutMap, + names_taken: &mut MutSet, +) { + use subs::Content::*; + use subs::FlatType::*; + + match subs.get(variable).content { + FlexVar(None) => { + let root = subs.get_root_key(variable); + + // If this var is *not* its own root, then the + // root var necessarily appears in multiple places. + // We need a name for it! + match root_appearances.get(&root) { + Some(Appearances::Single) => { + root_appearances.insert(root, Appearances::Multiple); + } + Some(Appearances::Multiple) => { + // It's already multiple, so do nothing! + } + None => { + roots.push(root); + root_appearances.insert(root, Appearances::Single); + } + } + } + FlexVar(Some(_)) => { + // This root already has a name. Nothing to do here! + } + Structure(Apply { + module_name: _, + name: _, + args, + }) => { + for var in args { + find_names_needed(var, subs, roots, root_appearances, names_taken); + } + } + Structure(Func(arg_vars, ret_var)) => { + for var in arg_vars { + find_names_needed(var, subs, roots, root_appearances, names_taken); + } + + find_names_needed(ret_var, subs, roots, root_appearances, names_taken); + } + RigidVar(name) => { + // User-defined names are already taken. + // We must not accidentally generate names that collide with them! + names_taken.insert(name.to_string()); + } + Error(_) | Structure(Erroneous(_)) | Structure(EmptyRecord) => { + // Errors and empty records don't need names. + } + } +} + +pub fn name_all_type_vars(variable: Variable, subs: &mut Subs) { + let mut roots = Vec::new(); + let mut letters_used = 0; + let mut appearances = MutMap::default(); + let mut taken = MutSet::default(); + + // Populate names_needed + find_names_needed(variable, subs, &mut roots, &mut appearances, &mut taken); + + for root in roots { + match appearances.get(&root) { + Some(Appearances::Multiple) => { + letters_used = name_root(letters_used, root, subs, &taken); + } + _ => (), + } + } +} + +fn name_root(letters_used: u32, root: Variable, subs: &mut Subs, taken: &MutSet) -> u32 { + // TODO we should arena-allocate this String, + // so all the strings in the entire pass only require ~1 allocation. + let generated_name = if letters_used < 26 { + // This should generate "a", then "b", etc. + std::char::from_u32(THE_LETTER_A + letters_used) + .unwrap_or_else(|| panic!("Tried to convert {} to a char", THE_LETTER_A + letters_used)) + .to_string() + } else { + panic!("TODO generate aa, ab, ac, ..."); + }; + + if taken.contains(&generated_name) { + // If the generated name is already taken, try again. + name_root(letters_used + 1, root, subs, taken) + } else { + set_root_name(root, &generated_name, subs); + + letters_used + 1 + } +} + +fn set_root_name(root: Variable, name: &str, subs: &mut Subs) { + use subs::Content::*; + + let mut descriptor = subs.get(root); + + match descriptor.content { + FlexVar(None) => { + descriptor.content = FlexVar(Some(name.into())); + + // TODO is this necessary, or was mutating descriptor in place sufficient? + subs.set(root, descriptor); + } + FlexVar(Some(_existing)) => { + panic!("TODO FIXME - make sure the generated name does not clash with any bound vars! In other words, if the user decided to name a type variable 'a', make sure we don't generate 'a' to name a different one!"); + } + _ => (), + } +} pub fn content_to_string(content: Content, subs: &mut Subs) -> String { let mut buf = String::new(); - write_content(content, subs, &mut buf, false); + write_content(content, subs, &mut buf, Parens::Unnecessary); buf } -fn write_content(content: Content, subs: &mut Subs, buf: &mut String, use_parens: bool) { +fn write_content(content: Content, subs: &mut Subs, buf: &mut String, parens: Parens) { use subs::Content::*; match content { FlexVar(Some(name)) => buf.push_str(&name), FlexVar(None) => buf.push_str(WILDCARD), RigidVar(name) => buf.push_str(&name), - Structure(flat_type) => write_flat_type(flat_type, subs, buf, use_parens), + Structure(flat_type) => write_flat_type(flat_type, subs, buf, parens), Error(_) => buf.push_str(""), } } -fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_parens: bool) { +fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, parens: Parens) { use subs::FlatType::*; match flat_type { @@ -38,11 +200,10 @@ fn write_flat_type(flat_type: FlatType, subs: &mut Subs, buf: &mut String, use_p args, subs, buf, - use_parens, + parens, ), EmptyRecord => buf.push_str(EMPTY_RECORD), - Func(args, ret) => write_fn(args, ret, subs, buf, use_parens), - BinOp(l_arg, r_arg, ret) => write_fn(vec![l_arg, r_arg], ret, subs, buf, use_parens), + Func(args, ret) => write_fn(args, ret, subs, buf, parens), Erroneous(problem) => { buf.push_str(&format!("", problem)); } @@ -55,9 +216,9 @@ fn write_apply( args: Vec, subs: &mut Subs, buf: &mut String, - use_parens: bool, + parens: Parens, ) { - let write_parens = use_parens && !args.is_empty(); + let write_parens = parens == Parens::InTypeParam && !args.is_empty(); // Hardcoded type aliases if module_name == "Str" && type_name == "Str" { @@ -70,7 +231,7 @@ fn write_apply( let arg_content = subs.get(arg).content; let mut arg_param = String::new(); - write_content(arg_content, subs, &mut arg_param, true); + write_content(arg_content, subs, &mut arg_param, Parens::InTypeParam); if arg_param == "Int.Integer" { buf.push_str("Int"); @@ -101,7 +262,7 @@ fn write_apply( .unwrap_or_else(|| panic!("List did not have any type parameters somehow.")); let arg_content = subs.get(arg).content; - write_content(arg_content, subs, buf, true); + write_content(arg_content, subs, buf, Parens::InTypeParam); if write_parens { buf.push_str(")"); @@ -115,7 +276,7 @@ fn write_apply( for arg in args { buf.push_str(" "); - write_content(subs.get(arg).content, subs, buf, true); + write_content(subs.get(arg).content, subs, buf, Parens::InTypeParam); } if write_parens { @@ -124,14 +285,9 @@ fn write_apply( } } -fn write_fn( - args: Vec, - ret: Variable, - subs: &mut Subs, - buf: &mut String, - use_parens: bool, -) { +fn write_fn(args: Vec, ret: Variable, subs: &mut Subs, buf: &mut String, parens: Parens) { let mut needs_comma = false; + let use_parens = parens != Parens::Unnecessary; if use_parens { buf.push_str("("); @@ -144,11 +300,11 @@ fn write_fn( needs_comma = true; } - write_content(subs.get(arg).content, subs, buf, false); + write_content(subs.get(arg).content, subs, buf, Parens::InFn); } buf.push_str(" -> "); - write_content(subs.get(ret).content, subs, buf, false); + write_content(subs.get(ret).content, subs, buf, Parens::InFn); if use_parens { buf.push_str(")"); diff --git a/src/solve.rs b/src/solve.rs index 3a046fdcdc..efb3a8bfed 100644 --- a/src/solve.rs +++ b/src/solve.rs @@ -39,13 +39,13 @@ pub fn solve(env: &Env, subs: &mut Subs, constraint: &Constraint) { subs.union(actual, expected); } Let(let_con) => { - match let_con.ret_constraint { + match &let_con.ret_constraint { True => { // If the return expression is guaranteed to solve, // solve the assignments themselves and move on. solve(env, subs, &let_con.assignments_constraint) } - ref ret_con => { + ret_con => { // Solve the assignments' constraints first. solve(env, subs, &let_con.assignments_constraint); diff --git a/src/subs.rs b/src/subs.rs index fe0a3792b0..8d84d9f7fa 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -8,7 +8,7 @@ pub struct Subs { utable: UnificationTable>, } -#[derive(Copy, PartialEq, Eq, Clone)] +#[derive(Copy, PartialEq, Eq, Clone, Hash)] pub struct Variable(u32); impl Variable { @@ -67,6 +67,10 @@ impl Subs { self.utable.probe_value(key) } + pub fn get_root_key(&mut self, key: Variable) -> Variable { + self.utable.get_root_key(key) + } + 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); @@ -131,8 +135,13 @@ impl From for Descriptor { #[derive(Clone, Debug, PartialEq, Eq)] pub enum Content { + /// A type variable which the user did not name in an annotation, + /// + /// When we auto-generate a type var name, e.g. the "a" in (a -> a), we + /// change the Option in here from None to Some. FlexVar(Option> /* name - e.g. in pattern matching */), - RigidVar(String /* name given in a user-written annotation */), + /// name given in a user-written annotation + RigidVar(Box), Structure(FlatType), Error(Problem), } @@ -145,7 +154,6 @@ pub enum FlatType { args: Vec, }, Func(Vec, Variable), - BinOp(Variable, Variable, Variable), Erroneous(Problem), EmptyRecord, } diff --git a/src/types.rs b/src/types.rs index 9080894660..743a45b2f1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -184,9 +184,9 @@ pub enum AnnotationSource { #[derive(Debug, Clone, PartialEq, Eq)] pub enum Reason { - AnonymousFnArg(u8 /* arg index */), + AnonymousFnArg { arg_index: u8 }, NamedFnArg(String /* function name */, u8 /* arg index */), - AnonymousFnCall(u8 /* arity */), + AnonymousFnCall { arity: u8 }, NamedFnCall(String /* function name */, u8 /* arity */), BinOpArg(BinOp, ArgSide), BinOpRet(BinOp), diff --git a/src/unify.rs b/src/unify.rs index 7dde1ed1c2..1c066877ef 100644 --- a/src/unify.rs +++ b/src/unify.rs @@ -90,14 +90,6 @@ fn unify_flat_type(subs: &mut Subs, left: &FlatType, right: &FlatType) -> Descri from_content(Error(Problem::MissingArguments)) } } - (BinOp(l_l_arg, l_r_arg, l_ret), BinOp(r_l_arg, r_r_arg, r_ret)) => { - let l_arg = union_vars(subs, *l_l_arg, *r_l_arg); - let r_arg = union_vars(subs, *l_r_arg, *r_r_arg); - let ret = union_vars(subs, *l_ret, *r_ret); - let flat_type = BinOp(l_arg, r_arg, ret); - - from_content(Structure(flat_type)) - } _ => from_content(Error(Problem::GenericMismatch)), } } @@ -137,7 +129,7 @@ fn unify_rigid(name: &str, other: &Content) -> Descriptor { match other { FlexVar(_) => { // If the other is flex, rigid wins! - from_content(RigidVar(name.to_string())) + from_content(RigidVar(name.into())) } RigidVar(_) | Structure(_) => { // Type mismatch! Rigid can only unify with flex, even if the diff --git a/tests/test_infer.rs b/tests/test_infer.rs index 26f31a9af1..6f92320cff 100644 --- a/tests/test_infer.rs +++ b/tests/test_infer.rs @@ -12,7 +12,7 @@ mod helpers; mod test_infer { use helpers::can_expr; use roc::infer::infer_expr; - use roc::pretty_print_types::content_to_string; + use roc::pretty_print_types::{content_to_string, name_all_type_vars}; // HELPERS @@ -20,6 +20,9 @@ mod test_infer { let (_, output, _, procedures, mut subs, variable) = can_expr(src); let content = infer_expr(&mut subs, procedures, &output.constraint, variable); + + name_all_type_vars(variable, &mut subs); + let actual_str = content_to_string(content, &mut subs); assert_eq!(actual_str, expected.to_string()); @@ -486,7 +489,6 @@ mod test_infer { } // TODO type annotations - // TODO fix identity inference // TODO BoundTypeVariables // TODO conditionals @@ -530,25 +532,137 @@ mod test_infer { "Int", ); } - // #[test] - // fn identity() { - // infer_eq( - // indoc!(r#" - // \val -> val - // "#), - // "a -> a" - // ); - // } - // #[test] - // fn always_function() { - // infer_eq( - // indoc!(r#" - // \val -> \_ -> val - // "#), - // "a -> (* -> a)" - // ); - // } + #[test] + fn anonymous_identity() { + infer_eq( + indoc!( + r#" + (\a -> a) 3.14 + "# + ), + "Float", + ); + } + + #[test] + fn identity_of_identity() { + infer_eq( + indoc!( + r#" + (\val -> val) (\val -> val) + "# + ), + "a -> a", + ); + } + + // #[test] + // TODO FIXME this should work, but instead causes a stack overflow! + // fn recursive_identity() { + // infer_eq( + // indoc!( + // r#" + // identity = \val -> val + + // identity identity + // "# + // ), + // "a -> a", + // ); + // } + + #[test] + fn identity_function() { + infer_eq( + indoc!( + r#" + \val -> val + "# + ), + "a -> a", + ); + } + + #[test] + fn use_apply() { + infer_eq( + indoc!( + r#" + apply = \f x -> f x + identity = \a -> a + + apply identity 5 + "# + ), + "Int", + ); + } + + #[test] + fn apply_function() { + infer_eq( + indoc!( + r#" + \f x -> f x + "# + ), + "(a -> b), a -> b", + ); + } + + // #[test] + // TODO FIXME this should pass, but instead fails to canonicalize + // fn use_flip() { + // infer_eq( + // indoc!( + // r#" + // flip = \f -> (\a b -> f b a) + // neverendingInt = \f int -> f int + // x = neverendingInt (\a -> a) 5 + + // flip neverendingInt + // "# + // ), + // "(Int, (a -> a)) -> Int", + // ); + // } + + #[test] + fn flip_function() { + infer_eq( + indoc!( + r#" + \f -> (\a b -> f b a), + "# + ), + "(a, b -> c) -> (b, a -> c)", + ); + } + + #[test] + fn always_function() { + infer_eq( + indoc!( + r#" + \val -> \_ -> val + "# + ), + "a -> (* -> a)", + ); + } + + #[test] + fn pass_a_function() { + infer_eq( + indoc!( + r#" + \f -> f {} + "# + ), + "({} -> a) -> a", + ); + } // OPERATORS