Merge pull request #4304 from roc-lang/i4259

Make sure type variables bound to abilities are instantiated in aliases
This commit is contained in:
Ayaz 2022-10-14 16:07:58 -05:00 committed by GitHub
commit c1c339dbdf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 440 additions and 66 deletions

View file

@ -553,7 +553,7 @@ fn can_annotation_help(
references, references,
); );
args.push(arg_ann); args.push(Loc::at(arg.region, arg_ann));
} }
match scope.lookup_alias(symbol) { match scope.lookup_alias(symbol) {
@ -573,8 +573,14 @@ fn can_annotation_help(
let mut type_var_to_arg = Vec::new(); let mut type_var_to_arg = Vec::new();
for (_, arg_ann) in alias.type_variables.iter().zip(args) { for (alias_arg, arg_ann) in alias.type_variables.iter().zip(args) {
type_var_to_arg.push(arg_ann); type_var_to_arg.push(Loc::at(
arg_ann.region,
OptAbleType {
typ: arg_ann.value,
opt_ability: alias_arg.value.opt_bound_ability,
},
));
} }
let mut lambda_set_variables = let mut lambda_set_variables =

View file

@ -829,7 +829,15 @@ fn canonicalize_opaque<'a>(
type_arguments: alias type_arguments: alias
.type_variables .type_variables
.iter() .iter()
.map(|_| Type::Variable(var_store.fresh())) .map(|alias_var| {
Loc::at(
alias_var.region,
OptAbleType {
typ: Type::Variable(var_store.fresh()),
opt_ability: alias_var.value.opt_bound_ability,
},
)
})
.collect(), .collect(),
lambda_set_variables: alias lambda_set_variables: alias
.lambda_set_variables .lambda_set_variables

View file

@ -3,7 +3,7 @@ use roc_can::constraint::{Constraint, Constraints};
use roc_can::expected::Expected::{self, *}; use roc_can::expected::Expected::{self, *};
use roc_can::num::{FloatBound, FloatWidth, IntBound, IntLitWidth, NumBound, SignDemand}; use roc_can::num::{FloatBound, FloatWidth, IntBound, IntLitWidth, NumBound, SignDemand};
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_region::all::Region; use roc_region::all::{Loc, Region};
use roc_types::num::{NumericRange, SingleQuoteBound}; use roc_types::num::{NumericRange, SingleQuoteBound};
use roc_types::subs::Variable; use roc_types::subs::Variable;
use roc_types::types::Type::{self, *}; use roc_types::types::Type::{self, *};
@ -198,7 +198,11 @@ pub fn num_literal(
#[inline(always)] #[inline(always)]
pub fn builtin_type(symbol: Symbol, args: Vec<Type>) -> Type { pub fn builtin_type(symbol: Symbol, args: Vec<Type>) -> Type {
Type::Apply(symbol, args, Region::zero()) Type::Apply(
symbol,
args.into_iter().map(Loc::at_zero).collect(),
Region::zero(),
)
} }
#[inline(always)] #[inline(always)]

View file

@ -50,7 +50,14 @@ pub struct PendingDerivesTable(
); );
impl PendingDerivesTable { impl PendingDerivesTable {
pub fn new(subs: &mut Subs, aliases: &mut Aliases, pending_derives: PendingDerives) -> Self { pub fn new(
subs: &mut Subs,
aliases: &mut Aliases,
pending_derives: PendingDerives,
problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
) -> Self {
let mut table = VecMap::with_capacity(pending_derives.len()); let mut table = VecMap::with_capacity(pending_derives.len());
for (opaque, (typ, derives)) in pending_derives.into_iter() { for (opaque, (typ, derives)) in pending_derives.into_iter() {
@ -66,8 +73,16 @@ impl PendingDerivesTable {
let derive_key = RequestedDeriveKey { opaque, ability }; let derive_key = RequestedDeriveKey { opaque, ability };
// Neither rank nor pools should matter here. // Neither rank nor pools should matter here.
let opaque_var = let opaque_var = type_to_var(
type_to_var(subs, Rank::toplevel(), &mut Pools::default(), aliases, &typ); subs,
Rank::toplevel(),
problems,
abilities_store,
obligation_cache,
&mut Pools::default(),
aliases,
&typ,
);
let real_var = match subs.get_content_without_compacting(opaque_var) { let real_var = match subs.get_content_without_compacting(opaque_var) {
Content::Alias(_, _, real_var, AliasKind::Opaque) => real_var, Content::Alias(_, _, real_var, AliasKind::Opaque) => real_var,
_ => internal_error!("Non-opaque in derives table"), _ => internal_error!("Non-opaque in derives table"),

View file

@ -1,3 +1,5 @@
#![allow(clippy::too_many_arguments)]
use crate::ability::{ use crate::ability::{
resolve_ability_specialization, type_implementing_specialization, AbilityImplError, resolve_ability_specialization, type_implementing_specialization, AbilityImplError,
CheckedDerives, ObligationCache, PendingDerivesTable, Resolved, CheckedDerives, ObligationCache, PendingDerivesTable, Resolved,
@ -274,6 +276,9 @@ impl Aliases {
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &AbilitiesStore,
obligation_cache: &mut ObligationCache,
arena: &bumpalo::Bump, arena: &bumpalo::Bump,
symbol: Symbol, symbol: Symbol,
alias_variables: AliasVariables, alias_variables: AliasVariables,
@ -375,7 +380,18 @@ impl Aliases {
if !can_reuse_old_definition { if !can_reuse_old_definition {
let mut typ = typ.clone(); let mut typ = typ.clone();
typ.substitute_variables(&substitutions); 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) (alias_variable, kind)
} else { } else {
if !substitutions.is_empty() { if !substitutions.is_empty() {
@ -389,7 +405,18 @@ impl Aliases {
// assumption: an alias does not (transitively) syntactically contain itself // 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 // (if it did it would have to be a recursive tag union, which we should have fixed up
// during canonicalization) // 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) { match self.aliases.iter_mut().find(|(s, _, _, _)| *s == symbol) {
@ -562,7 +589,14 @@ fn run_in_place(
let mut obligation_cache = ObligationCache::default(); let mut obligation_cache = ObligationCache::default();
let mut awaiting_specializations = AwaitingSpecializations::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 { let CheckedDerives {
legal_derives: _, legal_derives: _,
problems: derives_problems, problems: derives_problems,
@ -687,6 +721,9 @@ fn solve(
constraints, constraints,
rank, rank,
pools, pools,
problems,
abilities_store,
obligation_cache,
aliases, aliases,
subs, subs,
let_con.def_types, let_con.def_types,
@ -747,6 +784,9 @@ fn solve(
constraints, constraints,
next_rank, next_rank,
pools, pools,
problems,
abilities_store,
obligation_cache,
aliases, aliases,
subs, subs,
let_con.def_types, let_con.def_types,
@ -858,11 +898,29 @@ fn solve(
Eq(roc_can::constraint::Eq(type_index, expectation_index, category_index, region)) => { Eq(roc_can::constraint::Eq(type_index, expectation_index, category_index, region)) => {
let category = &constraints.categories[category_index.index()]; let category = &constraints.categories[category_index.index()];
let actual = let actual = either_type_index_to_var(
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index); constraints,
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
*type_index,
);
let expectation = &constraints.expectations[expectation_index.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) { match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
Success { Success {
@ -927,6 +985,9 @@ fn solve(
subs, subs,
rank, rank,
pools, pools,
&mut vec![], // don't report any extra errors
abilities_store,
obligation_cache,
aliases, aliases,
*source_index, *source_index,
); );
@ -962,8 +1023,16 @@ fn solve(
let actual = deep_copy_var_in(subs, rank, pools, var, arena); let actual = deep_copy_var_in(subs, rank, pools, var, arena);
let expectation = &constraints.expectations[expectation_index.index()]; let expectation = &constraints.expectations[expectation_index.index()];
let expected = let expected = type_to_var(
type_to_var(subs, rank, pools, aliases, expectation.get_type_ref()); subs,
rank,
problems,
abilities_store,
obligation_cache,
pools,
aliases,
expectation.get_type_ref(),
);
match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) { match unify(&mut UEnv::new(subs), actual, expected, Mode::EQ) {
Success { Success {
@ -1048,11 +1117,29 @@ fn solve(
| PatternPresence(type_index, expectation_index, category_index, region) => { | PatternPresence(type_index, expectation_index, category_index, region) => {
let category = &constraints.pattern_categories[category_index.index()]; let category = &constraints.pattern_categories[category_index.index()];
let actual = let actual = either_type_index_to_var(
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index); constraints,
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
*type_index,
);
let expectation = &constraints.pattern_expectations[expectation_index.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 { let mode = match constraint {
PatternPresence(..) => Mode::PRESENT, PatternPresence(..) => Mode::PRESENT,
@ -1209,8 +1296,17 @@ fn solve(
} }
} }
IsOpenType(type_index) => { IsOpenType(type_index) => {
let actual = let actual = either_type_index_to_var(
either_type_index_to_var(constraints, subs, rank, pools, aliases, *type_index); constraints,
subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
aliases,
*type_index,
);
open_tag_union(subs, actual); open_tag_union(subs, actual);
@ -1231,12 +1327,30 @@ fn solve(
let tys = &constraints.types[types.indices()]; let tys = &constraints.types[types.indices()];
let pattern_category = &constraints.pattern_categories[pattern_category.index()]; 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( let tag_ty = Type::TagUnion(
vec![(tag_name.clone(), tys.to_vec())], vec![(tag_name.clone(), tys.to_vec())],
TypeExtension::Closed, 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) { match unify(&mut UEnv::new(subs), actual, includes, Mode::PRESENT) {
Success { Success {
@ -1337,10 +1451,28 @@ fn solve(
} }
}; };
let real_var = let real_var = either_type_index_to_var(
either_type_index_to_var(constraints, subs, rank, pools, aliases, real_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 real_content = subs.get_content_without_compacting(real_var);
let branches_content = subs.get_content_without_compacting(branches_var); let branches_content = subs.get_content_without_compacting(branches_var);
@ -1889,6 +2021,9 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
constraints: &Constraints, constraints: &Constraints,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
aliases: &mut Aliases, aliases: &mut Aliases,
subs: &mut Subs, subs: &mut Subs,
def_types_slice: roc_can::constraint::DefTypes, def_types_slice: roc_can::constraint::DefTypes,
@ -1899,7 +2034,16 @@ impl LocalDefVarsVec<(Symbol, Loc<Variable>)> {
let mut local_def_vars = Self::with_length(types_slice.len()); let mut local_def_vars = Self::with_length(types_slice.len());
for (&(symbol, region), typ) in (loc_symbols_slice.iter()).zip(types_slice) { 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 })); local_def_vars.push((symbol, Loc { value: var, region }));
} }
@ -1930,6 +2074,9 @@ fn either_type_index_to_var(
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
aliases: &mut Aliases, aliases: &mut Aliases,
either_type_index: roc_collections::soa::EitherIndex<Type, Variable>, either_type_index: roc_collections::soa::EitherIndex<Type, Variable>,
) -> Variable { ) -> Variable {
@ -1937,7 +2084,16 @@ fn either_type_index_to_var(
Ok(type_index) => { Ok(type_index) => {
let typ = &constraints.types[type_index.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) => { Err(var_index) => {
// we cheat, and store the variable directly in the index // we cheat, and store the variable directly in the index
@ -1949,6 +2105,9 @@ fn either_type_index_to_var(
pub(crate) fn type_to_var( pub(crate) fn type_to_var(
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
problems: &mut Vec<TypeError>,
abilities_store: &mut AbilitiesStore,
obligation_cache: &mut ObligationCache,
pools: &mut Pools, pools: &mut Pools,
aliases: &mut Aliases, aliases: &mut Aliases,
typ: &Type, typ: &Type,
@ -1958,7 +2117,18 @@ pub(crate) fn type_to_var(
} else { } else {
let mut arena = take_scratchpad(); 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(); arena.reset();
put_scratchpad(arena); put_scratchpad(arena);
@ -2109,6 +2279,9 @@ fn type_to_variable<'a>(
subs: &mut Subs, subs: &mut Subs,
rank: Rank, rank: Rank,
pools: &mut Pools, pools: &mut Pools,
problems: &mut Vec<TypeError>,
abilities_store: &AbilitiesStore,
obligation_cache: &mut ObligationCache,
arena: &'a bumpalo::Bump, arena: &'a bumpalo::Bump,
aliases: &mut Aliases, aliases: &mut Aliases,
typ: &Type, typ: &Type,
@ -2118,6 +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);
macro_rules! helper { macro_rules! helper {
($typ:expr, $ambient_function_policy:expr) => {{ ($typ:expr, $ambient_function_policy:expr) => {{
@ -2165,7 +2339,7 @@ fn type_to_variable<'a>(
Apply(symbol, arguments, _) => { Apply(symbol, arguments, _) => {
let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len()); let new_arguments = VariableSubsSlice::reserve_into_subs(subs, arguments.len());
for (target_index, var_index) in (new_arguments.indices()).zip(arguments) { 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; subs.variables[target_index] = var;
} }
@ -2356,8 +2530,11 @@ fn type_to_variable<'a>(
let new_variables = VariableSubsSlice::reserve_into_subs(subs, length); let new_variables = VariableSubsSlice::reserve_into_subs(subs, length);
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); 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 {
bind_to_ability.push((Loc::at(arg_type.region, copy_var), ability));
}
} }
let it = (new_variables.indices().skip(type_arguments.len())) let it = (new_variables.indices().skip(type_arguments.len()))
@ -2365,8 +2542,18 @@ fn type_to_variable<'a>(
for (target_index, ls) in it { for (target_index, ls) in it {
// We MUST do this now, otherwise when linking the ambient function during // We MUST do this now, otherwise when linking the ambient function during
// instantiation of the real var, there will be nothing to link against. // instantiation of the real var, there will be nothing to link against.
let copy_var = let copy_var = type_to_variable(
type_to_variable(subs, rank, pools, arena, aliases, &ls.0, true); subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
aliases,
&ls.0,
true,
);
subs.variables[target_index] = copy_var; subs.variables[target_index] = copy_var;
} }
@ -2381,6 +2568,9 @@ fn type_to_variable<'a>(
subs, subs,
rank, rank,
pools, pools,
problems,
abilities_store,
obligation_cache,
arena, arena,
*symbol, *symbol,
alias_variables, alias_variables,
@ -2488,8 +2678,18 @@ fn type_to_variable<'a>(
for (target_index, ls) in it { for (target_index, ls) in it {
// We MUST do this now, otherwise when linking the ambient function during // We MUST do this now, otherwise when linking the ambient function during
// instantiation of the real var, there will be nothing to link against. // instantiation of the real var, there will be nothing to link against.
let copy_var = let copy_var = type_to_variable(
type_to_variable(subs, rank, pools, arena, aliases, &ls.0, true); subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
aliases,
&ls.0,
true,
);
subs.variables[target_index] = copy_var; subs.variables[target_index] = copy_var;
} }
@ -2501,8 +2701,18 @@ fn type_to_variable<'a>(
}; };
// cannot use helper! here because this variable may be involved in unification below // cannot use helper! here because this variable may be involved in unification below
let alias_variable = let alias_variable = type_to_variable(
type_to_variable(subs, rank, pools, arena, aliases, alias_type, false); subs,
rank,
pools,
problems,
abilities_store,
obligation_cache,
arena,
aliases,
alias_type,
false,
);
// TODO(opaques): I think host-exposed aliases should always be structural // TODO(opaques): I think host-exposed aliases should always be structural
// (when does it make sense to give a host an opaque type?) // (when does it make sense to give a host an opaque type?)
let content = Content::Alias( let content = Content::Alias(
@ -2533,6 +2743,67 @@ 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));
}
Content::RigidAbleVar(_, ab) if ab == ability => {
// pass, already bound
}
_ => {
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 result
} }

View file

@ -7991,4 +7991,22 @@ mod solve_expr {
"Bool", "Bool",
); );
} }
#[test]
fn expand_able_variables_in_type_alias() {
infer_queries!(
indoc!(
r#"
app "test" provides [main] to "./platform"
F a : a | a has Hash
main : F a -> F a
#^^^^{-1}
"#
),
@"main : a -[[main(0)]]-> a | a has Hash"
print_only_under_alias: true
);
}
} }

View file

@ -209,7 +209,7 @@ impl LambdaSet {
#[derive(PartialEq, Eq, Clone)] #[derive(PartialEq, Eq, Clone)]
pub struct AliasCommon { pub struct AliasCommon {
pub symbol: Symbol, pub symbol: Symbol,
pub type_arguments: Vec<Type>, pub type_arguments: Vec<Loc<OptAbleType>>,
pub lambda_set_variables: Vec<LambdaSet>, pub lambda_set_variables: Vec<LambdaSet>,
} }
@ -282,7 +282,7 @@ pub enum Type {
}, },
RecursiveTagUnion(Variable, Vec<(TagName, Vec<Type>)>, TypeExtension), RecursiveTagUnion(Variable, Vec<(TagName, Vec<Type>)>, TypeExtension),
/// Applying a type to some arguments (e.g. Dict.Dict String Int) /// Applying a type to some arguments (e.g. Dict.Dict String Int)
Apply(Symbol, Vec<Type>, Region), Apply(Symbol, Vec<Loc<Type>>, Region),
Variable(Variable), Variable(Variable),
RangedNumber(NumericRange), RangedNumber(NumericRange),
/// A type error, which will code gen to a runtime error /// A type error, which will code gen to a runtime error
@ -793,7 +793,7 @@ impl Type {
.. ..
}) => { }) => {
for value in type_arguments.iter_mut() { for value in type_arguments.iter_mut() {
stack.push(value); stack.push(&mut value.value.typ);
} }
for lambda_set in lambda_set_variables.iter_mut() { for lambda_set in lambda_set_variables.iter_mut() {
@ -833,7 +833,7 @@ impl Type {
stack.push(actual_type); stack.push(actual_type);
} }
Apply(_, args, _) => { Apply(_, args, _) => {
stack.extend(args); stack.extend(args.iter_mut().map(|t| &mut t.value));
} }
RangedNumber(_) => {} RangedNumber(_) => {}
UnspecializedLambdaSet { UnspecializedLambdaSet {
@ -915,7 +915,7 @@ impl Type {
.. ..
}) => { }) => {
for value in type_arguments.iter_mut() { for value in type_arguments.iter_mut() {
stack.push(value); stack.push(&mut value.value.typ);
} }
for lambda_set in lambda_set_variables.iter_mut() { for lambda_set in lambda_set_variables.iter_mut() {
@ -954,7 +954,7 @@ impl Type {
stack.push(actual_type); stack.push(actual_type);
} }
Apply(_, args, _) => { Apply(_, args, _) => {
stack.extend(args); stack.extend(args.iter_mut().map(|t| &mut t.value));
} }
RangedNumber(_) => {} RangedNumber(_) => {}
UnspecializedLambdaSet { UnspecializedLambdaSet {
@ -1021,7 +1021,9 @@ impl Type {
.. ..
}) => { }) => {
for ta in type_arguments { for ta in type_arguments {
ta.substitute_alias(rep_symbol, rep_args, actual)?; ta.value
.typ
.substitute_alias(rep_symbol, rep_args, actual)?;
} }
Ok(()) Ok(())
@ -1042,13 +1044,16 @@ impl Type {
} => actual_type.substitute_alias(rep_symbol, rep_args, actual), } => actual_type.substitute_alias(rep_symbol, rep_args, actual),
Apply(symbol, args, region) if *symbol == rep_symbol => { Apply(symbol, args, region) if *symbol == rep_symbol => {
if args.len() == rep_args.len() if args.len() == rep_args.len()
&& args.iter().zip(rep_args.iter()).all(|(t1, t2)| t1 == t2) && args
.iter()
.zip(rep_args.iter())
.all(|(t1, t2)| &t1.value == t2)
{ {
*self = actual.clone(); *self = actual.clone();
if let Apply(_, args, _) = self { if let Apply(_, args, _) = self {
for arg in args { for arg in args {
arg.substitute_alias(rep_symbol, rep_args, actual)?; arg.value.substitute_alias(rep_symbol, rep_args, actual)?;
} }
} }
return Ok(()); return Ok(());
@ -1057,7 +1062,7 @@ impl Type {
} }
Apply(_, args, _) => { Apply(_, args, _) => {
for arg in args { for arg in args {
arg.substitute_alias(rep_symbol, rep_args, actual)?; arg.value.substitute_alias(rep_symbol, rep_args, actual)?;
} }
Ok(()) Ok(())
} }
@ -1103,7 +1108,9 @@ impl Type {
.. ..
}) => { }) => {
symbol == &rep_symbol symbol == &rep_symbol
|| type_arguments.iter().any(|v| v.contains_symbol(rep_symbol)) || type_arguments
.iter()
.any(|v| v.value.typ.contains_symbol(rep_symbol))
|| lambda_set_variables || lambda_set_variables
.iter() .iter()
.any(|v| v.0.contains_symbol(rep_symbol)) .any(|v| v.0.contains_symbol(rep_symbol))
@ -1117,7 +1124,7 @@ impl Type {
name == &rep_symbol || actual.contains_symbol(rep_symbol) name == &rep_symbol || actual.contains_symbol(rep_symbol)
} }
Apply(symbol, _, _) if *symbol == rep_symbol => true, Apply(symbol, _, _) if *symbol == rep_symbol => true,
Apply(_, args, _) => args.iter().any(|arg| arg.contains_symbol(rep_symbol)), Apply(_, args, _) => args.iter().any(|arg| arg.value.contains_symbol(rep_symbol)),
RangedNumber(_) => false, RangedNumber(_) => false,
UnspecializedLambdaSet { UnspecializedLambdaSet {
unspecialized: Uls(_, sym, _), unspecialized: Uls(_, sym, _),
@ -1174,7 +1181,9 @@ impl Type {
.. ..
} => actual_type.contains_variable(rep_variable), } => actual_type.contains_variable(rep_variable),
HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable), HostExposedAlias { actual, .. } => actual.contains_variable(rep_variable),
Apply(_, args, _) => args.iter().any(|arg| arg.contains_variable(rep_variable)), Apply(_, args, _) => args
.iter()
.any(|arg| arg.value.contains_variable(rep_variable)),
RangedNumber(_) => false, RangedNumber(_) => false,
EmptyRec | EmptyTagUnion | Erroneous(_) => false, EmptyRec | EmptyTagUnion | Erroneous(_) => false,
} }
@ -1259,7 +1268,12 @@ impl Type {
.iter() .iter()
.all(|lambda_set| matches!(lambda_set.0, Type::Variable(..)))); .all(|lambda_set| matches!(lambda_set.0, Type::Variable(..))));
type_arguments.iter_mut().for_each(|t| { type_arguments.iter_mut().for_each(|t| {
t.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables) t.value.typ.instantiate_aliases(
region,
aliases,
var_store,
new_lambda_set_variables,
)
}); });
} }
HostExposedAlias { HostExposedAlias {
@ -1316,8 +1330,14 @@ impl Type {
if false { if false {
let mut type_var_to_arg = Vec::new(); let mut type_var_to_arg = Vec::new();
for (_, arg_ann) in alias.type_variables.iter().zip(args) { for (alias_var, arg_ann) in alias.type_variables.iter().zip(args) {
type_var_to_arg.push(arg_ann.clone()); type_var_to_arg.push(Loc::at(
arg_ann.region,
OptAbleType {
typ: arg_ann.value.clone(),
opt_ability: alias_var.value.opt_bound_ability,
},
));
} }
let mut lambda_set_variables = let mut lambda_set_variables =
@ -1370,17 +1390,17 @@ impl Type {
) in alias.type_variables.iter().zip(args.iter()) ) in alias.type_variables.iter().zip(args.iter())
{ {
let mut filler = filler.clone(); let mut filler = filler.clone();
filler.instantiate_aliases( filler.value.instantiate_aliases(
region, region,
aliases, aliases,
var_store, var_store,
new_lambda_set_variables, new_lambda_set_variables,
); );
named_args.push(OptAbleType { named_args.push(OptAbleType {
typ: filler.clone(), typ: filler.value.clone(),
opt_ability: *opt_bound_ability, opt_ability: *opt_bound_ability,
}); });
substitution.insert(*placeholder, filler); substitution.insert(*placeholder, filler.value);
} }
// make sure hidden variables are freshly instantiated // make sure hidden variables are freshly instantiated
@ -1435,7 +1455,12 @@ impl Type {
} else { } else {
// one of the special-cased Apply types. // one of the special-cased Apply types.
for x in args { for x in args {
x.instantiate_aliases(region, aliases, var_store, new_lambda_set_variables); x.value.instantiate_aliases(
region,
aliases,
var_store,
new_lambda_set_variables,
);
} }
} }
} }
@ -1552,7 +1577,7 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
.. ..
}) => { }) => {
output.push(*symbol); output.push(*symbol);
stack.extend(type_arguments); stack.extend(type_arguments.iter().map(|ta| &ta.value.typ));
} }
Alias { Alias {
symbol: alias_symbol, symbol: alias_symbol,
@ -1572,7 +1597,7 @@ fn symbols_help(initial: &Type) -> Vec<Symbol> {
} }
Apply(symbol, args, _) => { Apply(symbol, args, _) => {
output.push(*symbol); output.push(*symbol);
stack.extend(args); stack.extend(args.iter().map(|t| &t.value));
} }
Erroneous(Problem::CyclicAlias(alias, _, _)) => { Erroneous(Problem::CyclicAlias(alias, _, _)) => {
output.push(*alias); output.push(*alias);
@ -1679,7 +1704,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
.. ..
}) => { }) => {
for arg in type_arguments { for arg in type_arguments {
variables_help(arg, accum); variables_help(&arg.value.typ, accum);
} }
for lambda_set in lambda_set_variables { for lambda_set in lambda_set_variables {
@ -1709,7 +1734,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet<Variable>) {
RangedNumber(_) => {} RangedNumber(_) => {}
Apply(_, args, _) => { Apply(_, args, _) => {
for x in args { for x in args {
variables_help(x, accum); variables_help(&x.value, accum);
} }
} }
} }
@ -1823,7 +1848,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
.. ..
}) => { }) => {
for arg in type_arguments { for arg in type_arguments {
variables_help_detailed(arg, accum); variables_help_detailed(&arg.value.typ, accum);
} }
for lambda_set in lambda_set_variables { for lambda_set in lambda_set_variables {
@ -1857,7 +1882,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) {
RangedNumber(_) => {} RangedNumber(_) => {}
Apply(_, args, _) => { Apply(_, args, _) => {
for x in args { for x in args {
variables_help_detailed(x, accum); variables_help_detailed(&x.value, accum);
} }
} }
} }
@ -2928,7 +2953,7 @@ fn instantiate_lambda_sets_as_unspecialized(
debug_assert!(matches!(lambda_set.0, Type::Variable(_))); debug_assert!(matches!(lambda_set.0, Type::Variable(_)));
lambda_set.0 = new_uls(); lambda_set.0 = new_uls();
} }
stack.extend(type_arguments.iter_mut().rev()); stack.extend(type_arguments.iter_mut().rev().map(|ta| &mut ta.value.typ));
} }
Type::Alias { Type::Alias {
symbol: _, symbol: _,
@ -2959,7 +2984,7 @@ fn instantiate_lambda_sets_as_unspecialized(
stack.extend(type_arguments.iter_mut().rev()); stack.extend(type_arguments.iter_mut().rev());
} }
Type::Apply(_sym, args, _region) => { Type::Apply(_sym, args, _region) => {
stack.extend(args.iter_mut().rev()); stack.extend(args.iter_mut().rev().map(|t| &mut t.value));
} }
Type::Variable(_) => {} Type::Variable(_) => {}
Type::RangedNumber(_) => {} Type::RangedNumber(_) => {}

View file

@ -11430,4 +11430,31 @@ All branches in an `if` must have the same type!
Bool.false Bool.false
"### "###
); );
test_report!(
expand_ability_from_type_alias_mismatch,
indoc!(
r#"
app "test" provides [f] to "./platform"
F a : a | a has Hash
f : F ({} -> {})
"#
),
@r###"
TYPE MISMATCH /code/proj/Main.roc
This expression has a type that does not implement the abilities it's expected to:
5 f : F ({} -> {})
^^^^^^^^
I can't generate an implementation of the `Hash` ability for
{} -> {}
Note: `Hash` cannot be generated for functions.
"###
);
} }