diff --git a/compiler/builtins/src/roc.rs b/compiler/builtins/src/roc.rs index 9c1b6bfc11..430a9135ed 100644 --- a/compiler/builtins/src/roc.rs +++ b/compiler/builtins/src/roc.rs @@ -12,6 +12,7 @@ pub fn module_source(module_id: ModuleId) -> &'static str { ModuleId::BOX => BOX, ModuleId::BOOL => BOOL, ModuleId::ENCODE => ENCODE, + ModuleId::JSON => JSON, _ => panic!( "ModuleId {:?} is not part of the standard library", module_id @@ -28,3 +29,4 @@ const SET: &str = include_str!("../roc/Set.roc"); const BOX: &str = include_str!("../roc/Box.roc"); const BOOL: &str = include_str!("../roc/Bool.roc"); const ENCODE: &str = include_str!("../roc/Encode.roc"); +const JSON: &str = include_str!("../roc/Json.roc"); diff --git a/compiler/can/src/abilities.rs b/compiler/can/src/abilities.rs index 511ed8eaea..cb4f3cd1dd 100644 --- a/compiler/can/src/abilities.rs +++ b/compiler/can/src/abilities.rs @@ -1,4 +1,4 @@ -use roc_collections::{all::MutMap, VecSet}; +use roc_collections::{all::MutMap, VecMap, VecSet}; use roc_error_macros::internal_error; use roc_module::symbol::Symbol; use roc_region::all::Region; @@ -46,6 +46,8 @@ impl AbilityMemberData { } } +pub type SolvedSpecializations = VecMap<(Symbol, Symbol), MemberSpecialization>; + /// A particular specialization of an ability member. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct MemberSpecialization { @@ -88,7 +90,7 @@ pub struct AbilitiesStore { /// Maps a tuple (member, type) specifying that `type` declares an implementation of an ability /// member `member`, to the exact symbol that implements the ability. - declared_specializations: MutMap<(Symbol, Symbol), MemberSpecialization>, + declared_specializations: SolvedSpecializations, next_specialization_id: u32, diff --git a/compiler/constrain/src/module.rs b/compiler/constrain/src/module.rs index ecb8e960dc..02bdaab6d9 100644 --- a/compiler/constrain/src/module.rs +++ b/compiler/constrain/src/module.rs @@ -1,6 +1,6 @@ use crate::expr::{constrain_def_make_constraint, constrain_def_pattern, Env}; use roc_builtins::std::StdLib; -use roc_can::abilities::{AbilitiesStore, MemberTypeInfo}; +use roc_can::abilities::{AbilitiesStore, MemberTypeInfo, SolvedSpecializations}; use roc_can::constraint::{Constraint, Constraints}; use roc_can::def::Declaration; use roc_can::expected::Expected; @@ -89,6 +89,7 @@ impl ExposedForModule { pub struct ExposedModuleTypes { pub stored_vars_by_symbol: Vec<(Symbol, Variable)>, pub storage_subs: roc_types::subs::StorageSubs, + pub solved_specializations: SolvedSpecializations, } pub fn constrain_module( diff --git a/compiler/load/build.rs b/compiler/load/build.rs index 86d70ccb0c..0ec18fa578 100644 --- a/compiler/load/build.rs +++ b/compiler/load/build.rs @@ -14,6 +14,7 @@ const MODULES: &[(ModuleId, &str)] = &[ (ModuleId::SET, "Set.roc"), (ModuleId::BOX, "Box.roc"), (ModuleId::ENCODE, "Encode.roc"), + (ModuleId::JSON, "Json.roc"), ]; fn main() { diff --git a/compiler/load_internal/src/file.rs b/compiler/load_internal/src/file.rs index 91bd518db7..2501d42fed 100644 --- a/compiler/load_internal/src/file.rs +++ b/compiler/load_internal/src/file.rs @@ -6,11 +6,11 @@ use crossbeam::thread; use parking_lot::Mutex; use roc_builtins::roc::module_source; use roc_builtins::std::borrow_stdlib; -use roc_can::abilities::AbilitiesStore; +use roc_can::abilities::{AbilitiesStore, SolvedSpecializations}; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints}; use roc_can::def::Declaration; use roc_can::module::{canonicalize_module_defs, Module}; -use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecSet}; +use roc_collections::{default_hasher, BumpMap, MutMap, MutSet, VecMap, VecSet}; use roc_constrain::module::{ constrain_builtin_imports, constrain_module, ExposedByModule, ExposedForModule, ExposedModuleTypes, @@ -166,6 +166,7 @@ impl Default for ModuleCache<'_> { NUM, BOX, ENCODE, + JSON, } Self { @@ -376,7 +377,7 @@ fn start_phase<'a>( constraint, var_store, imported_modules, - &mut state.exposed_types, + &state.exposed_types, dep_idents, declarations, state.cached_subs.clone(), @@ -2132,6 +2133,7 @@ fn update<'a>( ExposedModuleTypes { stored_vars_by_symbol: solved_module.stored_vars_by_symbol, storage_subs: solved_module.storage_subs, + solved_specializations: solved_module.solved_specializations, }, ); @@ -2687,6 +2689,7 @@ fn load_module<'a>( "Bool", ModuleId::BOOL "Box", ModuleId::BOX "Encode", ModuleId::ENCODE + "Json", ModuleId::JSON } let (filename, opt_shorthand) = module_name_to_path(src_dir, module_name, arc_shorthands); @@ -3486,19 +3489,41 @@ impl<'a> BuildTask<'a> { // TODO trim down these arguments - possibly by moving Constraint into Module #[allow(clippy::too_many_arguments)] fn solve_module( - module: Module, + mut module: Module, ident_ids: IdentIds, module_timing: ModuleTiming, constraints: Constraints, constraint: ConstraintSoa, var_store: VarStore, imported_modules: MutMap, - exposed_types: &mut ExposedByModule, + exposed_types: &ExposedByModule, dep_idents: IdentIdsByModule, declarations: Vec, cached_subs: CachedSubs, ) -> Self { let exposed_by_module = exposed_types.retain_modules(imported_modules.keys()); + + let abilities_store = &mut module.abilities_store; + + for module in imported_modules.keys() { + let exposed = exposed_by_module + .get(module) + .unwrap_or_else(|| internal_error!("No exposed types for {:?}", module)); + if let ExposedModuleTypes::Valid { + solved_specializations, + .. + } = exposed + { + for ((member, typ), specialization) in solved_specializations.iter() { + abilities_store.register_specialization_for_type( + *member, + *typ, + *specialization, + ); + } + } + } + let exposed_for_module = ExposedForModule::new(module.referenced_values.iter(), exposed_by_module); @@ -3594,6 +3619,7 @@ fn run_solve_solve( module: Module, ) -> ( Solved, + SolvedSpecializations, Vec<(Symbol, Variable)>, Vec, AbilitiesStore, @@ -3611,6 +3637,19 @@ fn run_solve_solve( let mut subs = Subs::new_from_varstore(var_store); + // We don't know what types we're about to solve for in our module, so we need to include the + // solved abilities across all dependencies. + // TODO: there's got to be a better way to do this. Maybe keep a cache of module -> solved + // abilities? + // let mut exposed_for_module = exposed_for_module; + // let mut imported_modules = VecSet::with_capacity(2); + // for imported in exposed_for_module.imported_values.iter() { + // imported_modules.insert(imported.module_id()); + // } + // for module in imported_modules.into_iter() { + // let typechecked = + // } + let import_variables = add_imports( &mut subs, &mut abilities_store, @@ -3628,7 +3667,7 @@ fn run_solve_solve( solve_aliases.insert(*name, alias.clone()); } - let (solved_subs, exposed_vars_by_symbol, problems, abilities_store) = { + let (solved_subs, solved_specializations, exposed_vars_by_symbol, problems, abilities_store) = { let (solved_subs, solved_env, problems, abilities_store) = roc_solve::module::run_solve( &constraints, actual_constraint, @@ -3638,13 +3677,36 @@ fn run_solve_solve( abilities_store, ); + // STORE ABILITIES + let module_id = module.module_id; + let known_specializations = + abilities_store + .get_known_specializations() + .filter(|(member, typ)| { + // This module solved this specialization if either the member or the type comes from the + // module. + member.module_id() == module_id || typ.module_id() == module_id + }); + + let mut solved_specializations: SolvedSpecializations = VecMap::default(); + let mut specialization_symbols = VecSet::default(); + for (member, typ) in known_specializations { + let specialization = abilities_store.get_specialization(member, typ).unwrap(); + specialization_symbols.insert(specialization.symbol); + solved_specializations.insert((member, typ), specialization); + } + // END STORE ABILITIES + + // Expose anything that is explicitly exposed by the header, or is a specialization of an + // ability. let exposed_vars_by_symbol: Vec<_> = solved_env .vars_by_symbol() - .filter(|(k, _)| exposed_symbols.contains(k)) + .filter(|(k, _)| exposed_symbols.contains(k) || specialization_symbols.contains(k)) .collect(); ( solved_subs, + solved_specializations, exposed_vars_by_symbol, problems, abilities_store, @@ -3653,6 +3715,7 @@ fn run_solve_solve( ( solved_subs, + solved_specializations, exposed_vars_by_symbol, problems, abilities_store, @@ -3680,7 +3743,7 @@ fn run_solve<'a>( // TODO remove when we write builtins in roc let aliases = module.aliases.clone(); - let (solved_subs, exposed_vars_by_symbol, problems, abilities_store) = { + let (solved_subs, solved_specializations, exposed_vars_by_symbol, problems, abilities_store) = { if module_id.is_builtin() { match cached_subs.lock().remove(&module_id) { None => run_solve_solve( @@ -3694,6 +3757,8 @@ fn run_solve<'a>( Some((subs, exposed_vars_by_symbol)) => { ( Solved(subs), + // TODO(abilities) replace when we have abilities for builtins + VecMap::default(), exposed_vars_by_symbol.to_vec(), vec![], // TODO(abilities) replace when we have abilities for builtins @@ -3722,6 +3787,7 @@ fn run_solve<'a>( problems, aliases, stored_vars_by_symbol, + solved_specializations, storage_subs, }; diff --git a/compiler/module/src/ident.rs b/compiler/module/src/ident.rs index fccc5c62b1..783af558cb 100644 --- a/compiler/module/src/ident.rs +++ b/compiler/module/src/ident.rs @@ -90,6 +90,7 @@ impl ModuleName { pub const RESULT: &'static str = "Result"; pub const BOX: &'static str = "Box"; pub const ENCODE: &'static str = "Encode"; + pub const JSON: &'static str = "Json"; pub fn as_str(&self) -> &str { self.0.as_str() diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 62279a936f..ae25aeffb5 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -1313,6 +1313,9 @@ define_builtins! { 9 ENCODE: "Encode" => { 0 ENCODE_ENCODE: "Encode" } + 10 JSON: "Json" => { + 0 JSON_JSON: "Json" + } - num_modules: 10 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) + num_modules: 11 // Keep this count up to date by hand! (TODO: see the mut_map! macro for how we could determine this count correctly in the macro) } diff --git a/compiler/solve/src/module.rs b/compiler/solve/src/module.rs index 93570bb62e..8003a01b18 100644 --- a/compiler/solve/src/module.rs +++ b/compiler/solve/src/module.rs @@ -1,5 +1,5 @@ use crate::solve::{self, Aliases}; -use roc_can::abilities::AbilitiesStore; +use roc_can::abilities::{AbilitiesStore, SolvedSpecializations}; use roc_can::constraint::{Constraint as ConstraintSoa, Constraints}; use roc_can::module::RigidVariables; use roc_collections::all::MutMap; @@ -20,11 +20,15 @@ pub struct SolvedModule { /// to create the types for HostExposed. This /// has some overlap with the StorageSubs fields, /// so maybe we can get rid of this at some point + /// + /// Contains both variables of symbols that are explicitly exposed by the header, + /// and the variables of any solved ability specializations we have. pub exposed_vars_by_symbol: Vec<(Symbol, Variable)>, /// Used when importing this module into another module pub stored_vars_by_symbol: Vec<(Symbol, Variable)>, pub storage_subs: StorageSubs, + pub solved_specializations: SolvedSpecializations, } pub fn run_solve(