Actually use probes when needed and stop relying on existing outer probes

This commit is contained in:
Michael Goulet 2024-04-26 13:31:18 -04:00
parent 5776aec662
commit 7cf1c547c2
11 changed files with 349 additions and 334 deletions

View file

@ -48,21 +48,23 @@ pub(super) trait GoalKind<'tcx>:
/// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
fn probe_and_match_goal_against_assumption(
ecx: &mut EvalCtxt<'_, 'tcx>,
source: CandidateSource,
goal: Goal<'tcx, Self>,
assumption: ty::Clause<'tcx>,
then: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> QueryResult<'tcx>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// Consider a clause, which consists of a "assumption" and some "requirements",
/// to satisfy a goal. If the requirements hold, then attempt to satisfy our
/// goal by equating it with the assumption.
fn probe_and_consider_implied_clause(
ecx: &mut EvalCtxt<'_, 'tcx>,
source: CandidateSource,
goal: Goal<'tcx, Self>,
assumption: ty::Clause<'tcx>,
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
) -> Result<Candidate<'tcx>, NoSolution> {
Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
// FIXME(-Znext-solver=coinductive): check whether this should be
// `GoalSource::ImplWhereBound` for any caller.
ecx.add_goals(GoalSource::Misc, requirements);
@ -75,10 +77,11 @@ pub(super) trait GoalKind<'tcx>:
/// since they're not implied by the well-formedness of the object type.
fn probe_and_consider_object_bound_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
source: CandidateSource,
goal: Goal<'tcx, Self>,
assumption: ty::Clause<'tcx>,
) -> QueryResult<'tcx> {
Self::probe_and_match_goal_against_assumption(ecx, goal, assumption, |ecx| {
) -> Result<Candidate<'tcx>, NoSolution> {
Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
let tcx = ecx.tcx();
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
bug!("expected object type in `probe_and_consider_object_bound_candidate`");
@ -112,7 +115,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_error_guaranteed_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
guar: ErrorGuaranteed,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A type implements an `auto trait` if its components do as well.
///
@ -121,13 +124,13 @@ pub(super) trait GoalKind<'tcx>:
fn consider_auto_trait_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A trait alias holds if the RHS traits and `where` clauses hold.
fn consider_trait_alias_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A type is `Sized` if its tail component is `Sized`.
///
@ -136,7 +139,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_builtin_sized_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`.
///
@ -145,20 +148,20 @@ pub(super) trait GoalKind<'tcx>:
fn consider_builtin_copy_clone_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A type is `PointerLike` if we can compute its layout, and that layout
/// matches the layout of `usize`.
fn consider_builtin_pointer_like_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A type is a `FnPtr` if it is of `FnPtr` type.
fn consider_builtin_fn_ptr_trait_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
/// family of traits where `A` is given by the signature of the type.
@ -166,7 +169,7 @@ pub(super) trait GoalKind<'tcx>:
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
kind: ty::ClosureKind,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// An async closure is known to implement the `AsyncFn<A>` family of traits
/// where `A` is given by the signature of the type.
@ -174,7 +177,7 @@ pub(super) trait GoalKind<'tcx>:
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
kind: ty::ClosureKind,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which
/// is used internally to delay computation for async closures until after
@ -182,13 +185,13 @@ pub(super) trait GoalKind<'tcx>:
fn consider_builtin_async_fn_kind_helper_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// `Tuple` is implemented if the `Self` type is a tuple.
fn consider_builtin_tuple_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// `Pointee` is always implemented.
///
@ -198,7 +201,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_builtin_pointee_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A coroutine (that comes from an `async` desugaring) is known to implement
/// `Future<Output = O>`, where `O` is given by the coroutine's return type
@ -206,7 +209,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_builtin_future_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A coroutine (that comes from a `gen` desugaring) is known to implement
/// `Iterator<Item = O>`, where `O` is given by the generator's yield type
@ -214,19 +217,19 @@ pub(super) trait GoalKind<'tcx>:
fn consider_builtin_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A coroutine (that comes from a `gen` desugaring) is known to implement
/// `FusedIterator`
fn consider_builtin_fused_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
fn consider_builtin_async_iterator_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to
/// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield,
@ -234,27 +237,27 @@ pub(super) trait GoalKind<'tcx>:
fn consider_builtin_coroutine_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
fn consider_builtin_discriminant_kind_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
fn consider_builtin_async_destruct_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
fn consider_builtin_destruct_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
fn consider_builtin_transmute_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
) -> Result<Candidate<'tcx>, NoSolution>;
/// Consider (possibly several) candidates to upcast or unsize a type to another
/// type, excluding the coercion of a sized type into a `dyn Trait`.
@ -266,7 +269,7 @@ pub(super) trait GoalKind<'tcx>:
fn consider_structural_builtin_unsize_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
) -> Vec<Candidate<'tcx>>;
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
@ -282,7 +285,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if normalized_self_ty.is_ty_var() {
debug!("self type has been normalized to infer");
return self.forced_ambiguity(MaybeCause::Ambiguity);
return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect();
}
let goal =
@ -315,7 +318,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
candidates
}
fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec<Candidate<'tcx>> {
pub(super) fn forced_ambiguity(
&mut self,
cause: MaybeCause,
) -> Result<Candidate<'tcx>, NoSolution> {
// This may fail if `try_evaluate_added_goals` overflows because it
// fails to reach a fixpoint but ends up getting an error after
// running for some additional step.
@ -323,10 +329,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// cc trait-system-refactor-initiative#105
let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
let certainty = Certainty::Maybe(cause);
let result = self
.probe_trait_candidate(source)
.enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty));
if let Ok(cand) = result { vec![cand] } else { vec![] }
self.probe_trait_candidate(source)
.enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty))
}
#[instrument(level = "debug", skip_all)]
@ -533,20 +537,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
Err(NoSolution)
};
match result {
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}),
Err(NoSolution) => (),
}
candidates.extend(result);
// There may be multiple unsize candidates for a trait with several supertraits:
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
if lang_items.unsize_trait() == Some(trait_def_id) {
for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) {
candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
}
candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal));
}
}
@ -557,12 +553,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
candidates: &mut Vec<Candidate<'tcx>>,
) {
for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
match G::probe_and_consider_implied_clause(self, goal, assumption, []) {
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result })
}
Err(NoSolution) => (),
}
candidates.extend(G::probe_and_consider_implied_clause(
self,
CandidateSource::ParamEnv(i),
goal,
assumption,
[],
));
}
}
@ -648,12 +645,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
for assumption in
self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
{
match G::probe_and_consider_implied_clause(self, goal, assumption, []) {
Ok(result) => {
candidates.push(Candidate { source: CandidateSource::AliasBound, result });
}
Err(NoSolution) => {}
}
candidates.extend(G::probe_and_consider_implied_clause(
self,
CandidateSource::AliasBound,
goal,
assumption,
[],
));
}
if kind != ty::Projection {
@ -728,17 +726,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
}
ty::ExistentialPredicate::Projection(_)
| ty::ExistentialPredicate::AutoTrait(_) => {
match G::probe_and_consider_object_bound_candidate(
candidates.extend(G::probe_and_consider_object_bound_candidate(
self,
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
goal,
bound.with_self_ty(tcx, self_ty),
) {
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}),
Err(NoSolution) => (),
}
));
}
}
}
@ -749,19 +742,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
if let Some(principal) = bounds.principal() {
let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
self.walk_vtable(principal_trait_ref, |ecx, assumption, vtable_base, _| {
match G::probe_and_consider_object_bound_candidate(
candidates.extend(G::probe_and_consider_object_bound_candidate(
ecx,
CandidateSource::BuiltinImpl(BuiltinImplSource::Object { vtable_base }),
goal,
assumption.to_predicate(tcx),
) {
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Object {
vtable_base,
}),
result,
}),
Err(NoSolution) => (),
}
));
});
}
}
@ -779,25 +765,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
candidates: &mut Vec<Candidate<'tcx>>,
) {
let tcx = self.tcx();
let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
let trait_ref = goal.predicate.trait_ref(tcx);
let lazily_normalize_ty = |ty| ecx.structurally_normalize_ty(goal.param_env, ty);
match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty)? {
Ok(()) => Err(NoSolution),
Err(_) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
candidates.extend(self.probe_trait_candidate(CandidateSource::CoherenceUnknowable).enter(
|ecx| {
let trait_ref = goal.predicate.trait_ref(tcx);
let lazily_normalize_ty = |ty| ecx.structurally_normalize_ty(goal.param_env, ty);
match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty)? {
Ok(()) => Err(NoSolution),
Err(_) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
}
}
});
match result {
Ok(result) => candidates.push(Candidate {
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
result,
}),
Err(NoSolution) => {}
}
},
))
}
/// If there's a where-bound for the current goal, do not use any impl candidates
@ -842,6 +823,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
false
}
CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true,
CandidateSource::CoherenceUnknowable => bug!("uh oh"),
});
}
// If it is still ambiguous we instead just force the whole goal
@ -849,7 +831,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs
Certainty::Maybe(cause) => {
debug!(?cause, "force ambiguity");
*candidates = self.forced_ambiguity(cause);
*candidates = self.forced_ambiguity(cause).into_iter().collect();
}
}
}