Skip to content

Commit 1e2a69c

Browse files
arora-amanehuss
authored andcommitted
Copy capture analyis alogrithm from hackmd
1 parent 5fb1c02 commit 1e2a69c

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
@@ -326,6 +326,139 @@ If a closure captures a field of a composite types such as structs, tuples, and
326326
} // tuple.1 dropped here -----------------------------+
327327
```
328328

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

331464
## Closure types difference

0 commit comments

Comments
 (0)