You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
NLL: unsound verification of higher ranked outlives bounds #98095
fnassert_static<T>()wherefor<'a>T:'a,{}// `test` passes without requiring `T: 'static`fntest<T>(){assert_static::<T>();}
assert_static should be able to infer that T: 'static because, in the language of Chalk, I believe the bound for<'a> T: 'a should be lowered to forall<'a> { Outlives(T: 'a) :- WellFormed(T). } and because the well-formedness of T is assumed to be true, we can instantiate 'a to any lifetime including 'static.
Interestingly, not only calling step1 or step2 from test fails on stable, but calling step2 from step1 doesn’t work on stable either, so maybe a for<'a> T: 'a constraint on its own is actually kinda useless at the moment?
but calling step2 from step1 doesn’t work on stable either, so maybe a for<'a> T: 'a constraint on its own is actually kinda useless at the moment?
Yea fn foo<T>() where for<'a> T: 'a { foo::<T>() } fails to compile on stable right now, because higher kinded outlives bounds don't satisfy themselves
This is confirmed to be directly because of NLL and not something else.
I'm going to nominate for compiler team, since this is pretty significant. We'll continue to discuss within types team if there is a reasonable fix here, but I want this to be on people's radar since this might end up being a NLL stabilization revert in the short-term.
Discussed with @oli-obk and @lcnr -- we believe the problem is that the outlives code isn't handling placeholder regions correctly... they are in this fallthrough
On stable, both test1 and test2 fail, because the migrate borrowck mode is still enabled. On nightly, just test2 fails.
Why does test2 fail?
When we call outlives_forall::<Value<'a>> in test2, we get back a TypeOutlives constraint that looks like Value<'a>: RePlaceholder(U1, 'u). We walk the type and gather its outlives components, being [ReFree('a)]. So we registerReFree('a): RePlaceholder(U1, 'u).
This part is a bit more fuzzy, but I think the outlives constraint establishes an edge on the SCC graph, that causes us to call incompatible_universe(), forcing the placeholder region to be inferred as 'static, and leading to the error.
Why does test1not fail?
When we call outlives_forall::<S> in test1, we get an outlives constraint that instead looks like S: RePlaceholder(U1, 'u). We walk the type and gather its outlives components, being [U]. This means we don't register a region-outlives relationship, but instead delay this relationship as a type test. That type test looks like:
where '_#3r is the placeholder and '_#1r is the region vid associated with the ReFree lifetime of the type parameter. What's notable is that this is basically equivalent to '_#1r: '_#3r, except for the fact that we don't use this when we're solving regions, and instead delay it as a test to be performed afterwards. This means we never call incompatible_universe, meaning we never shorten(?) the placeholder region to 'static...
What @aliemjay and @nikomatsakis independently found is that a second bug exists in eval_outlives that Niko fixed in #98109, one that only shows up in type tests as above. That is fixed with the PR, but it manifests other bugs :^)
So @compiler-errors and I did a deep dive here. What we found is that:
There is a bug in the NLL type check (which @compiler-errors described above) that is causing it to be unsound.
However, fixing that bug exposes a (pre-existing) bug around how we handle for<'a> T: 'a bounds, particularly with projections, causing regressions.
The good news is that I think fixing this bug will benefit @oli-obk and others. (I'll describe it in detail later.) The bad news is that a proper fix is non-trivial, so for the short term it may be better to revert NLL stabilization. I'll have to check with @jackh726 about how difficult that will be.
Activity
steffahn commentedon Jun 14, 2022
Here’s a way to exploit this behavior for unsoundness:
(playground)
@rustbot label I-unsound
Interestingly, not only calling
step1orstep2fromtestfails on stable, but callingstep2fromstep1doesn’t work on stable either, so maybe afor<'a> T: 'aconstraint on its own is actually kinda useless at the moment?rustbot commentedon Jun 14, 2022
oli-obk commentedon Jun 14, 2022
Yea
fn foo<T>() where for<'a> T: 'a { foo::<T>() }fails to compile on stable right now, because higher kinded outlives bounds don't satisfy themselvesjackh726 commentedon Jun 14, 2022
This is confirmed to be directly because of NLL and not something else.
I'm going to nominate for compiler team, since this is pretty significant. We'll continue to discuss within types team if there is a reasonable fix here, but I want this to be on people's radar since this might end up being a NLL stabilization revert in the short-term.
lqd commentedon Jun 14, 2022
This is used to be rejected by NLLs, and started being accepted in
nightly-2020-07-28(commit range).List of merged PRs for that nightly
Write::write_all_vectored#74088 (Avoid writes without any data inWrite::write_all_vectored)closure#74799 (Fixed typo inclosure)recursion_limit#74822 (More ensure stack to avoid segfault with increasedrecursion_limit)Predicates toPredicateKind::ForAll#73503 - lcnr:forall-predicate-what-and-why-2, r=nikomatsakisOut of those, #73503 looks like the most likely candidate, cc PR author @lcnr and reviewer @nikomatsakis.
apiraino commentedon Jun 14, 2022
WG-prioritization assigning priority (Zulip discussion).
@rustbot label -I-prioritize +P-critical
nikomatsakis commentedon Jun 14, 2022
Discussed with @oli-obk and @lcnr -- we believe the problem is that the outlives code isn't handling placeholder regions correctly... they are in this fallthrough
rust/compiler/rustc_infer/src/infer/outlives/components.rs
Line 180 in edab34a
but they should be handled more like parameters, except that (at least at present) we can only bound them by
'static.I might be able to make this fix today else could mentor someone potentially (ping me on Zulip).
compiler-errors commentedon Jun 14, 2022
I have a somewhat clear understanding for why this happens. Specifically, consider the two cases:
On stable, both
test1andtest2fail, because the migrate borrowck mode is still enabled. On nightly, justtest2fails.Why does
test2fail?When we call
outlives_forall::<Value<'a>>intest2, we get back aTypeOutlivesconstraint that looks likeValue<'a>: RePlaceholder(U1, 'u). We walk the type and gather its outlives components, being[ReFree('a)]. So we registerReFree('a): RePlaceholder(U1, 'u).This part is a bit more fuzzy, but I think the outlives constraint establishes an edge on the SCC graph, that causes us to call
incompatible_universe(), forcing the placeholder region to be inferred as'static, and leading to the error.Why does
test1not fail?When we call
outlives_forall::<S>intest1, we get an outlives constraint that instead looks likeS: RePlaceholder(U1, 'u). We walk the type and gather its outlives components, being[U]. This means we don't register a region-outlives relationship, but instead delay this relationship as a type test. That type test looks like:where
'_#3ris the placeholder and'_#1ris the region vid associated with the ReFree lifetime of the type parameter. What's notable is that this is basically equivalent to'_#1r: '_#3r, except for the fact that we don't use this when we're solving regions, and instead delay it as a test to be performed afterwards. This means we never callincompatible_universe, meaning we never shorten(?) the placeholder region to'static...What @aliemjay and @nikomatsakis independently found is that a second bug exists in
eval_outlivesthat Niko fixed in #98109, one that only shows up in type tests as above. That is fixed with the PR, but it manifests other bugs :^)nikomatsakis commentedon Jun 14, 2022
So @compiler-errors and I did a deep dive here. What we found is that:
for<'a> T: 'abounds, particularly with projections, causing regressions.nikomatsakis commentedon Jun 15, 2022
Fix is in #98109, this includes the fix for both this bug and the bug that the fix revealed :)
Auto merge of rust-lang#98109 - nikomatsakis:issue-98095, r=jackh726