mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-27 13:59:08 +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 = [
|
||||
"bumpalo",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_module",
|
||||
"roc_solve",
|
||||
"roc_types",
|
||||
|
@ -3910,6 +3911,7 @@ dependencies = [
|
|||
"roc_constrain",
|
||||
"roc_debug_flags",
|
||||
"roc_error_macros",
|
||||
"roc_late_solve",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_parse",
|
||||
|
|
|
@ -11,4 +11,5 @@ roc_can = { path = "../can" }
|
|||
roc_module = { path = "../module" }
|
||||
roc_unify = { path = "../unify" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_collections = { path = "../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
|
||||
//! the compiler backend.
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_collections::MutMap;
|
||||
use roc_module::symbol::ModuleId;
|
||||
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};
|
||||
|
||||
pub use roc_solve::solve::instantiate_rigids;
|
||||
|
||||
pub use roc_solve::ability::resolve_ability_specialization;
|
||||
pub use roc_solve::ability::{Resolved, WorldAbilities};
|
||||
pub use roc_solve::ability::Resolved;
|
||||
|
||||
#[derive(Debug)]
|
||||
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.
|
||||
/// Ranks and other ability demands are disregarded.
|
||||
pub fn unify(
|
||||
home: ModuleId,
|
||||
arena: &Bump,
|
||||
subs: &mut Subs,
|
||||
world_abilities: &WorldAbilities,
|
||||
abilities: &AbilitiesView,
|
||||
left: Variable,
|
||||
right: Variable,
|
||||
) -> Result<(), UnificationFailed> {
|
||||
|
@ -34,13 +148,14 @@ pub fn unify(
|
|||
} => {
|
||||
let mut pools = Pools::default();
|
||||
|
||||
let late_phase = LatePhase { home, abilities };
|
||||
|
||||
compact_lambda_sets_of_vars(
|
||||
subs,
|
||||
arena,
|
||||
&mut pools,
|
||||
world_abilities,
|
||||
lambda_sets_to_specialize,
|
||||
Phase::Late { home },
|
||||
&late_phase,
|
||||
);
|
||||
// Pools are only used to keep track of variable ranks for generalization purposes.
|
||||
// Since we break generalization during monomorphization, `pools` is irrelevant
|
||||
|
|
|
@ -18,6 +18,7 @@ roc_problem = { path = "../problem" }
|
|||
roc_unify = { path = "../unify" }
|
||||
roc_parse = { path = "../parse" }
|
||||
roc_solve = { path = "../solve" }
|
||||
roc_late_solve = { path = "../late_solve" }
|
||||
roc_mono = { path = "../mono" }
|
||||
roc_target = { path = "../roc_target" }
|
||||
roc_reporting = { path = "../../reporting" }
|
||||
|
|
|
@ -23,6 +23,7 @@ use roc_debug_flags::{
|
|||
ROC_PRINT_LOAD_LOG,
|
||||
};
|
||||
use roc_error_macros::internal_error;
|
||||
use roc_late_solve::{AbilitiesView, WorldAbilities};
|
||||
use roc_module::ident::{Ident, ModuleName, QualifiedModuleName};
|
||||
use roc_module::symbol::{
|
||||
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_region::all::{LineInfo, Loc, Region};
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_solve::ability::{AllModuleAbilities, WorldAbilities};
|
||||
use roc_solve::module::SolvedModule;
|
||||
use roc_solve::solve;
|
||||
use roc_target::TargetInfo;
|
||||
|
@ -442,7 +442,6 @@ fn start_phase<'a>(
|
|||
.unwrap();
|
||||
|
||||
let FoundSpecializationsModule {
|
||||
module_id,
|
||||
ident_ids,
|
||||
subs,
|
||||
procs_base,
|
||||
|
@ -458,16 +457,13 @@ fn start_phase<'a>(
|
|||
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,
|
||||
(
|
||||
abilities_store,
|
||||
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)
|
||||
} else {
|
||||
let LateSpecializationsModule {
|
||||
|
@ -493,7 +489,7 @@ fn start_phase<'a>(
|
|||
layout_cache,
|
||||
specializations_we_must_make,
|
||||
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)]
|
||||
struct FoundSpecializationsModule<'a> {
|
||||
module_id: ModuleId,
|
||||
ident_ids: IdentIds,
|
||||
layout_cache: LayoutCache<'a>,
|
||||
procs_base: ProcsBase<'a>,
|
||||
|
@ -818,8 +813,8 @@ struct State<'a> {
|
|||
|
||||
pub render: RenderTarget,
|
||||
|
||||
/// At a given point in time, this map contains the abilities stores in reverse order
|
||||
pub inverse_world_abilities: AllModuleAbilities,
|
||||
/// All abilities across all modules.
|
||||
pub world_abilities: WorldAbilities,
|
||||
|
||||
make_specializations_pass: MakeSpecializationsPass,
|
||||
|
||||
|
@ -867,8 +862,8 @@ impl<'a> State<'a> {
|
|||
layout_caches: std::vec::Vec::with_capacity(number_of_workers),
|
||||
cached_subs: Arc::new(Mutex::new(cached_subs)),
|
||||
render,
|
||||
inverse_world_abilities: Default::default(),
|
||||
make_specializations_pass: MakeSpecializationsPass::Pass(1),
|
||||
world_abilities: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -996,7 +991,7 @@ enum BuildTask<'a> {
|
|||
layout_cache: LayoutCache<'a>,
|
||||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||
module_timing: ModuleTiming,
|
||||
world_abilities: AllModuleAbilities,
|
||||
world_abilities: WorldAbilities,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -2287,7 +2282,6 @@ fn update<'a>(
|
|||
.extend(procs_base.module_thunks.iter().copied());
|
||||
|
||||
let found_specializations_module = FoundSpecializationsModule {
|
||||
module_id,
|
||||
ident_ids,
|
||||
layout_cache,
|
||||
procs_base,
|
||||
|
@ -4368,7 +4362,7 @@ fn make_specializations<'a>(
|
|||
specializations_we_must_make: Vec<ExternalSpecializations>,
|
||||
mut module_timing: ModuleTiming,
|
||||
target_info: TargetInfo,
|
||||
world_abilities: AllModuleAbilities,
|
||||
world_abilities: WorldAbilities,
|
||||
) -> Msg<'a> {
|
||||
let make_specializations_start = SystemTime::now();
|
||||
let mut update_mode_ids = UpdateModeIds::new();
|
||||
|
@ -4382,7 +4376,7 @@ fn make_specializations<'a>(
|
|||
update_mode_ids: &mut update_mode_ids,
|
||||
// call_specialization_counter=0 is reserved
|
||||
call_specialization_counter: 1,
|
||||
abilities: WorldAbilities::BigWorld(Arc::clone(&world_abilities)),
|
||||
abilities: AbilitiesView::World(world_abilities),
|
||||
};
|
||||
|
||||
let mut procs = Procs::new_in(arena);
|
||||
|
@ -4468,7 +4462,10 @@ fn build_pending_specializations<'a>(
|
|||
update_mode_ids: &mut update_mode_ids,
|
||||
// call_specialization_counter=0 is reserved
|
||||
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
|
||||
|
|
|
@ -19,7 +19,7 @@ use roc_debug_flags::{
|
|||
use roc_error_macros::todo_abilities;
|
||||
use roc_exhaustive::{Ctor, CtorName, Guard, RenderAs, TagId};
|
||||
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::low_level::LowLevel;
|
||||
|
@ -1268,7 +1268,7 @@ pub struct Env<'a, 'i> {
|
|||
pub target_info: TargetInfo,
|
||||
pub update_mode_ids: &'i mut UpdateModeIds,
|
||||
pub call_specialization_counter: u32,
|
||||
pub abilities: WorldAbilities<'i>,
|
||||
pub abilities: AbilitiesView<'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)
|
||||
}
|
||||
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
|
||||
.get_resolved(specialization_id)
|
||||
.expect("Specialization was never made!")
|
||||
|
@ -4786,7 +4788,7 @@ pub fn with_hole<'a>(
|
|||
UnspecializedExpr(symbol) => {
|
||||
match procs.ability_member_aliases.get(symbol).unwrap() {
|
||||
&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)
|
||||
.expect("Recorded as an ability member, but it doesn't have a specialization")
|
||||
);
|
||||
|
@ -5166,7 +5168,7 @@ fn late_resolve_ability_specialization<'a>(
|
|||
) -> Symbol {
|
||||
let opt_resolved = env
|
||||
.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 {
|
||||
// Fast path: specialization is monomorphic, was found during solving.
|
||||
|
@ -5190,7 +5192,9 @@ fn late_resolve_ability_specialization<'a>(
|
|||
env.subs[spec_symbol_index]
|
||||
} else {
|
||||
// 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)
|
||||
.expect("Ability specialization is unknown - code generation cannot proceed!")
|
||||
});
|
||||
|
@ -7028,7 +7032,7 @@ where
|
|||
{
|
||||
let is_ability_member = env
|
||||
.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 {
|
||||
procs
|
||||
|
|
|
@ -1,14 +1,10 @@
|
|||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use roc_can::abilities::AbilitiesStore;
|
||||
use roc_can::expr::PendingDerives;
|
||||
use roc_collections::{MutMap, VecMap};
|
||||
use roc_collections::VecMap;
|
||||
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_types::subs::{
|
||||
Content, ExposedTypesStorageSubs, FlatType, GetSubsSlice, Rank, Subs, Variable,
|
||||
};
|
||||
use roc_types::subs::{Content, FlatType, GetSubsSlice, Rank, Subs, Variable};
|
||||
use roc_types::types::{AliasKind, Category, ErrorType, PatternCategory};
|
||||
use roc_unify::unify::MustImplementConstraints;
|
||||
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::{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)]
|
||||
pub enum AbilityImplError {
|
||||
/// Promote this to an error that the type does not fully implement an ability
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::ability::{
|
||||
resolve_ability_specialization, type_implementing_specialization, AbilityImplError,
|
||||
DeferredObligations, DeriveKey, PendingDerivesTable, Resolved, Unfulfilled, WorldAbilities,
|
||||
DeferredObligations, DeriveKey, PendingDerivesTable, Resolved, Unfulfilled,
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
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
|
||||
// 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)]
|
||||
pub enum TypeError {
|
||||
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)]
|
||||
struct State {
|
||||
env: Env,
|
||||
|
@ -576,9 +612,8 @@ fn run_in_place(
|
|||
subs,
|
||||
&arena,
|
||||
&mut pools,
|
||||
&WorldAbilities::TinyWorld(abilities_store),
|
||||
deferred_uls_to_resolve,
|
||||
Phase::Solve,
|
||||
&SolvePhase { abilities_store },
|
||||
);
|
||||
|
||||
state.env
|
||||
|
@ -1779,13 +1814,12 @@ fn find_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,
|
||||
arena: &Bump,
|
||||
pools: &mut Pools,
|
||||
abilities: &WorldAbilities,
|
||||
uls_of_var: UlsOfVar,
|
||||
phase: Phase,
|
||||
phase: &P,
|
||||
) {
|
||||
let mut seen = VecSet::default();
|
||||
for (_, lambda_sets) in uls_of_var.drain() {
|
||||
|
@ -1795,20 +1829,19 @@ pub fn compact_lambda_sets_of_vars(
|
|||
continue;
|
||||
}
|
||||
|
||||
compact_lambda_set(subs, arena, pools, abilities, root_lset, phase);
|
||||
compact_lambda_set(subs, arena, pools, root_lset, phase);
|
||||
|
||||
seen.insert(root_lset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compact_lambda_set(
|
||||
fn compact_lambda_set<P: Phase>(
|
||||
subs: &mut Subs,
|
||||
arena: &Bump,
|
||||
pools: &mut Pools,
|
||||
abilities: &WorldAbilities,
|
||||
this_lambda_set: Variable,
|
||||
phase: Phase,
|
||||
phase: &P,
|
||||
) {
|
||||
let LambdaSet {
|
||||
solved,
|
||||
|
@ -1865,19 +1898,19 @@ fn compact_lambda_set(
|
|||
}
|
||||
|
||||
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);
|
||||
match (phase, opt_specialization) {
|
||||
(Phase::Solve, None) => {
|
||||
match (P::IS_LATE, opt_specialization) {
|
||||
(false, None) => {
|
||||
// doesn't specialize, we'll have reported an error for this
|
||||
Spec::Skip
|
||||
}
|
||||
(Phase::Late { home }, None) => {
|
||||
(true, None) => {
|
||||
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,
|
||||
member,
|
||||
home
|
||||
);
|
||||
}
|
||||
(_, Some(specialization)) => {
|
||||
|
@ -1891,18 +1924,13 @@ fn compact_lambda_set(
|
|||
});
|
||||
|
||||
let specialized_lambda_set = match specialized_lambda_set {
|
||||
Spec::Some(lset) => match phase {
|
||||
Phase::Solve => lset,
|
||||
Phase::Late { home } => {
|
||||
abilities.copy_lambda_set_var_into(lset, opaque_home, subs, home)
|
||||
}
|
||||
},
|
||||
Spec::Some(lset) => phase.copy_lambda_set_var_to_home_subs(lset, opaque_home, subs),
|
||||
Spec::Skip => continue,
|
||||
};
|
||||
|
||||
// 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, 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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue