From 9835e2ae736235810b4ea1c162ca5e65c547e770 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:50 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- .../src/traits/query/type_op/ascribe_user_type.rs | 123 +++++++++++++- .../src/traits/query/type_op/custom.rs | 68 ++++---- .../src/traits/query/type_op/eq.rs | 14 +- .../query/type_op/implied_outlives_bounds.rs | 181 ++++++++++++++++++++- .../src/traits/query/type_op/mod.rs | 68 ++++++-- .../src/traits/query/type_op/normalize.rs | 24 ++- .../src/traits/query/type_op/outlives.rs | 16 +- .../src/traits/query/type_op/prove_predicate.rs | 28 +++- .../src/traits/query/type_op/subtype.rs | 14 +- 9 files changed, 462 insertions(+), 74 deletions(-) (limited to 'compiler/rustc_trait_selection/src/traits/query/type_op') diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index e6db96c9e..01d7a1e79 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -1,8 +1,13 @@ use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::query::Fallible; -use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use crate::traits::ObligationCtxt; +use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; +use rustc_infer::traits::Obligation; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::{ObligationCause, ObligationCauseCode}; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, UserSelfTy, UserSubsts, UserType}; pub use rustc_middle::traits::query::type_op::AscribeUserType; +use rustc_span::{Span, DUMMY_SP}; impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { type QueryResponse = (); @@ -17,7 +22,119 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { + ) -> Result, NoSolution> { tcx.type_op_ascribe_user_type(canonicalized) } + + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result { + type_op_ascribe_user_type_with_span(ocx, key, None) + } +} + +/// The core of the `type_op_ascribe_user_type` query: for diagnostics purposes in NLL HRTB errors, +/// this query can be re-run to better track the span of the obligation cause, and improve the error +/// message. Do not call directly unless you're in that very specific context. +pub fn type_op_ascribe_user_type_with_span<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, + span: Option, +) -> Result<(), NoSolution> { + let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); + debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); + let span = span.unwrap_or(DUMMY_SP); + match user_ty { + UserType::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, + UserType::TypeOf(def_id, user_substs) => { + relate_mir_and_user_substs(ocx, param_env, span, mir_ty, def_id, user_substs)? + } + }; + Ok(()) +} + +#[instrument(level = "debug", skip(ocx, param_env, span))] +fn relate_mir_and_user_ty<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + mir_ty: Ty<'tcx>, + user_ty: Ty<'tcx>, +) -> Result<(), NoSolution> { + let cause = ObligationCause::dummy_with_span(span); + let user_ty = ocx.normalize(&cause, param_env, user_ty); + ocx.eq(&cause, param_env, mir_ty, user_ty)?; + + // FIXME(#104764): We should check well-formedness before normalization. + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into())); + ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate)); + Ok(()) +} + +#[instrument(level = "debug", skip(ocx, param_env, span))] +fn relate_mir_and_user_substs<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + span: Span, + mir_ty: Ty<'tcx>, + def_id: DefId, + user_substs: UserSubsts<'tcx>, +) -> Result<(), NoSolution> { + let param_env = param_env.without_const(); + let UserSubsts { user_self_ty, substs } = user_substs; + let tcx = ocx.infcx.tcx; + let cause = ObligationCause::dummy_with_span(span); + + let ty = tcx.type_of(def_id).subst(tcx, substs); + let ty = ocx.normalize(&cause, param_env, ty); + debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); + + ocx.eq(&cause, param_env, mir_ty, ty)?; + + // Prove the predicates coming along with `def_id`. + // + // Also, normalize the `instantiated_predicates` + // because otherwise we wind up with duplicate "type + // outlives" error messages. + let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs); + + debug!(?instantiated_predicates); + for (instantiated_predicate, predicate_span) in instantiated_predicates { + let span = if span == DUMMY_SP { predicate_span } else { span }; + let cause = ObligationCause::new( + span, + CRATE_DEF_ID, + ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span), + ); + let instantiated_predicate = + ocx.normalize(&cause.clone(), param_env, instantiated_predicate); + + ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate)); + } + + if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { + let self_ty = ocx.normalize(&cause, param_env, self_ty); + let impl_self_ty = tcx.type_of(impl_def_id).subst(tcx, substs); + let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty); + + ocx.eq(&cause, param_env, self_ty, impl_self_ty)?; + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into())); + ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); + } + + // In addition to proving the predicates, we have to + // prove that `ty` is well-formed -- this is because + // the WF of `ty` is predicated on the substs being + // well-formed, and we haven't proven *that*. We don't + // want to prove the WF of types from `substs` directly because they + // haven't been normalized. + // + // FIXME(nmatsakis): Well, perhaps we should normalize + // them? This would only be relevant if some input + // type were ill-formed but did not appear in `ty`, + // which...could happen with normalization... + let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())); + ocx.register_obligation(Obligation::new(tcx, cause, param_env, predicate)); + Ok(()) } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs index 8f1b05c11..6d8d2103f 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs @@ -1,32 +1,32 @@ use crate::infer::canonical::query_response; -use crate::infer::{InferCtxt, InferOk}; +use crate::infer::InferCtxt; use crate::traits::query::type_op::TypeOpOutput; -use crate::traits::query::Fallible; use crate::traits::ObligationCtxt; +use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::region_constraints::RegionConstraintData; +use rustc_middle::traits::query::NoSolution; use rustc_span::source_map::DUMMY_SP; +use rustc_span::Span; use std::fmt; -pub struct CustomTypeOp { +pub struct CustomTypeOp { closure: F, - description: G, + description: &'static str, } -impl CustomTypeOp { - pub fn new<'tcx, R>(closure: F, description: G) -> Self +impl CustomTypeOp { + pub fn new<'tcx, R>(closure: F, description: &'static str) -> Self where - F: FnOnce(&InferCtxt<'tcx>) -> Fallible>, - G: Fn() -> String, + F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, { CustomTypeOp { closure, description } } } -impl<'tcx, F, R: fmt::Debug, G> super::TypeOp<'tcx> for CustomTypeOp +impl<'tcx, F, R: fmt::Debug> super::TypeOp<'tcx> for CustomTypeOp where - F: for<'a, 'cx> FnOnce(&'a InferCtxt<'tcx>) -> Fallible>, - G: Fn() -> String, + F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, { type Output = R; /// We can't do any custom error reporting for `CustomTypeOp`, so @@ -36,21 +36,22 @@ where /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints /// (they will be given over to the NLL region solver). - fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible> { + fn fully_perform( + self, + infcx: &InferCtxt<'tcx>, + span: Span, + ) -> Result, ErrorGuaranteed> { if cfg!(debug_assertions) { info!("fully_perform({:?})", self); } - Ok(scrape_region_constraints(infcx, || (self.closure)(infcx))?.0) + Ok(scrape_region_constraints(infcx, self.closure, self.description, span)?.0) } } -impl fmt::Debug for CustomTypeOp -where - G: Fn() -> String, -{ +impl fmt::Debug for CustomTypeOp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", (self.description)()) + self.description.fmt(f) } } @@ -58,8 +59,10 @@ where /// constraints that result, creating query-region-constraints. pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( infcx: &InferCtxt<'tcx>, - op: impl FnOnce() -> Fallible>, -) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> { + op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result, + name: &'static str, + span: Span, +) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> { // During NLL, we expect that nobody will register region // obligations **except** as part of a custom type op (and, at the // end of each custom type op, we scrape out the region @@ -72,16 +75,21 @@ pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>( pre_obligations, ); - let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?; - let ocx = ObligationCtxt::new(infcx); - ocx.register_obligations(obligations); - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.tcx.sess.diagnostic().delay_span_bug( - DUMMY_SP, - &format!("errors selecting obligation during MIR typeck: {:?}", errors), - ); - } + let value = infcx.commit_if_ok(|_| { + let ocx = ObligationCtxt::new_in_snapshot(infcx); + let value = op(&ocx).map_err(|_| { + infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}")) + })?; + let errors = ocx.select_all_or_error(); + if errors.is_empty() { + Ok(value) + } else { + Err(infcx.tcx.sess.delay_span_bug( + DUMMY_SP, + format!("errors selecting obligation during MIR typeck: {:?}", errors), + )) + } + })?; let region_obligations = infcx.take_registered_region_obligations(); let region_constraint_data = infcx.take_and_reset_region_constraints(); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs index 8c9b9610c..f65893088 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/eq.rs @@ -1,5 +1,7 @@ use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::query::Fallible; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; pub use rustc_middle::traits::query::type_op::Eq; @@ -17,7 +19,15 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Eq<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { + ) -> Result, NoSolution> { tcx.type_op_eq(canonicalized) } + + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result { + ocx.eq(&ObligationCause::dummy(), key.param_env, key.value.a, key.value.b)?; + Ok(()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index 18d7c9b19..9989fc9c4 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -1,7 +1,15 @@ -use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::query::Fallible; +use crate::traits::query::NoSolution; +use crate::traits::wf; +use crate::traits::ObligationCtxt; + +use rustc_infer::infer::canonical::Canonical; +use rustc_infer::infer::outlives::components::{push_outlives_components, Component}; use rustc_infer::traits::query::OutlivesBound; -use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt}; +use rustc_middle::infer::canonical::CanonicalQueryResponse; +use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::source_map::DUMMY_SP; +use smallvec::{smallvec, SmallVec}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] pub struct ImpliedOutlivesBounds<'tcx> { @@ -28,7 +36,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { + ) -> Result, NoSolution> { // FIXME this `unchecked_map` is only necessary because the // query is defined as taking a `ParamEnvAnd`; it should // take an `ImpliedOutlivesBounds` instead @@ -39,4 +47,169 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { tcx.implied_outlives_bounds(canonicalized) } + + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result { + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + } +} + +pub fn compute_implied_outlives_bounds_inner<'tcx>( + ocx: &ObligationCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, +) -> Result>, NoSolution> { + let tcx = ocx.infcx.tcx; + + // Sometimes when we ask what it takes for T: WF, we get back that + // U: WF is required; in that case, we push U onto this stack and + // process it next. Because the resulting predicates aren't always + // guaranteed to be a subset of the original type, so we need to store the + // WF args we've computed in a set. + let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); + let mut wf_args = vec![ty.into()]; + + let mut outlives_bounds: Vec, ty::Region<'tcx>>> = + vec![]; + + while let Some(arg) = wf_args.pop() { + if !checked_wf_args.insert(arg) { + continue; + } + + // Compute the obligations for `arg` to be well-formed. If `arg` is + // an unresolved inference variable, just substituted an empty set + // -- because the return type here is going to be things we *add* + // to the environment, it's always ok for this set to be smaller + // than the ultimate set. (Note: normally there won't be + // unresolved inference variables here anyway, but there might be + // during typeck under some circumstances.) + // + // FIXME(@lcnr): It's not really "always fine", having fewer implied + // bounds can be backward incompatible, e.g. #101951 was caused by + // us not dealing with inference vars in `TypeOutlives` predicates. + let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) + .unwrap_or_default(); + + for obligation in obligations { + debug!(?obligation); + assert!(!obligation.has_escaping_bound_vars()); + + // While these predicates should all be implied by other parts of + // the program, they are still relevant as they may constrain + // inference variables, which is necessary to add the correct + // implied bounds in some cases, mostly when dealing with projections. + // + // Another important point here: we only register `Projection` + // predicates, since otherwise we might register outlives + // predicates containing inference variables, and we don't + // learn anything new from those. + if obligation.predicate.has_non_region_infer() { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::AliasRelate(..) => { + ocx.register_obligation(obligation.clone()); + } + _ => {} + } + } + + let pred = match obligation.predicate.kind().no_bound_vars() { + None => continue, + Some(pred) => pred, + }; + match pred { + ty::PredicateKind::Clause(ty::Clause::Trait(..)) + // FIXME(const_generics): Make sure that `<'a, 'b, const N: &'a &'b u32>` is sound + // if we ever support that + | ty::PredicateKind::Clause(ty::Clause::ConstArgHasType(..)) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Clause(ty::Clause::Projection(..)) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::Ambiguous + | ty::PredicateKind::AliasRelate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => {} + + // We need to search through *all* WellFormed predicates + ty::PredicateKind::WellFormed(arg) => { + wf_args.push(arg); + } + + // We need to register region relationships + ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate( + r_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)), + + ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate( + ty_a, + r_b, + ))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)), + } + } + } + + // This call to `select_all_or_error` is necessary to constrain inference variables, which we + // use further down when computing the implied bounds. + match ocx.select_all_or_error().as_slice() { + [] => (), + _ => return Err(NoSolution), + } + + // We lazily compute the outlives components as + // `select_all_or_error` constrains inference variables. + let implied_bounds = outlives_bounds + .into_iter() + .flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() { + ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)], + ty::GenericArgKind::Type(ty_a) => { + let ty_a = ocx.infcx.resolve_vars_if_possible(ty_a); + let mut components = smallvec![]; + push_outlives_components(tcx, ty_a, &mut components); + implied_bounds_from_components(r_b, components) + } + ty::GenericArgKind::Const(_) => unreachable!(), + }) + .collect(); + + Ok(implied_bounds) +} + +/// When we have an implied bound that `T: 'a`, we can further break +/// this down to determine what relationships would have to hold for +/// `T: 'a` to hold. We get to assume that the caller has validated +/// those relationships. +fn implied_bounds_from_components<'tcx>( + sub_region: ty::Region<'tcx>, + sup_components: SmallVec<[Component<'tcx>; 4]>, +) -> Vec> { + sup_components + .into_iter() + .filter_map(|component| { + match component { + Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), + Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), + Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)), + Component::EscapingAlias(_) => + // If the projection has escaping regions, don't + // try to infer any implied bounds even for its + // free components. This is conservative, because + // the caller will still have to prove that those + // free components outlive `sub_region`. But the + // idea is that the WAY that the caller proves + // that may change in the future and we want to + // give ourselves room to get smarter here. + { + None + } + Component::UnresolvedInferenceVariable(..) => None, + } + }) + .collect() } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 9e8bc8bce..642fdec2d 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -2,13 +2,14 @@ use crate::infer::canonical::{ Canonical, CanonicalQueryResponse, OriginalQueryValues, QueryRegionConstraints, }; use crate::infer::{InferCtxt, InferOk}; -use crate::traits::query::Fallible; -use crate::traits::ObligationCause; +use crate::traits::{ObligationCause, ObligationCtxt}; +use rustc_errors::ErrorGuaranteed; use rustc_infer::infer::canonical::Certainty; -use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::PredicateObligations; +use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use std::fmt; pub mod ascribe_user_type; @@ -22,6 +23,8 @@ pub mod subtype; pub use rustc_middle::traits::query::type_op::*; +use self::custom::scrape_region_constraints; + /// "Type ops" are used in NLL to perform some particular action and /// extract out the resulting region constraints (or an error if it /// cannot be completed). @@ -32,7 +35,11 @@ pub trait TypeOp<'tcx>: Sized + fmt::Debug { /// Processes the operation and all resulting obligations, /// returning the final result along with any region constraints /// (they will be given over to the NLL region solver). - fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible>; + fn fully_perform( + self, + infcx: &InferCtxt<'tcx>, + span: Span, + ) -> Result, ErrorGuaranteed>; } /// The output from performing a type op @@ -74,18 +81,32 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't fn perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible>; + ) -> Result, NoSolution>; + + /// In the new trait solver, we already do caching in the solver itself, + /// so there's no need to canonicalize and cache via the query system. + /// Additionally, even if we were to canonicalize, we'd still need to + /// make sure to feed it predefined opaque types and the defining anchor + /// and that would require duplicating all of the tcx queries. Instead, + /// just perform these ops locally. + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result; fn fully_perform_into( query_key: ParamEnvAnd<'tcx, Self>, infcx: &InferCtxt<'tcx>, output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, - ) -> Fallible<( - Self::QueryResponse, - Option>>, - PredicateObligations<'tcx>, - Certainty, - )> { + ) -> Result< + ( + Self::QueryResponse, + Option>>, + PredicateObligations<'tcx>, + Certainty, + ), + NoSolution, + > { if let Some(result) = QueryTypeOp::try_fast_path(infcx.tcx, &query_key) { return Ok((result, None, vec![], Certainty::Proven)); } @@ -120,10 +141,26 @@ where type Output = Q::QueryResponse; type ErrorInfo = Canonical<'tcx, ParamEnvAnd<'tcx, Q>>; - fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible> { + fn fully_perform( + self, + infcx: &InferCtxt<'tcx>, + span: Span, + ) -> Result, ErrorGuaranteed> { + if infcx.tcx.trait_solver_next() { + return Ok(scrape_region_constraints( + infcx, + |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self), + "query type op", + span, + )? + .0); + } + let mut region_constraints = QueryRegionConstraints::default(); let (output, error_info, mut obligations, _) = - Q::fully_perform_into(self, infcx, &mut region_constraints)?; + Q::fully_perform_into(self, infcx, &mut region_constraints).map_err(|_| { + infcx.tcx.sess.delay_span_bug(span, format!("error performing {self:?}")) + })?; // Typically, instantiating NLL query results does not // create obligations. However, in some cases there @@ -151,7 +188,10 @@ where } } if !progress { - return Err(NoSolution); + return Err(infcx.tcx.sess.delay_span_bug( + span, + format!("ambiguity processing {obligations:?} from {self:?}"), + )); } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 5b216c076..57ca14aa4 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -1,5 +1,7 @@ use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::query::Fallible; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; use std::fmt; @@ -19,23 +21,31 @@ where fn perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { + ) -> Result, NoSolution> { T::type_op_method(tcx, canonicalized) } + + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result { + // FIXME(-Ztrait-solver=next): shouldn't be using old normalizer + Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value)) + } } pub trait Normalizable<'tcx>: fmt::Debug + TypeFoldable> + Lift<'tcx> + Copy { fn type_op_method( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible>; + ) -> Result, NoSolution>; } impl<'tcx> Normalizable<'tcx> for Ty<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { + ) -> Result, NoSolution> { tcx.type_op_normalize_ty(canonicalized) } } @@ -44,7 +54,7 @@ impl<'tcx> Normalizable<'tcx> for ty::Predicate<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { + ) -> Result, NoSolution> { tcx.type_op_normalize_predicate(canonicalized) } } @@ -53,7 +63,7 @@ impl<'tcx> Normalizable<'tcx> for ty::PolyFnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { + ) -> Result, NoSolution> { tcx.type_op_normalize_poly_fn_sig(canonicalized) } } @@ -62,7 +72,7 @@ impl<'tcx> Normalizable<'tcx> for ty::FnSig<'tcx> { fn type_op_method( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Normalize>>, - ) -> Fallible> { + ) -> Result, NoSolution> { tcx.type_op_normalize_fn_sig(canonicalized) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index 21ef4e24f..988942633 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,6 +1,9 @@ use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::query::dropck_outlives::{trivial_dropck_outlives, DropckOutlivesResult}; -use crate::traits::query::Fallible; +use crate::traits::query::dropck_outlives::{ + compute_dropck_outlives_inner, trivial_dropck_outlives, +}; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, Ty, TyCtxt}; #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable, Lift)] @@ -27,7 +30,7 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { + ) -> Result, NoSolution> { // Subtle: note that we are not invoking // `infcx.at(...).dropck_outlives(...)` here, but rather the // underlying `dropck_outlives` query. This same underlying @@ -48,4 +51,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { tcx.dropck_outlives(canonicalized) } + + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result { + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value.dropped_ty)) + } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index b63da28e2..47850bc33 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -1,5 +1,8 @@ use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::query::Fallible; +use crate::traits::ObligationCtxt; +use rustc_infer::traits::Obligation; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; pub use rustc_middle::traits::query::type_op::ProvePredicate; @@ -32,14 +35,21 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, - mut canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { - match canonicalized.value.value.predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { - canonicalized.value.param_env.remap_constness_with(pred.constness); - } - _ => canonicalized.value.param_env = canonicalized.value.param_env.without_const(), - } + canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, + ) -> Result, NoSolution> { tcx.type_op_prove_predicate(canonicalized) } + + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result { + ocx.register_obligation(Obligation::new( + ocx.infcx.tcx, + ObligationCause::dummy(), + key.param_env, + key.value.predicate, + )); + Ok(()) + } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs index c51292eba..10976d5cd 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/subtype.rs @@ -1,5 +1,7 @@ use crate::infer::canonical::{Canonical, CanonicalQueryResponse}; -use crate::traits::query::Fallible; +use crate::traits::ObligationCtxt; +use rustc_middle::traits::query::NoSolution; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; pub use rustc_middle::traits::query::type_op::Subtype; @@ -14,7 +16,15 @@ impl<'tcx> super::QueryTypeOp<'tcx> for Subtype<'tcx> { fn perform_query( tcx: TyCtxt<'tcx>, canonicalized: Canonical<'tcx, ParamEnvAnd<'tcx, Self>>, - ) -> Fallible> { + ) -> Result, NoSolution> { tcx.type_op_subtype(canonicalized) } + + fn perform_locally_in_new_solver( + ocx: &ObligationCtxt<'_, 'tcx>, + key: ParamEnvAnd<'tcx, Self>, + ) -> Result { + ocx.sub(&ObligationCause::dummy(), key.param_env, key.value.sub, key.value.sup)?; + Ok(()) + } } -- cgit v1.2.3