diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index c9981e97ae307..4b7f53213886e 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -1,5 +1,6 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelegate}; use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; @@ -34,9 +35,10 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { /// `process_registered_region_obligations` has some special-cased /// logic expecting to see (e.g.) `ReStatic`, and if we supplied /// our special inference variable there, we would mess that up. + region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'tcx>, - known_type_outlives: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, category: ConstraintCategory<'tcx>, @@ -48,9 +50,10 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { pub(crate) fn new( infcx: &'a InferCtxt<'tcx>, universal_regions: &'a UniversalRegions<'tcx>, + region_bound_pairs: &'a RegionBoundPairs<'tcx>, implicit_region_bound: ty::Region<'tcx>, param_env: ty::ParamEnv<'tcx>, - known_type_outlives: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], + known_type_outlives_obligations: &'a [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, category: ConstraintCategory<'tcx>, @@ -60,9 +63,10 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { infcx, tcx: infcx.tcx, universal_regions, + region_bound_pairs, implicit_region_bound, param_env, - known_type_outlives, + known_type_outlives_obligations, locations, span, category, @@ -131,8 +135,9 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { let ConstraintConversion { tcx, infcx, + region_bound_pairs, implicit_region_bound, - known_type_outlives: known_type_outlives_obligations, + known_type_outlives_obligations, .. } = *self; @@ -174,6 +179,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { TypeOutlives::new( &mut *self, tcx, + region_bound_pairs, Some(implicit_region_bound), known_type_outlives_obligations, ) diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs index 0c7251a873039..edf612f4e97a4 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -2,6 +2,8 @@ use rustc_data_structures::frozen::Frozen; use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder}; use rustc_hir::def::DefKind; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::outlives::env::RegionBoundPairs; +use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::{InferCtxt, outlives}; use rustc_infer::traits::ScrubbedTraitError; use rustc_middle::mir::ConstraintCategory; @@ -42,7 +44,8 @@ type NormalizedInputsAndOutput<'tcx> = Vec>; pub(crate) struct CreateResult<'tcx> { pub(crate) universal_region_relations: Frozen>, - pub(crate) known_type_outlives: Vec>, + pub(crate) region_bound_pairs: RegionBoundPairs<'tcx>, + pub(crate) known_type_outlives_obligations: Vec>, pub(crate) normalized_inputs_and_output: NormalizedInputsAndOutput<'tcx>, } @@ -59,7 +62,7 @@ pub(crate) fn create<'tcx>( implicit_region_bound, constraints, universal_regions, - known_type_outlives: vec![], + region_bound_pairs: Default::default(), outlives: Default::default(), inverse_outlives: Default::default(), } @@ -186,8 +189,8 @@ struct UniversalRegionRelationsBuilder<'a, 'tcx> { // outputs: outlives: TransitiveRelationBuilder, - known_type_outlives: Vec>, inverse_outlives: TransitiveRelationBuilder, + region_bound_pairs: RegionBoundPairs<'tcx>, } impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { @@ -225,9 +228,15 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // Normalize the assumptions we use to borrowck the program. let mut constraints = vec![]; + let mut known_type_outlives_obligations = vec![]; for bound in param_env.caller_bounds() { if let Some(outlives) = bound.as_type_outlives_clause() { - self.normalize_and_push_type_outlives_obligation(outlives, span, &mut constraints); + self.normalize_and_push_type_outlives_obligation( + outlives, + span, + &mut known_type_outlives_obligations, + &mut constraints, + ); }; } @@ -242,8 +251,8 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // - Normalize the type. This will create some region // constraints, which we buffer up because we are // not ready to process them yet. - // - Then compute the implied bounds, updating the - // known outlives types and free region regions. + // - Then compute the implied bounds. This will adjust + // the `region_bound_pairs` and so forth. // - After this is done, we'll process the constraints, once // the `relations` is built. let mut normalized_inputs_and_output = @@ -314,9 +323,10 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { constraint_conversion::ConstraintConversion::new( self.infcx, &self.universal_regions, + &self.region_bound_pairs, self.implicit_region_bound, param_env, - &self.known_type_outlives, + &known_type_outlives_obligations, Locations::All(span), span, ConstraintCategory::Internal, @@ -331,15 +341,17 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { outlives: self.outlives.freeze(), inverse_outlives: self.inverse_outlives.freeze(), }), - known_type_outlives: self.known_type_outlives, + known_type_outlives_obligations, + region_bound_pairs: self.region_bound_pairs, normalized_inputs_and_output, } } fn normalize_and_push_type_outlives_obligation( - &mut self, + &self, mut outlives: ty::PolyTypeOutlivesPredicate<'tcx>, span: Span, + known_type_outlives_obligations: &mut Vec>, constraints: &mut Vec<&QueryRegionConstraints<'tcx>>, ) { // In the new solver, normalize the type-outlives obligation assumptions. @@ -370,7 +382,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } } - self.known_type_outlives.push(outlives); + known_type_outlives_obligations.push(outlives); } /// Update the type of a single local, which should represent @@ -416,17 +428,13 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } OutlivesBound::RegionSubParam(r_a, param_b) => { - self.known_type_outlives.push(ty::Binder::dummy(ty::OutlivesPredicate( - Ty::new_param(self.infcx.tcx, param_b.index, param_b.name), - r_a, - ))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); } OutlivesBound::RegionSubAlias(r_a, alias_b) => { - self.known_type_outlives.push(ty::Binder::dummy(ty::OutlivesPredicate( - Ty::new_alias(self.infcx.tcx, alias_b.kind(self.infcx.tcx), alias_b), - r_a, - ))); + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a)); } } } diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 07558e347aeb5..e0196d55f20a2 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::lang_items::LangItem; use rustc_index::{IndexSlice, IndexVec}; use rustc_infer::infer::canonical::QueryRegionConstraints; +use rustc_infer::infer::outlives::env::RegionBoundPairs; use rustc_infer::infer::region_constraints::RegionConstraintData; use rustc_infer::infer::{ BoundRegion, BoundRegionConversionTime, InferCtxt, NllRegionVariableOrigin, @@ -128,8 +129,9 @@ pub(crate) fn type_check<'a, 'tcx>( let CreateResult { universal_region_relations, + region_bound_pairs, normalized_inputs_and_output, - known_type_outlives, + known_type_outlives_obligations, } = free_region_relations::create( infcx, infcx.param_env, @@ -157,7 +159,8 @@ pub(crate) fn type_check<'a, 'tcx>( last_span: body.span, body, user_type_annotations: &body.user_type_annotations, - known_type_outlives, + region_bound_pairs, + known_type_outlives_obligations, implicit_region_bound, reported_errors: Default::default(), universal_regions: &universal_region_relations.universal_regions, @@ -552,7 +555,8 @@ struct TypeChecker<'a, 'tcx> { /// User type annotations are shared between the main MIR and the MIR of /// all of the promoted items. user_type_annotations: &'a CanonicalUserTypeAnnotations<'tcx>, - known_type_outlives: Vec>, + region_bound_pairs: RegionBoundPairs<'tcx>, + known_type_outlives_obligations: Vec>, implicit_region_bound: ty::Region<'tcx>, reported_errors: FxIndexSet<(Ty<'tcx>, Span)>, universal_regions: &'a UniversalRegions<'tcx>, @@ -744,9 +748,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { constraint_conversion::ConstraintConversion::new( self.infcx, self.universal_regions, + &self.region_bound_pairs, self.implicit_region_bound, self.infcx.param_env, - &self.known_type_outlives, + &self.known_type_outlives_obligations, locations, locations.span(self.body), category, @@ -2526,9 +2531,10 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { constraint_conversion::ConstraintConversion::new( self.infcx, self.universal_regions, + &self.region_bound_pairs, self.implicit_region_bound, self.infcx.param_env, - &self.known_type_outlives, + &self.known_type_outlives_obligations, locations, self.body.span, // irrelevant; will be overridden. ConstraintCategory::Boring, // same as above. diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs index 921742cbfc321..e37fb6142c348 100644 --- a/compiler/rustc_infer/src/infer/outlives/env.rs +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -1,13 +1,16 @@ +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::transitive_relation::TransitiveRelationBuilder; use rustc_middle::{bug, ty}; +use crate::infer::GenericKind; use crate::infer::free_regions::FreeRegionMap; /// The `OutlivesEnvironment` collects information about what outlives /// what in a given type-checking setting. For example, if we have a /// where-clause like `where T: 'a` in scope, then the -/// `OutlivesEnvironment` would record that. Similarly, it contains -/// methods for processing and adding implied bounds into the outlives +/// `OutlivesEnvironment` would record that (in its +/// `region_bound_pairs` field). Similarly, it contains methods for +/// processing and adding implied bounds into the outlives /// environment. /// /// Other code at present does not typically take a @@ -28,6 +31,11 @@ pub struct OutlivesEnvironment<'tcx> { known_type_outlives: Vec>, } +/// "Region-bound pairs" tracks outlives relations that are known to +/// be true, either because of explicit where-clauses like `T: 'a` or +/// because of implied bounds. +pub type RegionBoundPairs<'tcx> = FxIndexSet>>; + impl<'tcx> OutlivesEnvironment<'tcx> { /// Create a new `OutlivesEnvironment` from normalized outlives bounds. pub fn from_normalized_bounds( @@ -85,7 +93,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> { &self.free_region_map } - /// Borrows current `known_type_outlives`. + /// Borrows current `region_bound_pairs`. pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] { &self.known_type_outlives } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index 08ee6f8855102..a87607ffcf93c 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -72,6 +72,7 @@ use smallvec::smallvec; use tracing::{debug, instrument}; use super::env::OutlivesEnvironment; +use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::outlives::verify::VerifyBoundCx; use crate::infer::resolve::OpportunisticRegionResolver; use crate::infer::snapshot::undo_log::UndoLog; @@ -168,9 +169,12 @@ impl<'tcx> InferCtxt<'tcx> { debug!(?sup_type, ?sub_region, ?origin); + let region_bound_pairs = &Default::default(); let outlives = &mut TypeOutlives::new( self, self.tcx, + // TODO: + region_bound_pairs, None, outlives_env.known_type_outlives(), ); @@ -225,13 +229,19 @@ where pub fn new( delegate: D, tcx: TyCtxt<'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, implicit_region_bound: Option>, - known_type_outlives: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], + caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], ) -> Self { Self { delegate, tcx, - verify_bound: VerifyBoundCx::new(tcx, implicit_region_bound, known_type_outlives), + verify_bound: VerifyBoundCx::new( + tcx, + region_bound_pairs, + implicit_region_bound, + caller_bounds, + ), } } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 1f3841061f545..7a21c2883d1ac 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -5,8 +5,9 @@ use rustc_type_ir::outlives::{Component, compute_alias_components_recursive}; use smallvec::smallvec; use tracing::{debug, instrument, trace}; -use crate::infer::VerifyBound; +use crate::infer::outlives::env::RegionBoundPairs; use crate::infer::region_constraints::VerifyIfEq; +use crate::infer::{GenericKind, VerifyBound}; /// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` /// obligation into a series of `'a: 'b` constraints and "verifys", as @@ -16,22 +17,24 @@ use crate::infer::region_constraints::VerifyIfEq; /// use something else. pub(crate) struct VerifyBoundCx<'cx, 'tcx> { tcx: TyCtxt<'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, /// During borrowck, if there are no outlives bounds on a generic /// parameter `T`, we assume that `T: 'in_fn_body` holds. /// /// Outside of borrowck the only way to prove `T: '?0` is by /// setting `'?0` to `'empty`. implicit_region_bound: Option>, - known_type_outlives: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], + caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], } impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { pub(crate) fn new( tcx: TyCtxt<'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, implicit_region_bound: Option>, - known_type_outlives: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], + caller_bounds: &'cx [ty::PolyTypeOutlivesPredicate<'tcx>], ) -> Self { - Self { tcx, implicit_region_bound, known_type_outlives } + Self { tcx, region_bound_pairs, implicit_region_bound, caller_bounds } } #[instrument(level = "debug", skip(self))] @@ -201,17 +204,48 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { &self, erased_ty: Ty<'tcx>, ) -> Vec> { - self.known_type_outlives - .iter() - .copied() - .filter(move |outlives_predicate| { - super::test_type_match::can_match_erased_ty( - self.tcx, - *outlives_predicate, - erased_ty, - ) - }) - .collect() + let tcx = self.tcx; + let mut bounds = vec![]; + + // To start, collect bounds from user environment. Note that + // parameter environments are already elaborated, so we don't + // have to worry about that. + bounds.extend(self.caller_bounds.iter().copied().filter(move |outlives_predicate| { + super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty) + })); + + // Next, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must outlive `'a`, but we don't know that + // this holds from first principles. + bounds.extend(self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { + debug!( + "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", + (r, p) + ); + // Fast path for the common case. + match (&p, erased_ty.kind()) { + // In outlive routines, all types are expected to be fully normalized. + // And therefore we can safely use structural equality for alias types. + (GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {} + (GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {} + (GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.def_id == a2.def_id => {} + _ => return None, + } + + let p_ty = p.to_ty(tcx); + let erased_p_ty = self.tcx.erase_regions(p_ty); + (erased_p_ty == erased_ty).then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r))) + })); + + bounds } /// Given a projection like `>::Bar`, returns any bounds