Skip to content

Commit 8a9c99f

Browse files
arora-amanehuss
authored andcommitted
Copy capture analyis alogrithm from hackmd
1 parent 39a51cf commit 8a9c99f

File tree

1 file changed

+133
-0
lines changed

1 file changed

+133
-0
lines changed

src/types/closure.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,139 @@ If a closure captures a field of a composite types such as structs, tuples, and
312312
} // tuple.1 dropped here -----------------------------+
313313
```
314314

315+
316+
## Overall Capture analysis algorithm
317+
318+
* Input:
319+
* Analyzing the closure C yields a set of `(Mode, Place)` pairs that are accessed
320+
* Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max)
321+
* Closure mode is `ref` or `move`
322+
* Output:
323+
* Minimal `(Mode, Place)` pairs that are actually captured
324+
* Cleanup and truncation
325+
* Generate C' by mapping each (Mode, Place) in C:
326+
* `(Mode1, Place1) = ref_opt(unsafe_check(copy_type(Mode, Place)))`
327+
* if this is a ref closure:
328+
* Add `ref_xform(Mode1, Place1)` to C'
329+
* else:
330+
* Add `move_xform(Mode1, Place1)` to C'
331+
* Minimization
332+
* Until no rules apply:
333+
* For each two places (M1, P1), (M2, P2) where P1 is a prefix of P2:
334+
* Remove both places from the set
335+
* Add (max(M1, M2), P1) into the set
336+
* Helper functions:
337+
* `copy_type(Mode, Place) -> (Mode, Place)`
338+
* "By-value use of a copy type is a ref"
339+
* If Mode = "by-value" and type(Place) is `Copy`:
340+
* Return (ref, Place)
341+
* Else
342+
* Return (Mode, Place)
343+
* `unsafe_check(Mode, Place) -> (Mode, Place)`
344+
* "Ensure unsafe accesses occur within the closure"
345+
* If Place contains a deref of a raw pointer:
346+
* Let Place1 = Place truncated just before the deref
347+
* Return (Mode, Place1)
348+
* If Mode is `ref *` and the place contains a field of a packed struct:
349+
* Let Place1 = Place truncated just before the field
350+
* Return (Mode, Place1)
351+
* Else
352+
* Return (Mode, Place1)
353+
* `move_xform(Mode, Place) -> (Mode, Place)` (For move closures)
354+
* "Take ownership if data being accessed is owned by the variable used to access it (or if closure attempts to move data that it doesn't own)."
355+
* "When taking ownership, only capture data found on the stack."
356+
* "Otherwise, reborrow the reference."
357+
* If Mode is `ref mut` and the place contains a deref of an `&mut`:
358+
* Return (Mode, Place)
359+
* Else if Mode is `ref *` and the place contains a deref of an `&`:
360+
* Return (Mode, Place)
361+
* Else if place contains a deref:
362+
* Let Place1 = Place truncated just before the deref
363+
* Return (ByValue, Place1)
364+
* Else:
365+
* Return (ByValue, Place)
366+
* `ref_xform(Mode, Place) -> (Mode, Place)` (for ref closures)
367+
* "If taking ownership of data, only move data from enclosing stack frame."
368+
* Generate C' by mapping each (Mode, Place) in C
369+
* If Mode is ByValue and place contains a deref:
370+
* Let Place1 = Place truncated just before the deref
371+
* Return (ByValue, Place1)
372+
* Else:
373+
* Return (Mode, Place)
374+
* `ref_opt(Mode, Place) -> (Mode, Place)` (for ref closures)
375+
* "Optimization: borrow the ref, not data owned by ref."
376+
* If Place contains a deref of an `&`...
377+
* ...or something
378+
379+
## Key examples
380+
381+
### box-mut
382+
383+
```rust
384+
fn box_mut() {
385+
let mut s = Foo { x: 0 } ;
386+
387+
let px = &mut s;
388+
let bx = Box::new(px);
389+
390+
391+
let c = #[rustc_capture_analysis] move || bx.x += 10;
392+
// Mutable reference to this place:
393+
// (*(*bx)).x
394+
// ^ ^
395+
// | a Box
396+
// a &mut
397+
}
398+
```
399+
400+
```
401+
Closure mode = move
402+
C = {
403+
(ref mut, (*(*bx)).x)
404+
}
405+
C' = C
406+
```
407+
408+
Output is the same: `C' = C`
409+
410+
### Packed-field-ref-and-move
411+
412+
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:
413+
414+
```rust
415+
print(&packed.x);
416+
move_value(packed.x);
417+
```
418+
419+
420+
```rust
421+
struct Point { x: i32, y: i32 }
422+
fn f(p: &Point) -> impl Fn() {
423+
let c = move || {
424+
let x = p.x;
425+
};
426+
427+
// x.x -> ByValue
428+
// after rules x -> ByValue
429+
430+
c
431+
}
432+
433+
struct Point { x: i32, y: i32 }
434+
fn g(p: &mut Point) -> impl Fn() {
435+
let c = move || {
436+
let x = p.x; // ought to: (ref, (*p).x)
437+
};
438+
439+
move || {
440+
p.y += 1;
441+
}
442+
443+
444+
// x.x -> ByValue
445+
446+
```
447+
315448
# Edition 2018 and before
316449

317450
## Closure types difference

0 commit comments

Comments
 (0)