handle specialization in the new trait solver
uwu :3
This commit is contained in:
@@ -10,6 +10,7 @@ use rustc_type_ir::{
|
||||
};
|
||||
use tracing::{debug, instrument};
|
||||
|
||||
use super::has_only_region_constraints;
|
||||
use super::trait_goals::TraitGoalProvenVia;
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::inspect::ProbeKind;
|
||||
@@ -771,6 +772,69 @@ where
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum AllowInferenceConstraints {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
impl<D, I> EvalCtxt<'_, D>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
I: Interner,
|
||||
{
|
||||
/// Check whether we can ignore impl candidates due to specialization.
|
||||
///
|
||||
/// This is only necessary for `feature(specialization)` and seems quite ugly.
|
||||
pub(super) fn filter_specialized_impls(
|
||||
&mut self,
|
||||
allow_inference_constraints: AllowInferenceConstraints,
|
||||
candidates: &mut Vec<Candidate<I>>,
|
||||
) {
|
||||
match self.typing_mode() {
|
||||
TypingMode::Coherence => return,
|
||||
TypingMode::Analysis { .. }
|
||||
| TypingMode::Borrowck { .. }
|
||||
| TypingMode::PostBorrowckAnalysis { .. }
|
||||
| TypingMode::PostAnalysis => {}
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
'outer: while i < candidates.len() {
|
||||
let CandidateSource::Impl(victim_def_id) = candidates[i].source else {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
for (j, c) in candidates.iter().enumerate() {
|
||||
if i == j {
|
||||
continue;
|
||||
}
|
||||
|
||||
let CandidateSource::Impl(other_def_id) = c.source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// See if we can toss out `victim` based on specialization.
|
||||
//
|
||||
// While this requires us to know *for sure* that the `lhs` impl applies
|
||||
// we still use modulo regions here. This is fine as specialization currently
|
||||
// assumes that specializing impls have to be always applicable, meaning that
|
||||
// the only allowed region constraints may be constraints also present on the default impl.
|
||||
if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes)
|
||||
|| has_only_region_constraints(c.result)
|
||||
{
|
||||
if self.cx().impl_specializes(other_def_id, victim_def_id) {
|
||||
candidates.remove(i);
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// Assemble and merge candidates for goals which are related to an underlying trait
|
||||
/// goal. Right now, this is normalizes-to and host effect goals.
|
||||
@@ -857,7 +921,7 @@ where
|
||||
}
|
||||
}
|
||||
TraitGoalProvenVia::Misc => {
|
||||
let candidates =
|
||||
let mut candidates =
|
||||
self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
|
||||
|
||||
// Prefer "orphaned" param-env normalization predicates, which are used
|
||||
@@ -871,6 +935,13 @@ where
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
// We drop specialized impls to allow normalization via a final impl here. In case
|
||||
// the specializing impl has different inference constraints from the specialized
|
||||
// impl, proving the trait goal is already ambiguous, so we never get here. This
|
||||
// means we can just ignore inference constraints and don't have to special-case
|
||||
// constraining the normalized-to `term`.
|
||||
self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
|
||||
|
||||
let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
|
||||
if let Some(response) = self.try_merge_responses(&responses) {
|
||||
Ok(response)
|
||||
|
||||
@@ -16,6 +16,7 @@ use rustc_type_ir::{
|
||||
};
|
||||
use tracing::{instrument, trace};
|
||||
|
||||
use super::has_only_region_constraints;
|
||||
use crate::coherence;
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::inspect::{self, ProofTreeBuilder};
|
||||
@@ -476,13 +477,8 @@ where
|
||||
Ok(response) => response,
|
||||
};
|
||||
|
||||
let has_changed = if !response.value.var_values.is_identity_modulo_regions()
|
||||
|| !response.value.external_constraints.opaque_types.is_empty()
|
||||
{
|
||||
HasChanged::Yes
|
||||
} else {
|
||||
HasChanged::No
|
||||
};
|
||||
let has_changed =
|
||||
if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No };
|
||||
|
||||
let (normalization_nested_goals, certainty) =
|
||||
self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);
|
||||
|
||||
@@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints<I: Interner>(
|
||||
&& normalization_nested_goals.is_empty()
|
||||
}
|
||||
|
||||
fn has_only_region_constraints<I: Interner>(response: ty::Canonical<I, Response<I>>) -> bool {
|
||||
let ExternalConstraintsData {
|
||||
region_constraints: _,
|
||||
ref opaque_types,
|
||||
ref normalization_nested_goals,
|
||||
} = *response.value.external_constraints;
|
||||
response.value.var_values.is_identity_modulo_regions()
|
||||
&& opaque_types.is_empty()
|
||||
&& normalization_nested_goals.is_empty()
|
||||
}
|
||||
|
||||
impl<'a, D, I> EvalCtxt<'a, D>
|
||||
where
|
||||
D: SolverDelegate<Interner = I>,
|
||||
|
||||
@@ -213,9 +213,6 @@ where
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
};
|
||||
|
||||
// In case the associated item is hidden due to specialization, we have to
|
||||
// return ambiguity this would otherwise be incomplete, resulting in
|
||||
// unsoundness during coherence (#105782).
|
||||
let target_item_def_id = match ecx.fetch_eligible_assoc_item(
|
||||
goal_trait_ref,
|
||||
goal.predicate.def_id(),
|
||||
@@ -223,8 +220,28 @@ where
|
||||
) {
|
||||
Ok(Some(target_item_def_id)) => target_item_def_id,
|
||||
Ok(None) => {
|
||||
return ecx
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
|
||||
match ecx.typing_mode() {
|
||||
// In case the associated item is hidden due to specialization, we have to
|
||||
// return ambiguity this would otherwise be incomplete, resulting in
|
||||
// unsoundness during coherence (#105782).
|
||||
ty::TypingMode::Coherence => {
|
||||
return ecx.evaluate_added_goals_and_make_canonical_response(
|
||||
Certainty::AMBIGUOUS,
|
||||
);
|
||||
}
|
||||
// Outside of coherence, we treat the associated item as rigid instead.
|
||||
ty::TypingMode::Analysis { .. }
|
||||
| ty::TypingMode::Borrowck { .. }
|
||||
| ty::TypingMode::PostBorrowckAnalysis { .. }
|
||||
| ty::TypingMode::PostAnalysis => {
|
||||
ecx.structurally_instantiate_normalizes_to_term(
|
||||
goal,
|
||||
goal.predicate.alias,
|
||||
);
|
||||
return ecx
|
||||
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(guar) => return error_response(ecx, guar),
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ use tracing::{instrument, trace};
|
||||
|
||||
use crate::delegate::SolverDelegate;
|
||||
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
|
||||
use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
|
||||
use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate};
|
||||
use crate::solve::inspect::ProbeKind;
|
||||
use crate::solve::{
|
||||
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
|
||||
@@ -1338,6 +1338,8 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
|
||||
|
||||
// If there are *only* global where bounds, then make sure to return that this
|
||||
// is still reported as being proven-via the param-env so that rigid projections
|
||||
// operate correctly. Otherwise, drop all global where-bounds before merging the
|
||||
|
||||
Reference in New Issue
Block a user