Skip to content

Commit c5ca446

Browse files
committed
Copy capture analyis alogrithm from hackmd
1 parent bc0e50c commit c5ca446

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

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

316449
## Closure types difference

0 commit comments

Comments
 (0)