-
Notifications
You must be signed in to change notification settings - Fork 508
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
Update rust reference info about closures #1059
Conversation
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.
Thanks! Just some minor editorial comments. I'll let Niko review the actual content. 😉
37ce76c
to
bc0e50c
Compare
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.
The content is good. It'd be nice to also add in the "formal algorithm" that we came up with in our last meeting.
@@ -316,65 +316,69 @@ If a closure captures a field of a composite types such as structs, tuples, and | |||
## Overall Capture analysis algorithm | |||
|
|||
* Input: | |||
* Analyzing the closure C yields a set of `(Mode, Place)` pairs that are accessed | |||
* Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed |
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.
Add details for how things are currently stored in the initial analysis phase
src/types/closure.md
Outdated
* Helper functions: | ||
* `copy_type(Mode, Place) -> (Mode, Place)` |
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.
This is not a concern of the capture analysis anymore
src/types/closure.md
Outdated
|
||
struct Point { x: i32, y: i32 } |
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.
These examples were us discussiong what analysis should do in moving copy types and working through some examles
58a0132
to
880405c
Compare
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.
This is very nice. I left some comments.
src/types/closure.md
Outdated
* Input: | ||
* Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed | ||
* Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max) | ||
* For a `Place` that is used in two different acess modes within the same closure, the mode reported from closure analysis is the maximum access mode. |
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.
* For a `Place` that is used in two different acess modes within the same closure, the mode reported from closure analysis is the maximum access mode. | |
* For a `Place` that is used in two different access modes within the same closure, the mode reported from closure analysis is the maximum access mode. |
|
||
* Input: | ||
* Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed | ||
* Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max) |
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'm still a bit unclear on the best way to incorporate ref uniq here -- my sense is that the results that come in initially should never have ref uniq, but it gets determined later, whenever we do a truncation through a *
of an &mut
(as we talked about).
* Let Place1 = (Base, Projections[0..=l]) | ||
* Return (Place1, Ref) | ||
|
||
## Key examples |
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.
These are pretty inscrutable if you didn't attend our meeting, I think
92336ed
to
6b88e48
Compare
6b88e48
to
702a71f
Compare
@roxelo / @arora-aman / @nikomatsakis Is there an update on how this is going? It looks like niko left some comments that may not have been addressed? |
Co-authored-by: Josh Triplett <[email protected]>
@ehuss I think they have been addressed, github didn't auto resolve them because lines were added instead of them being changed. There was one style change that josh left that I have now committed. Hopefully tests pass given Edition 2021 has been stabilized |
I should have time tomorrow to fix the style errors. |
Just saw this now, can help if needed @arora-aman |
borrowed to iterate over, the code would not compile. | ||
### Capturing references in move contexts | ||
|
||
Moving fields out of references is not allowed. As a result, in the case of move closures, when values accessed through a shared references are moved into the closure body, the compiler will truncate right before a dereference. |
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 would love to understand the logic behind
Moving fields out of references is not allowed
Where could I learn more? My broader context is Why does moving a disjoint field capture into a closure differ when the type is a value vs a reference?
### Wild Card Patterns | ||
Closures only capture data that needs to be read, which means the following closures will not capture `x` | ||
|
||
```rust | ||
let x = 10; | ||
let c = || { | ||
let _ = x; | ||
}; | ||
|
||
let c = || match x { | ||
_ => println!("Hello World!") | ||
}; |
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.
This seems to be wrong. Both of these examples are captured by value today -> https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4044e24fa1cffb795c2dd03239eb5fb2
Nevermind, coercion logic does not have access to semantic captures
let c_box = || { | ||
println!("{}", (*b).0); // closure captures `b` | ||
}; | ||
``` | ||
|
||
## Unique immutable borrows in captures |
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.
The example in this section needs to be updated, as it now captures *x
by mutable borrow, not x
by unique immutable borrow.
@Veykril or @HKalbasi Would either of you be interested in helping to get this PR over the line? Are either of you familiar with how they are implemented? I am not familiar with how closures are implemented in rustc. I could start reviewing all of the RFC 2229 PR's and reviewing all of the current implementation, but I have been dragging because that sounds like weeks of work. It would help to have someone else to ask questions to and to help with reviewing. |
I guess I can help since I reimplemented this in rust-analyzer and got more or less familiar with the rustc implementation. What should I do? |
Thanks! I'm not sure exactly how to make progress, but perhaps some questions might help to get started:
I can try to review the comment you left and push changes where I feel confident in them, but if I end up having questions it would be helpful to have someone to ask. Hopefully there aren't too many gray areas where it isn't clear if some behavior was intended or not. |
Not yet, but will do it in the following days when I found time.
The whole thing happens in
It's pretty straightforward, specially if you are familiar with the |
### Reference into unaligned `struct`s | ||
|
||
Because it is `unsafe` to hold references to unaligned fields in a structure, closures will only capture the prefix of the path that runs up to, but not including, the first field access into an unaligned structure. | ||
|
||
```rust | ||
#[repr(packed)] | ||
struct T(String, String); | ||
|
||
let t = T(String::from("foo"), String::from("bar")); | ||
let c = || unsafe { | ||
println!("{}", t.0); // closure captures t | ||
}; | ||
``` |
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.
There seems to be an issue with this entire section. My understanding is that it is not unsafe to take a reference to an unaligned field; it is undefined behavior, and the compiler won't even let you naively do that (indeed this example doesn't compile).
I'm thinking about rewriting this, and changing the example to something like:
#[repr(packed)]
struct Packed {
f1: u8,
f2: u8,
}
let mut packed = Packed { f1: 1, f2: 2 };
let c = || {
// closure captures `packed` in its entirety as an immutable borrow
let raw_f2 = std::ptr::addr_of!(packed.f2);
assert_eq!(unsafe { raw_f2.read_unaligned() }, 2);
};
c();
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.
Yes the example is wrong and doesn't compile, but I'm not sure if your example is relevant here. I guess the addr_of!
macro will always capture the whole item, since it is expanded into something like packed as usize + some_offset
.
I think this example would work:
#[repr(packed)]
struct T(i32, i32);
fn main() {
let t = T(2, 5);
let c = || {
t.0; // closure captures t.0 if not packed, but t if packed
t.1;
};
println!("{}", core::mem::size_of_val(&c))
}
Normally, t.0
should be captured by immutable borrow (since i32
is copy) but now that t
is packed and taking &t.0
is UB, the whole t
will be captured and this code will print 8
instead of 16
when T
is not packed.
### Packed-field-ref-and-move | ||
|
||
When you have a closure that both references a packed field (which is unsafe) and moves from it (which is safe) we capture the entire struct, rather than just moving the field. This is to aid in predictability, so that removing the move doesn't make the closure become unsafe: |
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.
Similar to Reference into unaligned structs
, this doesn't compile and needs to be rewritten.
I have posted #1521 with some updates to this PR, since I am unable to push directly to this one. |
☔ The latest upstream changes (possibly bf115a4) made this pull request unmergeable. Please resolve the merge conflicts. |
Closing as this is now resolved by #1521. |
This PR updates the closure types page so it includes up to date information on the new behavior introduced by RFC2229/Edition 2021
cc: @arora-aman
r? @nikomatsakis
Closes #1066