From 65db28cd446253f5a17386649901553a749e4c51 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jan 2024 21:48:54 +0000 Subject: [PATCH 1/3] Use deeply_normalize_with_skipped_universes in when processing type outlives --- compiler/rustc_trait_selection/src/regions.rs | 3 ++- .../traits/next-solver/normalize-region-obligations.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/regions.rs b/compiler/rustc_trait_selection/src/regions.rs index e8929f114e165..e5a7b27446b4a 100644 --- a/compiler/rustc_trait_selection/src/regions.rs +++ b/compiler/rustc_trait_selection/src/regions.rs @@ -24,12 +24,13 @@ impl<'tcx> InferCtxtRegionExt<'tcx> for InferCtxt<'tcx> { let ty = self.resolve_vars_if_possible(ty); if self.next_trait_solver() { - crate::solve::deeply_normalize( + crate::solve::deeply_normalize_with_skipped_universes( self.at( &ObligationCause::dummy_with_span(origin.span()), outlives_env.param_env, ), ty, + vec![None; ty.outer_exclusive_binder().as_usize()], ) .map_err(|_| ty) } else { diff --git a/tests/ui/traits/next-solver/normalize-region-obligations.rs b/tests/ui/traits/next-solver/normalize-region-obligations.rs index 13c86b630f60e..d189e4893a320 100644 --- a/tests/ui/traits/next-solver/normalize-region-obligations.rs +++ b/tests/ui/traits/next-solver/normalize-region-obligations.rs @@ -1,4 +1,4 @@ -// revisions: normalize_param_env normalize_obligation +// revisions: normalize_param_env normalize_obligation hrtb // check-pass // compile-flags: -Znext-solver @@ -7,16 +7,23 @@ trait Foo { type Gat<'a> where ::Assoc: 'a; #[cfg(normalize_obligation)] type Gat<'a> where Self: 'a; + #[cfg(hrtb)] + type Gat<'b> where for<'a> >::Assoc: 'b; } trait Mirror { type Assoc: ?Sized; } impl Mirror for T { type Assoc = T; } +trait MirrorRegion<'a> { type Assoc: ?Sized; } +impl<'a, T> MirrorRegion<'a> for T { type Assoc = T; } + impl Foo for T { #[cfg(normalize_param_env)] type Gat<'a> = i32 where T: 'a; #[cfg(normalize_obligation)] type Gat<'a> = i32 where ::Assoc: 'a; + #[cfg(hrtb)] + type Gat<'b> = i32 where Self: 'b; } fn main() {} From 0eaa536bfa7e19e15ea7eec9280d9e369ed2a40a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jan 2024 21:49:55 +0000 Subject: [PATCH 2/3] Do process_registered_region_obligations in a loop --- .../src/infer/outlives/obligations.rs | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index b10bf98e8b50a..cf68a60e280d1 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -153,22 +153,28 @@ impl<'tcx> InferCtxt<'tcx> { .try_collect() .map_err(|e| (e, SubregionOrigin::AscribeUserTypeProvePredicate(DUMMY_SP)))?; - let my_region_obligations = self.take_registered_region_obligations(); - - for RegionObligation { sup_type, sub_region, origin } in my_region_obligations { - let sup_type = - deeply_normalize_ty(sup_type, origin.clone()).map_err(|e| (e, origin.clone()))?; - debug!(?sup_type, ?sub_region, ?origin); - - let outlives = &mut TypeOutlives::new( - self, - self.tcx, - outlives_env.region_bound_pairs(), - None, - &normalized_caller_bounds, - ); - let category = origin.to_constraint_category(); - outlives.type_must_outlive(origin, sup_type, sub_region, category); + // Must loop since the process of normalizing may itself register region obligations. + loop { + let my_region_obligations = self.take_registered_region_obligations(); + if my_region_obligations.is_empty() { + break; + } + + for RegionObligation { sup_type, sub_region, origin } in my_region_obligations { + let sup_type = deeply_normalize_ty(sup_type, origin.clone()) + .map_err(|e| (e, origin.clone()))?; + debug!(?sup_type, ?sub_region, ?origin); + + let outlives = &mut TypeOutlives::new( + self, + self.tcx, + outlives_env.region_bound_pairs(), + None, + &normalized_caller_bounds, + ); + let category = origin.to_constraint_category(); + outlives.type_must_outlive(origin, sup_type, sub_region, category); + } } Ok(()) From 870f77c040230ca768f6ab53848d3cbed400cf6c Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jan 2024 21:50:05 +0000 Subject: [PATCH 3/3] Normalize type outlives obligations in NLL --- .../src/type_check/constraint_conversion.rs | 97 +++++++++++++++---- .../src/type_check/free_region_relations.rs | 72 +++++++++----- compiler/rustc_borrowck/src/type_check/mod.rs | 6 +- .../normalize-type-outlives-in-param-env.rs | 18 ++++ .../next-solver/normalize-type-outlives.rs | 13 +++ 5 files changed, 156 insertions(+), 50 deletions(-) create mode 100644 tests/ui/traits/next-solver/normalize-type-outlives-in-param-env.rs create mode 100644 tests/ui/traits/next-solver/normalize-type-outlives.rs diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs index 52559f9039b65..71035eea5d175 100644 --- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs +++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs @@ -5,10 +5,15 @@ use rustc_infer::infer::outlives::obligations::{TypeOutlives, TypeOutlivesDelega use rustc_infer::infer::region_constraints::{GenericKind, VerifyBound}; use rustc_infer::infer::{self, InferCtxt, SubregionOrigin}; use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, ConstraintCategory}; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{TypeFoldable, TypeVisitableExt}; use rustc_span::{Span, DUMMY_SP}; +use rustc_trait_selection::solve::deeply_normalize; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; +use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp; +use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput}; use crate::{ constraints::OutlivesConstraint, @@ -33,6 +38,7 @@ pub(crate) struct ConstraintConversion<'a, 'tcx> { /// 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_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, @@ -47,6 +53,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, '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_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>], locations: Locations, span: Span, @@ -59,6 +66,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { universal_regions, region_bound_pairs, implicit_region_bound, + param_env, known_type_outlives_obligations, locations, span, @@ -137,36 +145,83 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> { // Extract out various useful fields we'll need below. let ConstraintConversion { tcx, + infcx, + param_env, region_bound_pairs, implicit_region_bound, known_type_outlives_obligations, .. } = *self; - let ty::OutlivesPredicate(k1, r2) = predicate; - match k1.unpack() { - GenericArgKind::Lifetime(r1) => { - let r1_vid = self.to_region_vid(r1); - let r2_vid = self.to_region_vid(r2); - self.add_outlives(r1_vid, r2_vid, constraint_category); - } + let mut outlives_predicates = vec![(predicate, constraint_category)]; + while let Some((ty::OutlivesPredicate(k1, r2), constraint_category)) = + outlives_predicates.pop() + { + match k1.unpack() { + GenericArgKind::Lifetime(r1) => { + let r1_vid = self.to_region_vid(r1); + let r2_vid = self.to_region_vid(r2); + self.add_outlives(r1_vid, r2_vid, constraint_category); + } - GenericArgKind::Type(t1) => { - // we don't actually use this for anything, but - // the `TypeOutlives` code needs an origin. - let origin = infer::RelateParamBound(DUMMY_SP, t1, None); + GenericArgKind::Type(mut t1) => { + // Normalize the type we receive from a `TypeOutlives` obligation + // in the new trait solver. + if infcx.next_trait_solver() { + let result = CustomTypeOp::new( + |ocx| { + match deeply_normalize( + ocx.infcx.at( + &ObligationCause::dummy_with_span(self.span), + param_env, + ), + t1, + ) { + Ok(normalized_ty) => { + t1 = normalized_ty; + } + Err(e) => { + infcx.err_ctxt().report_fulfillment_errors(e); + } + } - TypeOutlives::new( - &mut *self, - tcx, - region_bound_pairs, - Some(implicit_region_bound), - known_type_outlives_obligations, - ) - .type_must_outlive(origin, t1, r2, constraint_category); - } + Ok(()) + }, + "normalize type outlives obligation", + ) + .fully_perform(infcx, self.span); + + match result { + Ok(TypeOpOutput { output: (), constraints, .. }) => { + if let Some(constraints) = constraints { + assert!( + constraints.member_constraints.is_empty(), + "FIXME(-Znext-solver): How do I handle these?" + ); + outlives_predicates + .extend(constraints.outlives.iter().copied()); + } + } + Err(_) => {} + } + } - GenericArgKind::Const(_) => unreachable!(), + // we don't actually use this for anything, but + // the `TypeOutlives` code needs an origin. + let origin = infer::RelateParamBound(DUMMY_SP, t1, None); + + TypeOutlives::new( + &mut *self, + tcx, + region_bound_pairs, + Some(implicit_region_bound), + known_type_outlives_obligations, + ) + .type_must_outlive(origin, t1, r2, constraint_category); + } + + GenericArgKind::Const(_) => unreachable!(), + } } } 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 d518f54fd2533..4d53a53ee197b 100644 --- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs +++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs @@ -8,8 +8,11 @@ use rustc_infer::infer::region_constraints::GenericKind; use rustc_infer::infer::InferCtxt; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::query::OutlivesBound; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt}; -use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP}; +use rustc_span::{ErrorGuaranteed, DUMMY_SP}; +use rustc_trait_selection::solve::deeply_normalize_with_skipped_universes; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use rustc_trait_selection::traits::query::type_op::{self, TypeOp}; use std::rc::Rc; use type_op::TypeOpOutput; @@ -52,7 +55,6 @@ pub(crate) struct CreateResult<'tcx> { pub(crate) fn create<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>], implicit_region_bound: ty::Region<'tcx>, universal_regions: &Rc>, constraints: &mut MirTypeckRegionConstraints<'tcx>, @@ -60,7 +62,6 @@ pub(crate) fn create<'tcx>( UniversalRegionRelationsBuilder { infcx, param_env, - known_type_outlives_obligations, implicit_region_bound, constraints, universal_regions: universal_regions.clone(), @@ -178,7 +179,6 @@ impl UniversalRegionRelations<'_> { struct UniversalRegionRelationsBuilder<'this, 'tcx> { infcx: &'this InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - known_type_outlives_obligations: &'tcx [ty::PolyTypeOutlivesPredicate<'tcx>], universal_regions: Rc>, implicit_region_bound: ty::Region<'tcx>, constraints: &'this mut MirTypeckRegionConstraints<'tcx>, @@ -222,6 +222,35 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { self.relate_universal_regions(fr, fr_fn_body); } + // 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() { + let Some(outlives) = bound.as_type_outlives_clause() else { continue }; + let ty::OutlivesPredicate(mut ty, region) = outlives.skip_binder(); + + // In the new solver, normalize the type-outlives obligation assumptions. + if self.infcx.next_trait_solver() { + match deeply_normalize_with_skipped_universes( + self.infcx.at(&ObligationCause::misc(span, defining_ty_def_id), self.param_env), + ty, + vec![None; ty.outer_exclusive_binder().as_usize()], + ) { + Ok(normalized_ty) => { + ty = normalized_ty; + } + Err(e) => { + self.infcx.err_ctxt().report_fulfillment_errors(e); + } + } + } + + known_type_outlives_obligations + .push(outlives.rebind(ty::OutlivesPredicate(ty, region))); + } + let known_type_outlives_obligations = + self.infcx.tcx.arena.alloc_slice(&known_type_outlives_obligations); + let unnormalized_input_output_tys = self .universal_regions .unnormalized_input_tys @@ -239,7 +268,6 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { // the `relations` is built. let mut normalized_inputs_and_output = Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1); - let mut constraints = vec![]; for ty in unnormalized_input_output_tys { debug!("build: input_or_output={:?}", ty); // We add implied bounds from both the unnormalized and normalized ty. @@ -304,7 +332,19 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { } for c in constraints { - self.push_region_constraints(c, span); + constraint_conversion::ConstraintConversion::new( + self.infcx, + &self.universal_regions, + &self.region_bound_pairs, + self.implicit_region_bound, + param_env, + known_type_outlives_obligations, + Locations::All(span), + span, + ConstraintCategory::Internal, + self.constraints, + ) + .convert_all(c); } CreateResult { @@ -313,30 +353,12 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> { outlives: self.outlives.freeze(), inverse_outlives: self.inverse_outlives.freeze(), }), - known_type_outlives_obligations: self.known_type_outlives_obligations, + known_type_outlives_obligations, region_bound_pairs: self.region_bound_pairs, normalized_inputs_and_output, } } - #[instrument(skip(self, data), level = "debug")] - fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) { - debug!("constraints generated: {:#?}", data); - - constraint_conversion::ConstraintConversion::new( - self.infcx, - &self.universal_regions, - &self.region_bound_pairs, - self.implicit_region_bound, - self.known_type_outlives_obligations, - Locations::All(span), - span, - ConstraintCategory::Internal, - self.constraints, - ) - .convert_all(data); - } - /// Update the type of a single local, which should represent /// either the return type of the MIR or one of its arguments. At /// the same time, compute and add any implied bounds that come diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 59c4d9a6c78ca..c65173baea57f 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -156,10 +156,6 @@ pub(crate) fn type_check<'mir, 'tcx>( } = free_region_relations::create( infcx, param_env, - // FIXME(-Znext-solver): These are unnormalized. Normalize them. - infcx.tcx.arena.alloc_from_iter( - param_env.caller_bounds().iter().filter_map(|clause| clause.as_type_outlives_clause()), - ), implicit_region_bound, universal_regions, &mut constraints, @@ -1136,6 +1132,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.borrowck_context.universal_regions, self.region_bound_pairs, self.implicit_region_bound, + self.param_env, self.known_type_outlives_obligations, locations, locations.span(self.body), @@ -2740,6 +2737,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.borrowck_context.universal_regions, self.region_bound_pairs, self.implicit_region_bound, + self.param_env, self.known_type_outlives_obligations, locations, DUMMY_SP, // irrelevant; will be overridden. diff --git a/tests/ui/traits/next-solver/normalize-type-outlives-in-param-env.rs b/tests/ui/traits/next-solver/normalize-type-outlives-in-param-env.rs new file mode 100644 index 0000000000000..7477c56cd5487 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize-type-outlives-in-param-env.rs @@ -0,0 +1,18 @@ +// check-pass +// compile-flags: -Znext-solver + +trait Mirror { + type Assoc; +} + +impl Mirror for T { + type Assoc = T; +} + +fn is_static() {} + +fn test() where ::Assoc: 'static { + is_static::(); +} + +fn main() {} diff --git a/tests/ui/traits/next-solver/normalize-type-outlives.rs b/tests/ui/traits/next-solver/normalize-type-outlives.rs new file mode 100644 index 0000000000000..f50eb6326e239 --- /dev/null +++ b/tests/ui/traits/next-solver/normalize-type-outlives.rs @@ -0,0 +1,13 @@ +// check-pass + +trait Tr<'a> { + type Assoc; +} + +fn outlives<'o, T: 'o>() {} + +fn foo<'a, 'b, T: Tr<'a, Assoc = ()>>() { + outlives::<'b, T::Assoc>(); +} + +fn main() {}