blob: 4a403196c7e05d89abf8a49c67cf9900eb095484 [file] [log] [blame]
use std::mem;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::solve::MaybeCause;
use rustc_infer::traits::Obligation;
use rustc_infer::traits::{
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
PredicateObligation, SelectionError, TraitEngine,
};
use rustc_middle::ty;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use super::{Certainty, InferCtxtEvalExt};
/// 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>>,
}
impl<'tcx> FulfillmentCtxt<'tcx> {
pub fn new() -> FulfillmentCtxt<'tcx> {
FulfillmentCtxt { obligations: Vec::new() }
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
fn register_predicate_obligation(
&mut self,
_infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>,
) {
self.obligations.push(obligation);
}
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
self.obligations
.drain(..)
.map(|obligation| {
let code =
infcx.probe(|_| match infcx.evaluate_root_goal(obligation.clone().into()) {
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
FulfillmentErrorCode::CodeAmbiguity { overflow: false }
}
Ok((_, Certainty::Maybe(MaybeCause::Overflow), _)) => {
FulfillmentErrorCode::CodeAmbiguity { overflow: true }
}
Ok((_, Certainty::Yes, _)) => {
bug!("did not expect successful goal when collecting ambiguity errors")
}
Err(_) => {
bug!("did not expect selection error when collecting ambiguity errors")
}
});
FulfillmentError {
obligation: obligation.clone(),
code,
root_obligation: obligation,
}
})
.collect()
}
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
let mut errors = Vec::new();
for i in 0.. {
if !infcx.tcx.recursion_limit().value_within_limit(i) {
unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
}
let mut has_changed = false;
for obligation in mem::take(&mut self.obligations) {
let goal = obligation.clone().into();
let (changed, certainty, nested_goals) = match infcx.evaluate_root_goal(goal) {
Ok(result) => result,
Err(NoSolution) => {
errors.push(FulfillmentError {
obligation: obligation.clone(),
code: match goal.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
FulfillmentErrorCode::CodeProjectionError(
// FIXME: This could be a `Sorts` if the term is a type
MismatchedProjectionTypes { err: TypeError::Mismatch },
)
}
ty::PredicateKind::AliasRelate(_, _, _) => {
FulfillmentErrorCode::CodeProjectionError(
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::CodeSubtypeError(
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::CodeSubtypeError(
expected_found,
TypeError::Sorts(expected_found),
)
}
ty::PredicateKind::ConstEquate(a, b) => {
let (a, b) = infcx.instantiate_binder_with_placeholders(
goal.predicate.kind().rebind((a, b)),
);
let expected_found = ExpectedFound::new(true, a, b);
FulfillmentErrorCode::CodeConstEquateError(
expected_found,
TypeError::ConstMismatch(expected_found),
)
}
ty::PredicateKind::Clause(_)
| ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
| ty::PredicateKind::ClosureKind(_, _, _)
| ty::PredicateKind::ConstEvaluatable(_)
| ty::PredicateKind::Ambiguous => {
FulfillmentErrorCode::CodeSelectionError(
SelectionError::Unimplemented,
)
}
ty::PredicateKind::TypeWellFormedFromEnv(_) => {
bug!("unexpected goal: {goal:?}")
}
},
root_obligation: obligation,
});
continue;
}
};
// 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,
)
}));
has_changed |= changed;
match certainty {
Certainty::Yes => {}
Certainty::Maybe(_) => self.obligations.push(obligation),
}
}
if !has_changed {
break;
}
}
errors
}
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.clone()
}
fn drain_unstalled_obligations(
&mut self,
_: &InferCtxt<'tcx>,
) -> Vec<PredicateObligation<'tcx>> {
std::mem::take(&mut self.obligations)
}
}