1
Fork 0

Make TraitEngines generic over error

This commit is contained in:
Michael Goulet 2024-06-01 14:12:34 -04:00
parent 084ccd2390
commit 54b2b7d460
23 changed files with 253 additions and 157 deletions

View file

@ -15,7 +15,7 @@ use crate::infer::canonical::{
use crate::infer::region_constraints::{Constraint, RegionConstraintData};
use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult};
use crate::traits::query::NoSolution;
use crate::traits::TraitEngine;
use crate::traits::{FulfillmentErrorLike, TraitEngine};
use crate::traits::{Obligation, ObligationCause, PredicateObligation};
use rustc_data_structures::captures::Captures;
use rustc_index::Idx;
@ -50,11 +50,11 @@ impl<'tcx> InferCtxt<'tcx> {
/// - Finally, if any of the obligations result in a hard error,
/// then `Err(NoSolution)` is returned.
#[instrument(skip(self, inference_vars, answer, fulfill_cx), level = "trace")]
pub fn make_canonicalized_query_response<T>(
pub fn make_canonicalized_query_response<T, E: FulfillmentErrorLike<'tcx>>(
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
@ -97,11 +97,11 @@ impl<'tcx> InferCtxt<'tcx> {
/// Helper for `make_canonicalized_query_response` that does
/// everything up until the final canonicalization.
#[instrument(skip(self, fulfill_cx), level = "debug")]
fn make_query_response<T>(
fn make_query_response<T, E: FulfillmentErrorLike<'tcx>>(
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> Result<QueryResponse<'tcx, T>, NoSolution>
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
@ -109,19 +109,13 @@ impl<'tcx> InferCtxt<'tcx> {
let tcx = self.tcx;
// Select everything, returning errors.
let true_errors = fulfill_cx.select_where_possible(self);
debug!("true_errors = {:#?}", true_errors);
let errors = fulfill_cx.select_all_or_error(self);
if !true_errors.is_empty() {
// FIXME -- we don't indicate *why* we failed to solve
debug!("make_query_response: true_errors={:#?}", true_errors);
// True error!
if errors.iter().any(|e| e.is_true_error()) {
return Err(NoSolution);
}
// Anything left unselected *now* must be an ambiguity.
let ambig_errors = fulfill_cx.select_all_or_error(self);
debug!("ambig_errors = {:#?}", ambig_errors);
let region_obligations = self.take_registered_region_obligations();
debug!(?region_obligations);
let region_constraints = self.with_region_constraints(|region_constraints| {
@ -135,8 +129,7 @@ impl<'tcx> InferCtxt<'tcx> {
});
debug!(?region_constraints);
let certainty =
if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous };
let certainty = if errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous };
let opaque_types = self.take_opaque_types_for_query_response();

View file

@ -12,7 +12,8 @@ pub use SubregionOrigin::*;
pub use ValuePairs::*;
use crate::traits::{
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
self, FulfillmentErrorLike, ObligationCause, ObligationInspector, PredicateObligations,
TraitEngine,
};
use error_reporting::TypeErrCtxt;
use free_regions::RegionRelations;
@ -737,10 +738,10 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
impl<'tcx, T> InferOk<'tcx, T> {
/// Extracts `value`, registering any obligations into `fulfill_cx`.
pub fn into_value_registering_obligations(
pub fn into_value_registering_obligations<E: FulfillmentErrorLike<'tcx>>(
self,
infcx: &InferCtxt<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> T {
let InferOk { value, obligations } = self;
fulfill_cx.register_predicate_obligations(infcx, obligations);

View file

@ -1,12 +1,13 @@
use std::fmt::Debug;
use crate::infer::InferCtxt;
use crate::traits::Obligation;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, Ty, Upcast};
use super::FulfillmentError;
use super::{ObligationCause, PredicateObligation};
pub trait TraitEngine<'tcx>: 'tcx {
pub trait TraitEngine<'tcx, E: FulfillmentErrorLike<'tcx>>: 'tcx {
/// Requires that `ty` must implement the trait with `def_id` in
/// the given environment. This trait must not have any type
/// parameters (except for `Self`).
@ -47,12 +48,12 @@ pub trait TraitEngine<'tcx>: 'tcx {
}
#[must_use]
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>;
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>;
#[must_use]
fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
let errors = self.select_where_possible(infcx);
if !errors.is_empty() {
return errors;
@ -71,3 +72,11 @@ pub trait TraitEngine<'tcx>: 'tcx {
infcx: &InferCtxt<'tcx>,
) -> Vec<PredicateObligation<'tcx>>;
}
pub trait FulfillmentErrorLike<'tcx>: Debug + 'tcx {
fn is_true_error(&self) -> bool;
}
pub trait FromSolverError<'tcx, E>: FulfillmentErrorLike<'tcx> {
fn from_solver_error(infcx: &InferCtxt<'tcx>, error: E) -> Self;
}

View file

@ -23,7 +23,7 @@ pub use self::ImplSource::*;
pub use self::SelectionError::*;
use crate::infer::InferCtxt;
pub use self::engine::TraitEngine;
pub use self::engine::{FromSolverError, FulfillmentErrorLike, TraitEngine};
pub use self::project::MismatchedProjectionTypes;
pub(crate) use self::project::UndoLog;
pub use self::project::{
@ -124,15 +124,7 @@ pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>;
pub type ObligationInspector<'tcx> =
fn(&InferCtxt<'tcx>, &PredicateObligation<'tcx>, Result<Certainty, NoSolution>);
pub struct FulfillmentError<'tcx> {
pub obligation: PredicateObligation<'tcx>,
pub code: FulfillmentErrorCode<'tcx>,
/// Diagnostics only: the 'root' obligation which resulted in
/// the failure to process `obligation`. This is the obligation
/// that was initially passed to `register_predicate_obligation`
pub root_obligation: PredicateObligation<'tcx>,
}
// TODO: Pull this down too
#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only
@ -198,28 +190,6 @@ impl<'tcx, O> Obligation<'tcx, O> {
}
}
impl<'tcx> FulfillmentError<'tcx> {
pub fn new(
obligation: PredicateObligation<'tcx>,
code: FulfillmentErrorCode<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
FulfillmentError { obligation, code, root_obligation }
}
pub fn is_true_error(&self) -> bool {
match self.code {
FulfillmentErrorCode::Select(_)
| FulfillmentErrorCode::Project(_)
| FulfillmentErrorCode::Subtype(_, _)
| FulfillmentErrorCode::ConstEquate(_, _) => true,
FulfillmentErrorCode::Cycle(_) | FulfillmentErrorCode::Ambiguity { overflow: _ } => {
false
}
}
}
}
impl<'tcx> PolyTraitObligation<'tcx> {
pub fn polarity(&self) -> ty::PredicatePolarity {
self.predicate.skip_binder().polarity

View file

@ -29,12 +29,6 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> {
}
}
impl<'tcx> fmt::Debug for traits::FulfillmentError<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
}
}
impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use traits::FulfillmentErrorCode::*;