mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 22:09:09 +00:00
Consolidate WorldAbilities and solve Phase
This commit is contained in:
parent
f5c4528919
commit
f21d68c9fb
8 changed files with 244 additions and 172 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3855,6 +3855,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"roc_can",
|
"roc_can",
|
||||||
|
"roc_collections",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_solve",
|
"roc_solve",
|
||||||
"roc_types",
|
"roc_types",
|
||||||
|
@ -3910,6 +3911,7 @@ dependencies = [
|
||||||
"roc_constrain",
|
"roc_constrain",
|
||||||
"roc_debug_flags",
|
"roc_debug_flags",
|
||||||
"roc_error_macros",
|
"roc_error_macros",
|
||||||
|
"roc_late_solve",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_mono",
|
"roc_mono",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
|
|
|
@ -11,4 +11,5 @@ roc_can = { path = "../can" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
|
roc_collections = { path = "../collections" }
|
||||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||||
|
|
|
@ -1,27 +1,141 @@
|
||||||
//! Crate roc_late_solve exposes type unification and solving primitives from the perspective of
|
//! Crate roc_late_solve exposes type unification and solving primitives from the perspective of
|
||||||
//! the compiler backend.
|
//! the compiler backend.
|
||||||
|
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use roc_can::abilities::AbilitiesStore;
|
||||||
|
use roc_collections::MutMap;
|
||||||
use roc_module::symbol::ModuleId;
|
use roc_module::symbol::ModuleId;
|
||||||
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools};
|
use roc_solve::solve::{compact_lambda_sets_of_vars, Phase, Pools};
|
||||||
use roc_types::subs::{Subs, Variable};
|
use roc_types::subs::Content;
|
||||||
|
use roc_types::subs::{ExposedTypesStorageSubs, Subs, Variable};
|
||||||
use roc_unify::unify::{unify as unify_unify, Mode, Unified};
|
use roc_unify::unify::{unify as unify_unify, Mode, Unified};
|
||||||
|
|
||||||
pub use roc_solve::solve::instantiate_rigids;
|
pub use roc_solve::solve::instantiate_rigids;
|
||||||
|
|
||||||
pub use roc_solve::ability::resolve_ability_specialization;
|
pub use roc_solve::ability::resolve_ability_specialization;
|
||||||
pub use roc_solve::ability::{Resolved, WorldAbilities};
|
pub use roc_solve::ability::Resolved;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UnificationFailed;
|
pub struct UnificationFailed;
|
||||||
|
|
||||||
|
/// A global view of all abilities across all modules in a program under compilation.
|
||||||
|
/// [WorldAbilities::insert] adds a solved abilities store for a module to the global map.
|
||||||
|
/// Use [WorldAbilities::clone] to get a thread-safe, reference-counted copy of the global map.
|
||||||
|
/// Note that this is indeed a map shared amongst everything during a compilation.
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct WorldAbilities {
|
||||||
|
world: Arc<RwLock<MutMap<ModuleId, (AbilitiesStore, ExposedTypesStorageSubs)>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldAbilities {
|
||||||
|
pub fn insert(
|
||||||
|
&mut self,
|
||||||
|
module: ModuleId,
|
||||||
|
store: AbilitiesStore,
|
||||||
|
exposed_types: ExposedTypesStorageSubs,
|
||||||
|
) {
|
||||||
|
let old_store = self
|
||||||
|
.world
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(module, (store, exposed_types));
|
||||||
|
|
||||||
|
debug_assert!(old_store.is_none(), "{:?} abilities not new", module);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
world: Arc::clone(&self.world),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AbilitiesView<'a> {
|
||||||
|
World(WorldAbilities),
|
||||||
|
Module(&'a AbilitiesStore),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AbilitiesView<'_> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn with_module_abilities_store<T, F>(&self, module: ModuleId, mut f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnMut(&AbilitiesStore) -> T,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
AbilitiesView::World(wa) => {
|
||||||
|
let world = wa.world.read().unwrap();
|
||||||
|
let (module_store, _module_types) = world.get(&module).unwrap();
|
||||||
|
f(module_store)
|
||||||
|
}
|
||||||
|
AbilitiesView::Module(store) => f(store),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LatePhase<'a> {
|
||||||
|
home: ModuleId,
|
||||||
|
abilities: &'a AbilitiesView<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Phase for LatePhase<'_> {
|
||||||
|
const IS_LATE: bool = true;
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn with_module_abilities_store<T, F>(&self, module: ModuleId, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnMut(&AbilitiesStore) -> T,
|
||||||
|
{
|
||||||
|
self.abilities.with_module_abilities_store(module, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn copy_lambda_set_var_to_home_subs(
|
||||||
|
&self,
|
||||||
|
external_lambda_set_var: Variable,
|
||||||
|
external_module_id: ModuleId,
|
||||||
|
target_subs: &mut Subs,
|
||||||
|
) -> Variable {
|
||||||
|
match (external_module_id == self.home, self.abilities) {
|
||||||
|
(true, _) | (false, AbilitiesView::Module(_)) => {
|
||||||
|
debug_assert!(matches!(
|
||||||
|
target_subs.get_content_without_compacting(external_lambda_set_var),
|
||||||
|
Content::LambdaSet(..)
|
||||||
|
));
|
||||||
|
external_lambda_set_var
|
||||||
|
}
|
||||||
|
(false, AbilitiesView::World(wa)) => {
|
||||||
|
let mut world = wa.world.write().unwrap();
|
||||||
|
let (_module_store, module_types) = world.get_mut(&external_module_id).unwrap();
|
||||||
|
|
||||||
|
let storage_lambda_set_var = *module_types
|
||||||
|
.stored_specialization_lambda_set_vars
|
||||||
|
.get(&external_lambda_set_var)
|
||||||
|
.unwrap();
|
||||||
|
let copied = module_types
|
||||||
|
.storage_subs
|
||||||
|
.export_variable_to(target_subs, storage_lambda_set_var);
|
||||||
|
let our_lambda_set_var = copied.variable;
|
||||||
|
|
||||||
|
debug_assert!(matches!(
|
||||||
|
target_subs.get_content_without_compacting(our_lambda_set_var),
|
||||||
|
Content::LambdaSet(..)
|
||||||
|
));
|
||||||
|
|
||||||
|
our_lambda_set_var
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Unifies two variables and performs lambda set compaction.
|
/// Unifies two variables and performs lambda set compaction.
|
||||||
/// Ranks and other ability demands are disregarded.
|
/// Ranks and other ability demands are disregarded.
|
||||||
pub fn unify(
|
pub fn unify(
|
||||||
home: ModuleId,
|
home: ModuleId,
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
world_abilities: &WorldAbilities,
|
abilities: &AbilitiesView,
|
||||||
left: Variable,
|
left: Variable,
|
||||||
right: Variable,
|
right: Variable,
|
||||||
) -> Result<(), UnificationFailed> {
|
) -> Result<(), UnificationFailed> {
|
||||||
|
@ -34,13 +148,14 @@ pub fn unify(
|
||||||
} => {
|
} => {
|
||||||
let mut pools = Pools::default();
|
let mut pools = Pools::default();
|
||||||
|
|
||||||
|
let late_phase = LatePhase { home, abilities };
|
||||||
|
|
||||||
compact_lambda_sets_of_vars(
|
compact_lambda_sets_of_vars(
|
||||||
subs,
|
subs,
|
||||||
arena,
|
arena,
|
||||||
&mut pools,
|
&mut pools,
|
||||||
world_abilities,
|
|
||||||
lambda_sets_to_specialize,
|
lambda_sets_to_specialize,
|
||||||
Phase::Late { home },
|
&late_phase,
|
||||||
);
|
);
|
||||||
// Pools are only used to keep track of variable ranks for generalization purposes.
|
// Pools are only used to keep track of variable ranks for generalization purposes.
|
||||||
// Since we break generalization during monomorphization, `pools` is irrelevant
|
// Since we break generalization during monomorphization, `pools` is irrelevant
|
||||||
|
|
|
@ -18,6 +18,7 @@ roc_problem = { path = "../problem" }
|
||||||
roc_unify = { path = "../unify" }
|
roc_unify = { path = "../unify" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_solve = { path = "../solve" }
|
roc_solve = { path = "../solve" }
|
||||||
|
roc_late_solve = { path = "../late_solve" }
|
||||||
roc_mono = { path = "../mono" }
|
roc_mono = { path = "../mono" }
|
||||||
roc_target = { path = "../roc_target" }
|
roc_target = { path = "../roc_target" }
|
||||||
roc_reporting = { path = "../../reporting" }
|
roc_reporting = { path = "../../reporting" }
|
||||||
|
|
|
@ -23,6 +23,7 @@ use roc_debug_flags::{
|
||||||
ROC_PRINT_LOAD_LOG,
|
ROC_PRINT_LOAD_LOG,
|
||||||
};
|
};
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
|
use roc_late_solve::{AbilitiesView, WorldAbilities};
|
||||||
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
||||||
use roc_module::symbol::{
|
use roc_module::symbol::{
|
||||||
IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds,
|
IdentIds, IdentIdsByModule, Interns, ModuleId, ModuleIds, PQModuleName, PackageModuleIds,
|
||||||
|
@ -41,7 +42,6 @@ use roc_parse::module::module_defs;
|
||||||
use roc_parse::parser::{FileError, Parser, SyntaxError};
|
use roc_parse::parser::{FileError, Parser, SyntaxError};
|
||||||
use roc_region::all::{LineInfo, Loc, Region};
|
use roc_region::all::{LineInfo, Loc, Region};
|
||||||
use roc_reporting::report::RenderTarget;
|
use roc_reporting::report::RenderTarget;
|
||||||
use roc_solve::ability::{AllModuleAbilities, WorldAbilities};
|
|
||||||
use roc_solve::module::SolvedModule;
|
use roc_solve::module::SolvedModule;
|
||||||
use roc_solve::solve;
|
use roc_solve::solve;
|
||||||
use roc_target::TargetInfo;
|
use roc_target::TargetInfo;
|
||||||
|
@ -442,7 +442,6 @@ fn start_phase<'a>(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let FoundSpecializationsModule {
|
let FoundSpecializationsModule {
|
||||||
module_id,
|
|
||||||
ident_ids,
|
ident_ids,
|
||||||
subs,
|
subs,
|
||||||
procs_base,
|
procs_base,
|
||||||
|
@ -458,16 +457,13 @@ fn start_phase<'a>(
|
||||||
internal_error!("Exposed types for {:?} missing", module_id)
|
internal_error!("Exposed types for {:?} missing", module_id)
|
||||||
});
|
});
|
||||||
|
|
||||||
let old_store = &state.inverse_world_abilities.write().unwrap().insert(
|
// Add our abilities to the world.
|
||||||
|
state.world_abilities.insert(
|
||||||
module_id,
|
module_id,
|
||||||
(
|
|
||||||
abilities_store,
|
abilities_store,
|
||||||
our_exposed_types.exposed_types_storage_subs,
|
our_exposed_types.exposed_types_storage_subs,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
debug_assert!(old_store.is_none(), "{:?} abilities not new", module_id);
|
|
||||||
|
|
||||||
(ident_ids, subs, procs_base, layout_cache, module_timing)
|
(ident_ids, subs, procs_base, layout_cache, module_timing)
|
||||||
} else {
|
} else {
|
||||||
let LateSpecializationsModule {
|
let LateSpecializationsModule {
|
||||||
|
@ -493,7 +489,7 @@ fn start_phase<'a>(
|
||||||
layout_cache,
|
layout_cache,
|
||||||
specializations_we_must_make,
|
specializations_we_must_make,
|
||||||
module_timing,
|
module_timing,
|
||||||
world_abilities: Arc::clone(&state.inverse_world_abilities),
|
world_abilities: state.world_abilities.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -596,7 +592,6 @@ pub struct TypeCheckedModule<'a> {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FoundSpecializationsModule<'a> {
|
struct FoundSpecializationsModule<'a> {
|
||||||
module_id: ModuleId,
|
|
||||||
ident_ids: IdentIds,
|
ident_ids: IdentIds,
|
||||||
layout_cache: LayoutCache<'a>,
|
layout_cache: LayoutCache<'a>,
|
||||||
procs_base: ProcsBase<'a>,
|
procs_base: ProcsBase<'a>,
|
||||||
|
@ -818,8 +813,8 @@ struct State<'a> {
|
||||||
|
|
||||||
pub render: RenderTarget,
|
pub render: RenderTarget,
|
||||||
|
|
||||||
/// At a given point in time, this map contains the abilities stores in reverse order
|
/// All abilities across all modules.
|
||||||
pub inverse_world_abilities: AllModuleAbilities,
|
pub world_abilities: WorldAbilities,
|
||||||
|
|
||||||
make_specializations_pass: MakeSpecializationsPass,
|
make_specializations_pass: MakeSpecializationsPass,
|
||||||
|
|
||||||
|
@ -867,8 +862,8 @@ impl<'a> State<'a> {
|
||||||
layout_caches: std::vec::Vec::with_capacity(number_of_workers),
|
layout_caches: std::vec::Vec::with_capacity(number_of_workers),
|
||||||
cached_subs: Arc::new(Mutex::new(cached_subs)),
|
cached_subs: Arc::new(Mutex::new(cached_subs)),
|
||||||
render,
|
render,
|
||||||
inverse_world_abilities: Default::default(),
|
|
||||||
make_specializations_pass: MakeSpecializationsPass::Pass(1),
|
make_specializations_pass: MakeSpecializationsPass::Pass(1),
|
||||||
|
world_abilities: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -996,7 +991,7 @@ enum BuildTask<'a> {
|
||||||
layout_cache: LayoutCache<'a>,
|
layout_cache: LayoutCache<'a>,
|
||||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||||
module_timing: ModuleTiming,
|
module_timing: ModuleTiming,
|
||||||
world_abilities: AllModuleAbilities,
|
world_abilities: WorldAbilities,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2287,7 +2282,6 @@ fn update<'a>(
|
||||||
.extend(procs_base.module_thunks.iter().copied());
|
.extend(procs_base.module_thunks.iter().copied());
|
||||||
|
|
||||||
let found_specializations_module = FoundSpecializationsModule {
|
let found_specializations_module = FoundSpecializationsModule {
|
||||||
module_id,
|
|
||||||
ident_ids,
|
ident_ids,
|
||||||
layout_cache,
|
layout_cache,
|
||||||
procs_base,
|
procs_base,
|
||||||
|
@ -4368,7 +4362,7 @@ fn make_specializations<'a>(
|
||||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||||
mut module_timing: ModuleTiming,
|
mut module_timing: ModuleTiming,
|
||||||
target_info: TargetInfo,
|
target_info: TargetInfo,
|
||||||
world_abilities: AllModuleAbilities,
|
world_abilities: WorldAbilities,
|
||||||
) -> Msg<'a> {
|
) -> Msg<'a> {
|
||||||
let make_specializations_start = SystemTime::now();
|
let make_specializations_start = SystemTime::now();
|
||||||
let mut update_mode_ids = UpdateModeIds::new();
|
let mut update_mode_ids = UpdateModeIds::new();
|
||||||
|
@ -4382,7 +4376,7 @@ fn make_specializations<'a>(
|
||||||
update_mode_ids: &mut update_mode_ids,
|
update_mode_ids: &mut update_mode_ids,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
abilities: WorldAbilities::BigWorld(Arc::clone(&world_abilities)),
|
abilities: AbilitiesView::World(world_abilities),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut procs = Procs::new_in(arena);
|
let mut procs = Procs::new_in(arena);
|
||||||
|
@ -4468,7 +4462,10 @@ fn build_pending_specializations<'a>(
|
||||||
update_mode_ids: &mut update_mode_ids,
|
update_mode_ids: &mut update_mode_ids,
|
||||||
// call_specialization_counter=0 is reserved
|
// call_specialization_counter=0 is reserved
|
||||||
call_specialization_counter: 1,
|
call_specialization_counter: 1,
|
||||||
abilities: WorldAbilities::TinyWorld(&abilities_store),
|
// NB: for getting pending specializations the module view is enough because we only need
|
||||||
|
// 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),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add modules' decls to Procs
|
// Add modules' decls to Procs
|
||||||
|
|
|
@ -19,7 +19,7 @@ use roc_debug_flags::{
|
||||||
use roc_error_macros::todo_abilities;
|
use roc_error_macros::todo_abilities;
|
||||||
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
||||||
use roc_late_solve::{
|
use roc_late_solve::{
|
||||||
instantiate_rigids, resolve_ability_specialization, Resolved, UnificationFailed, WorldAbilities,
|
instantiate_rigids, resolve_ability_specialization, AbilitiesView, Resolved, UnificationFailed,
|
||||||
};
|
};
|
||||||
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
use roc_module::ident::{ForeignSymbol, Lowercase, TagName};
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
|
@ -1268,7 +1268,7 @@ pub struct Env<'a, 'i> {
|
||||||
pub target_info: TargetInfo,
|
pub target_info: TargetInfo,
|
||||||
pub update_mode_ids: &'i mut UpdateModeIds,
|
pub update_mode_ids: &'i mut UpdateModeIds,
|
||||||
pub call_specialization_counter: u32,
|
pub call_specialization_counter: u32,
|
||||||
pub abilities: WorldAbilities<'i>,
|
pub abilities: AbilitiesView<'i>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'i> Env<'a, 'i> {
|
impl<'a, 'i> Env<'a, 'i> {
|
||||||
|
@ -3775,7 +3775,9 @@ pub fn with_hole<'a>(
|
||||||
specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol)
|
specialize_naked_symbol(env, variable, procs, layout_cache, assigned, hole, symbol)
|
||||||
}
|
}
|
||||||
AbilityMember(_member, specialization_id, _) => {
|
AbilityMember(_member, specialization_id, _) => {
|
||||||
let specialization_symbol = env.abilities.with_module_store(env.home, |store| {
|
let specialization_symbol =
|
||||||
|
env.abilities
|
||||||
|
.with_module_abilities_store(env.home, |store| {
|
||||||
store
|
store
|
||||||
.get_resolved(specialization_id)
|
.get_resolved(specialization_id)
|
||||||
.expect("Specialization was never made!")
|
.expect("Specialization was never made!")
|
||||||
|
@ -4786,7 +4788,7 @@ pub fn with_hole<'a>(
|
||||||
UnspecializedExpr(symbol) => {
|
UnspecializedExpr(symbol) => {
|
||||||
match procs.ability_member_aliases.get(symbol).unwrap() {
|
match procs.ability_member_aliases.get(symbol).unwrap() {
|
||||||
&self::AbilityMember(member) => {
|
&self::AbilityMember(member) => {
|
||||||
let resolved_proc = env.abilities.with_module_store(env.home, |store|
|
let resolved_proc = env.abilities.with_module_abilities_store(env.home, |store|
|
||||||
resolve_ability_specialization(env.subs, store, member, fn_var)
|
resolve_ability_specialization(env.subs, store, member, fn_var)
|
||||||
.expect("Recorded as an ability member, but it doesn't have a specialization")
|
.expect("Recorded as an ability member, but it doesn't have a specialization")
|
||||||
);
|
);
|
||||||
|
@ -5166,7 +5168,7 @@ fn late_resolve_ability_specialization<'a>(
|
||||||
) -> Symbol {
|
) -> Symbol {
|
||||||
let opt_resolved = env
|
let opt_resolved = env
|
||||||
.abilities
|
.abilities
|
||||||
.with_module_store(env.home, |store| store.get_resolved(specialization_id));
|
.with_module_abilities_store(env.home, |store| store.get_resolved(specialization_id));
|
||||||
|
|
||||||
if let Some(spec_symbol) = opt_resolved {
|
if let Some(spec_symbol) = opt_resolved {
|
||||||
// Fast path: specialization is monomorphic, was found during solving.
|
// Fast path: specialization is monomorphic, was found during solving.
|
||||||
|
@ -5190,7 +5192,9 @@ fn late_resolve_ability_specialization<'a>(
|
||||||
env.subs[spec_symbol_index]
|
env.subs[spec_symbol_index]
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, resolve by checking the able var.
|
// Otherwise, resolve by checking the able var.
|
||||||
let specialization = env.abilities.with_module_store(env.home, |store| {
|
let specialization = env
|
||||||
|
.abilities
|
||||||
|
.with_module_abilities_store(env.home, |store| {
|
||||||
resolve_ability_specialization(env.subs, store, member, specialization_var)
|
resolve_ability_specialization(env.subs, store, member, specialization_var)
|
||||||
.expect("Ability specialization is unknown - code generation cannot proceed!")
|
.expect("Ability specialization is unknown - code generation cannot proceed!")
|
||||||
});
|
});
|
||||||
|
@ -7028,7 +7032,7 @@ where
|
||||||
{
|
{
|
||||||
let is_ability_member = env
|
let is_ability_member = env
|
||||||
.abilities
|
.abilities
|
||||||
.with_module_store(env.home, |store| store.is_ability_member_name(right));
|
.with_module_abilities_store(env.home, |store| store.is_ability_member_name(right));
|
||||||
|
|
||||||
if is_ability_member {
|
if is_ability_member {
|
||||||
procs
|
procs
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
use std::sync::{Arc, RwLock};
|
|
||||||
|
|
||||||
use roc_can::abilities::AbilitiesStore;
|
use roc_can::abilities::AbilitiesStore;
|
||||||
use roc_can::expr::PendingDerives;
|
use roc_can::expr::PendingDerives;
|
||||||
use roc_collections::{MutMap, VecMap};
|
use roc_collections::VecMap;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::{ModuleId, Symbol};
|
use roc_module::symbol::Symbol;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
use roc_types::subs::{
|
use roc_types::subs::{Content, FlatType, GetSubsSlice, Rank, Subs, Variable};
|
||||||
Content, ExposedTypesStorageSubs, FlatType, GetSubsSlice, Rank, Subs, Variable,
|
|
||||||
};
|
|
||||||
use roc_types::types::{AliasKind, Category, ErrorType, PatternCategory};
|
use roc_types::types::{AliasKind, Category, ErrorType, PatternCategory};
|
||||||
use roc_unify::unify::MustImplementConstraints;
|
use roc_unify::unify::MustImplementConstraints;
|
||||||
use roc_unify::unify::{MustImplementAbility, Obligated};
|
use roc_unify::unify::{MustImplementAbility, Obligated};
|
||||||
|
@ -16,78 +12,6 @@ use roc_unify::unify::{MustImplementAbility, Obligated};
|
||||||
use crate::solve::{instantiate_rigids, type_to_var};
|
use crate::solve::{instantiate_rigids, type_to_var};
|
||||||
use crate::solve::{Aliases, Pools, TypeError};
|
use crate::solve::{Aliases, Pools, TypeError};
|
||||||
|
|
||||||
pub type AllModuleAbilities =
|
|
||||||
Arc<RwLock<MutMap<ModuleId, (AbilitiesStore, ExposedTypesStorageSubs)>>>;
|
|
||||||
|
|
||||||
pub enum WorldAbilities<'a> {
|
|
||||||
BigWorld(AllModuleAbilities),
|
|
||||||
TinyWorld(&'a AbilitiesStore),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WorldAbilities<'_> {
|
|
||||||
pub fn with_module_store<T, F>(&self, module: ModuleId, mut f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnMut(&AbilitiesStore) -> T,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
WorldAbilities::BigWorld(world) => {
|
|
||||||
let world = world.read().unwrap();
|
|
||||||
let (module_store, _module_types) = world.get(&module).unwrap();
|
|
||||||
f(module_store)
|
|
||||||
}
|
|
||||||
WorldAbilities::TinyWorld(store) => f(store),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn copy_lambda_set_var_into(
|
|
||||||
&self,
|
|
||||||
external_lambda_set_var: Variable,
|
|
||||||
external_module_id: ModuleId,
|
|
||||||
target_subs: &mut Subs,
|
|
||||||
target_module_id: ModuleId,
|
|
||||||
) -> Variable {
|
|
||||||
match (external_module_id == target_module_id, self) {
|
|
||||||
(true, _) => {
|
|
||||||
debug_assert!(matches!(
|
|
||||||
target_subs.get_content_without_compacting(external_lambda_set_var),
|
|
||||||
Content::LambdaSet(..)
|
|
||||||
));
|
|
||||||
external_lambda_set_var
|
|
||||||
}
|
|
||||||
(false, Self::TinyWorld(_)) => {
|
|
||||||
// If we're only aware of our module's abilities store, the var must be in our
|
|
||||||
// module store. Even if the specialization lambda set comes from another module,
|
|
||||||
// we would have taken care to import it during multi-module solving.
|
|
||||||
debug_assert!(matches!(
|
|
||||||
target_subs.get_content_without_compacting(external_lambda_set_var),
|
|
||||||
Content::LambdaSet(..)
|
|
||||||
));
|
|
||||||
external_lambda_set_var
|
|
||||||
}
|
|
||||||
(false, Self::BigWorld(world)) => {
|
|
||||||
let mut world = world.write().unwrap();
|
|
||||||
let (_module_store, module_types) = world.get_mut(&external_module_id).unwrap();
|
|
||||||
|
|
||||||
let storage_lambda_set_var = *module_types
|
|
||||||
.stored_specialization_lambda_set_vars
|
|
||||||
.get(&external_lambda_set_var)
|
|
||||||
.unwrap();
|
|
||||||
let copied = module_types
|
|
||||||
.storage_subs
|
|
||||||
.export_variable_to(target_subs, storage_lambda_set_var);
|
|
||||||
let our_lambda_set_var = copied.variable;
|
|
||||||
|
|
||||||
debug_assert!(matches!(
|
|
||||||
target_subs.get_content_without_compacting(our_lambda_set_var),
|
|
||||||
Content::LambdaSet(..)
|
|
||||||
));
|
|
||||||
|
|
||||||
our_lambda_set_var
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum AbilityImplError {
|
pub enum AbilityImplError {
|
||||||
/// Promote this to an error that the type does not fully implement an ability
|
/// Promote this to an error that the type does not fully implement an ability
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::ability::{
|
use crate::ability::{
|
||||||
resolve_ability_specialization, type_implementing_specialization, AbilityImplError,
|
resolve_ability_specialization, type_implementing_specialization, AbilityImplError,
|
||||||
DeferredObligations, DeriveKey, PendingDerivesTable, Resolved, Unfulfilled, WorldAbilities,
|
DeferredObligations, DeriveKey, PendingDerivesTable, Resolved, Unfulfilled,
|
||||||
};
|
};
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use roc_can::abilities::{AbilitiesStore, MemberSpecialization};
|
use roc_can::abilities::{AbilitiesStore, MemberSpecialization};
|
||||||
|
@ -81,21 +81,6 @@ use roc_unify::unify::{unify, Mode, Obligated, Unified::*};
|
||||||
// Ranks are used to limit the number of type variables considered for generalization. Only those inside
|
// Ranks are used to limit the number of type variables considered for generalization. Only those inside
|
||||||
// of the let (so those used in inferring the type of `\x -> x`) are considered.
|
// of the let (so those used in inferring the type of `\x -> x`) are considered.
|
||||||
|
|
||||||
/// What phase in the compiler is reaching out to solve types.
|
|
||||||
/// This is important to distinguish subtle differences in the behavior of the solving algorithm.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub enum Phase {
|
|
||||||
/// The regular type-solving phase.
|
|
||||||
/// Here we can assume that some information is still unknown, and react to that.
|
|
||||||
Solve,
|
|
||||||
/// Calls into solve during later phases of compilation, namely monomorphization.
|
|
||||||
/// Here we expect all information is known.
|
|
||||||
Late {
|
|
||||||
/// Module being solved
|
|
||||||
home: ModuleId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum TypeError {
|
pub enum TypeError {
|
||||||
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
|
BadExpr(Region, Category, ErrorType, Expected<ErrorType>),
|
||||||
|
@ -497,6 +482,57 @@ impl Pools {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What phase in the compiler is reaching out to solve types.
|
||||||
|
/// This is important to distinguish subtle differences in the behavior of the solving algorithm.
|
||||||
|
pub trait Phase {
|
||||||
|
/// The regular type-solving phase, or during some later phase of compilation.
|
||||||
|
/// During the solving phase we must anticipate that some information is still unknown and react to
|
||||||
|
/// that; during late phases, we expect that all information is resolved.
|
||||||
|
const IS_LATE: bool;
|
||||||
|
|
||||||
|
fn with_module_abilities_store<T, F>(&self, module: ModuleId, f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnMut(&AbilitiesStore) -> T;
|
||||||
|
|
||||||
|
fn copy_lambda_set_var_to_home_subs(
|
||||||
|
&self,
|
||||||
|
external_lambda_set_var: Variable,
|
||||||
|
external_module_id: ModuleId,
|
||||||
|
home_subs: &mut Subs,
|
||||||
|
) -> Variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SolvePhase<'a> {
|
||||||
|
abilities_store: &'a AbilitiesStore,
|
||||||
|
}
|
||||||
|
impl Phase for SolvePhase<'_> {
|
||||||
|
const IS_LATE: bool = false;
|
||||||
|
|
||||||
|
fn with_module_abilities_store<T, F>(&self, _module: ModuleId, mut f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnMut(&AbilitiesStore) -> T,
|
||||||
|
{
|
||||||
|
// During solving we're only aware of our module's abilities store.
|
||||||
|
f(self.abilities_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn copy_lambda_set_var_to_home_subs(
|
||||||
|
&self,
|
||||||
|
external_lambda_set_var: Variable,
|
||||||
|
_external_module_id: ModuleId,
|
||||||
|
home_subs: &mut Subs,
|
||||||
|
) -> Variable {
|
||||||
|
// During solving we're only aware of our module's abilities store, the var must
|
||||||
|
// be in our module store. Even if the specialization lambda set comes from another
|
||||||
|
// module, we should have taken care to import it before starting solving in this module.
|
||||||
|
debug_assert!(matches!(
|
||||||
|
home_subs.get_content_without_compacting(external_lambda_set_var),
|
||||||
|
Content::LambdaSet(..)
|
||||||
|
));
|
||||||
|
external_lambda_set_var
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct State {
|
struct State {
|
||||||
env: Env,
|
env: Env,
|
||||||
|
@ -576,9 +612,8 @@ fn run_in_place(
|
||||||
subs,
|
subs,
|
||||||
&arena,
|
&arena,
|
||||||
&mut pools,
|
&mut pools,
|
||||||
&WorldAbilities::TinyWorld(abilities_store),
|
|
||||||
deferred_uls_to_resolve,
|
deferred_uls_to_resolve,
|
||||||
Phase::Solve,
|
&SolvePhase { abilities_store },
|
||||||
);
|
);
|
||||||
|
|
||||||
state.env
|
state.env
|
||||||
|
@ -1779,13 +1814,12 @@ fn find_specialization_lambda_sets(
|
||||||
(leftover_uls, specialization_lambda_sets)
|
(leftover_uls, specialization_lambda_sets)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compact_lambda_sets_of_vars(
|
pub fn compact_lambda_sets_of_vars<P: Phase>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
abilities: &WorldAbilities,
|
|
||||||
uls_of_var: UlsOfVar,
|
uls_of_var: UlsOfVar,
|
||||||
phase: Phase,
|
phase: &P,
|
||||||
) {
|
) {
|
||||||
let mut seen = VecSet::default();
|
let mut seen = VecSet::default();
|
||||||
for (_, lambda_sets) in uls_of_var.drain() {
|
for (_, lambda_sets) in uls_of_var.drain() {
|
||||||
|
@ -1795,20 +1829,19 @@ pub fn compact_lambda_sets_of_vars(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
compact_lambda_set(subs, arena, pools, abilities, root_lset, phase);
|
compact_lambda_set(subs, arena, pools, root_lset, phase);
|
||||||
|
|
||||||
seen.insert(root_lset);
|
seen.insert(root_lset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compact_lambda_set(
|
fn compact_lambda_set<P: Phase>(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
arena: &Bump,
|
arena: &Bump,
|
||||||
pools: &mut Pools,
|
pools: &mut Pools,
|
||||||
abilities: &WorldAbilities,
|
|
||||||
this_lambda_set: Variable,
|
this_lambda_set: Variable,
|
||||||
phase: Phase,
|
phase: &P,
|
||||||
) {
|
) {
|
||||||
let LambdaSet {
|
let LambdaSet {
|
||||||
solved,
|
solved,
|
||||||
|
@ -1865,19 +1898,19 @@ fn compact_lambda_set(
|
||||||
}
|
}
|
||||||
|
|
||||||
let opaque_home = opaque.module_id();
|
let opaque_home = opaque.module_id();
|
||||||
let specialized_lambda_set = abilities.with_module_store(opaque_home, |abilities_store| {
|
let specialized_lambda_set =
|
||||||
|
phase.with_module_abilities_store(opaque_home, |abilities_store| {
|
||||||
let opt_specialization = abilities_store.get_specialization(member, *opaque);
|
let opt_specialization = abilities_store.get_specialization(member, *opaque);
|
||||||
match (phase, opt_specialization) {
|
match (P::IS_LATE, opt_specialization) {
|
||||||
(Phase::Solve, None) => {
|
(false, None) => {
|
||||||
// doesn't specialize, we'll have reported an error for this
|
// doesn't specialize, we'll have reported an error for this
|
||||||
Spec::Skip
|
Spec::Skip
|
||||||
}
|
}
|
||||||
(Phase::Late { home }, None) => {
|
(true, None) => {
|
||||||
internal_error!(
|
internal_error!(
|
||||||
"expected to know a specialization for {:?}#{:?}, but it wasn't found (from module {:?})",
|
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
|
||||||
opaque,
|
opaque,
|
||||||
member,
|
member,
|
||||||
home
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(_, Some(specialization)) => {
|
(_, Some(specialization)) => {
|
||||||
|
@ -1891,18 +1924,13 @@ fn compact_lambda_set(
|
||||||
});
|
});
|
||||||
|
|
||||||
let specialized_lambda_set = match specialized_lambda_set {
|
let specialized_lambda_set = match specialized_lambda_set {
|
||||||
Spec::Some(lset) => match phase {
|
Spec::Some(lset) => phase.copy_lambda_set_var_to_home_subs(lset, opaque_home, subs),
|
||||||
Phase::Solve => lset,
|
|
||||||
Phase::Late { home } => {
|
|
||||||
abilities.copy_lambda_set_var_into(lset, opaque_home, subs, home)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Spec::Skip => continue,
|
Spec::Skip => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure the specialization lambda set is already compacted.
|
// Ensure the specialization lambda set is already compacted.
|
||||||
if subs.get_root_key(specialized_lambda_set) != subs.get_root_key(this_lambda_set) {
|
if subs.get_root_key(specialized_lambda_set) != subs.get_root_key(this_lambda_set) {
|
||||||
compact_lambda_set(subs, arena, pools, abilities, specialized_lambda_set, phase);
|
compact_lambda_set(subs, arena, pools, specialized_lambda_set, phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the specialization lambda set we'll unify with is not a generalized one, but one
|
// Ensure the specialization lambda set we'll unify with is not a generalized one, but one
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue