From 7fda86249fd37e5ff148427771423bfefc0b1e4a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 30 Jun 2022 10:26:17 -0400 Subject: [PATCH] promote placeholder bounds to 'static obligations In NLL, when we are promoting a bound out from a closure, if we have a requirement that `T: 'a` where `'a` is in a higher universe, we were previously ignoring that, which is totally wrong. We should be promoting those constraints to `'static`, since universes are not expressible across closure boundaries. --- .../rustc_borrowck/src/region_infer/mod.rs | 38 ++++++++++++++++--- .../generic-associated-types/issue-91139.rs | 1 + .../issue-91139.stderr | 14 ++++++- src/test/ui/nll/issue-98693.rs | 21 ++++++++++ src/test/ui/nll/issue-98693.stderr | 17 +++++++++ 5 files changed, 85 insertions(+), 6 deletions(-) create mode 100644 src/test/ui/nll/issue-98693.rs create mode 100644 src/test/ui/nll/issue-98693.stderr diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index f5c9392948b..aa0af86fbf5 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -916,6 +916,7 @@ fn check_type_tests( /// The idea then is to lower the `T: 'X` constraint into multiple /// bounds -- e.g., if `'X` is the union of two free lifetimes, /// `'1` and `'2`, then we would create `T: '1` and `T: '2`. + #[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements))] fn try_promote_type_test( &self, infcx: &InferCtxt<'_, 'tcx>, @@ -933,11 +934,41 @@ fn try_promote_type_test( return false; }; + debug!("subject = {:?}", subject); + + let r_scc = self.constraint_sccs.scc(*lower_bound); + + debug!( + "lower_bound = {:?} r_scc={:?} universe={:?}", + lower_bound, r_scc, self.scc_universes[r_scc] + ); + + // If the type test requires that `T: 'a` where `'a` is a + // placeholder from another universe, that effectively requires + // `T: 'static`, so we have to propagate that requirement. + // + // It doesn't matter *what* universe because the promoted `T` will + // always be in the root universe. + if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() { + debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p); + let static_r = self.universal_regions.fr_static; + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject, + outlived_free_region: static_r, + blame_span: locations.span(body), + category: ConstraintCategory::Boring, + }); + + // we can return here -- the code below might push add'l constraints + // but they would all be weaker than this one. + return true; + } + // For each region outlived by lower_bound find a non-local, // universal region (it may be the same region) and add it to // `ClosureOutlivesRequirement`. - let r_scc = self.constraint_sccs.scc(*lower_bound); for ur in self.scc_values.universal_regions_outlived_by(r_scc) { + debug!("universal_region_outlived_by ur={:?}", ur); // Check whether we can already prove that the "subject" outlives `ur`. // If so, we don't have to propagate this requirement to our caller. // @@ -962,8 +993,6 @@ fn try_promote_type_test( continue; } - debug!("try_promote_type_test: ur={:?}", ur); - let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur); debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub); @@ -1000,6 +1029,7 @@ fn try_promote_type_test( /// will use it's *external name*, which will be a `RegionKind` /// variant that can be used in query responses such as /// `ReEarlyBound`. + #[instrument(level = "debug", skip(self, infcx))] fn try_promote_type_test_subject( &self, infcx: &InferCtxt<'_, 'tcx>, @@ -1007,8 +1037,6 @@ fn try_promote_type_test_subject( ) -> Option> { let tcx = infcx.tcx; - debug!("try_promote_type_test_subject(ty = {:?})", ty); - let ty = tcx.fold_regions(ty, |r, _depth| { let region_vid = self.to_region_vid(r); diff --git a/src/test/ui/generic-associated-types/issue-91139.rs b/src/test/ui/generic-associated-types/issue-91139.rs index 092fa939c30..40eef11f058 100644 --- a/src/test/ui/generic-associated-types/issue-91139.rs +++ b/src/test/ui/generic-associated-types/issue-91139.rs @@ -22,6 +22,7 @@ fn foo() { //~| ERROR `T` does not live long enough //~| ERROR `T` does not live long enough //~| ERROR `T` does not live long enough + //~| ERROR `T` may not live long enough // // FIXME: This error is bogus, but it arises because we try to validate // that `<() as Foo>::Type<'a>` is valid, which requires proving diff --git a/src/test/ui/generic-associated-types/issue-91139.stderr b/src/test/ui/generic-associated-types/issue-91139.stderr index 6c5092978c8..b789b3a42f3 100644 --- a/src/test/ui/generic-associated-types/issue-91139.stderr +++ b/src/test/ui/generic-associated-types/issue-91139.stderr @@ -34,6 +34,17 @@ error: `T` does not live long enough LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); | ^^^^^^^^^ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/issue-91139.rs:16:58 + | +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn foo() { + | +++++++++ + error: `T` does not live long enough --> $DIR/issue-91139.rs:16:58 | @@ -46,5 +57,6 @@ error: `T` does not live long enough LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); | ^^^^^^^^^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors +For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/nll/issue-98693.rs b/src/test/ui/nll/issue-98693.rs new file mode 100644 index 00000000000..18e6ec63046 --- /dev/null +++ b/src/test/ui/nll/issue-98693.rs @@ -0,0 +1,21 @@ +// Regression test for #98693. +// +// The closure encounters an obligation that `T` must outlive `!U1`, +// a placeholder from universe U1. We were ignoring this placeholder +// when promoting the constraint to the enclosing function, and +// thus incorrectly judging the closure to be safe. + +fn assert_static() +where + for<'a> T: 'a, +{ +} + +fn test() { + || { + //~^ ERROR the parameter type `T` may not live long enough + assert_static::(); + }; +} + +fn main() {} diff --git a/src/test/ui/nll/issue-98693.stderr b/src/test/ui/nll/issue-98693.stderr new file mode 100644 index 00000000000..31689620c64 --- /dev/null +++ b/src/test/ui/nll/issue-98693.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/issue-98693.rs:15:5 + | +LL | / || { +LL | | +LL | | assert_static::(); +LL | | }; + | |_____^ ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound... + | +LL | fn test() { + | +++++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`.