From 6bb97c92b98cbef788dcb883615f73eb84a81322 Mon Sep 17 00:00:00 2001 From: Ayaz Hafiz Date: Tue, 11 Oct 2022 17:45:33 -0500 Subject: [PATCH] Canonicalize multiple abilities bound to a type variable --- crates/compiler/can/src/annotation.rs | 68 ++++++++++++------------ crates/compiler/can/src/def.rs | 12 ++--- crates/compiler/can/src/effect_module.rs | 2 +- crates/compiler/can/src/module.rs | 4 +- crates/compiler/constrain/src/expr.rs | 4 +- crates/compiler/constrain/src/pattern.rs | 2 +- crates/compiler/solve/src/module.rs | 5 +- crates/compiler/solve/src/solve.rs | 34 +++++++----- crates/compiler/types/src/types.rs | 30 +++++------ 9 files changed, 84 insertions(+), 77 deletions(-) diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index aabec02597..d977ce14bc 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -105,10 +105,10 @@ impl OwnedNamedOrAble { } } - pub fn opt_ability(&self) -> Option { + pub fn opt_abilities(&self) -> Option<&[Symbol]> { match self { OwnedNamedOrAble::Named(_) => None, - OwnedNamedOrAble::Able(av) => Some(av.ability), + OwnedNamedOrAble::Able(av) => Some(&av.abilities), } } } @@ -127,7 +127,7 @@ pub struct NamedVariable { pub struct AbleVariable { pub variable: Variable, pub name: Lowercase, - pub ability: Symbol, + pub abilities: Vec, // NB: there may be multiple occurrences of a variable pub first_seen: Region, } @@ -166,12 +166,12 @@ impl IntroducedVariables { self.named.insert(named_variable); } - pub fn insert_able(&mut self, name: Lowercase, var: Loc, ability: Symbol) { + pub fn insert_able(&mut self, name: Lowercase, var: Loc, abilities: Vec) { self.debug_assert_not_already_present(var.value); let able_variable = AbleVariable { name, - ability, + abilities, variable: var.value, first_seen: var.region, }; @@ -450,8 +450,9 @@ pub fn find_type_def_symbols( stack.push(&annotation.value); for has_clause in clauses.iter() { - // TODO(abilities) - stack.push(&has_clause.value.abilities[0].value); + for ab in has_clause.value.abilities { + stack.push(&ab.value); + } } } Inferred | Wildcard | Malformed(_) => {} @@ -538,7 +539,7 @@ fn can_annotation_help( // Generate an variable bound to the ability so we can keep compiling. let var = var_store.fresh(); - introduced_variables.insert_able(fresh_ty_var, Loc::at(region, var), symbol); + introduced_variables.insert_able(fresh_ty_var, Loc::at(region, var), vec![symbol]); return Type::Variable(var); } @@ -579,7 +580,7 @@ fn can_annotation_help( arg_ann.region, OptAbleType { typ: arg_ann.value, - opt_ability: alias_arg.value.opt_bound_ability, + opt_abilities: alias_arg.value.opt_bound_abilities.clone(), }, )); } @@ -674,7 +675,7 @@ fn can_annotation_help( AliasVar { name: var_name, var, - opt_bound_ability: None, + opt_bound_abilities: None, }, )); } else { @@ -689,7 +690,7 @@ fn can_annotation_help( AliasVar { name: var_name, var, - opt_bound_ability: None, + opt_bound_abilities: None, }, )); } @@ -769,10 +770,7 @@ fn can_annotation_help( symbol, type_arguments: vars .into_iter() - .map(|typ| OptAbleType { - typ, - opt_ability: None, - }) + .map(|typ| OptAbleType::unbound(typ)) .collect(), lambda_set_variables: alias.lambda_set_variables.clone(), actual: Box::new(alias.typ.clone()), @@ -932,31 +930,33 @@ fn canonicalize_has_clause( ); let var_name = Lowercase::from(var_name); - // TODO(abilities) - let ability = abilities[0]; - let ability = match ability.value { - TypeAnnotation::Apply(module_name, ident, _type_arguments) => { - let symbol = make_apply_symbol(env, ability.region, scope, module_name, ident)?; + let mut can_abilities = Vec::with_capacity(abilities.len()); + for ability in *abilities { + let ability = match ability.value { + TypeAnnotation::Apply(module_name, ident, _type_arguments) => { + let symbol = make_apply_symbol(env, ability.region, scope, module_name, ident)?; - // Ability defined locally, whose members we are constructing right now... - if !pending_abilities_in_scope.contains_key(&symbol) + // Ability defined locally, whose members we are constructing right now... + if !pending_abilities_in_scope.contains_key(&symbol) // or an ability that was imported from elsewhere && !scope.abilities_store.is_ability(symbol) - { + { + let region = ability.region; + env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); + return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region))); + } + symbol + } + _ => { let region = ability.region; env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region))); } - symbol - } - _ => { - let region = ability.region; - env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); - return Err(Type::Erroneous(Problem::HasClauseIsNotAbility(region))); - } - }; + }; - references.insert(ability); + references.insert(ability); + can_abilities.push(ability); + } if let Some(shadowing) = introduced_variables.named_var_by_name(&var_name) { let var_name_ident = var_name.to_string().into(); @@ -974,7 +974,7 @@ fn canonicalize_has_clause( let var = var_store.fresh(); - introduced_variables.insert_able(var_name, Loc::at(region, var), ability); + introduced_variables.insert_able(var_name, Loc::at(region, var), can_abilities); Ok(()) } @@ -1126,7 +1126,7 @@ pub fn freshen_opaque_def( .iter() .map(|alias_var| OptAbleVar { var: var_store.fresh(), - opt_ability: alias_var.value.opt_bound_ability, + opt_abilities: alias_var.value.opt_bound_abilities.clone(), }) .collect(); diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 728124d1c9..d7f7650b3c 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -357,14 +357,14 @@ fn canonicalize_alias<'a>( // This is a valid lowercase rigid var for the type def. let named_variable = named.swap_remove(index); let var = named_variable.variable(); - let opt_bound_ability = named_variable.opt_ability(); + let opt_bound_abilities = named_variable.opt_abilities().map(ToOwned::to_owned); let name = named_variable.name(); can_vars.push(Loc { value: AliasVar { name, var, - opt_bound_ability, + opt_bound_abilities, }, region: loc_lowercase.region, }); @@ -386,7 +386,7 @@ fn canonicalize_alias<'a>( value: AliasVar { name: loc_lowercase.value.clone(), var: var_store.fresh(), - opt_bound_ability: None, + opt_bound_abilities: None, }, region: loc_lowercase.region, }); @@ -859,7 +859,7 @@ fn canonicalize_opaque<'a>( alias_var.region, OptAbleType { typ: Type::Variable(var_store.fresh()), - opt_ability: alias_var.value.opt_bound_ability, + opt_abilities: alias_var.value.opt_bound_abilities.clone(), }, ) }) @@ -1345,7 +1345,7 @@ fn resolve_abilities<'a>( .introduced_variables .able .iter() - .partition(|av| av.ability == ability); + .partition(|av| av.abilities.iter().any(|ab| *ab == ability)); let var_bound_to_ability = match variables_bound_to_ability.as_slice() { [one] => one.variable, @@ -2872,7 +2872,7 @@ fn make_tag_union_of_alias_recursive<'a>( let alias_opt_able_vars = alias.type_variables.iter().map(|l| OptAbleType { typ: Type::Variable(l.value.var), - opt_ability: l.value.opt_bound_ability, + opt_abilities: l.value.opt_bound_abilities.clone(), }); let lambda_set_vars = alias.lambda_set_variables.iter(); diff --git a/crates/compiler/can/src/effect_module.rs b/crates/compiler/can/src/effect_module.rs index dcc21b70fd..0ca0e24ccd 100644 --- a/crates/compiler/can/src/effect_module.rs +++ b/crates/compiler/can/src/effect_module.rs @@ -1552,7 +1552,7 @@ fn build_fresh_opaque_variables( ); let type_arguments = vec![OptAbleVar { var: a_var, - opt_ability: None, + opt_abilities: None, }]; let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))]; diff --git a/crates/compiler/can/src/module.rs b/crates/compiler/can/src/module.rs index 4e213f3589..24487cea18 100644 --- a/crates/compiler/can/src/module.rs +++ b/crates/compiler/can/src/module.rs @@ -136,7 +136,7 @@ pub struct Module { #[derive(Debug, Default)] pub struct RigidVariables { pub named: MutMap, - pub able: MutMap, + pub able: MutMap)>, pub wildcards: VecSet, } @@ -387,7 +387,7 @@ pub fn canonicalize_module_defs<'a>( for able in output.introduced_variables.able { rigid_variables .able - .insert(able.variable, (able.name, able.ability)); + .insert(able.variable, (able.name, able.abilities)); } for var in output.introduced_variables.wildcards { diff --git a/crates/compiler/constrain/src/expr.rs b/crates/compiler/constrain/src/expr.rs index c1e8b1738a..899a273dca 100644 --- a/crates/compiler/constrain/src/expr.rs +++ b/crates/compiler/constrain/src/expr.rs @@ -1143,7 +1143,7 @@ pub fn constrain_expr( .iter() .map(|v| OptAbleType { typ: Type::Variable(v.var), - opt_ability: v.opt_ability, + opt_abilities: v.opt_abilities.clone(), }) .collect(), lambda_set_variables: lambda_set_variables.clone(), @@ -1208,7 +1208,7 @@ pub fn constrain_expr( .iter() .map(|v| OptAbleType { typ: Type::Variable(v.var), - opt_ability: v.opt_ability, + opt_abilities: v.opt_abilities.clone(), }) .collect(), lambda_set_variables: lambda_set_variables.clone(), diff --git a/crates/compiler/constrain/src/pattern.rs b/crates/compiler/constrain/src/pattern.rs index babcbba774..da11ed9bfb 100644 --- a/crates/compiler/constrain/src/pattern.rs +++ b/crates/compiler/constrain/src/pattern.rs @@ -541,7 +541,7 @@ pub fn constrain_pattern( .iter() .map(|v| OptAbleType { typ: Type::Variable(v.var), - opt_ability: v.opt_ability, + opt_abilities: v.opt_abilities.clone(), }) .collect(), lambda_set_variables: lambda_set_variables.clone(), diff --git a/crates/compiler/solve/src/module.rs b/crates/compiler/solve/src/module.rs index 29428daf71..9f6f77ef26 100644 --- a/crates/compiler/solve/src/module.rs +++ b/crates/compiler/solve/src/module.rs @@ -70,8 +70,9 @@ pub fn run_solve( subs.rigid_var(var, name); } - for (var, (name, ability)) in rigid_variables.able { - subs.rigid_able_var(var, name, ability); + for (var, (name, abilities)) in rigid_variables.able { + // TODO(abilities) + subs.rigid_able_var(var, name, abilities[0]); } for var in rigid_variables.wildcards { diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 2676d6f25c..4110152e7e 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -345,12 +345,12 @@ impl Aliases { for OptAbleVar { var: rec_var, - opt_ability, + opt_abilities, } in delayed_variables .recursion_variables(&mut self.variables) .iter_mut() { - debug_assert!(opt_ability.is_none()); + debug_assert!(opt_abilities.is_none()); let new_var = subs.fresh_unnamed_flex_var(); substitutions.insert(*rec_var, new_var); @@ -367,7 +367,7 @@ impl Aliases { .iter_mut() .zip(new_lambda_set_variables) { - debug_assert!(old.opt_ability.is_none()); + debug_assert!(old.opt_abilities.is_none()); if old.var != *new { substitutions.insert(old.var, *new); @@ -2291,7 +2291,7 @@ fn type_to_variable<'a>( use bumpalo::collections::Vec; let mut stack = Vec::with_capacity_in(8, arena); - let mut bind_to_ability = Vec::new_in(arena); + let mut bind_to_abilities = Vec::new_in(arena); macro_rules! helper { ($typ:expr, $ambient_function_policy:expr) => {{ @@ -2533,8 +2533,8 @@ fn type_to_variable<'a>( for (target_index, arg_type) in (new_variables.indices()).zip(type_arguments) { let copy_var = helper!(&arg_type.value.typ); subs.variables[target_index] = copy_var; - if let Some(ability) = arg_type.value.opt_ability { - bind_to_ability.push((Loc::at(arg_type.region, copy_var), ability)); + if let Some(abilities) = arg_type.value.opt_abilities.as_ref() { + bind_to_abilities.push((Loc::at(arg_type.region, copy_var), abilities)); } } @@ -2595,12 +2595,15 @@ fn type_to_variable<'a>( let length = type_arguments.len() + lambda_set_variables.len(); let new_variables = VariableSubsSlice::reserve_into_subs(subs, length); - for (target_index, OptAbleType { typ, opt_ability }) in + for (target_index, OptAbleType { typ, opt_abilities }) in (new_variables.indices()).zip(type_arguments) { - let copy_var = match opt_ability { + let copy_var = match opt_abilities { None => helper!(typ), - Some(ability) => { + Some(abilities) => { + // TODO(abilities) + let ability = abilities[0]; + // If this type argument is marked as being bound to an ability, we must // now correctly instantiate it as so. match RegisterVariable::from_type(subs, rank, pools, arena, typ) { @@ -2608,7 +2611,7 @@ fn type_to_variable<'a>( use Content::*; match *subs.get_content_without_compacting(var) { FlexVar(opt_name) => subs - .set_content(var, FlexAbleVar(opt_name, *ability)), + .set_content(var, FlexAbleVar(opt_name, ability)), RigidVar(..) => internal_error!("Rigid var in type arg for {:?} - this is a bug in the solver, or our understanding", actual), RigidAbleVar(..) | FlexAbleVar(..) => internal_error!("Able var in type arg for {:?} - this is a bug in the solver, or our understanding", actual), _ => { @@ -2744,17 +2747,20 @@ fn type_to_variable<'a>( }; } - for (Loc { value: var, region }, ability) in bind_to_ability { + for (Loc { value: var, region }, abilities) in bind_to_abilities { match *subs.get_content_unchecked(var) { Content::RigidVar(a) => { - subs.set_content(var, Content::RigidAbleVar(a, ability)); + // TODO(multi-abilities) + subs.set_content(var, Content::RigidAbleVar(a, abilities[0])); } - Content::RigidAbleVar(_, ab) if ab == ability => { + // TODO(multi-abilities) + Content::RigidAbleVar(_, ab) if ab == abilities[0] => { // pass, already bound } _ => { + // TODO(multi-abilities) let flex_ability = subs.fresh(Descriptor { - content: Content::FlexAbleVar(None, ability), + content: Content::FlexAbleVar(None, abilities[0]), rank, mark: Mark::NONE, copy: OptVariable::NONE, diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index 713697793c..af50bc6de6 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -244,17 +244,17 @@ pub struct AliasCommon { pub lambda_set_variables: Vec, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub struct OptAbleVar { pub var: Variable, - pub opt_ability: Option, + pub opt_abilities: Option>, } impl OptAbleVar { pub fn unbound(var: Variable) -> Self { Self { var, - opt_ability: None, + opt_abilities: None, } } } @@ -262,14 +262,14 @@ impl OptAbleVar { #[derive(PartialEq, Eq, Debug)] pub struct OptAbleType { pub typ: Type, - pub opt_ability: Option, + pub opt_abilities: Option>, } impl OptAbleType { pub fn unbound(typ: Type) -> Self { Self { typ, - opt_ability: None, + opt_abilities: None, } } } @@ -421,7 +421,7 @@ impl Clone for OptAbleType { // This passes through `Type`, so defer to that to bump the clone counter. Self { typ: self.typ.clone(), - opt_ability: self.opt_ability, + opt_abilities: self.opt_abilities.clone(), } } } @@ -567,8 +567,8 @@ impl fmt::Debug for Type { for arg in type_arguments { write!(f, " {:?}", &arg.typ)?; - if let Some(ab) = arg.opt_ability { - write!(f, ":{:?}", ab)?; + if let Some(abs) = &arg.opt_abilities { + write!(f, ":{:?}", abs)?; } } @@ -1368,7 +1368,7 @@ impl Type { arg_ann.region, OptAbleType { typ: arg_ann.value.clone(), - opt_ability: alias_var.value.opt_bound_ability, + opt_abilities: alias_var.value.opt_bound_abilities.clone(), }, )); } @@ -1414,7 +1414,7 @@ impl Type { value: AliasVar { var: placeholder, - opt_bound_ability, + opt_bound_abilities, .. }, .. @@ -1431,7 +1431,7 @@ impl Type { ); named_args.push(OptAbleType { typ: filler.value.clone(), - opt_ability: *opt_bound_ability, + opt_abilities: opt_bound_abilities.clone(), }); substitution.insert(*placeholder, filler.value); } @@ -2111,8 +2111,8 @@ impl AliasKind { pub struct AliasVar { pub name: Lowercase, pub var: Variable, - /// `Some` if this variable is bound to an ability; `None` otherwise. - pub opt_bound_ability: Option, + /// `Some` if this variable is bound to abilities; `None` otherwise. + pub opt_bound_abilities: Option>, } impl AliasVar { @@ -2120,7 +2120,7 @@ impl AliasVar { Self { name, var, - opt_bound_ability: None, + opt_bound_abilities: None, } } } @@ -2129,7 +2129,7 @@ impl From<&AliasVar> for OptAbleVar { fn from(av: &AliasVar) -> OptAbleVar { OptAbleVar { var: av.var, - opt_ability: av.opt_bound_ability, + opt_abilities: av.opt_bound_abilities.clone(), } } }