-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Fix closure upvar soundness bug in regionck #17869
Conversation
e84c76a
to
04c8f68
Compare
@nikomatsakis, @pnkfelix, @pcwalton, would one of you be up for review? |
I'll take this. |
04c8f68
to
bc7bcbb
Compare
Rebased, fixed tidy error, and cleaned up some comments and commit messages. Hopefully my thinking is more clear now. |
So, I read this over in some detail. I think it's not quite working as I envision. Here is a summary of how I was thinking it ought to work. The basic idea is to have mem-categorization model as closely as possible the desugared version. I think the fundamental problem is that the code now attempts to classify I think the result of mem-categorization needs to depend both on whether the closure is We use the
We use the escaping vs non-escaping closure distinction as follows:
So if we have a non-escaping For an escaping closure ( Modulo #3696, this should be roughly what the desugared form would do. Does this make sense? |
Sorry for taking a few days to get to this. I had to clear an hour or so where I could read and think uninterrupted, which proved hard! I'll try to respond much faster now that I've thought it over some (and/or catch me on IRC). |
I think I understand. What I've essentially done is make regionck treat It seems easier to summarize with a table. Consider an upvar with type
It seems like What are your thoughts on the "maybe link" map I added? It seems intuitively incorrect to neglect to establish a region link because the upvar had not yet been inferred as mutable at the time the expression was processed. I've definitely seen it get exercised when compiling the standard libs. I haven't come up with an example yet where not using it causes things to "go wrong", though. |
bc7bcbb
to
d80cbb1
Compare
I reworked the PR to mem-categorize upvars appropriately and fix the resulting fallout. I think this is generally in line with the strategy you envisioned. There are a few details that still need some attention: Region of by-ref upvarsYou had said that the by-ref upvar should have a region which is the lifetime bound of the closure. I tried making this change, but I think the existing behavior is actually correct. When the closure expression is processed, an inference variable is instantiated for each upvar, the link struct Closure<'foo,'bar> {
foo: &'foo Foo,
bar: &'bar Bar
} If I change it to instead use struct Closure<'closure> {
foo: &'closure Foo,
bar: &'closure Bar
} This actually caused a build failure in rustc itself in let struct_fields = cx.struct_fields.borrow();
let mut results: SmallVector<&[field_ty]> = SmallVector::zero();
each_super_struct(cx, did, |s| {
match struct_fields.find(&s) {
Some(fields) => results.push(fields.as_slice()),
_ => {
cx.sess.bug(
format!("ID not mapped to struct fields: {}",
cx.map.node_to_string(did.node)).as_slice());
}
}
});
let len = results.as_slice().iter().map(|x| x.len()).sum();
Generating an anonymous free regionI initially tried to create the anonymous free region as a |
@bkoropoff this is looking really good. I think it'd be good to have tests for moving out of upvars in all the various combinations. In particular, |
d80cbb1
to
d78e661
Compare
I rebased, added tests for moving out of upvars, and addressed some of the review feedback. I kept these changes in separate "CR FIX" commits to make it clear what changed since last time. These will be rebased and squashed when the PR is ready to merge. |
This looks good. I'm ready to give r+. |
@bkoropoff will rebase to cleanup commit history, r+ when that is done |
d78e661
to
29d160f
Compare
…sakis This PR is based on #17784, which fixes closure soundness problems in borrowck. Only the last two commits are unique to this PR. My understanding of regionck is still evolving, so I'm not sure if this is the right approach. Feedback is appreciated. - In `link_reborrowed_region`, we account for the ability of upvars to change their mutability due to later processing. A map of recursive region links we may want to establish in the future is maintained, with the links being established when the mutability of the borrow is adjusted. - When asked to establish a region link for an upvar, we link it to the region of the closure body. This creates the necessary constraint to stop unsound reborrows from the closure environment. This partially (maybe completely) solves issue #17403. Remaining work: - This is only known to help with by-ref upvars. I have not looked at by-value upvars yet to see if they can cause problems. - The error diagnostics that result from failed region inference are pretty inscrutible.
- Unify the representations of `cat_upvar` and `cat_copied_upvar` - In `link_reborrowed_region`, account for the ability of upvars to change their mutability due to later processing. A map of recursive region links we may want to establish in the future is maintained, with the links being established when the kind of the borrow is adjusted. - When categorizing upvars, add an explicit deref that represents the closure environment pointer for closures that do not take the environment by value. The region for the implicit pointer is an anonymous free region type introduced for this purpose. This creates the necessary constraint to prevent unsound reborrows from the environment. - Add a note to categorizations to make it easier to tell when extra dereferences have been inserted by an upvar without having to perform deep pattern matching. - Adjust borrowck to deal with the changes. Where `cat_upvar` and `cat_copied_upvar` were previously treated differently, they are now both treated roughly like local variables within the closure body, as the explicit derefs now ensure proper behavior. However, error diagnostics had to be changed to explicitly look through the extra dereferences to avoid producing confusing messages about references not present in the source code. Closes issue rust-lang#17403. Remaining work: - The error diagnostics that result from failed region inference are pretty inscrutible and should be improved. Code like the following is now rejected: let mut x = 0u; let f = || &mut x; let y = f(); let z = f(); // multiple mutable references to the same location This also breaks code that uses a similar construction even if it does not go on to violate aliasability semantics. Such code will need to be reworked in some way, such as by using a capture-by-value closure type. [breaking-change]
The test was also renamed to be more descriptive.
29d160f
to
fdd69ac
Compare
Rebased to fix merge conflict |
…sakis This PR is based on #17784, which fixes closure soundness problems in borrowck. Only the last two commits are unique to this PR. My understanding of regionck is still evolving, so I'm not sure if this is the right approach. Feedback is appreciated. - In `link_reborrowed_region`, we account for the ability of upvars to change their mutability due to later processing. A map of recursive region links we may want to establish in the future is maintained, with the links being established when the mutability of the borrow is adjusted. - When asked to establish a region link for an upvar, we link it to the region of the closure body. This creates the necessary constraint to stop unsound reborrows from the closure environment. This partially (maybe completely) solves issue #17403. Remaining work: - This is only known to help with by-ref upvars. I have not looked at by-value upvars yet to see if they can cause problems. - The error diagnostics that result from failed region inference are pretty inscrutible.
fix: keep comments in convert_while_to_loop Fix rust-lang#17869.
This PR is based on #17784, which fixes closure soundness problems in borrowck. Only the last two commits are unique to this PR.
My understanding of regionck is still evolving, so I'm not sure if this is the right approach. Feedback is appreciated.
link_reborrowed_region
, we account for the ability of upvars tochange their mutability due to later processing. A map of recursive
region links we may want to establish in the future is maintained,
with the links being established when the mutability of the borrow
is adjusted.
the region of the closure body. This creates the necessary
constraint to stop unsound reborrows from the closure environment.
This partially (maybe completely) solves issue #17403. Remaining work:
by-value upvars yet to see if they can cause problems.
pretty inscrutible.