Rollup merge of #98713 - nikomatsakis:issue-98693, r=jackh726
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. Fixes #98693 ~~(Marking as WIP because I'm still running tests, haven't add the new test, etc)~~ r? ``@jackh726``
This commit is contained in:
commit
2cb7d1c933
5 changed files with 85 additions and 6 deletions
|
@ -917,6 +917,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
/// The idea then is to lower the `T: 'X` constraint into multiple
|
/// The idea then is to lower the `T: 'X` constraint into multiple
|
||||||
/// bounds -- e.g., if `'X` is the union of two free lifetimes,
|
/// bounds -- e.g., if `'X` is the union of two free lifetimes,
|
||||||
/// `'1` and `'2`, then we would create `T: '1` and `T: '2`.
|
/// `'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(
|
fn try_promote_type_test(
|
||||||
&self,
|
&self,
|
||||||
infcx: &InferCtxt<'_, 'tcx>,
|
infcx: &InferCtxt<'_, 'tcx>,
|
||||||
|
@ -934,11 +935,41 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
return false;
|
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,
|
// For each region outlived by lower_bound find a non-local,
|
||||||
// universal region (it may be the same region) and add it to
|
// universal region (it may be the same region) and add it to
|
||||||
// `ClosureOutlivesRequirement`.
|
// `ClosureOutlivesRequirement`.
|
||||||
let r_scc = self.constraint_sccs.scc(*lower_bound);
|
|
||||||
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
|
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`.
|
// Check whether we can already prove that the "subject" outlives `ur`.
|
||||||
// If so, we don't have to propagate this requirement to our caller.
|
// If so, we don't have to propagate this requirement to our caller.
|
||||||
//
|
//
|
||||||
|
@ -963,8 +994,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("try_promote_type_test: ur={:?}", ur);
|
|
||||||
|
|
||||||
let non_local_ub = self.universal_region_relations.non_local_upper_bounds(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);
|
debug!("try_promote_type_test: non_local_ub={:?}", non_local_ub);
|
||||||
|
|
||||||
|
@ -1001,6 +1030,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
/// will use it's *external name*, which will be a `RegionKind`
|
/// will use it's *external name*, which will be a `RegionKind`
|
||||||
/// variant that can be used in query responses such as
|
/// variant that can be used in query responses such as
|
||||||
/// `ReEarlyBound`.
|
/// `ReEarlyBound`.
|
||||||
|
#[instrument(level = "debug", skip(self, infcx))]
|
||||||
fn try_promote_type_test_subject(
|
fn try_promote_type_test_subject(
|
||||||
&self,
|
&self,
|
||||||
infcx: &InferCtxt<'_, 'tcx>,
|
infcx: &InferCtxt<'_, 'tcx>,
|
||||||
|
@ -1008,8 +1038,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
) -> Option<ClosureOutlivesSubject<'tcx>> {
|
) -> Option<ClosureOutlivesSubject<'tcx>> {
|
||||||
let tcx = infcx.tcx;
|
let tcx = infcx.tcx;
|
||||||
|
|
||||||
debug!("try_promote_type_test_subject(ty = {:?})", ty);
|
|
||||||
|
|
||||||
let ty = tcx.fold_regions(ty, |r, _depth| {
|
let ty = tcx.fold_regions(ty, |r, _depth| {
|
||||||
let region_vid = self.to_region_vid(r);
|
let region_vid = self.to_region_vid(r);
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ fn foo<T>() {
|
||||||
//~| ERROR `T` does not live long enough
|
//~| ERROR `T` does not live long enough
|
||||||
//~| ERROR `T` does not live long enough
|
//~| 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
|
// FIXME: This error is bogus, but it arises because we try to validate
|
||||||
// that `<() as Foo<T>>::Type<'a>` is valid, which requires proving
|
// that `<() as Foo<T>>::Type<'a>` is valid, which requires proving
|
||||||
|
|
|
@ -34,6 +34,17 @@ error: `T` does not live long enough
|
||||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
LL | let _: for<'a> fn(<() as Foo<T>>::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<T>>::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<T: 'static>() {
|
||||||
|
| +++++++++
|
||||||
|
|
||||||
error: `T` does not live long enough
|
error: `T` does not live long enough
|
||||||
--> $DIR/issue-91139.rs:16:58
|
--> $DIR/issue-91139.rs:16:58
|
||||||
|
|
|
|
||||||
|
@ -46,5 +57,6 @@ error: `T` does not live long enough
|
||||||
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
|
LL | let _: for<'a> fn(<() as Foo<T>>::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`.
|
||||||
|
|
21
src/test/ui/nll/issue-98693.rs
Normal file
21
src/test/ui/nll/issue-98693.rs
Normal file
|
@ -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<T>()
|
||||||
|
where
|
||||||
|
for<'a> T: 'a,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test<T>() {
|
||||||
|
|| {
|
||||||
|
//~^ ERROR the parameter type `T` may not live long enough
|
||||||
|
assert_static::<T>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
17
src/test/ui/nll/issue-98693.stderr
Normal file
17
src/test/ui/nll/issue-98693.stderr
Normal file
|
@ -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::<T>();
|
||||||
|
LL | | };
|
||||||
|
| |_____^ ...so that the type `T` will meet its required lifetime bounds
|
||||||
|
|
|
||||||
|
help: consider adding an explicit lifetime bound...
|
||||||
|
|
|
||||||
|
LL | fn test<T: 'static>() {
|
||||||
|
| +++++++++
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0310`.
|
Loading…
Add table
Add a link
Reference in a new issue