Skip to content

Commit b0926a1

Browse files
arora-amanehuss
authored andcommitted
Update copy type, optimization, make algorithm (place, mode)
1 parent 8a9c99f commit b0926a1

File tree

1 file changed

+82
-55
lines changed

1 file changed

+82
-55
lines changed

src/types/closure.md

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -316,79 +316,85 @@ If a closure captures a field of a composite types such as structs, tuples, and
316316
## Overall Capture analysis algorithm
317317

318318
* Input:
319-
* Analyzing the closure C yields a set of `(Mode, Place)` pairs that are accessed
319+
* Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed
320320
* Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max)
321+
* 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.
322+
* Note: `ByValue` use of a `Copy` type is seen as a `ref` access mode.
321323
* Closure mode is `ref` or `move`
322324
* Output:
323-
* Minimal `(Mode, Place)` pairs that are actually captured
325+
* Minimal `(Place, Mode)` pairs that are actually captured
324326
* Cleanup and truncation
325327
* Generate C' by mapping each (Mode, Place) in C:
326-
* `(Mode1, Place1) = ref_opt(unsafe_check(copy_type(Mode, Place)))`
328+
* `(Place1, Mode1) = ref_opt(unsafe_check(Place, Mode))`
327329
* if this is a ref closure:
328-
* Add `ref_xform(Mode1, Place1)` to C'
330+
* Add `ref_xform(Place1, Mode1)` to C'
329331
* else:
330-
* Add `move_xform(Mode1, Place1)` to C'
332+
* Add `move_xform(Place1, Mode1)` to C'
331333
* Minimization
332334
* Until no rules apply:
333-
* For each two places (M1, P1), (M2, P2) where P1 is a prefix of P2:
335+
* For each two places (P1, M1), (P2, M2) where P1 is a prefix of P2:
334336
* Remove both places from the set
335-
* Add (max(M1, M2), P1) into the set
337+
* Add (P1, max(M1, M2)) into the set
336338
* 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)`
339+
* `unsafe_check(Place, Mode) -> (Place, Mode)`
344340
* "Ensure unsafe accesses occur within the closure"
345341
* If Place contains a deref of a raw pointer:
346342
* Let Place1 = Place truncated just before the deref
347-
* Return (Mode, Place1)
343+
* Return (Place1, Mode)
348344
* If Mode is `ref *` and the place contains a field of a packed struct:
349345
* Let Place1 = Place truncated just before the field
350-
* Return (Mode, Place1)
346+
* Return (Place1, Mode)
351347
* Else
352-
* Return (Mode, Place1)
353-
* `move_xform(Mode, Place) -> (Mode, Place)` (For move closures)
348+
* Return (Place, Mode)
349+
* `move_xform(Place, Mode) -> (Place, Mode)` (For move closures)
354350
* "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)."
355351
* "When taking ownership, only capture data found on the stack."
356352
* "Otherwise, reborrow the reference."
357353
* If Mode is `ref mut` and the place contains a deref of an `&mut`:
358-
* Return (Mode, Place)
354+
* Return (Place, Mode)
359355
* Else if Mode is `ref *` and the place contains a deref of an `&`:
360-
* Return (Mode, Place)
356+
* Return (Place, Mode)
361357
* Else if place contains a deref:
362358
* Let Place1 = Place truncated just before the deref
363-
* Return (ByValue, Place1)
359+
* Return (Place1, ByValue)
364360
* Else:
365-
* Return (ByValue, Place)
366-
* `ref_xform(Mode, Place) -> (Mode, Place)` (for ref closures)
361+
* Return (Place, ByValue)
362+
* `ref_xform(Place, Mode) -> (Place, Mode)` (for ref closures)
367363
* "If taking ownership of data, only move data from enclosing stack frame."
368364
* Generate C' by mapping each (Mode, Place) in C
369365
* If Mode is ByValue and place contains a deref:
370366
* Let Place1 = Place truncated just before the deref
371-
* Return (ByValue, Place1)
367+
* Return (Place1, ByValue)
372368
* Else:
373-
* Return (Mode, Place)
374-
* `ref_opt(Mode, Place) -> (Mode, Place)` (for ref closures)
369+
* Return (Place, Mode)
370+
* `ref_opt(Place, Mode) -> (Place, Mode)`
375371
* "Optimization: borrow the ref, not data owned by ref."
376-
* If Place contains a deref of an `&`...
377-
* ...or something
372+
* Disjoint capture over immutable reference doesn't add too much value because the fields can either be borrowed immutably or copied.
373+
* Edge case: Field that is accessed via the referece lives longer than the reference.
374+
* Resolution: Only consider the last Deref
375+
* If Place is (Base, Projections), where Projections is a list of size N.
376+
* For all `i, 0 <= i < N`, Projections[i] != Deref
377+
* Return (Place, Mode)
378+
* If `l, 0 <= l < N` is the last/rightmost Deref Projection i.e. for any `i, l < i < N` Projection[i] != Deref,
379+
and `Place.type_before_projection(l) = ty::Ref(.., Mutability::Not)`
380+
* Let Place1 = (Base, Projections[0..=l])
381+
* Return (Place1, Ref)
378382

379383
## Key examples
380384

381385
### box-mut
382386

383387
```rust
388+
struct Foo { x: i32 }
389+
384390
fn box_mut() {
385391
let mut s = Foo { x: 0 } ;
386392

387393
let px = &mut s;
388394
let bx = Box::new(px);
389395

390396

391-
let c = #[rustc_capture_analysis] move || bx.x += 10;
397+
let c = move || bx.x += 10;
392398
// Mutable reference to this place:
393399
// (*(*bx)).x
394400
// ^ ^
@@ -397,7 +403,8 @@ fn box_mut() {
397403
}
398404
```
399405

400-
```
406+
<!-- ignore: Omit error about unterminated string literal when representing c_prime -->
407+
```ignore
401408
Closure mode = move
402409
C = {
403410
(ref mut, (*(*bx)).x)
@@ -412,37 +419,57 @@ Output is the same: `C' = C`
412419
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:
413420

414421
```rust
415-
print(&packed.x);
416-
move_value(packed.x);
422+
#[repr(packed)]
423+
struct Packed { x: String }
424+
425+
# fn use_ref<T>(_: &T) {}
426+
# fn move_value<T>(_: T) {}
427+
428+
fn main() {
429+
let packed = Packed { x: String::new() };
430+
431+
let c = || {
432+
use_ref(&packed.x);
433+
move_value(packed.x);
434+
};
435+
436+
c();
437+
}
438+
```
439+
440+
<!-- ignore: Omit error about unterminated string literal when representing c_prime -->
441+
```ignore
442+
Closure mode = ref
443+
C = {
444+
(ref mut, packed)
445+
}
446+
C' = C
417447
```
418448

449+
### Optimization-Edge-Case
450+
```edition2021
451+
struct Int(i32);
452+
struct B<'a>(&'a i32);
419453
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
454+
struct MyStruct<'a> {
455+
a: &'static Int,
456+
b: B<'a>,
457+
}
429458
459+
fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
460+
let c = || drop(&m.a.0);
430461
c
431-
}
462+
}
432463
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-
464+
```
465+
466+
<!-- ignore: Omit error about unterminated string literal when reprenting c_prime -->
467+
```ignore
468+
Closure mode = ref
469+
C = {
470+
(ref mut, *m.a)
471+
}
472+
C' = C
446473
```
447474

448475
# Edition 2018 and before

0 commit comments

Comments
 (0)