diff options
Diffstat (limited to '')
-rw-r--r-- | compiler/rustc_traits/src/chalk/db.rs | 2 | ||||
-rw-r--r-- | compiler/rustc_traits/src/chalk/lowering.rs | 13 | ||||
-rw-r--r-- | compiler/rustc_traits/src/dropck_outlives.rs | 220 | ||||
-rw-r--r-- | compiler/rustc_traits/src/evaluate_obligation.rs | 20 | ||||
-rw-r--r-- | compiler/rustc_traits/src/implied_outlives_bounds.rs | 76 | ||||
-rw-r--r-- | compiler/rustc_traits/src/lib.rs | 3 | ||||
-rw-r--r-- | compiler/rustc_traits/src/normalize_erasing_regions.rs | 46 | ||||
-rw-r--r-- | compiler/rustc_traits/src/type_op.rs | 68 |
8 files changed, 225 insertions, 223 deletions
diff --git a/compiler/rustc_traits/src/chalk/db.rs b/compiler/rustc_traits/src/chalk/db.rs index ff5ca0cbc..0de28b826 100644 --- a/compiler/rustc_traits/src/chalk/db.rs +++ b/compiler/rustc_traits/src/chalk/db.rs @@ -7,8 +7,8 @@ //! `crate::chalk::lowering` (to lower rustc types into Chalk types). use rustc_middle::traits::ChalkRustInterner as RustInterner; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; use rustc_middle::ty::{self, AssocKind, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable}; +use rustc_middle::ty::{InternalSubsts, SubstsRef}; use rustc_ast::ast; use rustc_attr as attr; diff --git a/compiler/rustc_traits/src/chalk/lowering.rs b/compiler/rustc_traits/src/chalk/lowering.rs index c7c604e14..45d5ea93d 100644 --- a/compiler/rustc_traits/src/chalk/lowering.rs +++ b/compiler/rustc_traits/src/chalk/lowering.rs @@ -191,7 +191,7 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::GoalData<RustInterner<'tcx>>> for ty::Predi GenericArgKind::Const(..) => { chalk_ir::GoalData::All(chalk_ir::Goals::empty(interner)) } - GenericArgKind::Lifetime(lt) => bug!("unexpect well formed predicate: {:?}", lt), + GenericArgKind::Lifetime(lt) => bug!("unexpected well formed predicate: {:?}", lt), }, ty::PredicateKind::ObjectSafe(t) => chalk_ir::GoalData::DomainGoal( @@ -326,7 +326,8 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Ty<RustInterner<'tcx>>> for Ty<'tcx> { )), }) } - ty::Dynamic(predicates, region) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy { + // FIXME(dyn-star): handle the dynamic kind (dyn or dyn*) + ty::Dynamic(predicates, region, _kind) => chalk_ir::TyKind::Dyn(chalk_ir::DynTy { bounds: predicates.lower_into(interner), lifetime: region.lower_into(interner), }), @@ -485,10 +486,6 @@ impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'t }) .intern(interner) } - ty::ReEmpty(ui) => { - chalk_ir::LifetimeData::Empty(chalk_ir::UniverseIndex { counter: ui.index() }) - .intern(interner) - } ty::ReErased => chalk_ir::LifetimeData::Erased.intern(interner), } } @@ -510,8 +507,8 @@ impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'t name: ty::BoundRegionKind::BrAnon(p.idx as u32), }), chalk_ir::LifetimeData::Static => return interner.tcx.lifetimes.re_static, - chalk_ir::LifetimeData::Empty(ui) => { - ty::ReEmpty(ty::UniverseIndex::from_usize(ui.counter)) + chalk_ir::LifetimeData::Empty(_) => { + bug!("Chalk should not have been passed an empty lifetime.") } chalk_ir::LifetimeData::Erased => return interner.tcx.lifetimes.re_erased, chalk_ir::LifetimeData::Phantom(void, _) => match *void {}, diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index a20de08b4..d5a8ca5ea 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -4,7 +4,7 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::TraitEngineExt as _; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::InternalSubsts; use rustc_middle::ty::{self, EarlyBinder, ParamEnvAnd, Ty, TyCtxt}; use rustc_span::source_map::{Span, DUMMY_SP}; use rustc_trait_selection::traits::query::dropck_outlives::trivial_dropck_outlives; @@ -27,128 +27,120 @@ fn dropck_outlives<'tcx>( ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, DropckOutlivesResult<'tcx>>>, NoSolution> { debug!("dropck_outlives(goal={:#?})", canonical_goal); - tcx.infer_ctxt().enter_with_canonical( - DUMMY_SP, - &canonical_goal, - |ref infcx, goal, canonical_inference_vars| { - let tcx = infcx.tcx; - let ParamEnvAnd { param_env, value: for_ty } = goal; - - let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; - - // A stack of types left to process. Each round, we pop - // something from the stack and invoke - // `dtorck_constraint_for_ty`. This may produce new types that - // have to be pushed on the stack. This continues until we have explored - // all the reachable types from the type `for_ty`. - // - // Example: Imagine that we have the following code: - // - // ```rust - // struct A { - // value: B, - // children: Vec<A>, - // } - // - // struct B { - // value: u32 - // } - // - // fn f() { - // let a: A = ...; - // .. - // } // here, `a` is dropped - // ``` - // - // at the point where `a` is dropped, we need to figure out - // which types inside of `a` contain region data that may be - // accessed by any destructors in `a`. We begin by pushing `A` - // onto the stack, as that is the type of `a`. We will then - // invoke `dtorck_constraint_for_ty` which will expand `A` - // into the types of its fields `(B, Vec<A>)`. These will get - // pushed onto the stack. Eventually, expanding `Vec<A>` will - // lead to us trying to push `A` a second time -- to prevent - // infinite recursion, we notice that `A` was already pushed - // once and stop. - let mut ty_stack = vec![(for_ty, 0)]; - - // Set used to detect infinite recursion. - let mut ty_set = FxHashSet::default(); - - let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); - - let cause = ObligationCause::dummy(); - let mut constraints = DropckConstraint::empty(); - while let Some((ty, depth)) = ty_stack.pop() { - debug!( - "{} kinds, {} overflows, {} ty_stack", - result.kinds.len(), - result.overflows.len(), - ty_stack.len() - ); - dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?; - - // "outlives" represent types/regions that may be touched - // by a destructor. - result.kinds.append(&mut constraints.outlives); - result.overflows.append(&mut constraints.overflows); - - // If we have even one overflow, we should stop trying to evaluate further -- - // chances are, the subsequent overflows for this evaluation won't provide useful - // information and will just decrease the speed at which we can emit these errors - // (since we'll be printing for just that much longer for the often enormous types - // that result here). - if !result.overflows.is_empty() { - break; - } + let (ref infcx, goal, canonical_inference_vars) = + tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal); + let tcx = infcx.tcx; + let ParamEnvAnd { param_env, value: for_ty } = goal; + + let mut result = DropckOutlivesResult { kinds: vec![], overflows: vec![] }; + + // A stack of types left to process. Each round, we pop + // something from the stack and invoke + // `dtorck_constraint_for_ty`. This may produce new types that + // have to be pushed on the stack. This continues until we have explored + // all the reachable types from the type `for_ty`. + // + // Example: Imagine that we have the following code: + // + // ```rust + // struct A { + // value: B, + // children: Vec<A>, + // } + // + // struct B { + // value: u32 + // } + // + // fn f() { + // let a: A = ...; + // .. + // } // here, `a` is dropped + // ``` + // + // at the point where `a` is dropped, we need to figure out + // which types inside of `a` contain region data that may be + // accessed by any destructors in `a`. We begin by pushing `A` + // onto the stack, as that is the type of `a`. We will then + // invoke `dtorck_constraint_for_ty` which will expand `A` + // into the types of its fields `(B, Vec<A>)`. These will get + // pushed onto the stack. Eventually, expanding `Vec<A>` will + // lead to us trying to push `A` a second time -- to prevent + // infinite recursion, we notice that `A` was already pushed + // once and stop. + let mut ty_stack = vec![(for_ty, 0)]; + + // Set used to detect infinite recursion. + let mut ty_set = FxHashSet::default(); + + let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); + + let cause = ObligationCause::dummy(); + let mut constraints = DropckConstraint::empty(); + while let Some((ty, depth)) = ty_stack.pop() { + debug!( + "{} kinds, {} overflows, {} ty_stack", + result.kinds.len(), + result.overflows.len(), + ty_stack.len() + ); + dtorck_constraint_for_ty(tcx, DUMMY_SP, for_ty, depth, ty, &mut constraints)?; + + // "outlives" represent types/regions that may be touched + // by a destructor. + result.kinds.append(&mut constraints.outlives); + result.overflows.append(&mut constraints.overflows); + + // If we have even one overflow, we should stop trying to evaluate further -- + // chances are, the subsequent overflows for this evaluation won't provide useful + // information and will just decrease the speed at which we can emit these errors + // (since we'll be printing for just that much longer for the often enormous types + // that result here). + if !result.overflows.is_empty() { + break; + } - // dtorck types are "types that will get dropped but which - // do not themselves define a destructor", more or less. We have - // to push them onto the stack to be expanded. - for ty in constraints.dtorck_types.drain(..) { - match infcx.at(&cause, param_env).normalize(ty) { - Ok(Normalized { value: ty, obligations }) => { - fulfill_cx.register_predicate_obligations(infcx, obligations); - - debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); - - match ty.kind() { - // All parameters live for the duration of the - // function. - ty::Param(..) => {} - - // A projection that we couldn't resolve - it - // might have a destructor. - ty::Projection(..) | ty::Opaque(..) => { - result.kinds.push(ty.into()); - } - - _ => { - if ty_set.insert(ty) { - ty_stack.push((ty, depth + 1)); - } - } - } + // dtorck types are "types that will get dropped but which + // do not themselves define a destructor", more or less. We have + // to push them onto the stack to be expanded. + for ty in constraints.dtorck_types.drain(..) { + match infcx.at(&cause, param_env).normalize(ty) { + Ok(Normalized { value: ty, obligations }) => { + fulfill_cx.register_predicate_obligations(infcx, obligations); + + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); + + match ty.kind() { + // All parameters live for the duration of the + // function. + ty::Param(..) => {} + + // A projection that we couldn't resolve - it + // might have a destructor. + ty::Projection(..) | ty::Opaque(..) => { + result.kinds.push(ty.into()); } - // We don't actually expect to fail to normalize. - // That implies a WF error somewhere else. - Err(NoSolution) => { - return Err(NoSolution); + _ => { + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); + } } } } + + // We don't actually expect to fail to normalize. + // That implies a WF error somewhere else. + Err(NoSolution) => { + return Err(NoSolution); + } } + } + } - debug!("dropck_outlives: result = {:#?}", result); + debug!("dropck_outlives: result = {:#?}", result); - infcx.make_canonicalized_query_response( - canonical_inference_vars, - result, - &mut *fulfill_cx, - ) - }, - ) + infcx.make_canonicalized_query_response(canonical_inference_vars, result, &mut *fulfill_cx) } /// Returns a set of constraints that needs to be satisfied in diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs index 49c9ba459..493d5de08 100644 --- a/compiler/rustc_traits/src/evaluate_obligation.rs +++ b/compiler/rustc_traits/src/evaluate_obligation.rs @@ -18,17 +18,15 @@ fn evaluate_obligation<'tcx>( debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal); // HACK This bubble is required for this tests to pass: // impl-trait/issue99642.rs - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bubble).enter_with_canonical( - DUMMY_SP, - &canonical_goal, - |ref infcx, goal, _canonical_inference_vars| { - debug!("evaluate_obligation: goal={:#?}", goal); - let ParamEnvAnd { param_env, value: predicate } = goal; + let (ref infcx, goal, _canonical_inference_vars) = tcx + .infer_ctxt() + .with_opaque_type_inference(DefiningAnchor::Bubble) + .build_with_canonical(DUMMY_SP, &canonical_goal); + debug!("evaluate_obligation: goal={:#?}", goal); + let ParamEnvAnd { param_env, value: predicate } = goal; - let mut selcx = SelectionContext::with_query_mode(&infcx, TraitQueryMode::Canonical); - let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); + let mut selcx = SelectionContext::with_query_mode(&infcx, TraitQueryMode::Canonical); + let obligation = Obligation::new(ObligationCause::dummy(), param_env, predicate); - selcx.evaluate_root_obligation(&obligation) - }, - ) + selcx.evaluate_root_obligation(&obligation) } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index e3e78f70b..7d36b9558 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -35,7 +35,7 @@ fn implied_outlives_bounds<'tcx>( } fn compute_implied_outlives_bounds<'tcx>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, ) -> Fallible<Vec<OutlivesBound<'tcx>>> { @@ -49,7 +49,8 @@ fn compute_implied_outlives_bounds<'tcx>( let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default(); let mut wf_args = vec![ty.into()]; - let mut implied_bounds = vec![]; + let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> = + vec![]; let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx); @@ -65,41 +66,28 @@ fn compute_implied_outlives_bounds<'tcx>( // 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(infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP) .unwrap_or_default(); - // N.B., all of these predicates *ought* to be easily proven - // true. In fact, their correctness is (mostly) implied by - // other parts of the program. However, in #42552, we had - // an annoying scenario where: - // - // - Some `T::Foo` gets normalized, resulting in a - // variable `_1` and a `T: Trait<Foo=_1>` constraint - // (not sure why it couldn't immediately get - // solved). This result of `_1` got cached. - // - These obligations were dropped on the floor here, - // rather than being registered. - // - Then later we would get a request to normalize - // `T::Foo` which would result in `_1` being used from - // the cache, but hence without the `T: Trait<Foo=_1>` - // constraint. As a result, `_1` never gets resolved, - // and we get an ICE (in dropck). - // - // Therefore, we register any predicates involving - // inference variables. We restrict ourselves to those - // involving inference variables both for efficiency and - // to avoids duplicate errors that otherwise show up. + // 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. fulfill_cx.register_predicate_obligations( infcx, - obligations.iter().filter(|o| o.predicate.has_infer_types_or_consts()).cloned(), + obligations.iter().filter(|o| o.predicate.has_non_region_infer()).cloned(), ); // From the full set of obligations, just filter down to the // region relationships. - implied_bounds.extend(obligations.into_iter().flat_map(|obligation| { + outlives_bounds.extend(obligations.into_iter().filter_map(|obligation| { assert!(!obligation.has_escaping_bound_vars()); match obligation.predicate.kind().no_bound_vars() { - None => vec![], + None => None, Some(pred) => match pred { ty::PredicateKind::Trait(..) | ty::PredicateKind::Subtype(..) @@ -109,21 +97,18 @@ fn compute_implied_outlives_bounds<'tcx>( | ty::PredicateKind::ObjectSafe(..) | ty::PredicateKind::ConstEvaluatable(..) | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => vec![], + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, ty::PredicateKind::WellFormed(arg) => { wf_args.push(arg); - vec![] + None } ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { - vec![OutlivesBound::RegionSubRegion(r_b, r_a)] + Some(ty::OutlivesPredicate(r_a.into(), r_b)) } ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => { - let ty_a = 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) + Some(ty::OutlivesPredicate(ty_a.into(), r_b)) } }, } @@ -133,9 +118,27 @@ fn compute_implied_outlives_bounds<'tcx>( // Ensure that those obligations that we had to solve // get solved *here*. match fulfill_cx.select_all_or_error(infcx).as_slice() { - [] => Ok(implied_bounds), - _ => Err(NoSolution), + [] => (), + _ => 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 = 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 @@ -153,6 +156,9 @@ fn implied_bounds_from_components<'tcx>( Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)), Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)), Component::Projection(p) => Some(OutlivesBound::RegionSubProjection(sub_region, p)), + Component::Opaque(def_id, substs) => { + Some(OutlivesBound::RegionSubOpaque(sub_region, def_id, substs)) + } Component::EscapingProjection(_) => // If the projection has escaping regions, don't // try to infer any implied bounds even for its diff --git a/compiler/rustc_traits/src/lib.rs b/compiler/rustc_traits/src/lib.rs index 2bea164c0..0da28737f 100644 --- a/compiler/rustc_traits/src/lib.rs +++ b/compiler/rustc_traits/src/lib.rs @@ -1,7 +1,8 @@ //! New recursive solver modeled on Chalk's recursive solver. Most of //! the guts are broken up into modules; see the comments in those modules. -#![feature(let_else)] +#![deny(rustc::untranslatable_diagnostic)] +#![deny(rustc::diagnostic_outside_of_impl)] #![recursion_limit = "256"] #[macro_use] diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 5d394ed22..2da64d73d 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -18,9 +18,6 @@ pub(crate) fn provide(p: &mut Providers) { try_normalize_after_erasing_regions(tcx, goal) }, - try_normalize_mir_const_after_erasing_regions: |tcx, goal| { - try_normalize_after_erasing_regions(tcx, goal) - }, ..*p }; } @@ -30,30 +27,29 @@ fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + goal: ParamEnvAnd<'tcx, T>, ) -> Result<T, NoSolution> { let ParamEnvAnd { param_env, value } = goal; - tcx.infer_ctxt().enter(|infcx| { - let cause = ObligationCause::dummy(); - match infcx.at(&cause, param_env).normalize(value) { - Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => { - // We don't care about the `obligations`; they are - // always only region relations, and we are about to - // erase those anyway: - debug_assert_eq!( - normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)), - None, - ); + let infcx = tcx.infer_ctxt().build(); + let cause = ObligationCause::dummy(); + match infcx.at(&cause, param_env).normalize(value) { + Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => { + // We don't care about the `obligations`; they are + // always only region relations, and we are about to + // erase those anyway: + debug_assert_eq!( + normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)), + None, + ); - let resolved_value = infcx.resolve_vars_if_possible(normalized_value); - // It's unclear when `resolve_vars` would have an effect in a - // fresh `InferCtxt`. If this assert does trigger, it will give - // us a test case. - debug_assert_eq!(normalized_value, resolved_value); - let erased = infcx.tcx.erase_regions(resolved_value); - debug_assert!(!erased.needs_infer(), "{:?}", erased); - Ok(erased) - } - Err(NoSolution) => Err(NoSolution), + let resolved_value = infcx.resolve_vars_if_possible(normalized_value); + // It's unclear when `resolve_vars` would have an effect in a + // fresh `InferCtxt`. If this assert does trigger, it will give + // us a test case. + debug_assert_eq!(normalized_value, resolved_value); + let erased = infcx.tcx.erase_regions(resolved_value); + debug_assert!(!erased.needs_infer(), "{:?}", erased); + Ok(erased) } - }) + Err(NoSolution) => Err(NoSolution), + } } fn not_outlives_predicate<'tcx>(p: ty::Predicate<'tcx>) -> bool { diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index d895b647d..bca7458ed 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -3,12 +3,12 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::{Canonical, QueryResponse}; use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt}; -use rustc_infer::traits::TraitEngineExt as _; +use rustc_infer::traits::{ObligationCauseCode, TraitEngineExt as _}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{GenericArg, Subst, UserSelfTy, UserSubsts}; use rustc_middle::ty::{ self, EarlyBinder, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable, Variance, }; +use rustc_middle::ty::{GenericArg, UserSelfTy, UserSubsts}; use rustc_middle::ty::{ParamEnv, ParamEnvAnd, Predicate, ToPredicate}; use rustc_span::{Span, DUMMY_SP}; use rustc_trait_selection::infer::InferCtxtBuilderExt; @@ -22,6 +22,7 @@ use rustc_trait_selection::traits::query::type_op::subtype::Subtype; use rustc_trait_selection::traits::query::{Fallible, NoSolution}; use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, TraitEngine}; use std::fmt; +use std::iter::zip; pub(crate) fn provide(p: &mut Providers) { *p = Providers { @@ -50,7 +51,7 @@ fn type_op_ascribe_user_type<'tcx>( /// 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<'a, 'tcx: 'a>( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, fulfill_cx: &'a mut dyn TraitEngine<'tcx>, key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, span: Option<Span>, @@ -61,14 +62,15 @@ pub fn type_op_ascribe_user_type_with_span<'a, 'tcx: 'a>( mir_ty, def_id, user_substs ); - let mut cx = AscribeUserTypeCx { infcx, param_env, fulfill_cx }; - cx.relate_mir_and_user_ty(mir_ty, def_id, user_substs, span)?; + let mut cx = AscribeUserTypeCx { infcx, param_env, span: span.unwrap_or(DUMMY_SP), fulfill_cx }; + cx.relate_mir_and_user_ty(mir_ty, def_id, user_substs)?; Ok(()) } struct AscribeUserTypeCx<'me, 'tcx> { - infcx: &'me InferCtxt<'me, 'tcx>, + infcx: &'me InferCtxt<'tcx>, param_env: ParamEnv<'tcx>, + span: Span, fulfill_cx: &'me mut dyn TraitEngine<'tcx>, } @@ -77,12 +79,15 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> { where T: TypeFoldable<'tcx>, { + self.normalize_with_cause(value, ObligationCause::misc(self.span, hir::CRATE_HIR_ID)) + } + + fn normalize_with_cause<T>(&mut self, value: T, cause: ObligationCause<'tcx>) -> T + where + T: TypeFoldable<'tcx>, + { self.infcx - .partially_normalize_associated_types_in( - ObligationCause::misc(DUMMY_SP, hir::CRATE_HIR_ID), - self.param_env, - value, - ) + .partially_normalize_associated_types_in(cause, self.param_env, value) .into_value_registering_obligations(self.infcx, self.fulfill_cx) } @@ -91,18 +96,13 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> { T: ToTrace<'tcx>, { self.infcx - .at(&ObligationCause::dummy(), self.param_env) + .at(&ObligationCause::dummy_with_span(self.span), self.param_env) .relate(a, variance, b)? .into_value_registering_obligations(self.infcx, self.fulfill_cx); Ok(()) } - fn prove_predicate(&mut self, predicate: Predicate<'tcx>, span: Option<Span>) { - let cause = if let Some(span) = span { - ObligationCause::dummy_with_span(span) - } else { - ObligationCause::dummy() - }; + fn prove_predicate(&mut self, predicate: Predicate<'tcx>, cause: ObligationCause<'tcx>) { self.fulfill_cx.register_predicate_obligation( self.infcx, Obligation::new(cause, self.param_env, predicate), @@ -120,20 +120,20 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> { EarlyBinder(value).subst(self.tcx(), substs) } + #[instrument(level = "debug", skip(self))] fn relate_mir_and_user_ty( &mut self, mir_ty: Ty<'tcx>, def_id: DefId, user_substs: UserSubsts<'tcx>, - span: Option<Span>, ) -> Result<(), NoSolution> { let UserSubsts { user_self_ty, substs } = user_substs; let tcx = self.tcx(); let ty = tcx.type_of(def_id); let ty = self.subst(ty, substs); - debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); let ty = self.normalize(ty); + debug!("relate_type_and_user_type: ty of def-id is {:?}", ty); self.relate(mir_ty, Variance::Invariant, ty)?; @@ -144,10 +144,22 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> { // outlives" error messages. let instantiated_predicates = self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs); - debug!(?instantiated_predicates.predicates); - for instantiated_predicate in instantiated_predicates.predicates { - let instantiated_predicate = self.normalize(instantiated_predicate); - self.prove_predicate(instantiated_predicate, span); + + let cause = ObligationCause::dummy_with_span(self.span); + + debug!(?instantiated_predicates); + for (instantiated_predicate, predicate_span) in + zip(instantiated_predicates.predicates, instantiated_predicates.spans) + { + let span = if self.span == DUMMY_SP { predicate_span } else { self.span }; + let cause = ObligationCause::new( + span, + hir::CRATE_HIR_ID, + ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span), + ); + let instantiated_predicate = + self.normalize_with_cause(instantiated_predicate, cause.clone()); + self.prove_predicate(instantiated_predicate, cause); } if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { @@ -160,7 +172,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> { self.prove_predicate( ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into())) .to_predicate(self.tcx()), - span, + cause.clone(), ); } @@ -177,7 +189,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> { // which...could happen with normalization... self.prove_predicate( ty::Binder::dummy(ty::PredicateKind::WellFormed(ty.into())).to_predicate(self.tcx()), - span, + cause, ); Ok(()) } @@ -198,7 +210,7 @@ fn type_op_eq<'tcx>( } fn type_op_normalize<'tcx, T>( - infcx: &InferCtxt<'_, 'tcx>, + infcx: &InferCtxt<'tcx>, fulfill_cx: &mut dyn TraitEngine<'tcx>, key: ParamEnvAnd<'tcx, Normalize<T>>, ) -> Fallible<T> @@ -273,7 +285,7 @@ fn type_op_prove_predicate<'tcx>( /// 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_prove_predicate_with_cause<'a, 'tcx: 'a>( - infcx: &'a InferCtxt<'a, 'tcx>, + infcx: &'a InferCtxt<'tcx>, fulfill_cx: &'a mut dyn TraitEngine<'tcx>, key: ParamEnvAnd<'tcx, ProvePredicate<'tcx>>, cause: ObligationCause<'tcx>, |