Make sure type variables bound to abilities are instantiated in aliases

Closes #4259
This commit is contained in:
Ayaz Hafiz 2022-10-11 15:04:38 -05:00
parent 89a4522faa
commit 20e4295eea
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
8 changed files with 435 additions and 66 deletions

View file

@ -274,6 +274,9 @@ impl Aliases {
subs: &mut Subs,
rank: Rank,
pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &AbilitiesStore,
obligation_cache: &mut ObligationCache,
arena: &bumpalo::Bump,
symbol: Symbol,
alias_variables: AliasVariables,
@ -375,7 +378,18 @@ impl Aliases {
if !can_reuse_old_definition {
let mut typ = typ.clone();
typ.substitute_variables(&substitutions);
let alias_variable = type_to_variable(subs, rank, pools, arena, self, &typ, false);
let alias_variable = type_to_variable(
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
self,
&typ,
false,
);
(alias_variable, kind)
} else {
if !substitutions.is_empty() {
@ -389,7 +403,18 @@ impl Aliases {
// assumption: an alias does not (transitively) syntactically contain itself
// (if it did it would have to be a recursive tag union, which we should have fixed up
// during canonicalization)
let alias_variable = type_to_variable(subs, rank, pools, arena, self, &t, false);
let alias_variable = type_to_variable(
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
self,
&t,
false,
);
{
match self.aliases.iter_mut().find(|(s, _, _, _)| *s == symbol) {
@ -562,7 +587,14 @@ fn run_in_place(
let mut obligation_cache = ObligationCache::default();
let mut awaiting_specializations = AwaitingSpecializations::default();
let pending_derives = PendingDerivesTable::new(subs, aliases, pending_derives);
let pending_derives = PendingDerivesTable::new(
subs,
aliases,
pending_derives,
problems,
abilities_store,
&mut obligation_cache,
);
let CheckedDerives {
legal_derives: _,
problems: derives_problems,
@ -687,6 +719,9 @@ fn solve(
constraints,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
subs,
let_con.def_types,
@ -747,6 +782,9 @@ fn solve(
constraints,
next_rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
subs,
let_con.def_types,
@ -858,11 +896,29 @@ fn solve(
Eq(roc_can::constraint::Eq(type_index, expectation_index, category_index, region)) => {
let category = &constraints.categories[category_index.index()];
let actual =
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index);
let actual = either_type_index_to_var(
constraints,
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
*type_index,
);
let expectation = &constraints.expectations[expectation_index.index()];
let expected = type_to_var(subs, rank, pools, aliases, expectation.get_type_ref());
let expected = type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
expectation.get_type_ref(),
);
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
Success {
@ -927,6 +983,9 @@ fn solve(
subs,
rank,
pools,
&mut vec![], // don't report any extra errors
abilities_store,
obligation_cache,
aliases,
*source_index,
);
@ -962,8 +1021,16 @@ fn solve(
let actual = deep_copy_var_in(subs, rank, pools, var, arena);
let expectation = &constraints.expectations[expectation_index.index()];
let expected =
type_to_var(subs, rank, pools, aliases, expectation.get_type_ref());
let expected = type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
expectation.get_type_ref(),
);
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
Success {
@ -1048,11 +1115,29 @@ fn solve(
| PatternPresence(type_index, expectation_index, category_index, region) => {
let category = &constraints.pattern_categories[category_index.index()];
let actual =
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index);
let actual = either_type_index_to_var(
constraints,
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
*type_index,
);
let expectation = &constraints.pattern_expectations[expectation_index.index()];
let expected = type_to_var(subs, rank, pools, aliases, expectation.get_type_ref());
let expected = type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
expectation.get_type_ref(),
);
let mode = match constraint {
PatternPresence(..) => Mode::PRESENT,
@ -1209,8 +1294,17 @@ fn solve(
}
}
IsOpenType(type_index) => {
let actual =
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index);
let actual = either_type_index_to_var(
constraints,
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
*type_index,
);
open_tag_union(subs, actual);
@ -1231,12 +1325,30 @@ fn solve(
let tys = &constraints.types[types.indices()];
let pattern_category = &constraints.pattern_categories[pattern_category.index()];
let actual = type_to_var(subs, rank, pools, aliases, typ);
let actual = type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
typ,
);
let tag_ty = Type::TagUnion(
vec![(tag_name.clone(), tys.to_vec())],
TypeExtension::Closed,
);
let includes = type_to_var(subs, rank, pools, aliases, &tag_ty);
let includes = type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
&tag_ty,
);
match unify(&mut UEnv::new(subs), actual, includes, Mode::PRESENT) {
Success {
@ -1337,10 +1449,28 @@ fn solve(
}
};
let real_var =
either_type_index_to_var(constraints, subs, rank, pools, aliases, real_var);
let real_var = either_type_index_to_var(
constraints,
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
real_var,
);
let branches_var = type_to_var(subs, rank, pools, aliases, expected_type);
let branches_var = type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
expected_type,
);
let real_content = subs.get_content_without_compacting(real_var);
let branches_content = subs.get_content_without_compacting(branches_var);
@ -1889,6 +2019,9 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
constraints: &Constraints,
rank: Rank,
pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
aliases: &mut Aliases,
subs: &mut Subs,
def_types_slice: roc_can::constraint::DefTypes,
@ -1899,7 +2032,16 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
let mut local_def_vars = Self::with_length(types_slice.len());
for (&(symbol, region), typ) in (loc_symbols_slice.iter()).zip(types_slice) {
let var = type_to_var(subs, rank, pools, aliases, typ);
let var = type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
typ,
);
local_def_vars.push((symbol, Loc { value: var, region }));
}
@ -1930,6 +2072,9 @@ fn either_type_index_to_var(
subs: &mut Subs,
rank: Rank,
pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
aliases: &mut Aliases,
either_type_index: roc_collections::soa::EitherIndex<Type, Variable>,
) -> Variable {
@ -1937,7 +2082,16 @@ fn either_type_index_to_var(
Ok(type_index) => {
let typ = &constraints.types[type_index.index()];
type_to_var(subs, rank, pools, aliases, typ)
type_to_var(
subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
typ,
)
}
Err(var_index) => {
// we cheat, and store the variable directly in the index
@ -1949,6 +2103,9 @@ fn either_type_index_to_var(
pub(crate) fn type_to_var(
subs: &mut Subs,
rank: Rank,
problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
pools: &mut Pools,
aliases: &mut Aliases,
typ: &Type,
@ -1958,7 +2115,18 @@ pub(crate) fn type_to_var(
} else {
let mut arena = take_scratchpad();
let var = type_to_variable(subs, rank, pools, &arena, aliases, typ, false);
let var = type_to_variable(
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
&arena,
aliases,
typ,
false,
);
arena.reset();
put_scratchpad(arena);
@ -2109,6 +2277,9 @@ fn type_to_variable<'a>(
subs: &mut Subs,
rank: Rank,
pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &AbilitiesStore,
obligation_cache: &mut ObligationCache,
arena: &'a bumpalo::Bump,
aliases: &mut Aliases,
typ: &Type,
@ -2118,6 +2289,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);
macro_rules! helper {
($typ:expr, $ambient_function_policy:expr) => {{
@ -2165,7 +2337,7 @@ fn type_to_variable<'a>(
Apply(symbol, arguments, _) => {
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
for (target_index, var_index) in (new_arguments.indices()).zip(arguments) {
let var = helper!(var_index);
let var = helper!(&var_index.value);
subs.variables[target_index] = var;
}
@ -2356,8 +2528,11 @@ fn type_to_variable<'a>(
let new_variables = VariableSubsSlice::reserve_into_subs(subs, length);
for (target_index, arg_type) in (new_variables.indices()).zip(type_arguments) {
let copy_var = helper!(arg_type);
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));
}
}
let it = (new_variables.indices().skip(type_arguments.len()))
@ -2365,8 +2540,18 @@ fn type_to_variable<'a>(
for (target_index, ls) in it {
// We MUST do this now, otherwise when linking the ambient function during
// instantiation of the real var, there will be nothing to link against.
let copy_var =
type_to_variable(subs, rank, pools, arena, aliases, &ls.0, true);
let copy_var = type_to_variable(
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
aliases,
&ls.0,
true,
);
subs.variables[target_index] = copy_var;
}
@ -2381,6 +2566,9 @@ fn type_to_variable<'a>(
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
*symbol,
alias_variables,
@ -2488,8 +2676,18 @@ fn type_to_variable<'a>(
for (target_index, ls) in it {
// We MUST do this now, otherwise when linking the ambient function during
// instantiation of the real var, there will be nothing to link against.
let copy_var =
type_to_variable(subs, rank, pools, arena, aliases, &ls.0, true);
let copy_var = type_to_variable(
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
aliases,
&ls.0,
true,
);
subs.variables[target_index] = copy_var;
}
@ -2501,8 +2699,18 @@ fn type_to_variable<'a>(
};
// cannot use helper! here because this variable may be involved in unification below
let alias_variable =
type_to_variable(subs, rank, pools, arena, aliases, alias_type, false);
let alias_variable = type_to_variable(
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
aliases,
alias_type,
false,
);
// TODO(opaques): I think host-exposed aliases should always be structural
// (when does it make sense to give a host an opaque type?)
let content = Content::Alias(
@ -2533,6 +2741,64 @@ fn type_to_variable<'a>(
};
}
for (Loc { value: var, region }, ability) in bind_to_ability {
match subs.get_content_unchecked(var) {
&Content::RigidVar(a) => {
subs.set_content(var, Content::RigidAbleVar(a, ability));
}
_ => {
let flex_ability = subs.fresh(Descriptor {
content: Content::FlexAbleVar(None, ability),
rank,
mark: Mark::NONE,
copy: OptVariable::NONE,
});
let category = Category::OpaqueArg;
match unify(&mut UEnv::new(subs), var, flex_ability, Mode::EQ) {
Success {
vars: _,
must_implement_ability,
lambda_sets_to_specialize,
extra_metadata: _,
} => {
// No introduction needed
if !must_implement_ability.is_empty() {
let new_problems = obligation_cache.check_obligations(
subs,
abilities_store,
must_implement_ability,
AbilityImplError::BadExpr(region, category, flex_ability),
);
problems.extend(new_problems);
}
debug_assert!(lambda_sets_to_specialize
.drain()
.all(|(_, vals)| vals.is_empty()));
}
Failure(_vars, actual_type, expected_type, _bad_impls) => {
// No introduction needed
let problem = TypeError::BadExpr(
region,
category,
actual_type,
Expected::NoExpectation(expected_type),
);
problems.push(problem);
}
BadType(_vars, problem) => {
// No introduction needed
problems.push(TypeError::BadType(problem));
}
}
}
}
}
result
}