mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-19 11:30:15 +00:00
Collect awaited lambda set specializations to be solved when a specialization is known
Despite our best efforts, sometimes we still can't specialize lambda sets on the fly, if a specialization lambda set's specialization type isn't yet well-known! This commit adds an `AwaitingSpecializations` data structure to keep track of the lambda sets blocked for specialization behind a specialization's full resolution in the module. After the specialization is resolved, its blocked lambda sets can be eagerly compacted.
This commit is contained in:
parent
350d9cd59b
commit
f2d4bf20ba
2 changed files with 328 additions and 77 deletions
|
@ -3,7 +3,9 @@ use crate::ability::{
|
|||
CheckedDerives, ObligationCache, PendingDerivesTable, Resolved,
|
||||
};
|
||||
use crate::module::Solved;
|
||||
use crate::specialize::{compact_lambda_sets_of_vars, DerivedEnv, SolvePhase};
|
||||
use crate::specialize::{
|
||||
compact_lambda_sets_of_vars, AwaitingSpecializations, CompactionResult, DerivedEnv, SolvePhase,
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use roc_can::abilities::{AbilitiesStore, MemberSpecializationInfo};
|
||||
use roc_can::constraint::Constraint::{self, *};
|
||||
|
@ -20,7 +22,7 @@ use roc_error_macros::internal_error;
|
|||
use roc_module::ident::TagName;
|
||||
use roc_module::symbol::{ModuleId, Symbol};
|
||||
use roc_problem::can::CycleEntry;
|
||||
use roc_region::all::{Loc, Region};
|
||||
use roc_region::all::Loc;
|
||||
use roc_solve_problem::TypeError;
|
||||
use roc_types::subs::{
|
||||
self, AliasVariables, Content, Descriptor, FlatType, GetSubsSlice, LambdaSet, Mark,
|
||||
|
@ -558,6 +560,7 @@ fn run_in_place(
|
|||
let arena = Bump::new();
|
||||
|
||||
let mut obligation_cache = ObligationCache::default();
|
||||
let mut awaiting_specializations = AwaitingSpecializations::default();
|
||||
|
||||
let pending_derives = PendingDerivesTable::new(subs, aliases, pending_derives);
|
||||
let CheckedDerives {
|
||||
|
@ -566,9 +569,10 @@ fn run_in_place(
|
|||
} = obligation_cache.check_derives(subs, abilities_store, pending_derives);
|
||||
problems.extend(derives_problems);
|
||||
|
||||
// Because we don't know what ability specializations are available until the entire module is
|
||||
// solved, we must wait to solve unspecialized lambda sets then.
|
||||
let mut deferred_uls_to_resolve = UlsOfVar::default();
|
||||
let derived_env = DerivedEnv {
|
||||
derived_module: &derived_module,
|
||||
exposed_types: exposed_by_module,
|
||||
};
|
||||
|
||||
let state = solve(
|
||||
&arena,
|
||||
|
@ -582,30 +586,9 @@ fn run_in_place(
|
|||
constraint,
|
||||
abilities_store,
|
||||
&mut obligation_cache,
|
||||
&mut deferred_uls_to_resolve,
|
||||
);
|
||||
|
||||
// Now that the module has been solved, we can run through and check all
|
||||
// types claimed to implement abilities. This will also tell us what derives
|
||||
// are legal, which we need to register.
|
||||
let derived_env = DerivedEnv {
|
||||
derived_module: &derived_module,
|
||||
exposed_types: exposed_by_module,
|
||||
};
|
||||
let new_must_implement = compact_lambda_sets_of_vars(
|
||||
subs,
|
||||
&mut awaiting_specializations,
|
||||
&derived_env,
|
||||
&arena,
|
||||
&mut pools,
|
||||
deferred_uls_to_resolve,
|
||||
&SolvePhase { abilities_store },
|
||||
);
|
||||
problems.extend(obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
new_must_implement,
|
||||
AbilityImplError::DoesNotImplement,
|
||||
));
|
||||
|
||||
state.env
|
||||
}
|
||||
|
@ -659,7 +642,8 @@ fn solve(
|
|||
constraint: &Constraint,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
obligation_cache: &mut ObligationCache,
|
||||
deferred_uls_to_resolve: &mut UlsOfVar,
|
||||
awaiting_specializations: &mut AwaitingSpecializations,
|
||||
derived_env: &DerivedEnv,
|
||||
) -> State {
|
||||
let initial = Work::Constraint {
|
||||
env: &Env::default(),
|
||||
|
@ -715,11 +699,13 @@ fn solve(
|
|||
check_ability_specialization(
|
||||
arena,
|
||||
subs,
|
||||
derived_env,
|
||||
pools,
|
||||
rank,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
problems,
|
||||
deferred_uls_to_resolve,
|
||||
*symbol,
|
||||
*loc_var,
|
||||
);
|
||||
|
@ -822,11 +808,13 @@ fn solve(
|
|||
check_ability_specialization(
|
||||
arena,
|
||||
subs,
|
||||
derived_env,
|
||||
pools,
|
||||
rank,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
problems,
|
||||
deferred_uls_to_resolve,
|
||||
*symbol,
|
||||
*loc_var,
|
||||
);
|
||||
|
@ -892,7 +880,17 @@ fn solve(
|
|||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
compact_lambdas_and_check_obligations(
|
||||
arena,
|
||||
pools,
|
||||
problems,
|
||||
subs,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
derived_env,
|
||||
lambda_sets_to_specialize,
|
||||
);
|
||||
|
||||
state
|
||||
}
|
||||
|
@ -942,7 +940,21 @@ fn solve(
|
|||
} => {
|
||||
introduce(subs, rank, pools, &vars);
|
||||
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
let CompactionResult {
|
||||
obligations,
|
||||
awaiting_specialization,
|
||||
} = compact_lambda_sets_of_vars(
|
||||
subs,
|
||||
&derived_env,
|
||||
&arena,
|
||||
pools,
|
||||
lambda_sets_to_specialize,
|
||||
&SolvePhase { abilities_store },
|
||||
);
|
||||
// implement obligations not reported
|
||||
_ = obligations;
|
||||
// but awaited specializations must be recorded
|
||||
awaiting_specializations.union(awaiting_specialization);
|
||||
|
||||
state
|
||||
}
|
||||
|
@ -1014,7 +1026,17 @@ fn solve(
|
|||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
compact_lambdas_and_check_obligations(
|
||||
arena,
|
||||
pools,
|
||||
problems,
|
||||
subs,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
derived_env,
|
||||
lambda_sets_to_specialize,
|
||||
);
|
||||
|
||||
state
|
||||
}
|
||||
|
@ -1094,7 +1116,17 @@ fn solve(
|
|||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
compact_lambdas_and_check_obligations(
|
||||
arena,
|
||||
pools,
|
||||
problems,
|
||||
subs,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
derived_env,
|
||||
lambda_sets_to_specialize,
|
||||
);
|
||||
|
||||
state
|
||||
}
|
||||
|
@ -1267,7 +1299,17 @@ fn solve(
|
|||
);
|
||||
problems.extend(new_problems);
|
||||
}
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
compact_lambdas_and_check_obligations(
|
||||
arena,
|
||||
pools,
|
||||
problems,
|
||||
subs,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
derived_env,
|
||||
lambda_sets_to_specialize,
|
||||
);
|
||||
|
||||
state
|
||||
}
|
||||
|
@ -1373,7 +1415,17 @@ fn solve(
|
|||
must_implement_ability,
|
||||
AbilityImplError::DoesNotImplement,
|
||||
));
|
||||
deferred_uls_to_resolve.union(lambda_sets_to_specialize);
|
||||
compact_lambdas_and_check_obligations(
|
||||
arena,
|
||||
pools,
|
||||
problems,
|
||||
subs,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
derived_env,
|
||||
lambda_sets_to_specialize,
|
||||
);
|
||||
|
||||
// Case 1: unify error types, but don't check exhaustiveness.
|
||||
// Case 2: run exhaustiveness to check for redundant branches.
|
||||
|
@ -1543,6 +1595,37 @@ fn solve(
|
|||
state
|
||||
}
|
||||
|
||||
fn compact_lambdas_and_check_obligations(
|
||||
arena: &Bump,
|
||||
pools: &mut Pools,
|
||||
problems: &mut Vec<TypeError>,
|
||||
subs: &mut Subs,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
obligation_cache: &mut ObligationCache,
|
||||
awaiting_specialization: &mut AwaitingSpecializations,
|
||||
derived_env: &DerivedEnv,
|
||||
lambda_sets_to_specialize: UlsOfVar,
|
||||
) {
|
||||
let CompactionResult {
|
||||
obligations,
|
||||
awaiting_specialization: new_awaiting,
|
||||
} = compact_lambda_sets_of_vars(
|
||||
subs,
|
||||
&derived_env,
|
||||
&arena,
|
||||
pools,
|
||||
lambda_sets_to_specialize,
|
||||
&SolvePhase { abilities_store },
|
||||
);
|
||||
problems.extend(obligation_cache.check_obligations(
|
||||
subs,
|
||||
abilities_store,
|
||||
obligations,
|
||||
AbilityImplError::DoesNotImplement,
|
||||
));
|
||||
awaiting_specialization.union(new_awaiting);
|
||||
}
|
||||
|
||||
fn open_tag_union(subs: &mut Subs, var: Variable) {
|
||||
let mut stack = vec![var];
|
||||
while let Some(var) = stack.pop() {
|
||||
|
@ -1589,11 +1672,13 @@ fn open_tag_union(subs: &mut Subs, var: Variable) {
|
|||
fn check_ability_specialization(
|
||||
arena: &Bump,
|
||||
subs: &mut Subs,
|
||||
derived_env: &DerivedEnv,
|
||||
pools: &mut Pools,
|
||||
rank: Rank,
|
||||
abilities_store: &mut AbilitiesStore,
|
||||
obligation_cache: &mut ObligationCache,
|
||||
awaiting_specializations: &mut AwaitingSpecializations,
|
||||
problems: &mut Vec<TypeError>,
|
||||
deferred_uls_to_resolve: &mut UlsOfVar,
|
||||
symbol: Symbol,
|
||||
symbol_loc_var: Loc<Variable>,
|
||||
) {
|
||||
|
@ -1626,7 +1711,7 @@ fn check_ability_specialization(
|
|||
Success {
|
||||
vars,
|
||||
must_implement_ability,
|
||||
lambda_sets_to_specialize: other_lambda_sets_to_specialize,
|
||||
lambda_sets_to_specialize,
|
||||
extra_metadata: SpecializationLsetCollector(specialization_lambda_sets),
|
||||
} => {
|
||||
let specialization_type =
|
||||
|
@ -1650,7 +1735,17 @@ fn check_ability_specialization(
|
|||
})
|
||||
.collect();
|
||||
|
||||
deferred_uls_to_resolve.union(other_lambda_sets_to_specialize);
|
||||
compact_lambdas_and_check_obligations(
|
||||
arena,
|
||||
pools,
|
||||
problems,
|
||||
subs,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
derived_env,
|
||||
lambda_sets_to_specialize,
|
||||
);
|
||||
|
||||
let specialization =
|
||||
MemberSpecializationInfo::new(symbol, specialization_lambda_sets);
|
||||
|
@ -1761,6 +1856,27 @@ fn check_ability_specialization(
|
|||
abilities_store
|
||||
.mark_implementation(impl_key, resolved_mark)
|
||||
.expect("marked as a custom implementation, but not recorded as such");
|
||||
|
||||
// Get the lambda sets that are ready for specialization because this ability member
|
||||
// specializaiton was resolved, and compact them.
|
||||
let new_lambda_sets_to_specialize =
|
||||
awaiting_specializations.remove_for_specialized(subs, impl_key);
|
||||
compact_lambdas_and_check_obligations(
|
||||
arena,
|
||||
pools,
|
||||
problems,
|
||||
subs,
|
||||
abilities_store,
|
||||
obligation_cache,
|
||||
awaiting_specializations,
|
||||
derived_env,
|
||||
new_lambda_sets_to_specialize,
|
||||
);
|
||||
debug_assert!(
|
||||
!awaiting_specializations.waiting_for(impl_key),
|
||||
"still have lambda sets waiting for {:?}, but it was just resolved",
|
||||
impl_key
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,11 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use roc_can::{abilities::AbilitiesStore, module::ExposedByModule};
|
||||
use roc_can::{
|
||||
abilities::{AbilitiesStore, ImplKey},
|
||||
module::ExposedByModule,
|
||||
};
|
||||
use roc_collections::{VecMap, VecSet};
|
||||
use roc_debug_flags::{dbg_do, ROC_TRACE_COMPACTION};
|
||||
use roc_derive::SharedDerivedModule;
|
||||
use roc_derive_key::{DeriveError, DeriveKey};
|
||||
|
@ -118,6 +122,62 @@ pub struct DerivedEnv<'a> {
|
|||
pub exposed_types: &'a ExposedByModule,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AwaitingSpecializations {
|
||||
// What variables' specialized lambda sets in `uls_of_var` will be unlocked for specialization
|
||||
// when an implementation key's specialization is resolved?
|
||||
waiting: VecMap<ImplKey, VecSet<Variable>>,
|
||||
uls_of_var: UlsOfVar,
|
||||
}
|
||||
|
||||
impl AwaitingSpecializations {
|
||||
pub fn remove_for_specialized(&mut self, subs: &Subs, impl_key: ImplKey) -> UlsOfVar {
|
||||
let spec_variables = self
|
||||
.waiting
|
||||
.remove(&impl_key)
|
||||
.map(|(_, set)| set)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut result = UlsOfVar::default();
|
||||
for var in spec_variables {
|
||||
let target_lambda_sets = self
|
||||
.uls_of_var
|
||||
.remove_dependent_unspecialized_lambda_sets(subs, var);
|
||||
|
||||
result.extend(var, target_lambda_sets);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn add(
|
||||
&mut self,
|
||||
impl_key: ImplKey,
|
||||
var: Variable,
|
||||
lambda_sets: impl IntoIterator<Item = Variable>,
|
||||
) {
|
||||
self.uls_of_var.extend(var, lambda_sets);
|
||||
let waiting = self.waiting.get_or_insert(impl_key, Default::default);
|
||||
waiting.insert(var);
|
||||
}
|
||||
|
||||
pub fn union(&mut self, other: Self) {
|
||||
for (impl_key, waiting_vars) in other.waiting {
|
||||
let waiting = self.waiting.get_or_insert(impl_key, Default::default);
|
||||
waiting.extend(waiting_vars);
|
||||
}
|
||||
self.uls_of_var.union(other.uls_of_var);
|
||||
}
|
||||
|
||||
pub fn waiting_for(&self, impl_key: ImplKey) -> bool {
|
||||
self.waiting.contains_key(&impl_key)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompactionResult {
|
||||
pub obligations: MustImplementConstraints,
|
||||
pub awaiting_specialization: AwaitingSpecializations,
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn trace_compaction_step_1(subs: &Subs, c_a: Variable, uls_a: &[Variable]) {
|
||||
let c_a = roc_types::subs::SubsFmtContent(subs.get_content_without_compacting(c_a), subs);
|
||||
|
@ -246,9 +306,9 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
pools: &mut Pools,
|
||||
uls_of_var: UlsOfVar,
|
||||
phase: &P,
|
||||
) -> MustImplementConstraints {
|
||||
// let mut seen = VecSet::default();
|
||||
) -> CompactionResult {
|
||||
let mut must_implement = MustImplementConstraints::default();
|
||||
let mut awaiting_specialization = AwaitingSpecializations::default();
|
||||
|
||||
let mut uls_of_var_queue = VecDeque::with_capacity(uls_of_var.len());
|
||||
uls_of_var_queue.extend(uls_of_var.drain());
|
||||
|
@ -374,22 +434,36 @@ pub fn compact_lambda_sets_of_vars<P: Phase>(
|
|||
// 3. Unify `t_f1 ~ t_f2`.
|
||||
trace_compact!(3start.);
|
||||
for l in uls_a {
|
||||
// let root_lset = subs.get_root_key_without_compacting(l);
|
||||
// if seen.contains(&root_lset) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
let (new_must_implement, new_uls_of_var) =
|
||||
let compaction_result =
|
||||
compact_lambda_set(subs, derived_env, arena, pools, c_a, l, phase);
|
||||
|
||||
must_implement.extend(new_must_implement);
|
||||
uls_of_var_queue.extend(new_uls_of_var.drain());
|
||||
|
||||
// seen.insert(root_lset);
|
||||
match compaction_result {
|
||||
OneCompactionResult::Compacted {
|
||||
new_obligations,
|
||||
new_lambda_sets_to_specialize,
|
||||
} => {
|
||||
must_implement.extend(new_obligations);
|
||||
uls_of_var_queue.extend(new_lambda_sets_to_specialize.drain());
|
||||
}
|
||||
OneCompactionResult::MustWaitForSpecialization(impl_key) => {
|
||||
awaiting_specialization.add(impl_key, c_a, [l])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
must_implement
|
||||
CompactionResult {
|
||||
obligations: must_implement,
|
||||
awaiting_specialization,
|
||||
}
|
||||
}
|
||||
|
||||
enum OneCompactionResult {
|
||||
Compacted {
|
||||
new_obligations: MustImplementConstraints,
|
||||
new_lambda_sets_to_specialize: UlsOfVar,
|
||||
},
|
||||
MustWaitForSpecialization(ImplKey),
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
@ -402,7 +476,7 @@ fn compact_lambda_set<P: Phase>(
|
|||
resolved_concrete: Variable,
|
||||
this_lambda_set: Variable,
|
||||
phase: &P,
|
||||
) -> (MustImplementConstraints, UlsOfVar) {
|
||||
) -> OneCompactionResult {
|
||||
// 3. For each `l` in `uls_a` with unique unspecialized lambda `C:f:r`:
|
||||
// 1. Let `t_f1` be the directly ambient function of the lambda set containing `C:f:r`. Remove `C:f:r` from `t_f1`'s lambda set.
|
||||
// - For example, `(b' -[[] + Fo:f:2]-> {})` if `C:f:r=Fo:f:2`. Removing `Fo:f:2`, we get `(b' -[[]]-> {})`.
|
||||
|
@ -426,6 +500,20 @@ fn compact_lambda_set<P: Phase>(
|
|||
|
||||
debug_assert!(subs.equivalent_without_compacting(c, resolved_concrete));
|
||||
|
||||
// Now decide: do we
|
||||
// - proceed with specialization
|
||||
// - simply drop the specialization lambda set (due to an error)
|
||||
// - or do we need to wait, because we don't know enough information for the specialization yet?
|
||||
let specialization_decision = make_specialization_decision(subs, phase, c, f);
|
||||
let specialization_key_or_drop = match specialization_decision {
|
||||
SpecializeDecision::Specialize(key) => Ok(key),
|
||||
SpecializeDecision::Drop => Err(()),
|
||||
SpecializeDecision::PendingSpecialization(impl_key) => {
|
||||
// Bail, we need to wait for the specialization to be known.
|
||||
return OneCompactionResult::MustWaitForSpecialization(impl_key);
|
||||
}
|
||||
};
|
||||
|
||||
// 1b. Remove `C:f:r` from `t_f1`'s lambda set.
|
||||
let new_unspecialized: Vec<_> = unspecialized
|
||||
.iter()
|
||||
|
@ -447,15 +535,16 @@ fn compact_lambda_set<P: Phase>(
|
|||
Content::LambdaSet(t_f1_lambda_set_without_concrete),
|
||||
);
|
||||
|
||||
let specialization_decision = make_specialization_decision(subs, c);
|
||||
|
||||
let specialization_key = match specialization_decision {
|
||||
SpecializeDecision::Specialize(key) => key,
|
||||
SpecializeDecision::Drop => {
|
||||
let specialization_key = match specialization_key_or_drop {
|
||||
Ok(specialization_key) => specialization_key,
|
||||
Err(()) => {
|
||||
// Do nothing other than to remove the concrete lambda to drop from the lambda set,
|
||||
// which we already did in 1b above.
|
||||
trace_compact!(3iter_end_skipped. subs, t_f1);
|
||||
return (Default::default(), Default::default());
|
||||
return OneCompactionResult::Compacted {
|
||||
new_obligations: Default::default(),
|
||||
new_lambda_sets_to_specialize: Default::default(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -475,7 +564,10 @@ fn compact_lambda_set<P: Phase>(
|
|||
// Do nothing other than to remove the concrete lambda to drop from the lambda set,
|
||||
// which we already did in 1b above.
|
||||
trace_compact!(3iter_end_skipped. subs, t_f1);
|
||||
return (Default::default(), Default::default());
|
||||
return OneCompactionResult::Compacted {
|
||||
new_obligations: Default::default(),
|
||||
new_lambda_sets_to_specialize: Default::default(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -485,14 +577,17 @@ fn compact_lambda_set<P: Phase>(
|
|||
|
||||
// 3. Unify `t_f1 ~ t_f2`.
|
||||
trace_compact!(3iter_start. subs, this_lambda_set, t_f1, t_f2);
|
||||
let (vars, new_must_implement_ability, new_lambda_sets_to_specialize, _meta) =
|
||||
let (vars, new_obligations, new_lambda_sets_to_specialize, _meta) =
|
||||
unify(&mut UEnv::new(subs), t_f1, t_f2, Mode::EQ)
|
||||
.expect_success("ambient functions don't unify");
|
||||
trace_compact!(3iter_end. subs, t_f1);
|
||||
|
||||
introduce(subs, target_rank, pools, &vars);
|
||||
|
||||
(new_must_implement_ability, new_lambda_sets_to_specialize)
|
||||
OneCompactionResult::Compacted {
|
||||
new_obligations,
|
||||
new_lambda_sets_to_specialize,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -505,14 +600,53 @@ enum SpecializationTypeKey {
|
|||
enum SpecializeDecision {
|
||||
Specialize(SpecializationTypeKey),
|
||||
Drop,
|
||||
|
||||
/// Only relevant during module solving of recursive defs - we don't yet know the
|
||||
/// specialization type for a declared ability implementation, so we must hold off on
|
||||
/// specialization.
|
||||
PendingSpecialization(ImplKey),
|
||||
}
|
||||
|
||||
fn make_specialization_decision(subs: &Subs, var: Variable) -> SpecializeDecision {
|
||||
fn make_specialization_decision<P: Phase>(
|
||||
subs: &Subs,
|
||||
phase: &P,
|
||||
var: Variable,
|
||||
ability_member: Symbol,
|
||||
) -> SpecializeDecision {
|
||||
use Content::*;
|
||||
use SpecializationTypeKey::*;
|
||||
match subs.get_content_without_compacting(var) {
|
||||
Alias(opaque, _, _, AliasKind::Opaque) if opaque.module_id() != ModuleId::NUM => {
|
||||
SpecializeDecision::Specialize(Opaque(*opaque))
|
||||
if P::IS_LATE {
|
||||
SpecializeDecision::Specialize(Opaque(*opaque))
|
||||
} else {
|
||||
// Solving within a module.
|
||||
phase.with_module_abilities_store(opaque.module_id(), |abilities_store| {
|
||||
let impl_key = ImplKey {
|
||||
opaque: *opaque,
|
||||
ability_member,
|
||||
};
|
||||
match abilities_store.get_implementation(impl_key) {
|
||||
None => {
|
||||
// Doesn't specialize; an error will already be reported for this.
|
||||
SpecializeDecision::Drop
|
||||
}
|
||||
Some(MemberImpl::Error | MemberImpl::Derived) => {
|
||||
// TODO: probably not right, we may want to choose a derive decision!
|
||||
SpecializeDecision::Specialize(Opaque(*opaque))
|
||||
}
|
||||
Some(MemberImpl::Impl(specialization_symbol)) => {
|
||||
match abilities_store.specialization_info(*specialization_symbol) {
|
||||
Some(_) => SpecializeDecision::Specialize(Opaque(*opaque)),
|
||||
|
||||
// If we expect a specialization impl but don't yet know it, we must hold off
|
||||
// compacting the lambda set until the specialization is well-known.
|
||||
None => SpecializeDecision::PendingSpecialization(impl_key),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Structure(_) | Alias(_, _, _, _) => {
|
||||
// This is a structural type, find the name of the derived ability function it
|
||||
|
@ -573,19 +707,20 @@ fn get_specialization_lambda_set_ambient_function<P: Phase>(
|
|||
};
|
||||
let opt_specialization =
|
||||
abilities_store.get_implementation(impl_key);
|
||||
match (P::IS_LATE, opt_specialization) {
|
||||
(false, None) => {
|
||||
// doesn't specialize, we'll have reported an error for this
|
||||
Err(())
|
||||
match opt_specialization {
|
||||
None => {
|
||||
if P::IS_LATE {
|
||||
internal_error!(
|
||||
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
|
||||
opaque,
|
||||
ability_member
|
||||
);
|
||||
} else {
|
||||
// doesn't specialize, we'll have reported an error for this
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
(true, None) => {
|
||||
internal_error!(
|
||||
"expected to know a specialization for {:?}#{:?}, but it wasn't found",
|
||||
opaque,
|
||||
ability_member,
|
||||
);
|
||||
}
|
||||
(_, Some(member_impl)) => match member_impl {
|
||||
Some(member_impl) => match member_impl {
|
||||
MemberImpl::Impl(spec_symbol) => {
|
||||
let specialization =
|
||||
abilities_store.specialization_info(*spec_symbol).expect("expected custom implementations to always have complete specialization info by this point");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue