From 7d1fda7b40b580e5d8c7de1e3bfef7e35e997cee Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 30 Jan 2024 21:50:05 +0000 Subject: [PATCH] Normalize type outlives obligations in NLL --- .../src/type_check/constraint_conversion.rs | 101 ++++++++++++++---- .../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, 158 insertions(+), 52 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 52559f9039b..71035eea5d1 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::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 @@ pub(crate) fn new( 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 @@ pub(crate) fn new( universal_regions, region_bound_pairs, implicit_region_bound, + param_env, known_type_outlives_obligations, locations, span, @@ -137,36 +145,83 @@ fn convert( // 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(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); + } + } + + 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(_) => {} + } + } + + // 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!(), } - - 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); - - 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 d518f54fd25..4d53a53ee19 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::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 @@ pub(crate) fn known_outlives(&self) -> impl Iterator { 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 @@ pub(crate) fn create(mut self) -> CreateResult<'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 @@ pub(crate) fn create(mut self) -> CreateResult<'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 @@ pub(crate) fn create(mut self) -> CreateResult<'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 @@ pub(crate) fn create(mut self) -> CreateResult<'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 59c4d9a6c78..c65173baea5 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 @@ fn push_region_constraints( 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 @@ fn prove_closure_bounds( 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 00000000000..7477c56cd54 --- /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 00000000000..f50eb6326e2 --- /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() {}