2022-12-04 03:19:10 +00:00
|
|
|
use std::mem;
|
|
|
|
|
|
2023-01-24 23:57:26 +00:00
|
|
|
use rustc_infer::infer::InferCtxt;
|
2023-04-09 04:29:43 +00:00
|
|
|
use rustc_infer::traits::solve::MaybeCause;
|
2023-03-22 16:23:36 +00:00
|
|
|
use rustc_infer::traits::Obligation;
|
2023-01-24 23:57:26 +00:00
|
|
|
use rustc_infer::traits::{
|
|
|
|
|
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
|
|
|
|
|
PredicateObligation, SelectionError, TraitEngine,
|
2022-12-04 03:19:10 +00:00
|
|
|
};
|
2023-01-24 23:57:26 +00:00
|
|
|
use rustc_middle::ty;
|
|
|
|
|
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
|
|
|
|
|
2023-06-09 00:19:54 +01:00
|
|
|
use super::eval_ctxt::GenerateProofTree;
|
2023-11-21 20:37:36 +01:00
|
|
|
use super::{Certainty, Goal, 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> {
|
|
|
|
|
obligations: Vec<PredicateObligation<'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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
);
|
2023-06-29 10:02:26 +02:00
|
|
|
FulfillmentCtxt { obligations: Vec::new(), 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>,
|
|
|
|
|
result: &Result<(bool, Certainty, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), NoSolution>,
|
|
|
|
|
) {
|
|
|
|
|
if let Some(inspector) = infcx.obligation_inspector.get() {
|
|
|
|
|
let result = match result {
|
|
|
|
|
Ok((_, c, _)) => Ok(*c),
|
|
|
|
|
Err(NoSolution) => Err(NoSolution),
|
|
|
|
|
};
|
|
|
|
|
(inspector)(infcx, &obligation, result);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
2023-07-03 09:24:02 +02:00
|
|
|
#[instrument(level = "debug", 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());
|
2022-12-04 03:19:10 +00:00
|
|
|
self.obligations.push(obligation);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-09 04:29:43 +00:00
|
|
|
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
|
2023-01-11 03:54:46 +00:00
|
|
|
self.obligations
|
|
|
|
|
.drain(..)
|
2023-04-09 04:29:43 +00:00
|
|
|
.map(|obligation| {
|
2023-06-09 00:19:54 +01:00
|
|
|
let code = infcx.probe(|_| {
|
|
|
|
|
match infcx
|
2023-07-10 15:17:01 +02:00
|
|
|
.evaluate_root_goal(obligation.clone().into(), GenerateProofTree::IfEnabled)
|
2023-06-09 00:19:54 +01:00
|
|
|
.0
|
|
|
|
|
{
|
2023-04-09 04:29:43 +00:00
|
|
|
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
|
2024-01-12 16:16:17 +00:00
|
|
|
FulfillmentErrorCode::Ambiguity { overflow: false }
|
2023-04-09 04:29:43 +00:00
|
|
|
}
|
|
|
|
|
Ok((_, Certainty::Maybe(MaybeCause::Overflow), _)) => {
|
2024-01-12 16:16:17 +00:00
|
|
|
FulfillmentErrorCode::Ambiguity { overflow: true }
|
2023-04-09 04:29:43 +00:00
|
|
|
}
|
|
|
|
|
Ok((_, Certainty::Yes, _)) => {
|
|
|
|
|
bug!("did not expect successful goal when collecting ambiguity errors")
|
|
|
|
|
}
|
|
|
|
|
Err(_) => {
|
|
|
|
|
bug!("did not expect selection error when collecting ambiguity errors")
|
|
|
|
|
}
|
2023-06-09 00:19:54 +01:00
|
|
|
}
|
|
|
|
|
});
|
2023-04-09 04:29:43 +00:00
|
|
|
|
|
|
|
|
FulfillmentError {
|
|
|
|
|
obligation: obligation.clone(),
|
|
|
|
|
code,
|
|
|
|
|
root_obligation: obligation,
|
|
|
|
|
}
|
2023-01-11 03:54:46 +00:00
|
|
|
})
|
|
|
|
|
.collect()
|
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) {
|
2023-01-10 20:24:10 +00:00
|
|
|
unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let mut has_changed = false;
|
2023-01-11 03:54:46 +00:00
|
|
|
for obligation in mem::take(&mut self.obligations) {
|
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);
|
|
|
|
|
let (changed, certainty, nested_goals) = match result {
|
|
|
|
|
Ok(result) => result,
|
|
|
|
|
Err(NoSolution) => {
|
|
|
|
|
errors.push(FulfillmentError {
|
|
|
|
|
obligation: obligation.clone(),
|
|
|
|
|
code: match goal.predicate.kind().skip_binder() {
|
|
|
|
|
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
|
|
|
|
|
FulfillmentErrorCode::ProjectionError(
|
|
|
|
|
// FIXME: This could be a `Sorts` if the term is a type
|
|
|
|
|
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ty::PredicateKind::NormalizesTo(..) => {
|
|
|
|
|
FulfillmentErrorCode::ProjectionError(
|
|
|
|
|
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ty::PredicateKind::AliasRelate(_, _, _) => {
|
|
|
|
|
FulfillmentErrorCode::ProjectionError(
|
|
|
|
|
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ty::PredicateKind::Subtype(pred) => {
|
|
|
|
|
let (a, b) = infcx.instantiate_binder_with_placeholders(
|
|
|
|
|
goal.predicate.kind().rebind((pred.a, pred.b)),
|
|
|
|
|
);
|
|
|
|
|
let expected_found = ExpectedFound::new(true, a, b);
|
|
|
|
|
FulfillmentErrorCode::SubtypeError(
|
|
|
|
|
expected_found,
|
|
|
|
|
TypeError::Sorts(expected_found),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ty::PredicateKind::Coerce(pred) => {
|
|
|
|
|
let (a, b) = infcx.instantiate_binder_with_placeholders(
|
|
|
|
|
goal.predicate.kind().rebind((pred.a, pred.b)),
|
|
|
|
|
);
|
|
|
|
|
let expected_found = ExpectedFound::new(false, a, b);
|
|
|
|
|
FulfillmentErrorCode::SubtypeError(
|
|
|
|
|
expected_found,
|
|
|
|
|
TypeError::Sorts(expected_found),
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ty::PredicateKind::Clause(_)
|
|
|
|
|
| ty::PredicateKind::ObjectSafe(_)
|
|
|
|
|
| ty::PredicateKind::Ambiguous => {
|
|
|
|
|
FulfillmentErrorCode::SelectionError(
|
|
|
|
|
SelectionError::Unimplemented,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
ty::PredicateKind::ConstEquate(..) => {
|
|
|
|
|
bug!("unexpected goal: {goal:?}")
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
root_obligation: obligation,
|
|
|
|
|
});
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-03-22 16:23:36 +00:00
|
|
|
// Push any nested goals that we get from unifying our canonical response
|
|
|
|
|
// with our obligation onto the fulfillment context.
|
|
|
|
|
self.obligations.extend(nested_goals.into_iter().map(|goal| {
|
|
|
|
|
Obligation::new(
|
|
|
|
|
infcx.tcx,
|
|
|
|
|
obligation.cause.clone(),
|
|
|
|
|
goal.param_env,
|
|
|
|
|
goal.predicate,
|
|
|
|
|
)
|
|
|
|
|
}));
|
2022-12-04 03:19:10 +00:00
|
|
|
has_changed |= changed;
|
|
|
|
|
match certainty {
|
|
|
|
|
Certainty::Yes => {}
|
2023-01-11 03:54:46 +00:00
|
|
|
Certainty::Maybe(_) => self.obligations.push(obligation),
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !has_changed {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errors
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
|
|
|
|
|
self.obligations.clone()
|
|
|
|
|
}
|
2022-10-01 15:57:22 +02:00
|
|
|
|
|
|
|
|
fn drain_unstalled_obligations(
|
|
|
|
|
&mut self,
|
|
|
|
|
_: &InferCtxt<'tcx>,
|
|
|
|
|
) -> Vec<PredicateObligation<'tcx>> {
|
2023-01-28 22:46:46 +00:00
|
|
|
std::mem::take(&mut self.obligations)
|
2022-10-01 15:57:22 +02:00
|
|
|
}
|
2022-12-04 03:19:10 +00:00
|
|
|
}
|