Auto merge of #125864 - compiler-errors:opt-in-error-reporting, r=lcnr

Opt-in to `FulfillmentError` generation to avoid doing extra work in the new solver

In the new solver, we do additional trait solving in order to generate fulfillment errors, because all we have is the root obligation. This is problematic because there are many cases where we don't need the full error information, and instead are just calling `ObligationCtxt::select_all_or_error` to probe whether a predicate holds or not. This is also problematic because we use `ObligationCtxt`s within the error reporting machinery itself, and so we can definitely cause stack overflows:

a94483a5f2/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs (L75-L84)

So instead, make `TraitEngine` and `ObligationCtxt` generic over `E: FulfillmentErrorLike<'tcx>`, and introduce a new `ScrubbedTraitError` which only stores whether the failure was due to a "true error" or an ambiguity. Then, introduce `ObligationCtxt::new_with_diagnostics` for the callsites that actually inspect their `FulfillmentError`s.

r? `@lcnr`

Number-wise, there are:
```
     39 ObligationCtxt::new
     32 ObligationCtxt::new_with_diagnostics
      1 ObligationCtxt::new_generic
```
calls to each `ObligationCtxt` constructor, which suggests that there are indeed a lot of callsites that don't care about diagnostics.
This commit is contained in:
bors 2024-06-03 15:43:06 +00:00
commit eb5e2449c5
49 changed files with 483 additions and 333 deletions

View File

@ -1347,7 +1347,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
return;
};
// Try to find predicates on *generic params* that would allow copying `ty`
let ocx = ObligationCtxt::new(self.infcx);
let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);
let cause = ObligationCause::misc(span, self.mir_def_id());
ocx.register_bound(cause, self.param_env, ty, def_id);

View File

@ -11,7 +11,7 @@ use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::CoroutineKind;
use rustc_index::IndexSlice;
use rustc_infer::infer::BoundRegionConversionTime;
use rustc_infer::traits::{FulfillmentErrorCode, SelectionError};
use rustc_infer::traits::SelectionError;
use rustc_middle::bug;
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
@ -29,7 +29,9 @@ use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
use rustc_target::abi::{FieldIdx, VariantIdx};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
use rustc_trait_selection::traits::{
type_known_to_meet_bound_modulo_regions, FulfillmentErrorCode,
};
use crate::fluent_generated as fluent;

View File

@ -6,7 +6,6 @@ use hir::{ExprKind, Param};
use rustc_errors::{Applicability, Diag};
use rustc_hir::intravisit::Visitor;
use rustc_hir::{self as hir, BindingMode, ByRef, Node};
use rustc_infer::traits;
use rustc_middle::bug;
use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt, Upcast};
@ -18,6 +17,7 @@ use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, BytePos, DesugaringKind, Span};
use rustc_target::abi::FieldIdx;
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use crate::diagnostics::BorrowedContentSource;

View File

@ -340,7 +340,7 @@ fn check_opaque_type_well_formed<'tcx>(
.with_next_trait_solver(next_trait_solver)
.with_opaque_type_inference(parent_def_id)
.build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let identity_args = GenericArgs::identity_for_item(tcx, def_id);
// Require that the hidden type actually fulfills all the bounds of the opaque type, even without

View File

