diff --git a/compiler/can/src/annotation.rs b/compiler/can/src/annotation.rs index 69a3a3d564..f42771477b 100644 --- a/compiler/can/src/annotation.rs +++ b/compiler/can/src/annotation.rs @@ -14,6 +14,13 @@ pub struct Annotation { pub introduced_variables: IntroducedVariables, pub references: MutSet, pub aliases: SendMap, + pub variably_sized_types: VariablySizedTypes, +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct VariablySizedTypes { + rigids: MutMap, + aliases: MutMap, } #[derive(Clone, Debug, PartialEq, Default)] @@ -58,12 +65,6 @@ impl IntroducedVariables { } } -#[derive(Debug, Default)] -pub struct VariablySizedTypes { - rigids: MutMap, - aliases: MutMap, -} - pub fn canonicalize_annotation( env: &mut Env, scope: &mut Scope, @@ -74,6 +75,7 @@ pub fn canonicalize_annotation( let mut introduced_variables = IntroducedVariables::default(); let mut references = MutSet::default(); let mut aliases = SendMap::default(); + let variably_sized_types = VariablySizedTypes::default(); let typ = can_annotation_help( env, annotation, @@ -90,6 +92,7 @@ pub fn canonicalize_annotation( introduced_variables, references, aliases, + variably_sized_types, } } diff --git a/compiler/constrain/src/uniq.rs b/compiler/constrain/src/uniq.rs index dac1a98eb7..2311ae184a 100644 --- a/compiler/constrain/src/uniq.rs +++ b/compiler/constrain/src/uniq.rs @@ -2105,6 +2105,48 @@ fn annotation_to_attr_type( let alias = Type::Alias(*symbol, new_fields, Box::new(actual_type)); + ( + actual_vars, + crate::builtins::builtin_type(Symbol::ATTR_ATTR, vec![uniq_type, alias]), + ) + } else { + panic!("lifted type is not Attr") + } + } + HostExposedAlias { + name: symbol, + def_name, + arguments: fields, + actual_var, + actual, + } => { + let (mut actual_vars, lifted_actual) = + annotation_to_attr_type(var_store, actual, rigids, change_var_kind); + + if let Type::Apply(attr_symbol, args) = lifted_actual { + debug_assert!(attr_symbol == Symbol::ATTR_ATTR); + + let uniq_type = args[0].clone(); + let actual_type = args[1].clone(); + + let mut new_fields = Vec::with_capacity(fields.len()); + for (name, tipe) in fields { + let (lifted_vars, lifted) = + annotation_to_attr_type(var_store, tipe, rigids, change_var_kind); + + actual_vars.extend(lifted_vars); + + new_fields.push((name.clone(), lifted)); + } + + let alias = Type::HostExposedAlias { + name: *symbol, + def_name: *def_name, + arguments: new_fields, + actual_var: *actual_var, + actual: Box::new(actual_type), + }; + ( actual_vars, crate::builtins::builtin_type(Symbol::ATTR_ATTR, vec![uniq_type, alias]), diff --git a/compiler/load/src/file.rs b/compiler/load/src/file.rs index 283127a09a..388f6cf843 100644 --- a/compiler/load/src/file.rs +++ b/compiler/load/src/file.rs @@ -5,7 +5,6 @@ use crossbeam::deque::{Injector, Stealer, Worker}; use crossbeam::thread; use parking_lot::Mutex; use roc_builtins::std::{Mode, StdLib}; -use roc_can::annotation::VariablySizedTypes; use roc_can::constraint::Constraint; use roc_can::def::Declaration; use roc_can::module::{canonicalize_module_defs, Module}; @@ -14,7 +13,7 @@ use roc_constrain::module::{ constrain_imports, pre_constrain_imports, ConstrainableImports, Import, }; use roc_constrain::module::{constrain_module, ExposedModuleTypes, SubsByModule}; -use roc_module::ident::{Ident, ModuleName}; +use roc_module::ident::{Ident, Lowercase, ModuleName}; use roc_module::symbol::{IdentIds, Interns, ModuleId, ModuleIds, Symbol}; use roc_mono::ir::{ CapturedSymbols, ExternalSpecializations, MonoProblem, PartialProc, PendingSpecialization, @@ -211,7 +210,7 @@ struct ModuleCache<'a> { found_specializations: MutMap>, external_specializations_requested: MutMap, documentation: MutMap, - host_exposed_variably_sized_types: MutMap, + variably_sized_layouts: MutMap>, } fn start_phase<'a>(module_id: ModuleId, phase: Phase, state: &mut State<'a>) -> BuildTask<'a> { @@ -471,10 +470,17 @@ pub struct MonomorphizedModule<'a> { pub mono_problems: Vec, pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>, pub exposed_to_host: MutMap, + pub variably_sized_layouts: MutMap>, pub src: Box, pub timings: MutMap, } +#[derive(Debug, Default)] +pub struct VariablySizedLayouts<'a> { + rigids: MutMap>, + aliases: MutMap>, +} + #[derive(Debug)] struct ParsedModule<'a> { module_id: ModuleId, @@ -533,6 +539,7 @@ enum Msg<'a> { ident_ids: IdentIds, layout_cache: LayoutCache<'a>, external_specializations_requested: MutMap, + variably_sized_layouts: MutMap>, procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>, problems: Vec, subs: Subs, @@ -1473,6 +1480,7 @@ fn update<'a>( finished_info, procedures, external_specializations_requested, + variably_sized_layouts, problems, .. } => { @@ -1480,6 +1488,11 @@ fn update<'a>( state.mono_problems.extend(problems); + state + .module_cache + .variably_sized_layouts + .extend(variably_sized_layouts); + for (module_id, requested) in external_specializations_requested { let existing = match state .module_cache @@ -1560,6 +1573,7 @@ fn finish_specialization<'a>( type_problems, can_problems, procedures, + module_cache, .. } = state; @@ -1573,6 +1587,7 @@ fn finish_specialization<'a>( interns, procedures, src: src.into(), + variably_sized_layouts: module_cache.variably_sized_layouts, timings: state.timings, } } @@ -2233,6 +2248,9 @@ fn make_specializations<'a>( let external_specializations_requested = procs.externals_we_need.clone(); let procedures = procs.get_specialized_procs_without_rc(mono_env.arena); + // TODO + let variably_sized_layouts = MutMap::default(); + Msg::MadeSpecializations { module_id: home, ident_ids, @@ -2242,6 +2260,7 @@ fn make_specializations<'a>( subs, finished_info, external_specializations_requested, + variably_sized_layouts, } } diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index 1d95b4f007..b0d2975df1 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -813,6 +813,36 @@ fn type_to_variable( result } + HostExposedAlias { + name: symbol, + arguments: args, + actual: alias_type, + actual_var, + .. + } => { + let mut arg_vars = Vec::with_capacity(args.len()); + let mut new_aliases = ImMap::default(); + + for (arg, arg_type) in args { + let arg_var = type_to_variable(subs, rank, pools, cached, arg_type); + + arg_vars.push((arg.clone(), arg_var)); + new_aliases.insert(arg.clone(), arg_var); + } + + let alias_var = type_to_variable(subs, rank, pools, cached, alias_type); + let content = Content::Alias(*symbol, arg_vars, alias_var); + + let result = register(subs, rank, pools, content); + + // unify the actual_var with the result var + // this can be used to access the type of the actual_var + // to determine its layout later + let descriptor = subs.get(result); + subs.union(*actual_var, result, descriptor); + + result + } Erroneous(problem) => { let content = Content::Structure(FlatType::Erroneous(problem.clone())); diff --git a/compiler/types/src/solved_types.rs b/compiler/types/src/solved_types.rs index c29d64c71f..c7aa099d77 100644 --- a/compiler/types/src/solved_types.rs +++ b/compiler/types/src/solved_types.rs @@ -53,6 +53,14 @@ pub enum SolvedType { /// A type alias Alias(Symbol, Vec<(Lowercase, SolvedType)>, Box), + HostExposedAlias { + name: Symbol, + def_name: Symbol, + arguments: Vec<(Lowercase, SolvedType)>, + actual_var: VarId, + actual: Box, + }, + /// a boolean algebra Bool Boolean(SolvedBool), @@ -194,6 +202,28 @@ impl SolvedType { SolvedType::Alias(*symbol, solved_args, Box::new(solved_type)) } + HostExposedAlias { + name, + def_name, + arguments, + actual_var, + actual, + } => { + let solved_type = Self::from_type(solved_subs, actual); + let mut solved_args = Vec::with_capacity(arguments.len()); + + for (name, var) in arguments { + solved_args.push((name.clone(), Self::from_type(solved_subs, var))); + } + + SolvedType::HostExposedAlias { + name: *name, + def_name: *def_name, + arguments: solved_args, + actual_var: VarId::from_var(*actual_var, solved_subs.inner()), + actual: Box::new(solved_type), + } + } Boolean(val) => SolvedType::Boolean(SolvedBool::from_bool(&val, solved_subs.inner())), Variable(var) => Self::from_var(solved_subs.inner(), *var), } @@ -486,6 +516,29 @@ pub fn to_type( Type::Alias(*symbol, type_variables, Box::new(actual)) } + HostExposedAlias { + name, + def_name, + arguments: solved_type_variables, + actual_var, + actual: solved_actual, + } => { + let mut type_variables = Vec::with_capacity(solved_type_variables.len()); + + for (lowercase, solved_arg) in solved_type_variables { + type_variables.push((lowercase.clone(), to_type(solved_arg, free_vars, var_store))); + } + + let actual = to_type(solved_actual, free_vars, var_store); + + Type::HostExposedAlias { + name: *name, + def_name: *def_name, + arguments: type_variables, + actual_var: var_id_to_flex_var(*actual_var, free_vars, var_store), + actual: Box::new(actual), + } + } Error => Type::Erroneous(Problem::SolvedTypeError), Erroneous(problem) => Type::Erroneous(problem.clone()), } diff --git a/compiler/types/src/types.rs b/compiler/types/src/types.rs index d3c43d6b25..88d45b23c6 100644 --- a/compiler/types/src/types.rs +++ b/compiler/types/src/types.rs @@ -143,6 +143,13 @@ pub enum Type { Record(SendMap>, Box), TagUnion(Vec<(TagName, Vec)>, Box), Alias(Symbol, Vec<(Lowercase, Type)>, Box), + HostExposedAlias { + name: Symbol, + def_name: Symbol, + arguments: Vec<(Lowercase, Type)>, + actual_var: Variable, + actual: Box, + }, RecursiveTagUnion(Variable, Vec<(TagName, Vec)>, Box), /// Applying a type to some arguments (e.g. Map.Map String Int) Apply(Symbol, Vec), @@ -206,6 +213,20 @@ impl fmt::Debug for Type { Ok(()) } + Type::HostExposedAlias { + name, arguments, .. + } => { + write!(f, "HostExposedAlias {:?}", name)?; + + for (_, arg) in arguments { + write!(f, " {:?}", arg)?; + } + + // Sometimes it's useful to see the expansion of the alias + // write!(f, "[ but actually {:?} ]", _actual)?; + + Ok(()) + } Type::Record(fields, ext) => { write!(f, "{{")?; @@ -405,6 +426,16 @@ impl Type { } actual_type.substitute(substitutions); } + HostExposedAlias { + arguments, + actual: actual_type, + .. + } => { + for (_, value) in arguments.iter_mut() { + value.substitute(substitutions); + } + actual_type.substitute(substitutions); + } Apply(_, args) => { for arg in args { arg.substitute(substitutions); @@ -453,6 +484,12 @@ impl Type { Alias(_, _, actual_type) => { actual_type.substitute_alias(rep_symbol, actual); } + HostExposedAlias { + actual: actual_type, + .. + } => { + actual_type.substitute_alias(rep_symbol, actual); + } Apply(symbol, _) if *symbol == rep_symbol => { *self = actual.clone(); @@ -496,6 +533,9 @@ impl Type { Alias(alias_symbol, _, actual_type) => { alias_symbol == &rep_symbol || actual_type.contains_symbol(rep_symbol) } + HostExposedAlias { name, actual, .. } => { + 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)), EmptyRec | EmptyTagUnion | Erroneous(_) | Variable(_) | Boolean(_) => false, @@ -528,6 +568,7 @@ impl Type { .any(|arg| arg.contains_variable(rep_variable)) } Alias(_, _, actual_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)), EmptyRec | EmptyTagUnion | Erroneous(_) | Boolean(_) => false, } @@ -579,7 +620,12 @@ impl Type { } ext.instantiate_aliases(region, aliases, var_store, introduced); } - Alias(_, type_args, actual_type) => { + HostExposedAlias { + arguments: type_args, + actual: actual_type, + .. + } + | Alias(_, type_args, actual_type) => { for arg in type_args { arg.1 .instantiate_aliases(region, aliases, var_store, introduced); @@ -761,6 +807,10 @@ fn symbols_help(tipe: &Type, accum: &mut ImSet) { accum.insert(*alias_symbol); symbols_help(&actual_type, accum); } + HostExposedAlias { name, actual, .. } => { + accum.insert(*name); + symbols_help(&actual, accum); + } Apply(symbol, args) => { accum.insert(*symbol); args.iter().for_each(|arg| symbols_help(arg, accum)); @@ -830,6 +880,14 @@ fn variables_help(tipe: &Type, accum: &mut ImSet) { } variables_help(actual, accum); } + HostExposedAlias { + arguments, actual, .. + } => { + for (_, arg) in arguments { + variables_help(arg, accum); + } + variables_help(actual, accum); + } Apply(_, args) => { for x in args { variables_help(x, accum);