-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
make member constraints pick static if no upper bounds #89056
Conversation
The current member constraint algorithm has a failure mode when it encounters a variable `'0` and the only constraint is that `':static: '0`. In that case, there are no upper bounds, or at least no non-trivial upper bounds. As a result, we are not able to rule out any of the choices, so if you have a constraint like `'0 member ['a, 'b, 'static]`, where `'a` and `'b` are unrelated, then the algorithm gets stuck as there is no 'least choice' from that set. The tweak in this commit changes the algorithm so that *if* there are no upper bounds (and hence `'0` can get as large as desired without creating a region check error), it will just pick `'static`. This should only occur in cases where the data is flowing out from a `'static` value. This change is probably *not* right for impl Trait in let bindings, but those are challenging with member constraints anyway, and not currently supported. Furthermore, this change is not needed in a polonius-like formulation, which effectively permits "ad-hoc intersections" of lifetimes as the value for a region, and hence could give a value like `'a ^ 'b` as the resulting lifetime. Therefore I think there isn't forwards compat danger here. (famous last words?)
|
||
// If there are no upper bounds, and static is a choice (in practice, it always is), | ||
// then we should just pick static. | ||
if member_upper_bounds.is_empty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I notice the check here is simpler than the one with any_upper_bounds
done above (e.g., non-empty case with only 'static
s inside): does collect_bounding_regions
never yield 'static
s?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Conjecture on my part: I wouldn't expect it to ? It wouldn't constrain the variable to add it as an upper bound, it's already the ⊤
element.
We'll see when we do the Zoom session ^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was the zoom session recorded somewhere btw?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'll likely happen tomorrow
Sounds good. Even if the docs on member constraints are excellent, diving into the code and matching it to the prose will still be enlightening, I expect. |
@bors try Seems like a good idea to do a check crater run |
⌛ Trying commit b893cff with merge 8de4b308b608cded0ebccf658a0fc719bce87d4e... |
☀️ Try build successful - checks-actions |
Let's do that, the queue is empty ! @craterbot check |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
I've been looking at the results, and as mentioned on Zulip, here's the trait Future {}
impl Future for () {}
fn sink_error1<'a, F: 'a>(f: F) -> impl Future + 'a {
sink_error2(f) // error: `F` may not live long enough
}
fn sink_error2<'a, F: 'a>(_: F) -> impl Future + 'a {} |
(true, true) => Some(r1.min(r2)), | ||
(true, false) => Some(r2), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw, unrelated to this PR, just out of curiosity / to make sure I understand this part correctly: would this second branch already cover the first? (at the cost of breaking the "symmetry" in the code)
@lqd that's an interesting regression. I guess the problem is that our region inference here is kind of "missing" some of the actual bounds. Let me think about that. |
@bors try Pushed a fix for that crater case. We can re-run crater, perhaps. |
⌛ Trying commit 92b2be2 with merge 11084977fccce286b348dbb5473938f22b430250... |
We should be able to only rerun crater on the regressed crates. I don't know if we really need to do a full crater run. (In fact, if we do the subset, it might be done by the meeting tomorrow since the queue is empty) @Mark-Simulacrum can you help here? I'm not entirely sure how to run with a subset. |
#88827 (comment) is an example of queueing the regressions from a prior crater run, you can probably duplicate that once the try is done here. |
☀️ Try build successful - checks-actions |
@craterbot run name=pr-89056-2 start=db1fb85cff63ad5fffe435e17128f99f9e1d970c end=8de4b308b608cded0ebccf658a0fc719bce87d4e mode=build-and-test crates=https://crater-reports.s3.amazonaws.com/pr-89056/retry-regressed-list.txt |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
@craterbot abort name=pr-89056-2 |
🗑️ Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
This PR is on hold while @oli-obk explores intersection regions. |
I haven't gotten beyond code reading on the intersection regions. Let's unblock this, as afaict this is strictly forwards compatible with intersection regions |
@rustbot review |
This PR has been brought up during today's t-compiler meeting as part of the "oldest PRs waiting for review". I don't think we've published the walkthrough on youtube we did back then, right ? I would have loved to rewatch it, and bring back some of that context in cache. If we're going with this PR rather than intersection regions, then
Here's a @rust-lang/types ping to notify interested parties who weren't involved when this PR was initially made, and a @rust-lang/wg-nll one for good measure. With that: @rustbot author. |
cc #105300 |
unnominating in favor of an fcp in #105300 |
rework min_choice algorithm of member constraints See [this comment](rust-lang#105300 (comment)) for the description of the new algorithm. Fixes rust-lang#63033 Fixes rust-lang#104639 This uses a more general algorithm than rust-lang#89056 that doesn't treat `'static` as a special case. It thus accepts more code. For example: ```rust async fn test2<'s>(_: &'s u8, _: &'_ &'s u8, _: &'_ &'s u8) {} ``` I claim it's more correct as well because it fixes rust-lang#104639. cc `@nikomatsakis` `@lqd` `@tmandry` `@eholk` `@chenyukang` `@oli-obk` r? types
rework min_choice algorithm of member constraints See [this comment](rust-lang#105300 (comment)) for the description of the new algorithm. Fixes rust-lang#63033 Fixes rust-lang#104639 This uses a more general algorithm than rust-lang#89056 that doesn't treat `'static` as a special case. It thus accepts more code. For example: ```rust async fn test2<'s>(_: &'s u8, _: &'_ &'s u8, _: &'_ &'s u8) {} ``` I claim it's more correct as well because it fixes rust-lang#104639. cc ``@nikomatsakis`` ``@lqd`` ``@tmandry`` ``@eholk`` ``@chenyukang`` ``@oli-obk`` r? types
Closing since #105300 has landed and is a more general version of this. |
The current member constraint algorithm has a failure mode when it encounters a
variable
'0
and the only constraint is that':static: '0
. In that case,there are no upper bounds, or at least no non-trivial upper bounds. As a
result, we are not able to rule out any of the choices, so if you have a
constraint like
'0 member ['a, 'b, 'static]
, where'a
and'b
areunrelated, then the algorithm gets stuck as there is no 'least choice' from
that set.
The tweak in this commit changes the algorithm so that if there are no upper
bounds (and hence
'0
can get as large as desired without creating a regioncheck error), it will just pick
'static
. This should only occur in caseswhere the data is flowing out from a
'static
value.This change is probably not right for impl Trait in let bindings, but those
are challenging with member constraints anyway, and not currently supported.
Furthermore, this change is not needed in a polonius-like formulation, which
effectively permits "ad-hoc intersections" of lifetimes as the value for a
region, and hence could give a value like
'a ^ 'b
as the resulting lifetime.Therefore I think there isn't forwards compat danger here. (famous last words?)
r? @lqd
cc @tmandry @eholk @jackh726 @pnkfelix
I was thinking it might be nice to schedule a recorded zoom session to talk over this PR and how this bit of the code works, as I imagine it has a ... low bus factor. =)
Fixes #63033