Skip to content

Commit 7fda862

Browse files
committed
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.
1 parent 7425fb2 commit 7fda862

File tree

5 files changed

+85
-6
lines changed

5 files changed

+85
-6
lines changed

compiler/rustc_borrowck/src/region_infer/mod.rs

+33-5
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
916916
/// The idea then is to lower the `T: 'X` constraint into multiple
917917
/// bounds -- e.g., if `'X` is the union of two free lifetimes,
918918
/// `'1` and `'2`, then we would create `T: '1` and `T: '2`.
919+
#[instrument(level = "debug", skip(self, infcx, propagated_outlives_requirements))]
919920
fn try_promote_type_test(
920921
&self,
921922
infcx: &InferCtxt<'_, 'tcx>,
@@ -933,11 +934,41 @@ impl<'tcx> RegionInferenceContext<'tcx> {
933934
return false;
934935
};
935936

937+
debug!("subject = {:?}", subject);
938+
939+
let r_scc = self.constraint_sccs.scc(*lower_bound);
940+
941+
debug!(
942+
"lower_bound = {:?} r_scc={:?} universe={:?}",
943+
lower_bound, r_scc, self.scc_universes[r_scc]
944+
);
945+
946+
// If the type test requires that `T: 'a` where `'a` is a
947+
// placeholder from another universe, that effectively requires
948+
// `T: 'static`, so we have to propagate that requirement.
949+
//
950+
// It doesn't matter *what* universe because the promoted `T` will
951+
// always be in the root universe.
952+
if let Some(p) = self.scc_values.placeholders_contained_in(r_scc).next() {
953+
debug!("encountered placeholder in higher universe: {:?}, requiring 'static", p);
954+
let static_r = self.universal_regions.fr_static;
955+
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
956+
subject,
957+
outlived_free_region: static_r,
958+
blame_span: locations.span(body),
959+
category: ConstraintCategory::Boring,
960+
});
961+
962+
// we can return here -- the code below might push add'l constraints
963+
// but they would all be weaker than this one.
964+
return true;
965+
}
966+
936967
// For each region outlived by lower_bound find a non-local,
937968
// universal region (it may be the same region) and add it to
938969
// `ClosureOutlivesRequirement`.
939-
let r_scc = self.constraint_sccs.scc(*lower_bound);
940970
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
971+
debug!("universal_region_outlived_by ur={:?}", ur);
941972
// Check whether we can already prove that the "subject" outlives `ur`.
942973
// If so, we don't have to propagate this requirement to our caller.
943974
//
@@ -962,8 +993,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
962993
continue;
963994
}
964995

965-
debug!("try_promote_type_test: ur={:?}", ur);
966-
967996
let non_local_ub = self.universal_region_relations.non_local_upper_bounds(ur);
968997
debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub);
969998

@@ -1000,15 +1029,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
10001029
/// will use it's *external name*, which will be a `RegionKind`
10011030
/// variant that can be used in query responses such as
10021031
/// `ReEarlyBound`.
1032+
#[instrument(level = "debug", skip(self, infcx))]
10031033
fn try_promote_type_test_subject(
10041034
&self,
10051035
infcx: &InferCtxt<'_, 'tcx>,
10061036
ty: Ty<'tcx>,
10071037
) -> Option<ClosureOutlivesSubject<'tcx>> {
10081038
let tcx = infcx.tcx;
10091039

1010-
debug!("try_promote_type_test_subject(ty = {:?})", ty);
1011-
10121040
let ty = tcx.fold_regions(ty, |r, _depth| {
10131041
let region_vid = self.to_region_vid(r);
10141042

src/test/ui/generic-associated-types/issue-91139.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ fn foo<T>() {
2222
//~| ERROR `T` does not live long enough
2323
//~| ERROR `T` does not live long enough
2424
//~| ERROR `T` does not live long enough
25+
//~| ERROR `T` may not live long enough
2526
//
2627
// FIXME: This error is bogus, but it arises because we try to validate
2728
// that `<() as Foo<T>>::Type<'a>` is valid, which requires proving

src/test/ui/generic-associated-types/issue-91139.stderr

+13-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,17 @@ error: `T` does not live long enough
3434
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
3535
| ^^^^^^^^^
3636

37+
error[E0310]: the parameter type `T` may not live long enough
38+
--> $DIR/issue-91139.rs:16:58
39+
|
40+
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
41+
| ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
42+
|
43+
help: consider adding an explicit lifetime bound...
44+
|
45+
LL | fn foo<T: 'static>() {
46+
| +++++++++
47+
3748
error: `T` does not live long enough
3849
--> $DIR/issue-91139.rs:16:58
3950
|
@@ -46,5 +57,6 @@ error: `T` does not live long enough
4657
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
4758
| ^^^^^^^^^
4859

49-
error: aborting due to 8 previous errors
60+
error: aborting due to 9 previous errors
5061

62+
For more information about this error, try `rustc --explain E0310`.

src/test/ui/nll/issue-98693.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test for #98693.
2+
//
3+
// The closure encounters an obligation that `T` must outlive `!U1`,
4+
// a placeholder from universe U1. We were ignoring this placeholder
5+
// when promoting the constraint to the enclosing function, and
6+
// thus incorrectly judging the closure to be safe.
7+
8+
fn assert_static<T>()
9+
where
10+
for<'a> T: 'a,
11+
{
12+
}
13+
14+
fn test<T>() {
15+
|| {
16+
//~^ ERROR the parameter type `T` may not live long enough
17+
assert_static::<T>();
18+
};
19+
}
20+
21+
fn main() {}

src/test/ui/nll/issue-98693.stderr

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0310]: the parameter type `T` may not live long enough
2+
--> $DIR/issue-98693.rs:15:5
3+
|
4+
LL | / || {
5+
LL | |
6+
LL | | assert_static::<T>();
7+
LL | | };
8+
| |_____^ ...so that the type `T` will meet its required lifetime bounds
9+
|
10+
help: consider adding an explicit lifetime bound...
11+
|
12+
LL | fn test<T: 'static>() {
13+
| +++++++++
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0310`.

0 commit comments

Comments
 (0)