2022-12-04 03:19:10 +00:00
|
|
|
use std::mem;
|
2024-05-01 16:03:26 -04:00
|
|
|
use std::ops::ControlFlow;
|
2022-12-04 03:19:10 +00:00
|
|
|
|
2023-01-24 23:57:26 +00:00
|
|
|
use rustc_infer::infer::InferCtxt;
|
2024-05-01 16:03:26 -04:00
|
|
|
use rustc_infer::traits::query::NoSolution;
|
|
|
|
|
use rustc_infer::traits::solve::inspect::ProbeKind;
|
|
|
|
|
use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
|
2023-01-24 23:57:26 +00:00
|
|
|
use rustc_infer::traits::{
|
2024-05-01 16:03:26 -04:00
|
|
|
self, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation,
|
2024-05-09 20:05:59 -04:00
|
|
|
ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, TraitEngine,
|
2022-12-04 03:19:10 +00:00
|
|
|
};
|
2024-05-09 08:06:59 +10:00
|
|
|
use rustc_middle::bug;
|
2023-01-24 23:57:26 +00:00
|
|
|
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
2024-05-02 13:51:13 -04:00
|
|
|
use rustc_middle::ty::{self, TyCtxt};
|
2024-05-04 12:37:14 -04:00
|
|
|
use rustc_span::symbol::sym;
|
2023-01-24 23:57:26 +00:00
|
|
|
|
2023-06-09 00:19:54 +01:00
|
|
|
use super::eval_ctxt::GenerateProofTree;
|
2024-05-05 16:01:48 -04:00
|
|
|
use super::inspect::{InspectCandidate, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor};
|
2024-02-26 10:17:43 +01:00
|
|
|
use super::{Certainty, InferCtxtEvalExt};
|
2022-12-04 03:19:10 +00:00
|
|
|
|
|
|
|
|
/// A trait engine using the new trait solver.
|
|
|
|
|
///
|
|
|
|
|
/// This is mostly identical to how `evaluate_all` works inside of the
|
|
|
|
|
/// solver, except that the requirements are slightly different.
|
|
|
|
|
///
|
|
|
|
|
/// Unlike `evaluate_all` it is possible to add new obligations later on
|
|
|
|
|
/// and we also have to track diagnostics information by using `Obligation`
|
|
|
|
|
/// instead of `Goal`.
|
|
|
|
|
///
|
|
|
|
|
/// It is also likely that we want to use slightly different datastructures
|
|
|
|
|
/// here as this will have to deal with far more root goals than `evaluate_all`.
|
|
|
|
|
pub struct FulfillmentCtxt<'tcx> {
|
2024-02-23 11:25:57 +01:00
|
|
|
obligations: ObligationStorage<'tcx>,
|
2023-06-29 10:02:26 +02:00
|
|
|
|
|
|
|
|
/// The snapshot in which this context was created. Using the context
|
|
|
|
|
/// outside of this snapshot leads to subtle bugs if the snapshot
|
|
|
|
|
/// gets rolled back. Because of this we explicitly check that we only
|
|
|
|
|
/// use the context in exactly this snapshot.
|
|
|
|
|
usable_in_snapshot: usize,
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-23 11:25:57 +01:00
|
|
|
#[derive(Default)]
|
|
|
|
|
struct ObligationStorage<'tcx> {
|
|
|
|
|
/// Obligations which resulted in an overflow in fulfillment itself.
|
|
|
|
|
///
|
|
|
|
|
/// We cannot eagerly return these as error so we instead store them here
|
|
|
|
|
/// to avoid recomputing them each time `select_where_possible` is called.
|
|
|
|
|
/// This also allows us to return the correct `FulfillmentError` for them.
|
|
|
|
|
overflowed: Vec<PredicateObligation<'tcx>>,
|
|
|
|
|
pending: Vec<PredicateObligation<'tcx>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'tcx> ObligationStorage<'tcx> {
|
|
|
|
|
fn register(&mut self, obligation: PredicateObligation<'tcx>) {
|
|
|
|
|
self.pending.push(obligation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn clone_pending(&self) -> Vec<PredicateObligation<'tcx>> {
|
|
|
|
|
let mut obligations = self.pending.clone();
|
|
|
|
|
obligations.extend(self.overflowed.iter().cloned());
|
|
|
|
|
obligations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn take_pending(&mut self) -> Vec<PredicateObligation<'tcx>> {
|
|
|
|
|
let mut obligations = mem::take(&mut self.pending);
|
2024-03-02 14:18:47 +01:00
|
|
|
obligations.append(&mut self.overflowed);
|
2024-02-23 11:25:57 +01:00
|
|
|
obligations
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn unstalled_for_select(&mut self) -> impl Iterator<Item = PredicateObligation<'tcx>> {
|
|
|
|
|
mem::take(&mut self.pending).into_iter()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
|
|
|
|
|
infcx.probe(|_| {
|
|
|
|
|
// IMPORTANT: we must not use solve any inference variables in the obligations
|
|
|
|
|
// as this is all happening inside of a probe. We use a probe to make sure
|
|
|
|
|
// we get all obligations involved in the overflow. We pretty much check: if
|
|
|
|
|
// we were to do another step of `select_where_possible`, which goals would
|
|
|
|
|
// change.
|
|
|
|
|
self.overflowed.extend(self.pending.extract_if(|o| {
|
|
|
|
|
let goal = o.clone().into();
|
|
|
|
|
let result = infcx.evaluate_root_goal(goal, GenerateProofTree::Never).0;
|
|
|
|
|
match result {
|
|
|
|
|
Ok((has_changed, _)) => has_changed,
|
|
|
|
|
_ => false,
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-04 03:19:10 +00:00
|
|
|
impl<'tcx> FulfillmentCtxt<'tcx> {
|
2023-06-29 10:02:26 +02:00
|
|
|
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
|
2024-01-13 21:53:42 +00:00
|
|
|
assert!(
|
|
|
|
|
infcx.next_trait_solver(),
|
|
|
|
|
"new trait solver fulfillment context created when \
|
|
|
|
|
infcx is set up for old trait solver"
|
|
|
|
|
);
|
2024-02-23 11:25:57 +01:00
|
|
|
FulfillmentCtxt {
|
|
|
|
|
obligations: Default::default(),
|
|
|
|
|
usable_in_snapshot: infcx.num_open_snapshots(),
|
|
|
|
|
}
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
2023-11-21 20:37:36 +01:00
|
|
|
|
|
|
|
|
fn inspect_evaluated_obligation(
|
|
|
|
|
&self,
|
|
|
|
|
infcx: &InferCtxt<'tcx>,
|
|
|
|
|
obligation: &PredicateObligation<'tcx>,
|
2024-02-26 10:17:43 +01:00
|
|
|
result: &Result<(bool, Certainty), NoSolution>,
|
2023-11-21 20:37:36 +01:00
|
|
|
) {
|
|
|
|
|
if let Some(inspector) = infcx.obligation_inspector.get() {
|
|
|
|
|
let result = match result {
|
2024-02-26 10:17:43 +01:00
|
|
|
Ok((_, c)) => Ok(*c),
|
2023-11-21 20:37:36 +01:00
|
|
|
Err(NoSolution) => Err(NoSolution),
|
|
|
|
|
};
|
|
|
|
|
(inspector)(infcx, &obligation, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
2024-05-12 03:29:50 +00:00
|
|
|
#[instrument(level = "trace", skip(self, infcx))]
|
2022-12-04 03:19:10 +00:00
|
|
|
fn register_predicate_obligation(
|
|
|
|
|
&mut self,
|
2023-06-29 10:02:26 +02:00
|
|
|
infcx: &InferCtxt<'tcx>,
|
2022-12-04 03:19:10 +00:00
|
|
|
obligation: PredicateObligation<'tcx>,
|
|
|
|
|
) {
|
2023-06-29 10:02:26 +02:00
|
|
|
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
|
2024-02-23 11:25:57 +01:00
|
|
|
self.obligations.register(obligation);
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
2023-04-09 04:29:43 +00:00
|
|
|
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
|
2024-02-23 11:25:57 +01:00
|
|
|
let mut errors: Vec<_> = self
|
|
|
|
|
.obligations
|
|
|
|
|
.pending
|
2023-01-11 03:54:46 +00:00
|
|
|
.drain(..)
|
2024-02-23 10:35:43 +01:00
|
|
|
.map(|obligation| fulfillment_error_for_stalled(infcx, obligation))
|
2024-02-23 11:25:57 +01:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
errors.extend(self.obligations.overflowed.drain(..).map(|obligation| FulfillmentError {
|
2024-05-03 20:08:35 -04:00
|
|
|
obligation: find_best_leaf_obligation(infcx, &obligation, true),
|
2024-02-23 11:25:57 +01:00
|
|
|
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
|
2024-05-01 16:03:26 -04:00
|
|
|
root_obligation: obligation,
|
2024-02-23 11:25:57 +01:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
errors
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
|
2023-06-29 10:02:26 +02:00
|
|
|
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
|
2023-01-11 03:54:46 +00:00
|
|
|
let mut errors = Vec::new();
|
2022-12-04 03:19:10 +00:00
|
|
|
for i in 0.. {
|
|
|
|
|
if !infcx.tcx.recursion_limit().value_within_limit(i) {
|
2024-02-23 11:25:57 +01:00
|
|
|
self.obligations.on_fulfillment_overflow(infcx);
|
|
|
|
|
// Only return true errors that we have accumulated while processing.
|
2024-02-20 02:13:28 +00:00
|
|
|
return errors;
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut has_changed = false;
|
2024-02-23 11:25:57 +01:00
|
|
|
for obligation in self.obligations.unstalled_for_select() {
|
2023-01-17 10:21:30 +01:00
|
|
|
let goal = obligation.clone().into();
|
2023-11-21 20:37:36 +01:00
|
|
|
let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0;
|
|
|
|
|
self.inspect_evaluated_obligation(infcx, &obligation, &result);
|
2024-02-26 10:17:43 +01:00
|
|
|
let (changed, certainty) = match result {
|
2023-11-21 20:37:36 +01:00
|
|
|
Ok(result) => result,
|
|
|
|
|
Err(NoSolution) => {
|
2024-02-23 10:35:43 +01:00
|
|
|
errors.push(fulfillment_error_for_no_solution(infcx, obligation));
|
2023-11-21 20:37:36 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-12-04 03:19:10 +00:00
|
|
|
has_changed |= changed;
|
|
|
|
|
match certainty {
|
|
|
|
|
Certainty::Yes => {}
|
2024-02-23 11:25:57 +01:00
|
|
|
Certainty::Maybe(_) => self.obligations.register(obligation),
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !has_changed {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errors
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
|
2024-02-23 11:25:57 +01:00
|
|
|
self.obligations.clone_pending()
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
2022-10-01 15:57:22 +02:00
|
|
|
|
|
|
|
|
fn drain_unstalled_obligations(
|
|
|
|
|
&mut self,
|
|
|
|
|
_: &InferCtxt<'tcx>,
|
|
|
|
|
) -> Vec<PredicateObligation<'tcx>> {
|
2024-02-23 11:25:57 +01:00
|
|
|
self.obligations.take_pending()
|
2022-10-01 15:57:22 +02:00
|
|
|
}
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
2024-02-23 10:35:43 +01:00
|
|
|
|
|
|
|
|
fn fulfillment_error_for_no_solution<'tcx>(
|
|
|
|
|
infcx: &InferCtxt<'tcx>,
|
2024-05-01 16:03:26 -04:00
|
|
|
root_obligation: PredicateObligation<'tcx>,
|
2024-02-23 10:35:43 +01:00
|
|
|
) -> FulfillmentError<'tcx> {
|
2024-05-03 20:08:35 -04:00
|
|
|
let obligation = find_best_leaf_obligation(infcx, &root_obligation, false);
|
2024-05-01 16:03:26 -04:00
|
|
|
|
2024-02-23 10:35:43 +01:00
|
|
|
let code = match obligation.predicate.kind().skip_binder() {
|
|
|
|
|
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
|
2024-05-09 14:09:55 -04:00
|
|
|
FulfillmentErrorCode::Project(
|
2024-02-23 10:35:43 +01:00
|
|
|
// FIXME: This could be a `Sorts` if the term is a type
|
|
|
|
|
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ty::PredicateKind::NormalizesTo(..) => {
|
2024-05-09 14:09:55 -04:00
|
|
|
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
|
2024-02-23 10:35:43 +01:00
|
|
|
}
|
|
|
|
|
ty::PredicateKind::AliasRelate(_, _, _) => {
|
2024-05-09 14:09:55 -04:00
|
|
|
FulfillmentErrorCode::Project(MismatchedProjectionTypes { err: TypeError::Mismatch })
|
2024-02-23 10:35:43 +01:00
|
|
|
}
|
|
|
|
|
ty::PredicateKind::Subtype(pred) => {
|
|
|
|
|
let (a, b) = infcx.enter_forall_and_leak_universe(
|
2024-05-02 13:51:13 -04:00
|
|
|
obligation.predicate.kind().rebind((pred.a, pred.b)),
|
2024-02-23 10:35:43 +01:00
|
|
|
);
|
|
|
|
|
let expected_found = ExpectedFound::new(true, a, b);
|
2024-05-09 14:09:55 -04:00
|
|
|
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
|
2024-02-23 10:35:43 +01:00
|
|
|
}
|
|
|
|
|
ty::PredicateKind::Coerce(pred) => {
|
|
|
|
|
let (a, b) = infcx.enter_forall_and_leak_universe(
|
2024-05-02 13:51:13 -04:00
|
|
|
obligation.predicate.kind().rebind((pred.a, pred.b)),
|
2024-02-23 10:35:43 +01:00
|
|
|
);
|
|
|
|
|
let expected_found = ExpectedFound::new(false, a, b);
|
2024-05-09 14:09:55 -04:00
|
|
|
FulfillmentErrorCode::Subtype(expected_found, TypeError::Sorts(expected_found))
|
2024-02-23 10:35:43 +01:00
|
|
|
}
|
|
|
|
|
ty::PredicateKind::Clause(_)
|
|
|
|
|
| ty::PredicateKind::ObjectSafe(_)
|
|
|
|
|
| ty::PredicateKind::Ambiguous => {
|
2024-05-09 14:09:55 -04:00
|
|
|
FulfillmentErrorCode::Select(SelectionError::Unimplemented)
|
2024-02-23 10:35:43 +01:00
|
|
|
}
|
|
|
|
|
ty::PredicateKind::ConstEquate(..) => {
|
|
|
|
|
bug!("unexpected goal: {obligation:?}")
|
|
|
|
|
}
|
|
|
|
|
};
|
2024-05-01 16:03:26 -04:00
|
|
|
|
|
|
|
|
FulfillmentError { obligation, code, root_obligation }
|
2024-02-23 10:35:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fulfillment_error_for_stalled<'tcx>(
|
|
|
|
|
infcx: &InferCtxt<'tcx>,
|
|
|
|
|
obligation: PredicateObligation<'tcx>,
|
|
|
|
|
) -> FulfillmentError<'tcx> {
|
|
|
|
|
let code = infcx.probe(|_| {
|
|
|
|
|
match infcx.evaluate_root_goal(obligation.clone().into(), GenerateProofTree::Never).0 {
|
|
|
|
|
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
|
|
|
|
|
FulfillmentErrorCode::Ambiguity { overflow: None }
|
|
|
|
|
}
|
|
|
|
|
Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => {
|
|
|
|
|
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }
|
|
|
|
|
}
|
|
|
|
|
Ok((_, Certainty::Yes)) => {
|
|
|
|
|
bug!("did not expect successful goal when collecting ambiguity errors")
|
|
|
|
|
}
|
|
|
|
|
Err(_) => {
|
|
|
|
|
bug!("did not expect selection error when collecting ambiguity errors")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2024-05-01 16:03:26 -04:00
|
|
|
FulfillmentError {
|
2024-05-03 20:08:35 -04:00
|
|
|
obligation: find_best_leaf_obligation(infcx, &obligation, true),
|
2024-05-01 16:03:26 -04:00
|
|
|
code,
|
|
|
|
|
root_obligation: obligation,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-02 13:51:13 -04:00
|
|
|
fn find_best_leaf_obligation<'tcx>(
|
|
|
|
|
infcx: &InferCtxt<'tcx>,
|
|
|
|
|
obligation: &PredicateObligation<'tcx>,
|
2024-05-03 20:08:35 -04:00
|
|
|
consider_ambiguities: bool,
|
2024-05-02 13:51:13 -04:00
|
|
|
) -> PredicateObligation<'tcx> {
|
|
|
|
|
let obligation = infcx.resolve_vars_if_possible(obligation.clone());
|
|
|
|
|
infcx
|
|
|
|
|
.visit_proof_tree(
|
|
|
|
|
obligation.clone().into(),
|
2024-05-03 20:08:35 -04:00
|
|
|
&mut BestObligation { obligation: obligation.clone(), consider_ambiguities },
|
2024-05-02 13:51:13 -04:00
|
|
|
)
|
|
|
|
|
.break_value()
|
|
|
|
|
.unwrap_or(obligation)
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 16:03:26 -04:00
|
|
|
struct BestObligation<'tcx> {
|
|
|
|
|
obligation: PredicateObligation<'tcx>,
|
2024-05-03 20:08:35 -04:00
|
|
|
consider_ambiguities: bool,
|
2024-05-01 16:03:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'tcx> BestObligation<'tcx> {
|
|
|
|
|
fn with_derived_obligation(
|
|
|
|
|
&mut self,
|
2024-05-02 13:51:13 -04:00
|
|
|
derived_obligation: PredicateObligation<'tcx>,
|
2024-05-01 16:03:26 -04:00
|
|
|
and_then: impl FnOnce(&mut Self) -> <Self as ProofTreeVisitor<'tcx>>::Result,
|
|
|
|
|
) -> <Self as ProofTreeVisitor<'tcx>>::Result {
|
|
|
|
|
let old_obligation = std::mem::replace(&mut self.obligation, derived_obligation);
|
|
|
|
|
let res = and_then(self);
|
|
|
|
|
self.obligation = old_obligation;
|
|
|
|
|
res
|
|
|
|
|
}
|
2024-05-05 16:01:48 -04:00
|
|
|
|
|
|
|
|
/// Filter out the candidates that aren't either error or ambiguous (depending
|
|
|
|
|
/// on what we are looking for), and also throw out candidates that have no
|
|
|
|
|
/// failing WC (or higher-ranked obligations, for which there should only be
|
|
|
|
|
/// one candidate anyways -- but I digress). This most likely means that the
|
|
|
|
|
/// goal just didn't unify at all, e.g. a param candidate with an alias in it.
|
|
|
|
|
fn non_trivial_candidates<'a>(
|
|
|
|
|
&self,
|
|
|
|
|
goal: &'a InspectGoal<'a, 'tcx>,
|
|
|
|
|
) -> Vec<InspectCandidate<'a, 'tcx>> {
|
|
|
|
|
let mut candidates = goal
|
|
|
|
|
.candidates()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.filter(|candidate| match self.consider_ambiguities {
|
|
|
|
|
true => matches!(candidate.result(), Ok(Certainty::Maybe(_))),
|
|
|
|
|
false => matches!(candidate.result(), Err(_)),
|
|
|
|
|
})
|
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
// If we have >1 candidate, one may still be due to "boring" reasons, like
|
|
|
|
|
// an alias-relate that failed to hold when deeply evaluated. We really
|
|
|
|
|
// don't care about reasons like this.
|
|
|
|
|
if candidates.len() > 1 {
|
|
|
|
|
candidates.retain(|candidate| {
|
|
|
|
|
goal.infcx().probe(|_| {
|
|
|
|
|
candidate.instantiate_nested_goals(self.span()).iter().any(|nested_goal| {
|
|
|
|
|
matches!(
|
|
|
|
|
nested_goal.source(),
|
|
|
|
|
GoalSource::ImplWhereBound | GoalSource::InstantiateHigherRanked
|
|
|
|
|
) && match self.consider_ambiguities {
|
|
|
|
|
true => matches!(nested_goal.result(), Ok(Certainty::Maybe(_))),
|
|
|
|
|
false => matches!(nested_goal.result(), Err(_)),
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
candidates
|
|
|
|
|
}
|
2024-05-01 16:03:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
|
|
|
|
|
type Result = ControlFlow<PredicateObligation<'tcx>>;
|
|
|
|
|
|
|
|
|
|
fn span(&self) -> rustc_span::Span {
|
|
|
|
|
self.obligation.cause.span
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn visit_goal(&mut self, goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
|
2024-05-05 16:01:48 -04:00
|
|
|
let candidates = self.non_trivial_candidates(goal);
|
2024-05-01 16:03:26 -04:00
|
|
|
let [candidate] = candidates.as_slice() else {
|
|
|
|
|
return ControlFlow::Break(self.obligation.clone());
|
|
|
|
|
};
|
|
|
|
|
|
2024-05-04 12:37:14 -04:00
|
|
|
// Don't walk into impls that have `do_not_recommend`.
|
|
|
|
|
if let ProbeKind::TraitCandidate { source: CandidateSource::Impl(impl_def_id), result: _ } =
|
|
|
|
|
candidate.kind()
|
|
|
|
|
&& goal.infcx().tcx.has_attr(impl_def_id, sym::do_not_recommend)
|
|
|
|
|
{
|
|
|
|
|
return ControlFlow::Break(self.obligation.clone());
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-01 16:03:26 -04:00
|
|
|
// FIXME: Could we extract a trait ref from a projection here too?
|
|
|
|
|
// FIXME: Also, what about considering >1 layer up the stack? May be necessary
|
|
|
|
|
// for normalizes-to.
|
|
|
|
|
let Some(parent_trait_pred) = goal.goal().predicate.to_opt_poly_trait_pred() else {
|
|
|
|
|
return ControlFlow::Break(self.obligation.clone());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let tcx = goal.infcx().tcx;
|
|
|
|
|
let mut impl_where_bound_count = 0;
|
|
|
|
|
for nested_goal in candidate.instantiate_nested_goals(self.span()) {
|
2024-05-02 13:51:13 -04:00
|
|
|
let obligation;
|
|
|
|
|
match nested_goal.source() {
|
|
|
|
|
GoalSource::Misc => {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
GoalSource::ImplWhereBound => {
|
|
|
|
|
obligation = Obligation {
|
|
|
|
|
cause: derive_cause(
|
|
|
|
|
tcx,
|
|
|
|
|
candidate.kind(),
|
|
|
|
|
self.obligation.cause.clone(),
|
|
|
|
|
impl_where_bound_count,
|
|
|
|
|
parent_trait_pred,
|
|
|
|
|
),
|
|
|
|
|
param_env: nested_goal.goal().param_env,
|
|
|
|
|
predicate: nested_goal.goal().predicate,
|
|
|
|
|
recursion_depth: self.obligation.recursion_depth + 1,
|
|
|
|
|
};
|
|
|
|
|
impl_where_bound_count += 1;
|
|
|
|
|
}
|
|
|
|
|
GoalSource::InstantiateHigherRanked => {
|
|
|
|
|
obligation = self.obligation.clone();
|
|
|
|
|
}
|
2024-05-01 16:03:26 -04:00
|
|
|
}
|
|
|
|
|
|
2024-05-03 20:08:35 -04:00
|
|
|
// Skip nested goals that aren't the *reason* for our goal's failure.
|
|
|
|
|
match self.consider_ambiguities {
|
|
|
|
|
true if matches!(nested_goal.result(), Ok(Certainty::Maybe(_))) => {}
|
|
|
|
|
false if matches!(nested_goal.result(), Err(_)) => {}
|
|
|
|
|
_ => continue,
|
2024-05-01 16:03:26 -04:00
|
|
|
}
|
|
|
|
|
|
2024-05-02 13:51:13 -04:00
|
|
|
self.with_derived_obligation(obligation, |this| nested_goal.visit_with(this))?;
|
2024-05-01 16:03:26 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ControlFlow::Break(self.obligation.clone())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-02 13:51:13 -04:00
|
|
|
fn derive_cause<'tcx>(
|
|
|
|
|
tcx: TyCtxt<'tcx>,
|
|
|
|
|
candidate_kind: ProbeKind<'tcx>,
|
|
|
|
|
mut cause: ObligationCause<'tcx>,
|
|
|
|
|
idx: usize,
|
|
|
|
|
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
|
|
|
|
|
) -> ObligationCause<'tcx> {
|
|
|
|
|
match candidate_kind {
|
|
|
|
|
ProbeKind::TraitCandidate { source: CandidateSource::Impl(impl_def_id), result: _ } => {
|
|
|
|
|
if let Some((_, span)) =
|
|
|
|
|
tcx.predicates_of(impl_def_id).instantiate_identity(tcx).iter().nth(idx)
|
|
|
|
|
{
|
|
|
|
|
cause = cause.derived_cause(parent_trait_pred, |derived| {
|
2024-05-09 20:25:11 -04:00
|
|
|
ObligationCauseCode::ImplDerived(Box::new(traits::ImplDerivedCause {
|
2024-05-09 20:12:47 -04:00
|
|
|
derived,
|
|
|
|
|
impl_or_alias_def_id: impl_def_id,
|
|
|
|
|
impl_def_predicate_index: Some(idx),
|
|
|
|
|
span,
|
|
|
|
|
}))
|
2024-05-02 13:51:13 -04:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ProbeKind::TraitCandidate { source: CandidateSource::BuiltinImpl(..), result: _ } => {
|
2024-05-09 20:25:11 -04:00
|
|
|
cause = cause.derived_cause(parent_trait_pred, ObligationCauseCode::BuiltinDerived);
|
2024-05-02 13:51:13 -04:00
|
|
|
}
|
|
|
|
|
_ => {}
|
|
|
|
|
};
|
|
|
|
|
cause
|
2024-02-23 10:35:43 +01:00
|
|
|
}
|