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

@ -13,6 +13,7 @@ use rustc_span::Span;
use rustc_trait_selection::solve::deeply_normalize;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use rustc_trait_selection::traits::FulfillmentError;
use crate::{
constraints::OutlivesConstraint,
@ -286,7 +287,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
ocx.infcx.at(&ObligationCause::dummy_with_span(self.span), self.param_env),
ty,
)
.map_err(|_| NoSolution)
.map_err(|_: Vec<FulfillmentError<'tcx>>| NoSolution)
},
"normalize type outlives obligation",
)

View File

@ -10,7 +10,7 @@ use rustc_hir::intravisit;
use rustc_hir::{GenericParamKind, ImplItemKind};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::{util, FulfillmentError};
use rustc_infer::traits::util;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::util::ExplicitSelf;
@ -25,7 +25,7 @@ use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
};
use std::borrow::Cow;
use std::iter;

View File

@ -13,8 +13,8 @@ use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, Type
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::{DefId, LocalDefId};
use rustc_trait_selection::traits::{self, IsFirstInputType, UncoveredTyParams};
use rustc_trait_selection::traits::{FulfillmentError, StructurallyNormalizeExt, TraitEngineExt};
use rustc_trait_selection::traits::{OrphanCheckErr, OrphanCheckMode};
use rustc_trait_selection::traits::{StructurallyNormalizeExt, TraitEngineExt};
#[instrument(level = "debug", skip(tcx))]
pub(crate) fn orphan_check_impl(
@ -317,7 +317,8 @@ fn orphan_check<'tcx>(
}
let ty = if infcx.next_trait_solver() {
let mut fulfill_cx = <dyn traits::TraitEngine<'_>>::new(&infcx);
let mut fulfill_cx =
<dyn traits::TraitEngine<'tcx, FulfillmentError<'tcx>>>::new(&infcx);
infcx
.at(&cause, ty::ParamEnv::empty())
.structurally_normalize(ty, &mut *fulfill_cx)

View File

@ -15,7 +15,6 @@ use rustc_errors::{
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::traits::FulfillmentError;
use rustc_middle::bug;
use rustc_middle::query::Key;
use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@ -28,6 +27,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::BytePos;
use rustc_span::{Span, Symbol, DUMMY_SP};
use rustc_trait_selection::traits::FulfillmentError;
use rustc_trait_selection::traits::{
object_safety_violations_for_assoc_item, TraitAliasExpansionInfo,
};

View File

@ -11,7 +11,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefIdMap;
use rustc_span::Span;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _};
use rustc_trait_selection::traits::{
self, FulfillmentError, PredicateObligation, TraitEngine, TraitEngineExt as _,
};
use std::cell::RefCell;
use std::ops::Deref;
@ -34,7 +36,7 @@ pub(crate) struct TypeckRootCtxt<'tcx> {
pub(super) locals: RefCell<HirIdMap<Ty<'tcx>>>,
pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>,
pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx, FulfillmentError<'tcx>>>>,
/// Some additional `Sized` obligations badly affect type inference.
/// These obligations are added in a later stage of typeck.
@ -83,7 +85,7 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
TypeckRootCtxt {
typeck_results,
fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(&infcx)),
fulfillment_cx: RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx)),
infcx,
locals: RefCell::new(Default::default()),
deferred_sized_obligations: RefCell::new(Vec::new()),

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::*;

View File

@ -1,3 +1,4 @@
use crate::traits::FulfillmentError;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{InferCtxt, RegionResolutionError};
use rustc_macros::extension;
@ -27,7 +28,8 @@ impl<'tcx> InferCtxt<'tcx> {
),
ty,
)
.map_err(|_| NoSolution)
// TODO:
.map_err(|_: Vec<FulfillmentError<'tcx>>| NoSolution)
} else {
Ok(ty)
}

View File