@ -10,9 +10,9 @@ use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
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::ScrubbedTraitError;
use crate::{
constraints::OutlivesConstraint,
@ -282,11 +282,12 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
) -> Ty<'tcx> {
let result = CustomTypeOp::new(
|ocx| {
deeply_normalize(
ocx.infcx.at(&ObligationCause::dummy_with_span(self.span), self.param_env),
ocx.deeply_normalize(
&ObligationCause::dummy_with_span(self.span),
self.param_env,
ty,
)
.map_err(|_| NoSolution)
.map_err(|_: Vec<ScrubbedTraitError<'tcx>>| NoSolution)
},
"normalize type outlives obligation",
)

View File

@ -735,7 +735,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
// which path expressions are getting called on and which path expressions are only used
// as function pointers. This is required for correctness.
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let predicates = tcx.predicates_of(callee).instantiate(tcx, fn_args);
let cause = ObligationCause::new(

View File

@ -342,7 +342,7 @@ fn check_opaque_meets_bounds<'tcx>(
let param_env = tcx.param_env(defining_use_anchor);
let infcx = tcx.infer_ctxt().with_opaque_type_inference(defining_use_anchor).build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let args = match *origin {
hir::OpaqueTyOrigin::FnReturn(parent)
@ -1727,7 +1727,7 @@ pub(super) fn check_coroutine_obligations(
.with_opaque_type_inference(def_id)
.build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
for (predicate, cause) in &typeck_results.coroutine_stalled_predicates {
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, *predicate));
}

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;
@ -225,7 +225,7 @@ fn compare_method_predicate_entailment<'tcx>(
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
@ -493,7 +493,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
);
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
// Normalize the impl signature with fresh variables for lifetime inference.
let misc_cause = ObligationCause::misc(return_span, impl_m_def_id);
@ -764,17 +764,20 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
Ok(&*tcx.arena.alloc(remapped_types))
}
struct ImplTraitInTraitCollector<'a, 'tcx> {
ocx: &'a ObligationCtxt<'a, 'tcx>,
struct ImplTraitInTraitCollector<'a, 'tcx, E> {
ocx: &'a ObligationCtxt<'a, 'tcx, E>,
types: FxIndexMap<DefId, (Ty<'tcx>, ty::GenericArgsRef<'tcx>)>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
}
impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> {
impl<'a, 'tcx, E> ImplTraitInTraitCollector<'a, 'tcx, E>
where
E: 'tcx,
{
fn new(
ocx: &'a ObligationCtxt<'a, 'tcx>,
ocx: &'a ObligationCtxt<'a, 'tcx, E>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
body_id: LocalDefId,
@ -783,7 +786,10 @@ impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> {
}
}
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'_, 'tcx> {
impl<'tcx, E> TypeFolder<TyCtxt<'tcx>> for ImplTraitInTraitCollector<'_, 'tcx, E>
where
E: 'tcx,
{
fn interner(&self) -> TyCtxt<'tcx> {
self.ocx.infcx.tcx
}
@ -1777,7 +1783,7 @@ fn compare_const_predicate_entailment<'tcx>(
);
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args);
for (predicate, span) in impl_ct_own_bounds {
@ -1910,7 +1916,7 @@ fn compare_type_predicate_entailment<'tcx>(
let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
@ -1977,7 +1983,7 @@ pub(super) fn check_type_bounds<'tcx>(
let rebased_args = impl_ty_args.rebase_onto(tcx, container_id, impl_trait_ref.args);
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
// A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the
// span for an impl's associated type. Instead, for these, use the def_span for the synthesized

View File

@ -123,7 +123,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
adt_to_impl_args: GenericArgsRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
// Take the param-env of the adt and instantiate the args that show up in
// the implementation's self type. This gives us the assumptions that the

View File

@ -133,7 +133,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
main_diagnostics_def_id,
ObligationCauseCode::MainFunctionType,
);
let ocx = traits::ObligationCtxt::new(&infcx);
let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
let norm_return_ty = ocx.normalize(&cause, param_env, return_ty);
ocx.register_bound(cause, param_env, norm_return_ty, term_did);
let errors = ocx.select_all_or_error();

View File

@ -599,7 +599,7 @@ pub fn check_function_signature<'tcx>(
let param_env = ty::ParamEnv::empty();
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
let actual_sig = tcx.fn_sig(fn_id).instantiate_identity();

View File

@ -37,7 +37,7 @@ use rustc_trait_selection::traits::misc::{
use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
};
use rustc_type_ir::TypeFlags;
@ -45,13 +45,13 @@ use std::cell::LazyCell;
use std::ops::{ControlFlow, Deref};
pub(super) struct WfCheckingCtxt<'a, 'tcx> {
pub(super) ocx: ObligationCtxt<'a, 'tcx>,
pub(super) ocx: ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>>,
span: Span,
body_def_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> Deref for WfCheckingCtxt<'a, 'tcx> {
type Target = ObligationCtxt<'a, 'tcx>;
type Target = ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>>;
fn deref(&self) -> &Self::Target {
&self.ocx
}
@ -106,7 +106,7 @@ where
{
let param_env = tcx.param_env(body_def_id);
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
let mut wfcx = WfCheckingCtxt { ocx, span, body_def_id, param_env };

View File

@ -267,7 +267,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<()
.join(", "),
}));
} else {
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
for field in coerced_fields {
ocx.register_obligation(Obligation::new(
tcx,
@ -480,7 +480,7 @@ pub fn coerce_unsized_info<'tcx>(
};
// Register an obligation for `A: Trait<B>`.
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let cause = traits::ObligationCause::misc(span, impl_did);
let obligation = Obligation::new(
tcx,

View File

@ -14,7 +14,6 @@ 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::{OrphanCheckErr, OrphanCheckMode};
use rustc_trait_selection::traits::{StructurallyNormalizeExt, TraitEngineExt};
#[instrument(level = "debug", skip(tcx))]
pub(crate) fn orphan_check_impl(
@ -317,12 +316,12 @@ fn orphan_check<'tcx>(
}
let ty = if infcx.next_trait_solver() {
let mut fulfill_cx = <dyn traits::TraitEngine<'_>>::new(&infcx);
infcx
.at(&cause, ty::ParamEnv::empty())
.structurally_normalize(ty, &mut *fulfill_cx)
.map(|ty| infcx.resolve_vars_if_possible(ty))
.unwrap_or(ty)
ocx.structurally_normalize(
&cause,
ty::ParamEnv::empty(),
infcx.resolve_vars_if_possible(ty),
)
.unwrap_or(ty)
} else {
ty
};

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

@ -1298,7 +1298,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.copied()
.filter(|&(impl_, _)| {
infcx.probe(|_| {
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
let self_ty = ocx.normalize(&ObligationCause::dummy(), param_env, self_ty);
let impl_args = infcx.fresh_args_for_item(span, impl_);

View File

@ -67,7 +67,7 @@ fn diagnostic_hir_wf_check<'tcx>(
impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
let infcx = self.tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let tcx_ty = self.icx.lower_ty(ty);
// This visitor can walk into binders, resulting in the `tcx_ty` to

View File

@ -196,7 +196,7 @@ fn get_impl_args(
impl2_node: Node,
) -> Result<(GenericArgsRef<'_>, GenericArgsRef<'_>), ErrorGuaranteed> {
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
let param_env = tcx.param_env(impl1_def_id);
let impl1_span = tcx.def_span(impl1_def_id);
let assumed_wf_types = ocx.assumed_wf_types_and_report_errors(param_env, impl1_def_id)?;

View File

@ -3049,7 +3049,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.commit_if_ok(|snapshot| {
let outer_universe = self.universe();
let ocx = ObligationCtxt::new(self);
let ocx = ObligationCtxt::new_with_diagnostics(self);
let impl_args = self.fresh_args_for_item(base_expr.span, impl_def_id);
let impl_trait_ref =
self.tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(self.tcx, impl_args);

View File

@ -22,7 +22,6 @@ use rustc_hir::{
};
use rustc_hir_analysis::collect::suggest_impl_trait;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
use rustc_infer::traits;
use rustc_middle::lint::in_external_macro;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::span_bug;
@ -36,6 +35,7 @@ use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;

View File

@ -1390,7 +1390,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let mut result = ProbeResult::Match;
let cause = &self.misc(self.span);
let ocx = ObligationCtxt::new(self);
let ocx = ObligationCtxt::new_with_diagnostics(self);
let mut trait_predicate = None;
let (mut xform_self_ty, mut xform_ret_ty);

View File

@ -928,7 +928,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (obligation, _) =
self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types));
// FIXME: This should potentially just add the obligation to the `FnCtxt`
let ocx = ObligationCtxt::new(&self.infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&self.infcx);
ocx.register_obligation(obligation);
Err(ocx.select_all_or_error())
}

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

@ -16,7 +16,7 @@ use crate::infer::region_constraints::{Constraint, RegionConstraintData};
use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult};
use crate::traits::query::NoSolution;
use crate::traits::{Obligation, ObligationCause, PredicateObligation};
use crate::traits::{TraitEngine, TraitEngineExt};
use crate::traits::{ScrubbedTraitError, TraitEngine};
use rustc_data_structures::captures::Captures;
use rustc_index::Idx;
use rustc_index::IndexVec;
@ -54,7 +54,7 @@ impl<'tcx> InferCtxt<'tcx> {
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, ScrubbedTraitError<'tcx>>,
) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
@ -101,7 +101,7 @@ impl<'tcx> InferCtxt<'tcx> {
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
fulfill_cx: &mut dyn TraitEngine<'tcx>,
fulfill_cx: &mut dyn TraitEngine<'tcx, ScrubbedTraitError<'tcx>>,
) -> 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,7 @@ pub use SubregionOrigin::*;
pub use ValuePairs::*;
use crate::traits::{
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, TraitEngineExt,
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
};
use error_reporting::TypeErrCtxt;
use free_regions::RegionRelations;
@ -737,10 +737,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: '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,13 +1,38 @@
use std::fmt::Debug;
use crate::infer::InferCtxt;
use crate::traits::Obligation;
use rustc_hir::def_id::DefId;
use rustc_macros::extension;
use rustc_middle::ty::{self, Ty, Upcast};
use super::FulfillmentError;
use super::{ObligationCause, PredicateObligation};
pub trait TraitEngine<'tcx>: 'tcx {
/// A trait error with most of its information removed. This is the error
/// returned by an `ObligationCtxt` by default, and suitable if you just
/// want to see if a predicate holds, and don't particularly care about the
/// error itself (except for if it's an ambiguity or true error).
///
/// use `ObligationCtxt::new_with_diagnostics` to get a `FulfillmentError`.
#[derive(Clone, Debug)]
pub enum ScrubbedTraitError<'tcx> {
/// A real error. This goal definitely does not hold.
TrueError,
/// An ambiguity. This goal may hold if further inference is done.
Ambiguity,
/// An old-solver-style cycle error, which will fatal.
Cycle(Vec<PredicateObligation<'tcx>>),
}
impl<'tcx> ScrubbedTraitError<'tcx> {
pub fn is_true_error(&self) -> bool {
match self {
ScrubbedTraitError::TrueError => true,
ScrubbedTraitError::Ambiguity | ScrubbedTraitError::Cycle(_) => false,
}
}
}
pub trait TraitEngine<'tcx, E: '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`).
@ -37,10 +62,30 @@ pub trait TraitEngine<'tcx>: 'tcx {
obligation: PredicateObligation<'tcx>,
);
#[must_use]
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
fn register_predicate_obligations(
&mut self,
infcx: &InferCtxt<'tcx>,
obligations: Vec<PredicateObligation<'tcx>>,
) {
for obligation in obligations {
self.register_predicate_obligation(infcx, obligation);
}
}
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
#[must_use]
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>;
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E>;
#[must_use]
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;
}
self.collect_remaining_errors(infcx)
}
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
@ -53,25 +98,6 @@ pub trait TraitEngine<'tcx>: 'tcx {
) -> Vec<PredicateObligation<'tcx>>;
}
#[extension(pub trait TraitEngineExt<'tcx>)]
impl<'tcx, T: ?Sized + TraitEngine<'tcx>> T {
fn register_predicate_obligations(
&mut self,
infcx: &InferCtxt<'tcx>,
obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
) {
for obligation in obligations {
self.register_predicate_obligation(infcx, obligation);
}
}
#[must_use]
fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
let errors = self.select_where_possible(infcx);
if !errors.is_empty() {
return errors;
}
self.collect_remaining_errors(infcx)
}
pub trait FromSolverError<'tcx, E>: Debug + 'tcx {
fn from_solver_error(infcx: &InferCtxt<'tcx>, error: E) -> Self;
}

View File

@ -15,15 +15,14 @@ use hir::def_id::LocalDefId;
use rustc_hir as hir;
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::solve::Certainty;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, Const, Ty, TyCtxt, Upcast};
use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
use rustc_span::Span;
pub use self::ImplSource::*;
pub use self::SelectionError::*;
use crate::infer::InferCtxt;
pub use self::engine::{TraitEngine, TraitEngineExt};
pub use self::engine::{FromSolverError, ScrubbedTraitError, TraitEngine};
pub use self::project::MismatchedProjectionTypes;
pub(crate) use self::project::UndoLog;
pub use self::project::{
@ -124,32 +123,6 @@ 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>,
}
#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only
/// if it is already implemented.
Cycle(Vec<PredicateObligation<'tcx>>),
Select(SelectionError<'tcx>),
Project(MismatchedProjectionTypes<'tcx>),
Subtype(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
ConstEquate(ExpectedFound<Const<'tcx>>, TypeError<'tcx>),
Ambiguity {
/// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
/// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
/// emitting a fatal error instead.
overflow: Option<bool>,
},
}
impl<'tcx, O> Obligation<'tcx, O> {
pub fn new(
tcx: TyCtxt<'tcx>,
@ -198,28 +171,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,33 +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::*;
match *self {
Select(ref e) => write!(f, "{e:?}"),
Project(ref e) => write!(f, "{e:?}"),
Subtype(ref a, ref b) => {
write!(f, "CodeSubtypeError({a:?}, {b:?})")
}
ConstEquate(ref a, ref b) => {
write!(f, "CodeConstEquateError({a:?}, {b:?})")
}
Ambiguity { overflow: None } => write!(f, "Ambiguity"),
Ambiguity { overflow: Some(suggest_increasing_limit) } => {
write!(f, "Overflow({suggest_increasing_limit})")
}
Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
}
}
}
impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "MismatchedProjectionTypes({:?})", self.err)

View File

@ -6,6 +6,7 @@ use syn::spanned::Spanned;
use syn::{
braced, parse_macro_input, Attribute, Generics, ImplItem, Pat, PatIdent, Path, Signature,
Token, TraitItem, TraitItemConst, TraitItemFn, TraitItemMacro, TraitItemType, Type, Visibility,
WhereClause,
};
pub(crate) fn extension(
@ -13,7 +14,7 @@ pub(crate) fn extension(
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let ExtensionAttr { vis, trait_ } = parse_macro_input!(attr as ExtensionAttr);
let Impl { attrs, generics, self_ty, items } = parse_macro_input!(input as Impl);
let Impl { attrs, generics, self_ty, items, wc } = parse_macro_input!(input as Impl);
let headers: Vec<_> = items
.iter()
.map(|item| match item {
@ -59,7 +60,7 @@ pub(crate) fn extension(
#(#headers)*
}
impl #generics #trait_ for #self_ty {
impl #generics #trait_ for #self_ty #wc {
#(#items)*
}
}
@ -133,6 +134,7 @@ struct Impl {
generics: Generics,
self_ty: Type,
items: Vec<ImplItem>,
wc: Option<WhereClause>,
}
impl Parse for Impl {
@ -141,6 +143,7 @@ impl Parse for Impl {
let _: Token![impl] = input.parse()?;
let generics = input.parse()?;
let self_ty = input.parse()?;
let wc = input.parse()?;
let content;
let _brace_token = braced!(content in input);
@ -149,6 +152,6 @@ impl Parse for Impl {
items.push(content.parse()?);
}
Ok(Impl { attrs, generics, self_ty, items })
Ok(Impl { attrs, generics, self_ty, items, wc })
}
}

View File

@ -1608,7 +1608,7 @@ fn check_field_tys_sized<'tcx>(
let infcx = tcx.infer_ctxt().ignoring_regions().build();
let param_env = tcx.param_env(def_id);
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
for field_ty in &coroutine_layout.field_tys {
ocx.register_bound(
ObligationCause::new(

View File

@ -2321,7 +2321,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
let param_env = ty::ParamEnv::empty();
let infcx = tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let span = tcx.def_span(def_id);
let fresh_args = infcx.fresh_args_for_item(span, def_id.to_def_id());

View File

@ -54,7 +54,7 @@ pub fn ensure_wf<'tcx>(
pred,
);
let infcx = tcx.infer_ctxt().build();
let ocx = traits::ObligationCtxt::new(&infcx);
let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
ocx.register_obligation(obligation);
let errors = ocx.select_all_or_error();
if !errors.is_empty() {

View File

@ -1,14 +1,12 @@
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use crate::traits::{self, ObligationCtxt, SelectionContext};
use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::traits::Obligation;
use rustc_macros::extension;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::infer::canonical::{Canonical, CanonicalQueryResponse, QueryResponse};
use rustc_middle::traits::query::NoSolution;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_middle::ty::{GenericArg, Upcast};
use rustc_span::DUMMY_SP;
@ -94,7 +92,7 @@ impl<'tcx> InferCtxt<'tcx> {
ty::TraitRef::new(self.tcx, trait_def_id, [ty]),
)) {
Ok(Some(selection)) => {
let ocx = ObligationCtxt::new(self);
let ocx = ObligationCtxt::new_with_diagnostics(self);
ocx.register_obligations(selection.nested_obligations());
Some(ocx.select_all_or_error())
}

View File

@ -1,3 +1,4 @@
use crate::traits::ScrubbedTraitError;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{InferCtxt, RegionResolutionError};
use rustc_macros::extension;
@ -27,7 +28,7 @@ impl<'tcx> InferCtxt<'tcx> {
),
ty,
)
.map_err(|_| NoSolution)
.map_err(|_: Vec<ScrubbedTraitError<'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,16 @@ 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, 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, FulfillmentErrorCode, ScrubbedTraitError};
use super::eval_ctxt::GenerateProofTree;
use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
use super::{Certainty, InferCtxtEvalExt};
@ -28,7 +31,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: 'tcx> {
obligations: ObligationStorage<'tcx>,
/// The snapshot in which this context was created. Using the context
@ -36,6 +39,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 +93,8 @@ impl<'tcx> ObligationStorage<'tcx> {
}
}
impl<'tcx> FulfillmentCtxt<'tcx> {
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
impl<'tcx, E: '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 +103,7 @@ impl<'tcx> FulfillmentCtxt<'tcx> {
FulfillmentCtxt {
obligations: Default::default(),
usable_in_snapshot: infcx.num_open_snapshots(),
_errors: PhantomData,
}
}
@ -118,7 +123,10 @@ impl<'tcx> FulfillmentCtxt<'tcx> {
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentCtxt<'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
#[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,39 @@ 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)
}
}
}
}
impl<'tcx> FromSolverError<'tcx, NextSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: NextSolverError<'tcx>) -> Self {
match error {
NextSolverError::TrueError(_) => ScrubbedTraitError::TrueError,
NextSolverError::Ambiguity(_) | NextSolverError::Overflow(_) => {
ScrubbedTraitError::Ambiguity
}
}
}
}
fn fulfillment_error_for_no_solution<'tcx>(
infcx: &InferCtxt<'tcx>,
root_obligation: PredicateObligation<'tcx>,
@ -280,6 +322,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,24 +1,27 @@
use std::fmt::Debug;
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, PlaceholderReplacer, ScrubbedTraitError};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_infer::infer::at::At;
use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::TraitEngineExt;
use rustc_infer::traits::{FulfillmentError, Obligation, TraitEngine};
use rustc_infer::traits::{FromSolverError, 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>>>(
at: At<'_, 'tcx>,
value: T,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
pub fn deeply_normalize<'tcx, T, E>(at: At<'_, 'tcx>, value: T) -> Result<T, Vec<E>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
assert!(!value.has_escaping_bound_vars());
deeply_normalize_with_skipped_universes(at, value, vec![])
}
@ -29,29 +32,35 @@ 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, E>(
at: At<'_, 'tcx>,
value: T,
universes: Vec<Option<UniverseIndex>>,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
) -> Result<T, Vec<E>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
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> {
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> NormalizationFolder<'_, 'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
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;
@ -102,7 +111,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();
@ -142,8 +151,11 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
}
}
impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
type Error = Vec<FulfillmentError<'tcx>>;
impl<'tcx, E> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>> + Debug,
{
type Error = Vec<E>;
fn interner(&self) -> TyCtxt<'tcx> {
self.at.infcx.tcx
@ -243,7 +255,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
ty,
vec![None; ty.outer_exclusive_binder().as_usize()],
)
.unwrap_or_else(|_| ty.super_fold_with(self))
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self))
}
fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
@ -252,6 +264,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
ct,
vec![None; ct.outer_exclusive_binder().as_usize()],
)
.unwrap_or_else(|_| ct.super_fold_with(self))
.unwrap_or_else(|_: Vec<ScrubbedTraitError<'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};
@ -360,7 +360,7 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
let infcx = selcx.infcx;
if infcx.next_trait_solver() {
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
ocx.register_obligations(obligations.iter().cloned());
let errors_and_ambiguities = ocx.select_all_or_error();
// We only care about the obligations that are *definitely* true errors.

View File

@ -1,13 +1,16 @@
use std::cell::RefCell;
use std::fmt::Debug;
use super::FulfillmentContext;
use super::TraitEngine;
use super::{FromSolverError, TraitEngine};
use super::{FulfillmentContext, ScrubbedTraitError};
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,9 +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, TraitEngineExt as _,
};
use rustc_macros::extension;
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::traits::query::NoSolution;
@ -30,8 +30,11 @@ 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> dyn TraitEngine<'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>> + FromSolverError<'tcx, OldSolverError<'tcx>>,
{
fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
if infcx.next_trait_solver() {
Box::new(NextFulfillmentCtxt::new(infcx))
@ -49,16 +52,27 @@ impl<'tcx> dyn TraitEngine<'tcx> {
/// Used if you want to have pleasant experience when dealing
/// with obligations outside of hir or mir typeck.
pub struct ObligationCtxt<'a, 'tcx> {
pub struct ObligationCtxt<'a, 'tcx, E = ScrubbedTraitError<'tcx>> {
pub infcx: &'a InferCtxt<'tcx>,
engine: RefCell<Box<dyn TraitEngine<'tcx>>>,
engine: RefCell<Box<dyn TraitEngine<'tcx, E>>>,
}
impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) }
impl<'a, 'tcx> ObligationCtxt<'a, 'tcx, FulfillmentError<'tcx>> {
pub fn new_with_diagnostics(infcx: &'a InferCtxt<'tcx>) -> Self {
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'tcx, _>>::new(infcx)) }
}
}
impl<'a, 'tcx> ObligationCtxt<'a, 'tcx, ScrubbedTraitError<'tcx>> {
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
Self { infcx, engine: RefCell::new(<dyn TraitEngine<'tcx, _>>::new(infcx)) }
}
}
impl<'a, 'tcx, E> ObligationCtxt<'a, 'tcx, E>
where
E: 'tcx,
{
pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
self.engine.borrow_mut().register_predicate_obligation(self.infcx, obligation);
}
@ -110,26 +124,6 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
self.register_infer_ok_obligations(infer_ok)
}
pub fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: T,
) -> Result<T, Vec<FulfillmentError<'tcx>>> {
self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut())
}
pub fn structurally_normalize(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: Ty<'tcx>,
) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
self.infcx
.at(cause, param_env)
.structurally_normalize(value, &mut **self.engine.borrow_mut())
}
pub fn eq<T: ToTrace<'tcx>>(
&self,
cause: &ObligationCause<'tcx>,
@ -186,12 +180,12 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
}
#[must_use]
pub fn select_where_possible(&self) -> Vec<FulfillmentError<'tcx>> {
pub fn select_where_possible(&self) -> Vec<E> {
self.engine.borrow_mut().select_where_possible(self.infcx)
}
#[must_use]
pub fn select_all_or_error(&self) -> Vec<FulfillmentError<'tcx>> {
pub fn select_all_or_error(&self) -> Vec<E> {
self.engine.borrow_mut().select_all_or_error(self.infcx)
}
@ -235,7 +229,9 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
) -> Vec<RegionResolutionError<'tcx>> {
self.infcx.resolve_regions(outlives_env)
}
}
impl<'tcx> ObligationCtxt<'_, 'tcx, FulfillmentError<'tcx>> {
pub fn assumed_wf_types_and_report_errors(
&self,
param_env: ty::ParamEnv<'tcx>,
@ -244,12 +240,35 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
self.assumed_wf_types(param_env, def_id)
.map_err(|errors| self.infcx.err_ctxt().report_fulfillment_errors(errors))
}
}
impl<'tcx> ObligationCtxt<'_, 'tcx, ScrubbedTraitError<'tcx>> {
pub fn make_canonicalized_query_response<T>(
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
{
self.infcx.make_canonicalized_query_response(
inference_vars,
answer,
&mut **self.engine.borrow_mut(),
)
}
}
impl<'tcx, E> ObligationCtxt<'_, 'tcx, E>
where
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
pub fn assumed_wf_types(
&self,
param_env: ty::ParamEnv<'tcx>,
def_id: LocalDefId,
) -> Result<FxIndexSet<Ty<'tcx>>, Vec<FulfillmentError<'tcx>>> {
) -> Result<FxIndexSet<Ty<'tcx>>, Vec<E>> {
let tcx = self.infcx.tcx;
let mut implied_bounds = FxIndexSet::default();
let mut errors = Vec::new();
@ -281,19 +300,23 @@ impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
if errors.is_empty() { Ok(implied_bounds) } else { Err(errors) }
}
pub fn make_canonicalized_query_response<T>(
pub fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
&self,
inference_vars: CanonicalVarValues<'tcx>,
answer: T,
) -> Result<CanonicalQueryResponse<'tcx, T>, NoSolution>
where
T: Debug + TypeFoldable<TyCtxt<'tcx>>,
Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
{
self.infcx.make_canonicalized_query_response(
inference_vars,
answer,
&mut **self.engine.borrow_mut(),
)
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: T,
) -> Result<T, Vec<E>> {
self.infcx.at(cause, param_env).deeply_normalize(value, &mut **self.engine.borrow_mut())
}
pub fn structurally_normalize(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: Ty<'tcx>,
) -> Result<Ty<'tcx>, Vec<E>> {
self.infcx
.at(cause, param_env)
.structurally_normalize(value, &mut **self.engine.borrow_mut())
}
}

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, ProjectionCacheKey};
use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
use rustc_middle::bug;
use rustc_middle::mir::interpret::ErrorHandled;
@ -16,13 +16,13 @@ use rustc_middle::ty::GenericArgsRef;
use rustc_middle::ty::{self, Binder, Const, TypeVisitableExt};
use std::marker::PhantomData;
use super::const_evaluatable;
use super::project::{self, ProjectAndUnifyResult};
use super::select::SelectionContext;
use super::wf;
use super::EvaluationResult;
use super::PredicateObligation;
use super::Unimplemented;
use super::{const_evaluatable, ScrubbedTraitError};
use super::{FulfillmentError, FulfillmentErrorCode};
use crate::traits::project::PolyProjectionObligation;
@ -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: '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,12 @@ pub struct PendingPredicateObligation<'tcx> {
#[cfg(target_pointer_width = "64")]
rustc_data_structures::static_assert_size!(PendingPredicateObligation<'_>, 72);
impl<'tcx> FulfillmentContext<'tcx> {
impl<'tcx, E> FulfillmentContext<'tcx, E>
where
E: FromSolverError<'tcx, OldSolverError<'tcx>>,
{
/// 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 +92,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 +109,11 @@ 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, OldSolverError(err)))
.collect();
debug!(
"select({} predicates remaining, {} errors) done",
@ -115,7 +125,10 @@ impl<'tcx> FulfillmentContext<'tcx> {
}
}
impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
impl<'tcx, E> TraitEngine<'tcx, E> for FulfillmentContext<'tcx, E>
where
E: FromSolverError<'tcx, OldSolverError<'tcx>>,
{
#[inline]
fn register_predicate_obligation(
&mut self,
@ -134,18 +147,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, OldSolverError(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 +850,31 @@ 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)
#[derive(Debug)]
pub struct 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.0.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.0.error, root_obligation)
}
}
impl<'tcx> FromSolverError<'tcx, OldSolverError<'tcx>> for ScrubbedTraitError<'tcx> {
fn from_solver_error(_infcx: &InferCtxt<'tcx>, error: OldSolverError<'tcx>) -> Self {
match error.0.error {
FulfillmentErrorCode::Select(_)
| FulfillmentErrorCode::Project(_)
| FulfillmentErrorCode::Subtype(_, _)
| FulfillmentErrorCode::ConstEquate(_, _) => ScrubbedTraitError::TrueError,
FulfillmentErrorCode::Ambiguity { overflow: _ } => ScrubbedTraitError::Ambiguity,
FulfillmentErrorCode::Cycle(cycle) => ScrubbedTraitError::Cycle(cycle),
}
}
}

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;
@ -137,7 +137,7 @@ pub fn all_fields_implement_trait<'tcx>(
for field in &variant.fields {
// Do this per-field to get better error messages.
let infcx = tcx.infer_ctxt().build();
let ocx = traits::ObligationCtxt::new(&infcx);
let ocx = traits::ObligationCtxt::new_with_diagnostics(&infcx);
let unnormalized_ty = field.ty(tcx, args);
if unnormalized_ty.references_error() {

View File

@ -32,6 +32,7 @@ use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_errors::ErrorGuaranteed;
use rustc_middle::query::Providers;
use rustc_middle::span_bug;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder, TypeSuperVisitable, Upcast};
@ -46,7 +47,7 @@ pub use self::coherence::{add_placeholder_note, orphan_check_trait_ref, overlapp
pub use self::coherence::{InCrate, IsFirstInputType, UncoveredTyParams};
pub use self::coherence::{OrphanCheckErr, OrphanCheckMode, OverlapResult};
pub use self::engine::{ObligationCtxt, TraitEngineExt};
pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation};
pub use self::normalize::NormalizeExt;
pub use self::object_safety::hir_ty_lowering_object_safety_violations;
pub use self::object_safety::is_vtable_safe_method;
@ -70,6 +71,80 @@ 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> Debug for FulfillmentError<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code)
}
}
#[derive(Clone)]
pub enum FulfillmentErrorCode<'tcx> {
/// Inherently impossible to fulfill; this trait is implemented if and only
/// if it is already implemented.
Cycle(Vec<PredicateObligation<'tcx>>),
Select(SelectionError<'tcx>),
Project(MismatchedProjectionTypes<'tcx>),
Subtype(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
ConstEquate(ExpectedFound<ty::Const<'tcx>>, TypeError<'tcx>),
Ambiguity {
/// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
/// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
/// emitting a fatal error instead.
overflow: Option<bool>,
},
}
impl<'tcx> Debug for FulfillmentErrorCode<'tcx> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
FulfillmentErrorCode::Select(ref e) => write!(f, "{e:?}"),
FulfillmentErrorCode::Project(ref e) => write!(f, "{e:?}"),
FulfillmentErrorCode::Subtype(ref a, ref b) => {
write!(f, "CodeSubtypeError({a:?}, {b:?})")
}
FulfillmentErrorCode::ConstEquate(ref a, ref b) => {
write!(f, "CodeConstEquateError({a:?}, {b:?})")
}
FulfillmentErrorCode::Ambiguity { overflow: None } => write!(f, "Ambiguity"),
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
write!(f, "Overflow({suggest_increasing_limit})")
}
FulfillmentErrorCode::Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
}
}
}
/// Whether to skip the leak check, as part of a future compatibility warning step.
///
/// The "default" for skip-leak-check corresponds to the current
@ -407,7 +482,7 @@ pub fn fully_normalize<'tcx, T>(
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
let ocx = ObligationCtxt::new(infcx);
let ocx = ObligationCtxt::new_with_diagnostics(infcx);
debug!(?value);
let normalized_value = ocx.normalize(&cause, param_env, value);
debug!(?normalized_value);

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,15 @@ 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, E>(
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>>
where
T: TypeFoldable<TyCtxt<'tcx>>,
E: FromSolverError<'tcx, NextSolverError<'tcx>>,
{
if self.infcx.next_trait_solver() {
crate::solve::deeply_normalize(self, value)
} else {

View File

@ -8,11 +8,11 @@ use crate::infer::{InferCtxt, InferOk};
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::Normalized;
use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
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,9 @@ impl<'cx, 'tcx> At<'cx, 'tcx> {
};
if self.infcx.next_trait_solver() {
match crate::solve::deeply_normalize_with_skipped_universes(self, value, universes) {
match crate::solve::deeply_normalize_with_skipped_universes::<_, ScrubbedTraitError<'tcx>>(
self, value, universes,
) {
Ok(value) => return Ok(Normalized { value, obligations: vec![] }),
Err(_errors) => {
return Err(NoSolution);

View File

@ -1,4 +1,3 @@
use crate::solve;
use crate::traits::query::NoSolution;
use crate::traits::wf;
use crate::traits::ObligationCtxt;
@ -262,11 +261,9 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
let mut ty_a = ocx.infcx.resolve_vars_if_possible(ty_a);
// Need to manually normalize in the new solver as `wf::obligations` does not.
if ocx.infcx.next_trait_solver() {
ty_a = solve::deeply_normalize(
ocx.infcx.at(&ObligationCause::dummy(), param_env),
ty_a,
)
.map_err(|_errs| NoSolution)?;
ty_a = ocx
.deeply_normalize(&ObligationCause::dummy(), param_env, ty_a)
.map_err(|_| 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::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: '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() {

View File

@ -4,13 +4,13 @@
// general routines.
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::FulfillmentErrorCode;
use rustc_middle::bug;
use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::{
ImplSource, Obligation, ObligationCause, ObligationCtxt, SelectionContext, Unimplemented,
ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext,
Unimplemented,
};
use tracing::debug;
@ -50,6 +50,7 @@ pub fn codegen_select_candidate<'tcx>(
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters.
// FIXME(-Znext-solver): Doesn't need diagnostics if new solver.
let ocx = ObligationCtxt::new(&infcx);
let impl_source = selection.map(|obligation| {
ocx.register_obligation(obligation);
@ -64,7 +65,7 @@ pub fn codegen_select_candidate<'tcx>(
// Cycle errors are the only post-monomorphization errors possible; emit them now so
// `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
for err in errors {
if let FulfillmentErrorCode::Cycle(cycle) = err.code {
if let ScrubbedTraitError::Cycle(cycle) = err {
infcx.err_ctxt().report_overflow_obligation_cycle(&cycle);
}
}

View File

@ -7,9 +7,7 @@ use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
use rustc_trait_selection::traits::query::{
normalize::NormalizationResult, CanonicalAliasGoal, NoSolution,
};
use rustc_trait_selection::traits::{
self, FulfillmentErrorCode, ObligationCause, SelectionContext,
};
use rustc_trait_selection::traits::{self, ObligationCause, ScrubbedTraitError, SelectionContext};
use tracing::debug;
pub(crate) fn provide(p: &mut Providers) {
@ -49,7 +47,7 @@ fn normalize_canonicalized_projection_ty<'tcx>(
// that impl vars are constrained by the signature, for example).
if !tcx.sess.opts.actually_rustdoc {
for error in &errors {
if let FulfillmentErrorCode::Cycle(cycle) = &error.code {
if let ScrubbedTraitError::Cycle(cycle) = &error {
ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
}
}

View File

@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
let span = decl.output.span();
let infcx = cx.tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(&infcx);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
let cause = traits::ObligationCause::misc(span, fn_def_id);
ocx.register_bound(cause, cx.param_env, ret_ty, send_trait);
let send_errors = ocx.select_all_or_error();