Add JSON builtin, pass along solved specializations per module

This commit is contained in:
Ayaz Hafiz 2022-05-11 14:31:06 -04:00
parent 312cdd0b70
commit 0f4be93e44
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
8 changed files with 93 additions and 13 deletions

View file

@ -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");

View file

@ -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,

View file

@ -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(

View file

@ -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() {

View file

@ -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<ModuleId, Region>,
exposed_types: &mut ExposedByModule,
exposed_types: &ExposedByModule,
dep_idents: IdentIdsByModule,
declarations: Vec<Declaration>,
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<Subs>,
SolvedSpecializations,
Vec<(Symbol, Variable)>,
Vec<solve::TypeError>,
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,
};

View file

@ -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()

View file

@ -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)
}

View file

@ -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(