@ -1,3 +1,4 @@
use std::marker::PhantomData;
use std::mem;
use std::ops::ControlFlow;
@ -5,14 +6,17 @@ use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
use rustc_infer::traits::{
self, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation,
ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError, TraitEngine,
self, FromSolverError, FulfillmentErrorCode, FulfillmentErrorLike, MismatchedProjectionTypes,
Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError,
TraitEngine,
};
use rustc_middle::bug;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::sym;
use crate::traits::FulfillmentError;
use super::eval_ctxt::GenerateProofTree;
use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
use super::{Certainty, InferCtxtEvalExt};
@ -28,7 +32,7 @@ use super::{Certainty, InferCtxtEvalExt};
///
/// 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> {
pub struct FulfillmentCtxt<'tcx, E: FulfillmentErrorLike<'tcx>> {
obligations: ObligationStorage<'tcx>,
/// The snapshot in which this context was created. Using the context
@ -36,6 +40,7 @@ pub struct FulfillmentCtxt<'tcx> {
/// gets rolled back. Because of this we explicitly check that we only
/// use the context in exactly this snapshot.
usable_in_snapshot: usize,
_errors: PhantomData<E>,
}
#[derive(Default)]
@ -89,8 +94,8 @@ impl<'tcx> ObligationStorage<'tcx> {
}
}
impl<'tcx> FulfillmentCtxt<'tcx> {
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
impl<'tcx, E: FulfillmentErrorLike<'tcx>> FulfillmentCtxt<'tcx, E> {
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx, E> {
assert!(
infcx.next_trait_solver(),
"new trait solver fulfillment context created when \
@ -99,6 +104,7 @@ impl<'tcx> FulfillmentCtxt<'tcx> {
FulfillmentCtxt {
obligations: Default::default(),
usable_in_snapshot: infcx.num_open_snapshots(),
_errors: PhantomData,
}
}
@ -118,7 +124,9 @@ impl<'tcx> FulfillmentCtxt<'tcx> {
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
impl<'tcx, E: FromSolverError<'tcx, NextSolverError<'tcx>>> TraitEngine<'tcx, E>
for FulfillmentCtxt<'tcx, E>
{
#[instrument(level = "trace", skip(self, infcx))]
fn register_predicate_obligation(
&mut self,
@ -129,24 +137,22 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
self.obligations.register(obligation);
}
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
let mut errors: Vec<_> = self
.obligations
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
self.obligations
.pending
.drain(..)
.map(|obligation| fulfillment_error_for_stalled(infcx, obligation))
.collect();
errors.extend(self.obligations.overflowed.drain(..).map(|obligation| FulfillmentError {
obligation: find_best_leaf_obligation(infcx, &obligation, true),
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
root_obligation: obligation,
}));
errors
.map(|obligation| NextSolverError::Ambiguity(obligation))
.chain(
self.obligations
.overflowed
.drain(..)
.map(|obligation| NextSolverError::Overflow(obligation)),
)
.map(|e| E::from_solver_error(infcx, e))
.collect()
}
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
let mut errors = Vec::new();
for i in 0.. {
@ -164,7 +170,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
let (changed, certainty) = match result {
Ok(result) => result,
Err(NoSolution) => {
errors.push(fulfillment_error_for_no_solution(infcx, obligation));
errors.push(E::from_solver_error(
infcx,
NextSolverError::TrueError(obligation),
));
continue;
}
};
@ -195,6 +204,28 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
}
}
pub enum NextSolverError<'tcx> {
TrueError(PredicateObligation<'tcx>),
Ambiguity(PredicateObligation<'tcx>),
Overflow(PredicateObligation<'tcx>),
}
impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for FulfillmentError<'tcx> {
fn from_solver_error(infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
match error {
NextSolverError::TrueError(obligation) => {
fulfillment_error_for_no_solution(infcx, obligation)
}
NextSolverError::Ambiguity(obligation) => {
fulfillment_error_for_stalled(infcx, obligation)
}
NextSolverError::Overflow(obligation) => {
fulfillment_error_for_overflow(infcx, obligation)
}
}
}
}
fn fulfillment_error_for_no_solution<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
@ -280,6 +311,17 @@ fn fulfillment_error_for_stalled<'tcx>(
}
}
fn fulfillment_error_for_overflow<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
) -> FulfillmentError<'tcx> {
FulfillmentError {
obligation: find_best_leaf_obligation(infcx, &root_obligation, true),
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
root_obligation,
}
}
fn find_best_leaf_obligation<'tcx>(
infcx: &InferCtxt<'tcx>,
obligation: &PredicateObligation<'tcx>,

