Canonicalize multiple abilities bound to a type variable

This commit is contained in:
Ayaz Hafiz 2022-10-11 17:45:33 -05:00
parent 97dbac1631
commit 6bb97c92b9
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
9 changed files with 84 additions and 77 deletions

View file

@ -105,10 +105,10 @@ impl OwnedNamedOrAble {
} }
} }
pub fn opt_ability(&self) -> Option<Symbol> { pub fn opt_abilities(&self) -> Option<&[Symbol]> {
match self { match self {
OwnedNamedOrAble::Named(_) => None, 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 struct AbleVariable {
pub variable: Variable, pub variable: Variable,
pub name: Lowercase, pub name: Lowercase,
pub ability: Symbol, pub abilities: Vec<Symbol>,
// NB: there may be multiple occurrences of a variable // NB: there may be multiple occurrences of a variable
pub first_seen: Region, pub first_seen: Region,
} }
@ -166,12 +166,12 @@ impl IntroducedVariables {
self.named.insert(named_variable); self.named.insert(named_variable);
} }
pub fn insert_able(&mut self, name: Lowercase, var: Loc<Variable>, ability: Symbol) { pub fn insert_able(&mut self, name: Lowercase, var: Loc<Variable>, abilities: Vec<Symbol>) {
self.debug_assert_not_already_present(var.value); self.debug_assert_not_already_present(var.value);
let able_variable = AbleVariable { let able_variable = AbleVariable {
name, name,
ability, abilities,
variable: var.value, variable: var.value,
first_seen: var.region, first_seen: var.region,
}; };
@ -450,8 +450,9 @@ pub fn find_type_def_symbols(
stack.push(&annotation.value); stack.push(&annotation.value);
for has_clause in clauses.iter() { for has_clause in clauses.iter() {
// TODO(abilities) for ab in has_clause.value.abilities {
stack.push(&has_clause.value.abilities[0].value); stack.push(&ab.value);
}
} }
} }
Inferred | Wildcard | Malformed(_) => {} Inferred | Wildcard | Malformed(_) => {}
@ -538,7 +539,7 @@ fn can_annotation_help(
// Generate an variable bound to the ability so we can keep compiling. // Generate an variable bound to the ability so we can keep compiling.
let var = var_store.fresh(); 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); return Type::Variable(var);
} }
@ -579,7 +580,7 @@ fn can_annotation_help(
arg_ann.region, arg_ann.region,
OptAbleType { OptAbleType {
typ: arg_ann.value, 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 { AliasVar {
name: var_name, name: var_name,
var, var,
opt_bound_ability: None, opt_bound_abilities: None,
}, },
)); ));
} else { } else {
@ -689,7 +690,7 @@ fn can_annotation_help(
AliasVar { AliasVar {
name: var_name, name: var_name,
var, var,
opt_bound_ability: None, opt_bound_abilities: None,
}, },
)); ));
} }
@ -769,10 +770,7 @@ fn can_annotation_help(
symbol, symbol,
type_arguments: vars type_arguments: vars
.into_iter() .into_iter()
.map(|typ| OptAbleType { .map(|typ| OptAbleType::unbound(typ))
typ,
opt_ability: None,
})
.collect(), .collect(),
lambda_set_variables: alias.lambda_set_variables.clone(), lambda_set_variables: alias.lambda_set_variables.clone(),
actual: Box::new(alias.typ.clone()), actual: Box::new(alias.typ.clone()),
@ -932,31 +930,33 @@ fn canonicalize_has_clause(
); );
let var_name = Lowercase::from(var_name); let var_name = Lowercase::from(var_name);
// TODO(abilities) let mut can_abilities = Vec::with_capacity(abilities.len());
let ability = abilities[0]; for ability in *abilities {
let ability = match ability.value { let ability = match ability.value {
TypeAnnotation::Apply(module_name, ident, _type_arguments) => { TypeAnnotation::Apply(module_name, ident, _type_arguments) => {
let symbol = make_apply_symbol(env, ability.region, scope, module_name, ident)?; let symbol = make_apply_symbol(env, ability.region, scope, module_name, ident)?;
// Ability defined locally, whose members we are constructing right now... // Ability defined locally, whose members we are constructing right now...
if !pending_abilities_in_scope.contains_key(&symbol) if !pending_abilities_in_scope.contains_key(&symbol)
// or an ability that was imported from elsewhere // or an ability that was imported from elsewhere
&& !scope.abilities_store.is_ability(symbol) && !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; let region = ability.region;
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region });
return Err(Type::Erroneous(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) { if let Some(shadowing) = introduced_variables.named_var_by_name(&var_name) {
let var_name_ident = var_name.to_string().into(); let var_name_ident = var_name.to_string().into();
@ -974,7 +974,7 @@ fn canonicalize_has_clause(
let var = var_store.fresh(); 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(()) Ok(())
} }
@ -1126,7 +1126,7 @@ pub fn freshen_opaque_def(
.iter() .iter()
.map(|alias_var| OptAbleVar { .map(|alias_var| OptAbleVar {
var: var_store.fresh(), var: var_store.fresh(),
opt_ability: alias_var.value.opt_bound_ability, opt_abilities: alias_var.value.opt_bound_abilities.clone(),
}) })
.collect(); .collect();

View file

@ -357,14 +357,14 @@ fn canonicalize_alias<'a>(
// This is a valid lowercase rigid var for the type def. // This is a valid lowercase rigid var for the type def.
let named_variable = named.swap_remove(index); let named_variable = named.swap_remove(index);
let var = named_variable.variable(); 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(); let name = named_variable.name();
can_vars.push(Loc { can_vars.push(Loc {
value: AliasVar { value: AliasVar {
name, name,
var, var,
opt_bound_ability, opt_bound_abilities,
}, },
region: loc_lowercase.region, region: loc_lowercase.region,
}); });
@ -386,7 +386,7 @@ fn canonicalize_alias<'a>(
value: AliasVar { value: AliasVar {
name: loc_lowercase.value.clone(), name: loc_lowercase.value.clone(),
var: var_store.fresh(), var: var_store.fresh(),
opt_bound_ability: None, opt_bound_abilities: None,
}, },
region: loc_lowercase.region, region: loc_lowercase.region,
}); });
@ -859,7 +859,7 @@ fn canonicalize_opaque<'a>(
alias_var.region, alias_var.region,
OptAbleType { OptAbleType {
typ: Type::Variable(var_store.fresh()), 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 .introduced_variables
.able .able
.iter() .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() { let var_bound_to_ability = match variables_bound_to_ability.as_slice() {
[one] => one.variable, [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 { let alias_opt_able_vars = alias.type_variables.iter().map(|l| OptAbleType {
typ: Type::Variable(l.value.var), 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(); let lambda_set_vars = alias.lambda_set_variables.iter();

View file

@ -1552,7 +1552,7 @@ fn build_fresh_opaque_variables(
); );
let type_arguments = vec![OptAbleVar { let type_arguments = vec![OptAbleVar {
var: a_var, var: a_var,
opt_ability: None, opt_abilities: None,
}]; }];
let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))]; let lambda_set_variables = vec![roc_types::types::LambdaSet(Type::Variable(closure_var))];

View file

@ -136,7 +136,7 @@ pub struct Module {
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct RigidVariables { pub struct RigidVariables {
pub named: MutMap<Variable, Lowercase>, pub named: MutMap<Variable, Lowercase>,
pub able: MutMap<Variable, (Lowercase, Symbol)>, pub able: MutMap<Variable, (Lowercase, Vec<Symbol>)>,
pub wildcards: VecSet<Variable>, pub wildcards: VecSet<Variable>,
} }
@ -387,7 +387,7 @@ pub fn canonicalize_module_defs<'a>(
for able in output.introduced_variables.able { for able in output.introduced_variables.able {
rigid_variables rigid_variables
.able .able
.insert(able.variable, (able.name, able.ability)); .insert(able.variable, (able.name, able.abilities));
} }
for var in output.introduced_variables.wildcards { for var in output.introduced_variables.wildcards {

View file

@ -1143,7 +1143,7 @@ pub fn constrain_expr(
.iter() .iter()
.map(|v| OptAbleType { .map(|v| OptAbleType {
typ: Type::Variable(v.var), typ: Type::Variable(v.var),
opt_ability: v.opt_ability, opt_abilities: v.opt_abilities.clone(),
}) })
.collect(), .collect(),
lambda_set_variables: lambda_set_variables.clone(), lambda_set_variables: lambda_set_variables.clone(),
@ -1208,7 +1208,7 @@ pub fn constrain_expr(
.iter() .iter()
.map(|v| OptAbleType { .map(|v| OptAbleType {
typ: Type::Variable(v.var), typ: Type::Variable(v.var),
opt_ability: v.opt_ability, opt_abilities: v.opt_abilities.clone(),
}) })
.collect(), .collect(),
lambda_set_variables: lambda_set_variables.clone(), lambda_set_variables: lambda_set_variables.clone(),

View file

@ -541,7 +541,7 @@ pub fn constrain_pattern(
.iter() .iter()
.map(|v| OptAbleType { .map(|v| OptAbleType {
typ: Type::Variable(v.var), typ: Type::Variable(v.var),
opt_ability: v.opt_ability, opt_abilities: v.opt_abilities.clone(),
}) })
.collect(), .collect(),
lambda_set_variables: lambda_set_variables.clone(), lambda_set_variables: lambda_set_variables.clone(),

View file

@ -70,8 +70,9 @@ pub fn run_solve(
subs.rigid_var(var, name); subs.rigid_var(var, name);
} }
for (var, (name, ability)) in rigid_variables.able { for (var, (name, abilities)) in rigid_variables.able {
subs.rigid_able_var(var, name, ability); // TODO(abilities)
subs.rigid_able_var(var, name, abilities[0]);
} }
for var in rigid_variables.wildcards { for var in rigid_variables.wildcards {

View file

@ -345,12 +345,12 @@ impl Aliases {
for OptAbleVar { for OptAbleVar {
var: rec_var, var: rec_var,
opt_ability, opt_abilities,
} in delayed_variables } in delayed_variables
.recursion_variables(&mut self.variables) .recursion_variables(&mut self.variables)
.iter_mut() .iter_mut()
{ {
debug_assert!(opt_ability.is_none()); debug_assert!(opt_abilities.is_none());
let new_var = subs.fresh_unnamed_flex_var(); let new_var = subs.fresh_unnamed_flex_var();
substitutions.insert(*rec_var, new_var); substitutions.insert(*rec_var, new_var);
@ -367,7 +367,7 @@ impl Aliases {
.iter_mut() .iter_mut()
.zip(new_lambda_set_variables) .zip(new_lambda_set_variables)
{ {
debug_assert!(old.opt_ability.is_none()); debug_assert!(old.opt_abilities.is_none());
if old.var != *new { if old.var != *new {
substitutions.insert(old.var, *new); substitutions.insert(old.var, *new);
@ -2291,7 +2291,7 @@ fn type_to_variable<'a>(
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
let mut stack = Vec::with_capacity_in(8, arena); 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 { macro_rules! helper {
($typ:expr, $ambient_function_policy:expr) => {{ ($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) { for (target_index, arg_type) in (new_variables.indices()).zip(type_arguments) {
let copy_var = helper!(&arg_type.value.typ); let copy_var = helper!(&arg_type.value.typ);
subs.variables[target_index] = copy_var; subs.variables[target_index] = copy_var;
if let Some(ability) = arg_type.value.opt_ability { if let Some(abilities) = arg_type.value.opt_abilities.as_ref() {
bind_to_ability.push((Loc::at(arg_type.region, copy_var), ability)); 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 length = type_arguments.len() + lambda_set_variables.len();
let new_variables = VariableSubsSlice::reserve_into_subs(subs, length); 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) (new_variables.indices()).zip(type_arguments)
{ {
let copy_var = match opt_ability { let copy_var = match opt_abilities {
None => helper!(typ), 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 // If this type argument is marked as being bound to an ability, we must
// now correctly instantiate it as so. // now correctly instantiate it as so.
match RegisterVariable::from_type(subs, rank, pools, arena, typ) { match RegisterVariable::from_type(subs, rank, pools, arena, typ) {
@ -2608,7 +2611,7 @@ fn type_to_variable<'a>(
use Content::*; use Content::*;
match *subs.get_content_without_compacting(var) { match *subs.get_content_without_compacting(var) {
FlexVar(opt_name) => subs 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), 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), 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) { match *subs.get_content_unchecked(var) {
Content::RigidVar(a) => { 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 // pass, already bound
} }
_ => { _ => {
// TODO(multi-abilities)
let flex_ability = subs.fresh(Descriptor { let flex_ability = subs.fresh(Descriptor {
content: Content::FlexAbleVar(None, ability), content: Content::FlexAbleVar(None, abilities[0]),
rank, rank,
mark: Mark::NONE, mark: Mark::NONE,
copy: OptVariable::NONE, copy: OptVariable::NONE,

View file

@ -244,17 +244,17 @@ pub struct AliasCommon {
pub lambda_set_variables: Vec<LambdaSet>, pub lambda_set_variables: Vec<LambdaSet>,
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Debug)]
pub struct OptAbleVar { pub struct OptAbleVar {
pub var: Variable, pub var: Variable,
pub opt_ability: Option<Symbol>, pub opt_abilities: Option<Vec<Symbol>>,
} }
impl OptAbleVar { impl OptAbleVar {
pub fn unbound(var: Variable) -> Self { pub fn unbound(var: Variable) -> Self {
Self { Self {
var, var,
opt_ability: None, opt_abilities: None,
} }
} }
} }
@ -262,14 +262,14 @@ impl OptAbleVar {
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub struct OptAbleType { pub struct OptAbleType {
pub typ: Type, pub typ: Type,
pub opt_ability: Option<Symbol>, pub opt_abilities: Option<Vec<Symbol>>,
} }
impl OptAbleType { impl OptAbleType {
pub fn unbound(typ: Type) -> Self { pub fn unbound(typ: Type) -> Self {
Self { Self {
typ, 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. // This passes through `Type`, so defer to that to bump the clone counter.
Self { Self {
typ: self.typ.clone(), 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 { for arg in type_arguments {
write!(f, " {:?}", &arg.typ)?; write!(f, " {:?}", &arg.typ)?;
if let Some(ab) = arg.opt_ability { if let Some(abs) = &arg.opt_abilities {
write!(f, ":{:?}", ab)?; write!(f, ":{:?}", abs)?;
} }
} }
@ -1368,7 +1368,7 @@ impl Type {
arg_ann.region, arg_ann.region,
OptAbleType { OptAbleType {
typ: arg_ann.value.clone(), 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: value:
AliasVar { AliasVar {
var: placeholder, var: placeholder,
opt_bound_ability, opt_bound_abilities,
.. ..
}, },
.. ..
@ -1431,7 +1431,7 @@ impl Type {
); );
named_args.push(OptAbleType { named_args.push(OptAbleType {
typ: filler.value.clone(), typ: filler.value.clone(),
opt_ability: *opt_bound_ability, opt_abilities: opt_bound_abilities.clone(),
}); });
substitution.insert(*placeholder, filler.value); substitution.insert(*placeholder, filler.value);
} }
@ -2111,8 +2111,8 @@ impl AliasKind {
pub struct AliasVar { pub struct AliasVar {
pub name: Lowercase, pub name: Lowercase,
pub var: Variable, pub var: Variable,
/// `Some` if this variable is bound to an ability; `None` otherwise. /// `Some` if this variable is bound to abilities; `None` otherwise.
pub opt_bound_ability: Option<Symbol>, pub opt_bound_abilities: Option<Vec<Symbol>>,
} }
impl AliasVar { impl AliasVar {
@ -2120,7 +2120,7 @@ impl AliasVar {
Self { Self {
name, name,
var, var,
opt_bound_ability: None, opt_bound_abilities: None,
} }
} }
} }
@ -2129,7 +2129,7 @@ impl From<&AliasVar> for OptAbleVar {
fn from(av: &AliasVar) -> OptAbleVar { fn from(av: &AliasVar) -> OptAbleVar {
OptAbleVar { OptAbleVar {
var: av.var, var: av.var,
opt_ability: av.opt_bound_ability, opt_abilities: av.opt_bound_abilities.clone(),
} }
} }
} }