diff --git a/Cargo.lock b/Cargo.lock index e1fa1bad52..7e8a4a96bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3869,6 +3869,7 @@ dependencies = [ "bumpalo", "roc_can", "roc_collections", + "roc_derive_key", "roc_module", "roc_solve", "roc_types", @@ -3923,6 +3924,7 @@ dependencies = [ "roc_collections", "roc_constrain", "roc_debug_flags", + "roc_derive_key", "roc_error_macros", "roc_late_solve", "roc_module", @@ -4119,6 +4121,7 @@ dependencies = [ "roc_can", "roc_collections", "roc_debug_flags", + "roc_derive_key", "roc_error_macros", "roc_exhaustive", "roc_load", diff --git a/compiler/derive_key/src/lib.rs b/compiler/derive_key/src/lib.rs index 3e4eadacc1..c0335caaba 100644 --- a/compiler/derive_key/src/lib.rs +++ b/compiler/derive_key/src/lib.rs @@ -15,6 +15,8 @@ pub mod encoding; +use std::sync::{Arc, RwLock}; + use encoding::{FlatEncodable, FlatEncodableKey}; use roc_collections::MutMap; @@ -50,19 +52,13 @@ impl Derived { } /// Map of [`DeriveKey`]s to their derived symbols. +#[derive(Debug, Default)] pub struct DerivedMethods { map: MutMap, derived_ident_ids: IdentIds, } impl DerivedMethods { - pub fn new(derived_ident_ids: IdentIds) -> Self { - Self { - map: MutMap::default(), - derived_ident_ids, - } - } - pub fn get_or_insert(&mut self, key: DeriveKey) -> Symbol { let symbol = self.map.entry(key).or_insert_with(|| { let ident_id = self.derived_ident_ids.gen_unique(); @@ -72,3 +68,6 @@ impl DerivedMethods { *symbol } } + +/// Thread-sharable [`DerivedMethods`]. +pub type GlobalDerivedMethods = Arc>; diff --git a/compiler/late_solve/Cargo.toml b/compiler/late_solve/Cargo.toml index b4c12f9df1..5d72dda7ea 100644 --- a/compiler/late_solve/Cargo.toml +++ b/compiler/late_solve/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] roc_types = { path = "../types" } roc_can = { path = "../can" } +roc_derive_key = { path = "../derive_key" } roc_module = { path = "../module" } roc_unify = { path = "../unify" } roc_solve = { path = "../solve" } diff --git a/compiler/late_solve/src/lib.rs b/compiler/late_solve/src/lib.rs index 399d35299c..23f18e261a 100644 --- a/compiler/late_solve/src/lib.rs +++ b/compiler/late_solve/src/lib.rs @@ -6,6 +6,7 @@ use std::sync::{Arc, RwLock}; use bumpalo::Bump; use roc_can::abilities::AbilitiesStore; use roc_collections::MutMap; +use roc_derive_key::GlobalDerivedMethods; use roc_module::symbol::ModuleId; use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools}; use roc_types::subs::Content; @@ -136,6 +137,7 @@ pub fn unify( arena: &Bump, subs: &mut Subs, abilities: &AbilitiesView, + derived_methods: &GlobalDerivedMethods, left: Variable, right: Variable, ) -> Result<(), UnificationFailed> { @@ -157,6 +159,7 @@ pub fn unify( &mut pools, lambda_sets_to_specialize, &late_phase, + derived_methods, ); // Pools are only used to keep track of variable ranks for generalization purposes. // Since we break generalization during monomorphization, `pools` is irrelevant diff --git a/compiler/load_internal/Cargo.toml b/compiler/load_internal/Cargo.toml index fe6be842cb..3f84288f21 100644 --- a/compiler/load_internal/Cargo.toml +++ b/compiler/load_internal/Cargo.toml @@ -13,6 +13,7 @@ roc_module = { path = "../module" } roc_types = { path = "../types" } roc_can = { path = "../can" } roc_constrain = { path = "../constrain" } +roc_derive_key = { path = "../derive_key" } roc_builtins = { path = "../builtins" } roc_problem = { path = "../problem" } roc_unify = { path = "../unify" } diff --git a/compiler/load_internal/src/file.rs b/compiler/load_internal/src/file.rs index 3b5add8e95..d6662d639a 100644 --- a/compiler/load_internal/src/file.rs +++ b/compiler/load_internal/src/file.rs @@ -22,6 +22,7 @@ use roc_debug_flags::{ ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, ROC_PRINT_LOAD_LOG, }; +use roc_derive_key::GlobalDerivedMethods; use roc_error_macros::internal_error; use roc_late_solve::{AbilitiesView, WorldAbilities}; use roc_module::ident::{Ident, ModuleName, QualifiedModuleName}; @@ -372,6 +373,8 @@ fn start_phase<'a>( .. } = constrained; + let derived_methods = GlobalDerivedMethods::clone(&state.derived_methods); + BuildTask::solve_module( module, ident_ids, @@ -385,6 +388,7 @@ fn start_phase<'a>( dep_idents, declarations, state.cached_subs.clone(), + derived_methods, ) } Phase::FindSpecializations => { @@ -412,6 +416,8 @@ fn start_phase<'a>( } } + let derived_methods = GlobalDerivedMethods::clone(&state.derived_methods); + BuildTask::BuildPendingSpecializations { layout_cache, module_id, @@ -422,6 +428,7 @@ fn start_phase<'a>( ident_ids, exposed_to_host: state.exposed_to_host.clone(), abilities_store, + derived_methods, } } Phase::MakeSpecializations => { @@ -479,6 +486,8 @@ fn start_phase<'a>( (ident_ids, subs, procs_base, layout_cache, module_timing) }; + let derived_methods = GlobalDerivedMethods::clone(&state.derived_methods); + BuildTask::MakeSpecializations { module_id, ident_ids, @@ -488,6 +497,7 @@ fn start_phase<'a>( specializations_we_must_make, module_timing, world_abilities: state.world_abilities.clone_ref(), + derived_methods, } } } @@ -795,6 +805,8 @@ struct State<'a> { /// From now on, these will be used by multiple threads; time to make an Arc>! pub arc_modules: Arc>>, pub arc_shorthands: Arc>>>, + #[allow(unused)] + pub derived_methods: GlobalDerivedMethods, pub ident_ids_by_module: SharedIdentIdsByModule, @@ -854,6 +866,7 @@ impl<'a> State<'a> { exposed_types, arc_modules, arc_shorthands, + derived_methods: Default::default(), constrained_ident_ids: IdentIds::exposed_builtins(0), ident_ids_by_module, declarations_by_id: MutMap::default(), @@ -973,6 +986,7 @@ enum BuildTask<'a> { declarations: Vec, dep_idents: IdentIdsByModule, cached_subs: CachedSubs, + derived_methods: GlobalDerivedMethods, }, BuildPendingSpecializations { module_timing: ModuleTiming, @@ -984,6 +998,7 @@ enum BuildTask<'a> { decls: Vec, exposed_to_host: ExposedToHost, abilities_store: AbilitiesStore, + derived_methods: GlobalDerivedMethods, }, MakeSpecializations { module_id: ModuleId, @@ -994,6 +1009,7 @@ enum BuildTask<'a> { specializations_we_must_make: Vec, module_timing: ModuleTiming, world_abilities: WorldAbilities, + derived_methods: GlobalDerivedMethods, }, } @@ -3719,6 +3735,7 @@ impl<'a> BuildTask<'a> { dep_idents: IdentIdsByModule, declarations: Vec, cached_subs: CachedSubs, + derived_methods: GlobalDerivedMethods, ) -> Self { let exposed_by_module = exposed_types.retain_modules(imported_modules.keys()); @@ -3746,6 +3763,7 @@ impl<'a> BuildTask<'a> { dep_idents, module_timing, cached_subs, + derived_methods, } } } @@ -3876,6 +3894,7 @@ fn run_solve_solve( pending_derives: PendingDerives, mut var_store: VarStore, module: Module, + derived_methods: GlobalDerivedMethods, ) -> ( Solved, ResolvedSpecializations, @@ -3923,6 +3942,7 @@ fn run_solve_solve( solve_aliases, abilities_store, pending_derives, + derived_methods, ); let module_id = module.module_id; @@ -3979,6 +3999,7 @@ fn run_solve<'a>( decls: Vec, dep_idents: IdentIdsByModule, cached_subs: CachedSubs, + derived_methods: GlobalDerivedMethods, ) -> Msg<'a> { let solve_start = SystemTime::now(); @@ -3998,6 +4019,7 @@ fn run_solve<'a>( pending_derives, var_store, module, + derived_methods, ), Some((subs, exposed_vars_by_symbol)) => { ( @@ -4020,6 +4042,7 @@ fn run_solve<'a>( pending_derives, var_store, module, + derived_methods, ) } }; @@ -4375,6 +4398,7 @@ fn make_specializations<'a>( mut module_timing: ModuleTiming, target_info: TargetInfo, world_abilities: WorldAbilities, + derived_methods: GlobalDerivedMethods, ) -> Msg<'a> { let make_specializations_start = SystemTime::now(); let mut update_mode_ids = UpdateModeIds::new(); @@ -4389,6 +4413,7 @@ fn make_specializations<'a>( // call_specialization_counter=0 is reserved call_specialization_counter: 1, abilities: AbilitiesView::World(world_abilities), + derived_methods: &derived_methods, }; let mut procs = Procs::new_in(arena); @@ -4449,9 +4474,9 @@ fn build_pending_specializations<'a>( mut module_timing: ModuleTiming, mut layout_cache: LayoutCache<'a>, target_info: TargetInfo, - // TODO remove - exposed_to_host: ExposedToHost, + exposed_to_host: ExposedToHost, // TODO remove abilities_store: AbilitiesStore, + derived_methods: GlobalDerivedMethods, ) -> Msg<'a> { let find_specializations_start = SystemTime::now(); @@ -4480,6 +4505,7 @@ fn build_pending_specializations<'a>( // to know the types and abilities in our modules. Only for building *all* specializations // do we need a global view. abilities: AbilitiesView::Module(&abilities_store), + derived_methods: &derived_methods, }; // Add modules' decls to Procs @@ -4747,6 +4773,7 @@ fn run_task<'a>( declarations, dep_idents, cached_subs, + derived_methods, } => Ok(run_solve( module, ident_ids, @@ -4760,6 +4787,7 @@ fn run_task<'a>( declarations, dep_idents, cached_subs, + derived_methods, )), BuildPendingSpecializations { module_id, @@ -4771,6 +4799,7 @@ fn run_task<'a>( imported_module_thunks, exposed_to_host, abilities_store, + derived_methods, } => Ok(build_pending_specializations( arena, solved_subs, @@ -4783,6 +4812,7 @@ fn run_task<'a>( target_info, exposed_to_host, abilities_store, + derived_methods, )), MakeSpecializations { module_id, @@ -4793,6 +4823,7 @@ fn run_task<'a>( specializations_we_must_make, module_timing, world_abilities, + derived_methods, } => Ok(make_specializations( arena, module_id, @@ -4804,6 +4835,7 @@ fn run_task<'a>( module_timing, target_info, world_abilities, + derived_methods, )), }?; diff --git a/compiler/mono/src/derive/encoding.rs b/compiler/mono/src/derive/encoding.rs index e677e8e8d1..708426bc4f 100644 --- a/compiler/mono/src/derive/encoding.rs +++ b/compiler/mono/src/derive/encoding.rs @@ -9,6 +9,7 @@ use roc_can::expr::{AnnotatedMark, ClosureData, Expr, Field, Recursive, WhenBran use roc_can::pattern::Pattern; use roc_collections::SendMap; use roc_derive_key::encoding::FlatEncodableKey; +use roc_derive_key::GlobalDerivedMethods; use roc_error_macros::internal_error; use roc_late_solve::{instantiate_rigids, AbilitiesView}; use roc_module::called_via::CalledVia; @@ -43,6 +44,7 @@ pub struct Env<'a> { pub subs: &'a mut Subs, pub ident_ids: &'a mut IdentIds, pub exposed_encode_types: &'a mut ExposedTypesStorageSubs, + pub derived_methods: &'a GlobalDerivedMethods, } impl Env<'_> { @@ -78,6 +80,7 @@ impl Env<'_> { self.arena, self.subs, &AbilitiesView::Module(&AbilitiesStore::default()), + self.derived_methods, left, right, ) diff --git a/compiler/mono/src/ir.rs b/compiler/mono/src/ir.rs index 2e7d489660..8dae524a56 100644 --- a/compiler/mono/src/ir.rs +++ b/compiler/mono/src/ir.rs @@ -16,6 +16,7 @@ use roc_debug_flags::dbg_do; use roc_debug_flags::{ ROC_PRINT_IR_AFTER_REFCOUNT, ROC_PRINT_IR_AFTER_RESET_REUSE, ROC_PRINT_IR_AFTER_SPECIALIZATION, }; +use roc_derive_key::GlobalDerivedMethods; use roc_error_macros::todo_abilities; use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId}; use roc_late_solve::{ @@ -1269,6 +1270,7 @@ pub struct Env<'a, 'i> { pub update_mode_ids: &'i mut UpdateModeIds, pub call_specialization_counter: u32, pub abilities: AbilitiesView<'i>, + pub derived_methods: &'i GlobalDerivedMethods, } impl<'a, 'i> Env<'a, 'i> { @@ -1304,6 +1306,7 @@ impl<'a, 'i> Env<'a, 'i> { self.arena, self.subs, &self.abilities, + self.derived_methods, left, right, ) diff --git a/compiler/solve/Cargo.toml b/compiler/solve/Cargo.toml index 3044589259..04ceb0ff57 100644 --- a/compiler/solve/Cargo.toml +++ b/compiler/solve/Cargo.toml @@ -13,6 +13,7 @@ roc_region = { path = "../region" } roc_module = { path = "../module" } roc_types = { path = "../types" } roc_can = { path = "../can" } +roc_derive_key = { path = "../derive_key" } roc_problem = { path = "../problem" } roc_unify = { path = "../unify" } roc_debug_flags = { path = "../debug_flags" } diff --git a/compiler/solve/src/module.rs b/compiler/solve/src/module.rs index 2a1561bf9d..9b8ac2ee12 100644 --- a/compiler/solve/src/module.rs +++ b/compiler/solve/src/module.rs @@ -5,6 +5,7 @@ use roc_can::expr::PendingDerives; use roc_can::module::RigidVariables; use roc_collections::all::MutMap; use roc_collections::VecMap; +use roc_derive_key::GlobalDerivedMethods; use roc_module::symbol::Symbol; use roc_types::solved_types::Solved; use roc_types::subs::{ExposedTypesStorageSubs, StorageSubs, Subs, Variable}; @@ -40,6 +41,7 @@ pub fn run_solve( mut aliases: Aliases, mut abilities_store: AbilitiesStore, pending_derives: PendingDerives, + derived_methods: GlobalDerivedMethods, ) -> ( Solved, solve::Env, @@ -71,6 +73,7 @@ pub fn run_solve( &constraint, pending_derives, &mut abilities_store, + derived_methods, ); (solved_subs, solved_env, problems, abilities_store) diff --git a/compiler/solve/src/solve.rs b/compiler/solve/src/solve.rs index ab34eebe68..8678d48166 100644 --- a/compiler/solve/src/solve.rs +++ b/compiler/solve/src/solve.rs @@ -13,6 +13,7 @@ use roc_collections::VecSet; use roc_debug_flags::dbg_do; #[cfg(debug_assertions)] use roc_debug_flags::ROC_VERIFY_RIGID_LET_GENERALIZED; +use roc_derive_key::{Derived, GlobalDerivedMethods}; use roc_error_macros::internal_error; use roc_module::ident::TagName; use roc_module::symbol::{ModuleId, Symbol}; @@ -550,6 +551,7 @@ pub fn run( constraint: &Constraint, pending_derives: PendingDerives, abilities_store: &mut AbilitiesStore, + derived_methods: GlobalDerivedMethods, ) -> (Solved, Env) { let env = run_in_place( constraints, @@ -559,6 +561,7 @@ pub fn run( constraint, pending_derives, abilities_store, + derived_methods, ); (Solved(subs), env) @@ -573,6 +576,7 @@ fn run_in_place( constraint: &Constraint, pending_derives: PendingDerives, abilities_store: &mut AbilitiesStore, + derived_methods: GlobalDerivedMethods, ) -> Env { let mut pools = Pools::default(); @@ -617,6 +621,7 @@ fn run_in_place( &mut pools, deferred_uls_to_resolve, &SolvePhase { abilities_store }, + &derived_methods, ); state.env @@ -1748,6 +1753,7 @@ pub fn compact_lambda_sets_of_vars( pools: &mut Pools, uls_of_var: UlsOfVar, phase: &P, + derived_methods: &GlobalDerivedMethods, ) { let mut seen = VecSet::default(); for (_, lambda_sets) in uls_of_var.drain() { @@ -1757,7 +1763,7 @@ pub fn compact_lambda_sets_of_vars( continue; } - compact_lambda_set(subs, arena, pools, root_lset, phase); + compact_lambda_set(subs, arena, pools, root_lset, phase, derived_methods); seen.insert(root_lset); } @@ -1770,6 +1776,7 @@ fn compact_lambda_set( pools: &mut Pools, this_lambda_set: Variable, phase: &P, + derived_methods: &GlobalDerivedMethods, ) { let LambdaSet { solved, @@ -1795,14 +1802,30 @@ fn compact_lambda_set( continue; } Structure(_) | Alias(_, _, _, AliasKind::Structural) => { - // TODO: figure out a convention for references to structural types in the - // unspecialized lambda set. This may very well happen, for example - // - // Default has default : {} -> a | a has Default - // - // {a, b} = default {} - // # ^^^^^^^ {} -[{a: t1, b: t2}:default:1]-> {a: t1, b: t2} - new_unspecialized.push(uls); + // This is a structural type, find the name of the derived ability function it + // should use. + let specialization_symbol = match Derived::encoding(subs, var) { + Derived::Immediate(symbol) => symbol, + Derived::Key(derive_key) => { + let mut derived_methods = derived_methods.write().unwrap(); + derived_methods.get_or_insert(derive_key) + } + }; + + let specialization_symbol_slice = + UnionLabels::insert_into_subs(subs, vec![(specialization_symbol, vec![])]); + let lambda_set_for_derived = subs.fresh(Descriptor { + content: LambdaSet(subs::LambdaSet { + solved: specialization_symbol_slice, + recursion_var: OptVariable::NONE, + unspecialized: SubsSlice::default(), + }), + rank: target_rank, + mark: Mark::NONE, + copy: OptVariable::NONE, + }); + + specialized_to_unify_with.push(lambda_set_for_derived); continue; } Alias(opaque, _, _, AliasKind::Opaque) => opaque, @@ -1864,7 +1887,14 @@ fn compact_lambda_set( // Ensure the specialization lambda set is already compacted. if subs.get_root_key(specialized_lambda_set) != subs.get_root_key(this_lambda_set) { - compact_lambda_set(subs, arena, pools, specialized_lambda_set, phase); + compact_lambda_set( + subs, + arena, + pools, + specialized_lambda_set, + phase, + derived_methods, + ); } // Ensure the specialization lambda set we'll unify with is not a generalized one, but one @@ -1889,7 +1919,7 @@ fn compact_lambda_set( unify(subs, this_lambda_set, other_specialized, Mode::EQ) .expect_success("lambda sets don't unify"); - introduce(subs, subs.get_rank(this_lambda_set), pools, &vars); + introduce(subs, target_rank, pools, &vars); debug_assert!( must_implement_ability.is_empty(),