View File

@ -40,7 +40,7 @@ mod search_graph;
mod trait_goals;
pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt};
pub use fulfill::FulfillmentCtxt;
pub use fulfill::{FulfillmentCtxt, NextSolverError};
pub(crate) use normalize::deeply_normalize_for_diagnostics;
pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};

View File

@ -1,23 +1,29 @@
use std::marker::PhantomData;
use crate::traits::error_reporting::{OverflowCause, TypeErrCtxtExt};
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
use crate::traits::{BoundVarReplacer, FulfillmentError, PlaceholderReplacer};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::infer::at::At;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine};
use rustc_infer::traits::{FromSolverError, FulfillmentErrorLike, Obligation, TraitEngine};
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex};
use rustc_middle::ty::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::{TypeFoldable, TypeVisitableExt};
use super::FulfillmentCtxt;
use super::{FulfillmentCtxt, NextSolverError};
/// Deeply normalize all aliases in `value`. This does not handle inference and expects
/// its input to be already fully resolved.
pub fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
pub fn deeply_normalize<
'tcx,
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
>(
at: At<'_, 'tcx>,
value: T,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
) -> Result<T, Vec<E>> {
assert!(!value.has_escaping_bound_vars());
deeply_normalize_with_skipped_universes(at, value, vec![])
}
@ -28,29 +34,32 @@ pub fn deeply_normalize<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
/// Additionally takes a list of universes which represents the binders which have been
/// entered before passing `value` to the function. This is currently needed for
/// `normalize_erasing_regions`, which skips binders as it walks through a type.
pub fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
pub fn deeply_normalize_with_skipped_universes<
'tcx,
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
>(
at: At<'_, 'tcx>,
value: T,
universes: Vec<Option<UniverseIndex>>,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
) -> Result<T, Vec<E>> {
let fulfill_cx = FulfillmentCtxt::new(at.infcx);
let mut folder = NormalizationFolder { at, fulfill_cx, depth: 0, universes };
let mut folder =
NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData };
value.try_fold_with(&mut folder)
}
struct NormalizationFolder<'me, 'tcx> {
struct NormalizationFolder<'me, 'tcx, E: FulfillmentErrorLike<'tcx>> {
at: At<'me, 'tcx>,
fulfill_cx: FulfillmentCtxt<'tcx>,
fulfill_cx: FulfillmentCtxt<'tcx, E>,
depth: usize,
universes: Vec<Option<UniverseIndex>>,
_errors: PhantomData<E>,
}
impl<'tcx> NormalizationFolder<'_, 'tcx> {
fn normalize_alias_ty(
&mut self,
alias_ty: Ty<'tcx>,
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
impl<'tcx, E: FromSolverError<'tcx, NextSolverError<'tcx>>> NormalizationFolder<'_, 'tcx, E> {
fn normalize_alias_ty(&mut self, alias_ty: Ty<'tcx>) -> Result<Ty<'tcx>, Vec<E>> {
assert!(matches!(alias_ty.kind(), ty::Alias(..)));
let infcx = self.at.infcx;
@ -101,7 +110,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
&mut self,
ty: Ty<'tcx>,
uv: ty::UnevaluatedConst<'tcx>,
) -> Result<ty::Const<'tcx>, Vec<FulfillmentError<'tcx>>> {
) -> Result<ty::Const<'tcx>, Vec<E>> {
let infcx = self.at.infcx;
let tcx = infcx.tcx;
let recursion_limit = tcx.recursion_limit();
@ -141,8 +150,10 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
}
}
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
type Error = Vec<FulfillmentError<'tcx>>;
impl<'tcx, E: FromSolverError<'tcx, NextSolverError<'tcx>>> FallibleTypeFolder<TyCtxt<'tcx>>
for NormalizationFolder<'_, 'tcx, E>
{
type Error = Vec<E>;
fn interner(&self) -> TyCtxt<'tcx> {
self.at.infcx.tcx
@ -242,7 +253,8 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
ty,
vec![None; ty.outer_exclusive_binder().as_usize()],
)
.unwrap_or_else(|_| ty.super_fold_with(self))
// TODO:
.unwrap_or_else(|_: Vec<FulfillmentError<'tcx>>| ty.super_fold_with(self))
}
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
@ -251,6 +263,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
ct,
vec![None; ct.outer_exclusive_binder().as_usize()],
)
.unwrap_or_else(|_| ct.super_fold_with(self))
// TODO:
.unwrap_or_else(|_: Vec<FulfillmentError<'tcx>>| ct.super_fold_with(self))
}
}

