diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 1dae84e6c9..a3c8ed95d2 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -201,17 +201,17 @@ fn can_annotation_help( let mut substitutions = ImMap::default(); let mut vars = Vec::new(); - if alias.vars.len() != args.len() { + if alias.type_variables.len() != args.len() { let error = Type::Erroneous(Problem::BadTypeArguments { symbol, region, - alias_needs: alias.vars.len() as u8, + alias_needs: alias.type_variables.len() as u8, type_got: args.len() as u8, }); return error; } - for (loc_var, arg_ann) in alias.vars.iter().zip(args.into_iter()) { + for (loc_var, arg_ann) in alias.type_variables.iter().zip(args.into_iter()) { let name = loc_var.value.0.clone(); let var = loc_var.value.1; @@ -227,8 +227,8 @@ fn can_annotation_help( } // make sure hidden variables are freshly instantiated - for var in alias.hidden_variables.iter() { - substitutions.insert(*var, Type::Variable(var_store.fresh())); + for var in alias.lambda_set_variables.iter() { + substitutions.insert(var.into_inner(), Type::Variable(var_store.fresh())); } // instantiate variables diff --git a/compiler/can/src/module.rs b/compiler/can/src/module.rs index 73a174a375..ae1d5015d9 100644 --- a/compiler/can/src/module.rs +++ b/compiler/can/src/module.rs @@ -61,7 +61,7 @@ where let num_deps = dep_idents.len(); for (name, alias) in aliases.into_iter() { - scope.add_alias(name, alias.region, alias.vars, alias.typ); + scope.add_alias(name, alias.region, alias.type_variables, alias.typ); } // Desugar operators (convert them to Apply calls, taking into account diff --git a/compiler/can/src/scope.rs b/compiler/can/src/scope.rs index 4c8c2b4ab5..bb4c969ced 100644 --- a/compiler/can/src/scope.rs +++ b/compiler/can/src/scope.rs @@ -47,8 +47,9 @@ impl Scope { let alias = Alias { region, typ, - hidden_variables: MutSet::default(), - vars: variables, + lambda_set_variables: MutSet::default(), + recursion_variables: MutSet::default(), + type_variables: variables, }; aliases.insert(symbol, alias); @@ -174,17 +175,34 @@ impl Scope { vars: Vec>, typ: Type, ) { - let mut hidden_variables = MutSet::default(); - hidden_variables.extend(typ.variables()); + let roc_types::types::VariableDetail { + type_variables, + lambda_set_variables, + recursion_variables, + } = typ.variables_detail(); - for loc_var in vars.iter() { - hidden_variables.remove(&loc_var.value.1); - } + debug_assert!({ + let mut hidden = type_variables; + + for loc_var in vars.iter() { + hidden.remove(&loc_var.value.1); + } + + if !hidden.is_empty() { + panic!( + "Found unbound type variables {:?} \n in type alias {:?} {:?} : {:?}", + hidden, name, &vars, &typ + ) + } + + true + }); let alias = Alias { region, - vars, - hidden_variables, + type_variables: vars, + lambda_set_variables, + recursion_variables, typ, }; diff --git a/compiler/can/tests/test_can.rs b/compiler/can/tests/test_can.rs index faed8ff43c..40eea5a5ff 100644 --- a/compiler/can/tests/test_can.rs +++ b/compiler/can/tests/test_can.rs @@ -524,7 +524,7 @@ mod test_can { fn annotation_followed_with_unrelated_affectation() { let src = indoc!( r#" - F : Int * + F : Str x = 1 @@ -545,9 +545,9 @@ mod test_can { fn two_annotations_followed_with_unrelated_affectation() { let src = indoc!( r#" - G : Int * + G : Str - F : Int * + F : {} x = 1 diff --git a/compiler/reporting/tests/test_reporting.rs b/compiler/reporting/tests/test_reporting.rs index 881224d128..bcf235f2a1 100644 --- a/compiler/reporting/tests/test_reporting.rs +++ b/compiler/reporting/tests/test_reporting.rs @@ -2663,9 +2663,9 @@ mod test_reporting { report_problem_as( indoc!( r#" - Foo : { x : Int * } + Foo a : { x : Int a } - f : Foo -> Int * + f : Foo a -> Int a f = \r -> r.x f { y: 3.14 } @@ -2687,7 +2687,7 @@ mod test_reporting { But `f` needs the 1st argument to be: - { x : Int * } + { x : Int a } Tip: Seems like a record field typo. Maybe `y` should be `x`? diff --git a/compiler/solve/src/module.rs b/compiler/solve/src/module.rs index ca31f6fceb..f888e2208e 100644 --- a/compiler/solve/src/module.rs +++ b/compiler/solve/src/module.rs @@ -50,9 +50,9 @@ pub fn make_solved_types( let mut solved_types = MutMap::default(); for (symbol, alias) in solved_env.aliases.iter() { - let mut args = Vec::with_capacity(alias.vars.len()); + let mut args = Vec::with_capacity(alias.type_variables.len()); - for loc_named_var in alias.vars.iter() { + for loc_named_var in alias.type_variables.iter() { let (name, var) = &loc_named_var.value; args.push((name.clone(), SolvedType::new(&solved_subs, *var))); diff --git a/compiler/test_gen/src/gen_primitives.rs b/compiler/test_gen/src/gen_primitives.rs index 5c429fda15..685ea7f94f 100644 --- a/compiler/test_gen/src/gen_primitives.rs +++ b/compiler/test_gen/src/gen_primitives.rs @@ -1241,9 +1241,9 @@ fn recursive_functon_with_rigid() { r#" app "test" provides [ main ] to "./platform" - State a : { count : Int *, x : a } + State a : { count : I64, x : a } - foo : State a -> Int * + foo : State a -> Int * foo = \state -> if state.count == 0 then 0 @@ -1632,15 +1632,15 @@ fn binary_tree_double_pattern_match() { r#" app "test" provides [ main ] to "./platform" - BTree : [ Node BTree BTree, Leaf (Int *) ] + BTree : [ Node BTree BTree, Leaf I64 ] - foo : BTree -> Int * + foo : BTree -> I64 foo = \btree -> when btree is Node (Node (Leaf x) _) _ -> x _ -> 0 - main : Int * + main : I64 main = foo (Node (Node (Leaf 32) (Leaf 0)) (Leaf 0)) "# diff --git a/compiler/types/src/subs.rs b/compiler/types/src/subs.rs index d43866bd01..c43107f7d8 100644 --- a/compiler/types/src/subs.rs +++ b/compiler/types/src/subs.rs @@ -96,12 +96,6 @@ impl VarStore { } } -// impl From for Variable { -// fn from(store: VarStore) -> Self { -// Variable(store.next) -// } -// } - #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct OptVariable(u32); @@ -220,6 +214,18 @@ impl fmt::Debug for LambdaSet { } } +impl LambdaSet { + pub fn into_inner(self) -> Variable { + self.0 + } +} + +impl From for LambdaSet { + fn from(variable: Variable) -> Self { + LambdaSet(variable) + } +} + /// Used in SolvedType #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct VarId(u32); diff --git a/compiler/types/src/types.rs b/compiler/types/src/types.rs index 17022943a1..ca192dcee6 100644 --- a/compiler/types/src/types.rs +++ b/compiler/types/src/types.rs @@ -1,5 +1,5 @@ use crate::pretty_print::Parens; -use crate::subs::{Subs, VarStore, Variable}; +use crate::subs::{LambdaSet, Subs, VarStore, Variable}; use inlinable_string::InlinableString; use roc_collections::all::{union, ImMap, ImSet, Index, MutMap, MutSet, SendMap}; use roc_module::ident::{ForeignSymbol, Ident, Lowercase, TagName}; @@ -373,6 +373,13 @@ impl Type { result } + pub fn variables_detail(&self) -> VariableDetail { + let mut result = Default::default(); + variables_help_detailed(self, &mut result); + + result + } + pub fn substitute(&mut self, substitutions: &ImMap) { use Type::*; @@ -617,12 +624,12 @@ impl Type { } Apply(symbol, args) => { if let Some(alias) = aliases.get(symbol) { - if args.len() != alias.vars.len() { + if args.len() != alias.type_variables.len() { *self = Type::Erroneous(Problem::BadTypeArguments { symbol: *symbol, region, type_got: args.len() as u8, - alias_needs: alias.vars.len() as u8, + alias_needs: alias.type_variables.len() as u8, }); return; } @@ -639,7 +646,7 @@ impl Type { .. }, filler, - ) in alias.vars.iter().zip(args.iter()) + ) in alias.type_variables.iter().zip(args.iter()) { let mut filler = filler.clone(); filler.instantiate_aliases(region, aliases, var_store, introduced); @@ -836,6 +843,93 @@ fn variables_help(tipe: &Type, accum: &mut ImSet) { } } +#[derive(Default)] +pub struct VariableDetail { + pub type_variables: MutSet, + pub lambda_set_variables: MutSet, + pub recursion_variables: MutSet, +} + +fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) { + use Type::*; + + match tipe { + EmptyRec | EmptyTagUnion | Erroneous(_) => (), + + Variable(v) => { + accum.type_variables.insert(*v); + } + + Function(args, closure, ret) => { + for arg in args { + variables_help_detailed(arg, accum); + } + if let Type::Variable(v) = **closure { + accum.lambda_set_variables.insert(LambdaSet::from(v)); + } else { + variables_help_detailed(closure, accum); + } + + variables_help_detailed(ret, accum); + } + Record(fields, ext) => { + use RecordField::*; + + for (_, field) in fields { + match field { + Optional(x) => variables_help_detailed(x, accum), + Required(x) => variables_help_detailed(x, accum), + Demanded(x) => variables_help_detailed(x, accum), + }; + } + variables_help_detailed(ext, accum); + } + TagUnion(tags, ext) => { + for (_, args) in tags { + for x in args { + variables_help_detailed(x, accum); + } + } + variables_help_detailed(ext, accum); + } + RecursiveTagUnion(rec, tags, ext) => { + for (_, args) in tags { + for x in args { + variables_help_detailed(x, accum); + } + } + variables_help_detailed(ext, accum); + + // just check that this is actually a recursive type + debug_assert!(accum.type_variables.contains(rec)); + + // this rec var doesn't need to be in flex_vars or rigid_vars + accum.type_variables.remove(rec); + + accum.recursion_variables.insert(*rec); + } + Alias(_, args, actual) => { + for (_, arg) in args { + variables_help_detailed(arg, accum); + } + variables_help_detailed(actual, accum); + } + HostExposedAlias { + arguments, actual, .. + } => { + for (_, arg) in arguments { + variables_help_detailed(arg, accum); + } + variables_help_detailed(actual, accum); + } + Apply(_, args) => { + for x in args { + variables_help_detailed(x, accum); + } + } + } +} + pub struct RecordStructure { pub fields: MutMap>, pub ext: Variable, @@ -959,10 +1053,13 @@ pub enum PatternCategory { #[derive(Clone, Debug, PartialEq)] pub struct Alias { pub region: Region, - pub vars: Vec>, + pub type_variables: Vec>, - /// hidden type variables, like the closure variable in `a -> b` - pub hidden_variables: MutSet, + /// lambda set variables, e.g. the one annotating the arrow in + /// a |c|-> b + pub lambda_set_variables: MutSet, + + pub recursion_variables: MutSet, pub typ: Type, }