diff --git a/crates/compiler/can/src/annotation.rs b/crates/compiler/can/src/annotation.rs index e63d60553a..7c5e789f4c 100644 --- a/crates/compiler/can/src/annotation.rs +++ b/crates/compiler/can/src/annotation.rs @@ -553,7 +553,7 @@ fn can_annotation_help( references, ); - args.push(arg_ann); + args.push(Loc::at(arg.region, arg_ann)); } match scope.lookup_alias(symbol) { @@ -573,8 +573,14 @@ fn can_annotation_help( let mut type_var_to_arg = Vec::new(); - for (_, arg_ann) in alias.type_variables.iter().zip(args) { - type_var_to_arg.push(arg_ann); + for (alias_arg, arg_ann) in alias.type_variables.iter().zip(args) { + 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 = diff --git a/crates/compiler/can/src/def.rs b/crates/compiler/can/src/def.rs index 3084daeeaa..0984707718 100644 --- a/crates/compiler/can/src/def.rs +++ b/crates/compiler/can/src/def.rs @@ -829,7 +829,15 @@ fn canonicalize_opaque<'a>( type_arguments: alias .type_variables .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(), lambda_set_variables: alias .lambda_set_variables diff --git a/crates/compiler/constrain/src/builtins.rs b/crates/compiler/constrain/src/builtins.rs index d26dc58b34..b0fd06a1b0 100644 --- a/crates/compiler/constrain/src/builtins.rs +++ b/crates/compiler/constrain/src/builtins.rs @@ -3,7 +3,7 @@ use roc_can::constraint::{Constraint, Constraints}; use roc_can::expected::Expected::{self, *}; use roc_can::num::{FloatBound, FloatWidth, IntBound, IntLitWidth, NumBound, SignDemand}; 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::subs::Variable; use roc_types::types::Type::{self, *}; @@ -198,7 +198,11 @@ pub fn num_literal( #[inline(always)] pub fn builtin_type(symbol: Symbol, args: Vec) -> Type { - Type::Apply(symbol, args, Region::zero()) + Type::Apply( + symbol, + args.into_iter().map(Loc::at_zero).collect(), + Region::zero(), + ) } #[inline(always)] diff --git a/crates/compiler/solve/src/ability.rs b/crates/compiler/solve/src/ability.rs index 3533672f1b..97fe8472f1 100644 --- a/crates/compiler/solve/src/ability.rs +++ b/crates/compiler/solve/src/ability.rs @@ -50,7 +50,14 @@ pub struct 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, + abilities_store: &mut AbilitiesStore, + obligation_cache: &mut ObligationCache, + ) -> Self { let mut table = VecMap::with_capacity(pending_derives.len()); for (opaque, (typ, derives)) in pending_derives.into_iter() { @@ -66,8 +73,16 @@ impl PendingDerivesTable { let derive_key = RequestedDeriveKey { opaque, ability }; // Neither rank nor pools should matter here. - let opaque_var = - type_to_var(subs, Rank::toplevel(), &mut Pools::default(), aliases, &typ); + let opaque_var = type_to_var( + subs, + Rank::toplevel(), + problems, + abilities_store, + obligation_cache, + &mut Pools::default(), + aliases, + &typ, + ); let real_var = match subs.get_content_without_compacting(opaque_var) { Content::Alias(_, _, real_var, AliasKind::Opaque) => real_var, _ => internal_error!("Non-opaque in derives table"), diff --git a/crates/compiler/solve/src/solve.rs b/crates/compiler/solve/src/solve.rs index 475ba772a0..bdfcb24265 100644 --- a/crates/compiler/solve/src/solve.rs +++ b/crates/compiler/solve/src/solve.rs @@ -1,3 +1,5 @@ +#![allow(clippy::too_many_arguments)] + use crate::ability::{ resolve_ability_specialization, type_implementing_specialization, AbilityImplError, CheckedDerives, ObligationCache, PendingDerivesTable, Resolved, @@ -274,6 +276,9 @@ impl Aliases { subs: &mut Subs, rank: Rank, pools: &mut Pools, + problems: &mut Vec, + abilities_store: &AbilitiesStore, + obligation_cache: &mut ObligationCache, arena: &bumpalo::Bump, symbol: Symbol, alias_variables: AliasVariables, @@ -375,7 +380,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 +405,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 +589,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 +721,9 @@ fn solve( constraints, rank, pools, + problems, + abilities_store, + obligation_cache, aliases, subs, let_con.def_types, @@ -747,6 +784,9 @@ fn solve( constraints, next_rank, pools, + problems, + abilities_store, + obligation_cache, aliases, subs, let_con.def_types, @@ -858,11 +898,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 +985,9 @@ fn solve( subs, rank, pools, + &mut vec![], // don't report any extra errors + abilities_store, + obligation_cache, aliases, *source_index, ); @@ -962,8 +1023,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 +1117,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 +1296,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 +1327,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 +1451,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 +2021,9 @@ impl LocalDefVarsVec<(Symbol, Loc)> { constraints: &Constraints, rank: Rank, pools: &mut Pools, + problems: &mut Vec, + abilities_store: &mut AbilitiesStore, + obligation_cache: &mut ObligationCache, aliases: &mut Aliases, subs: &mut Subs, def_types_slice: roc_can::constraint::DefTypes, @@ -1899,7 +2034,16 @@ impl LocalDefVarsVec<(Symbol, Loc)> { 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 +2074,9 @@ fn either_type_index_to_var( subs: &mut Subs, rank: Rank, pools: &mut Pools, + problems: &mut Vec, + abilities_store: &mut AbilitiesStore, + obligation_cache: &mut ObligationCache, aliases: &mut Aliases, either_type_index: roc_collections::soa::EitherIndex, ) -> Variable { @@ -1937,7 +2084,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 +2105,9 @@ fn either_type_index_to_var( pub(crate) fn type_to_var( subs: &mut Subs, rank: Rank, + problems: &mut Vec, + abilities_store: &mut AbilitiesStore, + obligation_cache: &mut ObligationCache, pools: &mut Pools, aliases: &mut Aliases, typ: &Type, @@ -1958,7 +2117,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 +2279,9 @@ fn type_to_variable<'a>( subs: &mut Subs, rank: Rank, pools: &mut Pools, + problems: &mut Vec, + abilities_store: &AbilitiesStore, + obligation_cache: &mut ObligationCache, arena: &'a bumpalo::Bump, aliases: &mut Aliases, typ: &Type, @@ -2118,6 +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); macro_rules! helper { ($typ:expr, $ambient_function_policy:expr) => {{ @@ -2165,7 +2339,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 +2530,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 +2542,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 +2568,9 @@ fn type_to_variable<'a>( subs, rank, pools, + problems, + abilities_store, + obligation_cache, arena, *symbol, alias_variables, @@ -2488,8 +2678,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 +2701,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 +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 } diff --git a/crates/compiler/solve/tests/solve_expr.rs b/crates/compiler/solve/tests/solve_expr.rs index 0113ee380d..60658558cf 100644 --- a/crates/compiler/solve/tests/solve_expr.rs +++ b/crates/compiler/solve/tests/solve_expr.rs @@ -7991,4 +7991,22 @@ mod solve_expr { "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 + ); + } } diff --git a/crates/compiler/types/src/types.rs b/crates/compiler/types/src/types.rs index fba4398d9f..7fba003170 100644 --- a/crates/compiler/types/src/types.rs +++ b/crates/compiler/types/src/types.rs @@ -209,7 +209,7 @@ impl LambdaSet { #[derive(PartialEq, Eq, Clone)] pub struct AliasCommon { pub symbol: Symbol, - pub type_arguments: Vec, + pub type_arguments: Vec>, pub lambda_set_variables: Vec, } @@ -282,7 +282,7 @@ pub enum Type { }, RecursiveTagUnion(Variable, Vec<(TagName, Vec)>, TypeExtension), /// Applying a type to some arguments (e.g. Dict.Dict String Int) - Apply(Symbol, Vec, Region), + Apply(Symbol, Vec>, Region), Variable(Variable), RangedNumber(NumericRange), /// A type error, which will code gen to a runtime error @@ -793,7 +793,7 @@ impl Type { .. }) => { 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() { @@ -833,7 +833,7 @@ impl Type { stack.push(actual_type); } Apply(_, args, _) => { - stack.extend(args); + stack.extend(args.iter_mut().map(|t| &mut t.value)); } RangedNumber(_) => {} UnspecializedLambdaSet { @@ -915,7 +915,7 @@ impl Type { .. }) => { 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() { @@ -954,7 +954,7 @@ impl Type { stack.push(actual_type); } Apply(_, args, _) => { - stack.extend(args); + stack.extend(args.iter_mut().map(|t| &mut t.value)); } RangedNumber(_) => {} UnspecializedLambdaSet { @@ -1021,7 +1021,9 @@ impl Type { .. }) => { for ta in type_arguments { - ta.substitute_alias(rep_symbol, rep_args, actual)?; + ta.value + .typ + .substitute_alias(rep_symbol, rep_args, actual)?; } Ok(()) @@ -1042,13 +1044,16 @@ impl Type { } => actual_type.substitute_alias(rep_symbol, rep_args, actual), Apply(symbol, args, region) if *symbol == rep_symbol => { 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(); if let Apply(_, args, _) = self { for arg in args { - arg.substitute_alias(rep_symbol, rep_args, actual)?; + arg.value.substitute_alias(rep_symbol, rep_args, actual)?; } } return Ok(()); @@ -1057,7 +1062,7 @@ impl Type { } Apply(_, args, _) => { for arg in args { - arg.substitute_alias(rep_symbol, rep_args, actual)?; + arg.value.substitute_alias(rep_symbol, rep_args, actual)?; } Ok(()) } @@ -1103,7 +1108,9 @@ impl Type { .. }) => { 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 .iter() .any(|v| v.0.contains_symbol(rep_symbol)) @@ -1117,7 +1124,7 @@ impl Type { name == &rep_symbol || actual.contains_symbol(rep_symbol) } 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, UnspecializedLambdaSet { unspecialized: Uls(_, sym, _), @@ -1174,7 +1181,9 @@ impl Type { .. } => actual_type.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, EmptyRec | EmptyTagUnion | Erroneous(_) => false, } @@ -1259,7 +1268,12 @@ impl Type { .iter() .all(|lambda_set| matches!(lambda_set.0, Type::Variable(..)))); 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 { @@ -1316,8 +1330,14 @@ impl Type { if false { let mut type_var_to_arg = Vec::new(); - for (_, arg_ann) in alias.type_variables.iter().zip(args) { - type_var_to_arg.push(arg_ann.clone()); + for (alias_var, arg_ann) in alias.type_variables.iter().zip(args) { + 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 = @@ -1370,17 +1390,17 @@ impl Type { ) in alias.type_variables.iter().zip(args.iter()) { let mut filler = filler.clone(); - filler.instantiate_aliases( + filler.value.instantiate_aliases( region, aliases, var_store, new_lambda_set_variables, ); named_args.push(OptAbleType { - typ: filler.clone(), + typ: filler.value.clone(), opt_ability: *opt_bound_ability, }); - substitution.insert(*placeholder, filler); + substitution.insert(*placeholder, filler.value); } // make sure hidden variables are freshly instantiated @@ -1435,7 +1455,12 @@ impl Type { } else { // one of the special-cased Apply types. 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 { .. }) => { output.push(*symbol); - stack.extend(type_arguments); + stack.extend(type_arguments.iter().map(|ta| &ta.value.typ)); } Alias { symbol: alias_symbol, @@ -1572,7 +1597,7 @@ fn symbols_help(initial: &Type) -> Vec { } Apply(symbol, args, _) => { output.push(*symbol); - stack.extend(args); + stack.extend(args.iter().map(|t| &t.value)); } Erroneous(Problem::CyclicAlias(alias, _, _)) => { output.push(*alias); @@ -1679,7 +1704,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet) { .. }) => { for arg in type_arguments { - variables_help(arg, accum); + variables_help(&arg.value.typ, accum); } for lambda_set in lambda_set_variables { @@ -1709,7 +1734,7 @@ fn variables_help(tipe: &Type, accum: &mut ImSet) { RangedNumber(_) => {} Apply(_, 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 { - variables_help_detailed(arg, accum); + variables_help_detailed(&arg.value.typ, accum); } for lambda_set in lambda_set_variables { @@ -1857,7 +1882,7 @@ fn variables_help_detailed(tipe: &Type, accum: &mut VariableDetail) { RangedNumber(_) => {} Apply(_, 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(_))); 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 { symbol: _, @@ -2959,7 +2984,7 @@ fn instantiate_lambda_sets_as_unspecialized( stack.extend(type_arguments.iter_mut().rev()); } 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::RangedNumber(_) => {} diff --git a/crates/reporting/tests/test_reporting.rs b/crates/reporting/tests/test_reporting.rs index d0e3d19feb..f26dab8ea9 100644 --- a/crates/reporting/tests/test_reporting.rs +++ b/crates/reporting/tests/test_reporting.rs @@ -11430,4 +11430,31 @@ All branches in an `if` must have the same type! 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. + "### + ); }