View File

@ -11,6 +11,7 @@ use crate::solve::{deeply_normalize_for_diagnostics, inspect};
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::NormalizeExt;
use crate::traits::SkipLeakCheck;
use crate::traits::{util, FulfillmentErrorCode};
use crate::traits::{
Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext,
};
@ -19,7 +20,6 @@ use rustc_errors::{Diag, EmissionGuarantee};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::{util, FulfillmentErrorCode};
use rustc_middle::bug;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};

View File

@ -2,12 +2,15 @@ use std::cell::RefCell;
use std::fmt::Debug;
use super::FulfillmentContext;
use super::TraitEngine;
use super::{FromSolverError, TraitEngine};
use crate::regions::InferCtxtRegionExt;
use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
use crate::solve::NextSolverError;
use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::fulfill::OldSolverError;
use crate::traits::NormalizeExt;
use crate::traits::StructurallyNormalizeExt;
use crate::traits::{FulfillmentError, Obligation, ObligationCause, PredicateObligation};
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::{DefId, LocalDefId};
@ -18,7 +21,6 @@ use rustc_infer::infer::canonical::{
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::RegionResolutionError;
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
use rustc_infer::traits::{FulfillmentError, Obligation, ObligationCause, PredicateObligation};
use rustc_macros::extension;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::traits::query::NoSolution;
@ -28,8 +30,12 @@ use rustc_middle::ty::Upcast;
use rustc_middle::ty::Variance;
use rustc_middle::ty::{self, Ty, TyCtxt};
#[extension(pub trait TraitEngineExt<'tcx>)]
impl<'tcx> dyn TraitEngine<'tcx> {
#[extension(pub trait TraitEngineExt<'tcx, E>)]
impl<
'tcx,
E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
> dyn TraitEngine<'tcx, E>
{
fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
if infcx.next_trait_solver() {
Box::new(NextFulfillmentCtxt::new(infcx))
@ -49,12 +55,16 @@ impl<'tcx> dyn TraitEngine<'tcx> {
/// with obligations outside of hir or mir typeck.
pub struct ObligationCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>,
engine: RefCell<Box<dyn TraitEngine<'tcx>>>,
engine: RefCell<Box<dyn TraitEngine<'tcx, FulfillmentError<'tcx>>>>,
}
impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) }
// TODO:
Self {
infcx,
engine: RefCell::new(<dyn TraitEngine<'tcx, FulfillmentError<'tcx>>>::new(infcx)),
}
}
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {

View File

@ -6,7 +6,7 @@ use rustc_data_structures::obligation_forest::ProcessResult;
use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
use rustc_infer::infer::DefineOpaqueTypes;
use rustc_infer::traits::ProjectionCacheKey;
use rustc_infer::traits::{FromSolverError, FulfillmentErrorLike, ProjectionCacheKey};
use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
use rustc_middle::bug;
use rustc_middle::mir::interpret::ErrorHandled;
@ -50,7 +50,7 @@ impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
/// along. Once all type inference constraints have been generated, the
/// method `select_all_or_error` can be used to report any remaining
/// ambiguous cases as errors.
pub struct FulfillmentContext<'tcx> {
pub struct FulfillmentContext<'tcx, E: FulfillmentErrorLike<'tcx>> {
/// A list of all obligations that have been registered with this
/// fulfillment context.
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
@ -60,6 +60,8 @@ pub struct FulfillmentContext<'tcx> {
/// gets rolled back. Because of this we explicitly check that we only
/// use the context in exactly this snapshot.
usable_in_snapshot: usize,
_errors: PhantomData<E>,
}
#[derive(Clone, Debug)]
@ -76,9 +78,9 @@ pub struct PendingPredicateObligation<'tcx> {
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(PendingPredicateObligation<'_>, 72);
impl<'tcx> FulfillmentContext<'tcx> {
impl<'tcx, E: FromSolverError<'tcx, OldSolverError<'tcx>>> FulfillmentContext<'tcx, E> {
/// Creates a new fulfillment context.
pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx> {
pub(super) fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentContext<'tcx, E> {
assert!(
!infcx.next_trait_solver(),
"old trait solver fulfillment context created when \
@ -87,13 +89,15 @@ impl<'tcx> FulfillmentContext<'tcx> {
FulfillmentContext {
predicates: ObligationForest::new(),
usable_in_snapshot: infcx.num_open_snapshots(),
_errors: PhantomData,
}
}
/// Attempts to select obligations using `selcx`.
fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<FulfillmentError<'tcx>> {
fn select(&mut self, selcx: SelectionContext<'_, 'tcx>) -> Vec<E> {
let span = debug_span!("select", obligation_forest_size = ?self.predicates.len());
let _enter = span.enter();
let infcx = selcx.infcx;
// Process pending obligations.
let outcome: Outcome<_, _> =
@ -102,8 +106,8 @@ impl<'tcx> FulfillmentContext<'tcx> {
// FIXME: if we kept the original cache key, we could mark projection
// obligations as complete for the projection cache here.
let errors: Vec<FulfillmentError<'tcx>> =
outcome.errors.into_iter().map(to_fulfillment_error).collect();
let errors: Vec<E> =
outcome.errors.into_iter().map(|err| E::from_solver_error(infcx, err)).collect();
debug!(
"select({} predicates remaining, {} errors) done",
@ -115,7 +119,9 @@ impl<'tcx> FulfillmentContext<'tcx> {
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
impl<'tcx, E: FromSolverError<'tcx, OldSolverError<'tcx>>> TraitEngine<'tcx, E>
for FulfillmentContext<'tcx, E>
{
#[inline]
fn register_predicate_obligation(
&mut self,
@ -134,18 +140,15 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
}
fn collect_remaining_errors(
&mut self,
_infcx: &InferCtxt<'tcx>,
) -> Vec<FulfillmentError<'tcx>> {
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
self.predicates
.to_errors(FulfillmentErrorCode::Ambiguity { overflow: None })
.into_iter()
.map(to_fulfillment_error)
.map(|err| E::from_solver_error(infcx, err))
.collect()
}
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
let selcx = SelectionContext::new(infcx);
self.select(selcx)
}
@ -840,13 +843,15 @@ fn args_infer_vars<'a, 'tcx>(
.filter_map(TyOrConstInferVar::maybe_from_generic_arg)
}
fn to_fulfillment_error<'tcx>(
error: Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>,
) -> FulfillmentError<'tcx> {
let mut iter = error.backtrace.into_iter();
let obligation = iter.next().unwrap().obligation;
// The root obligation is the last item in the backtrace - if there's only
// one item, then it's the same as the main obligation
let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation);
FulfillmentError::new(obligation, error.error, root_obligation)
pub type OldSolverError<'tcx> = Error<PendingPredicateObligation<'tcx>, FulfillmentErrorCode<'tcx>>;
impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for FulfillmentError<'tcx> {
fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: OldSolverError<'tcx>) -> Self {
let mut iter = error.backtrace.into_iter();
let obligation = iter.next().unwrap().obligation;
// The root obligation is the last item in the backtrace - if there's only
// one item, then it's the same as the main obligation
let root_obligation = iter.next_back().map_or_else(|| obligation.clone(), |e| e.obligation);
FulfillmentError::new(obligation, error.error, root_obligation)
}
}

View File

@ -1,13 +1,13 @@
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
use crate::regions::InferCtxtRegionExt;
use crate::traits::{self, ObligationCause};
use crate::traits::{self, FulfillmentError, ObligationCause};
use hir::LangItem;
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt, TypeVisitableExt};
use super::outlives_bounds::InferCtxtExt;

View File

@ -70,6 +70,49 @@ pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, Placeh
pub use rustc_infer::traits::*;
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>,
}
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> FulfillmentErrorLike<'tcx> for FulfillmentError<'tcx> {
fn is_true_error(&self) -> bool {
self.is_true_error()
}
}
impl<'tcx> Debug for FulfillmentError<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
}
}
/// Whether to skip the leak check, as part of a future compatibility warning step.
///
/// The "default" for skip-leak-check corresponds to the current

View File

@ -3,11 +3,13 @@ use super::error_reporting::OverflowCause;
use super::error_reporting::TypeErrCtxtExt;
use super::SelectionContext;
use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
use crate::solve::NextSolverError;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::infer::at::At;
use rustc_infer::infer::InferOk;
use rustc_infer::traits::FromSolverError;
use rustc_infer::traits::PredicateObligation;
use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine};
use rustc_infer::traits::{Normalized, Obligation, TraitEngine};
use rustc_macros::extension;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder};
@ -44,11 +46,14 @@ impl<'tcx> At<'_, 'tcx> {
/// existing fulfillment context in the old solver. Once we also eagerly prove goals with
/// the old solver or have removed the old solver, remove `traits::fully_normalize` and
/// rename this function to `At::fully_normalize`.
fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
fn deeply_normalize<
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
>(
self,
value: T,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> Result<T, Vec<E>> {
if self.infcx.next_trait_solver() {
crate::solve::deeply_normalize(self, value)
} else {

View File

@ -9,10 +9,10 @@ use crate::traits::error_reporting::OverflowCause;
use crate::traits::error_reporting::TypeErrCtxtExt;
use crate::traits::normalize::needs_normalization;
use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
use crate::traits::{FulfillmentError, Normalized};
use crate::traits::{ObligationCause, PredicateObligation, Reveal};
use rustc_data_structures::sso::SsoHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::traits::Normalized;
use rustc_macros::extension;
use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt};
@ -76,7 +76,10 @@ impl<'cx, 'tcx> At<'cx, 'tcx> {
};
if self.infcx.next_trait_solver() {
match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) {
// TODO:
match crate::solve::deeply_normalize_with_skipped_universes::<_, FulfillmentError<'tcx>>(
self, value, universes,
) {
Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
Err(_errors) => {
return Err(NoSolution);

View File

@ -1,6 +1,7 @@
use crate::solve;
use crate::traits::query::NoSolution;
use crate::traits::wf;
use crate::traits::FulfillmentError;
use crate::traits::ObligationCtxt;
use rustc_infer::infer::canonical::Canonical;
@ -266,7 +267,8 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
ocx.infcx.at(&ObligationCause::dummy(), param_env),
ty_a,
)
.map_err(|_errs| NoSolution)?;
// TODO:
.map_err(|_errs: Vec<FulfillmentError<'tcx>>| NoSolution)?;
}
let mut components = smallvec![];
push_outlives_components(tcx, ty_a, &mut components);

View File

@ -1,5 +1,5 @@
use rustc_infer::infer::at::At;
use rustc_infer::traits::{FulfillmentError, TraitEngine};
use rustc_infer::traits::{FulfillmentErrorLike, TraitEngine};
use rustc_macros::extension;
use rustc_middle::ty::{self, Ty};
@ -7,11 +7,11 @@ use crate::traits::{NormalizeExt, Obligation};
#[extension(pub trait StructurallyNormalizeExt<'tcx>)]
impl<'tcx> At<'_, 'tcx> {
fn structurally_normalize(
fn structurally_normalize<E: FulfillmentErrorLike<'tcx>>(
&self,
ty: Ty<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
fulfill_cx: &mut dyn TraitEngine<'tcx, E>,
) -> Result<Ty<'tcx>, Vec<E>> {
assert!(!ty.is_ty_var(), "should have resolved vars before calling");
if self.infcx.next_trait_solver() {