Skip to content

Commit cfa1467

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

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
@@ -330,79 +330,85 @@ If a closure captures a field of a composite types such as structs, tuples, and
330330
## Overall Capture analysis algorithm
331331

332332
* Input:
333-
* Analyzing the closure C yields a set of `(Mode, Place)` pairs that are accessed
333+
* Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed
334334
* Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max)
335+
* 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.
336+
* Note: `ByValue` use of a `Copy` type is seen as a `ref` access mode.
335337
* Closure mode is `ref` or `move`
336338
* Output:
337-
* Minimal `(Mode, Place)` pairs that are actually captured
339+
* Minimal `(Place, Mode)` pairs that are actually captured
338340
* Cleanup and truncation
339341
* Generate C' by mapping each (Mode, Place) in C:
340-
* `(Mode1, Place1) = ref_opt(unsafe_check(copy_type(Mode, Place)))`
342+
* `(Place1, Mode1) = ref_opt(unsafe_check(Place, Mode))`
341343
* if this is a ref closure:
342-
* Add `ref_xform(Mode1, Place1)` to C'
344+
* Add `ref_xform(Place1, Mode1)` to C'
343345
* else:
344-
* Add `move_xform(Mode1, Place1)` to C'
346+
* Add `move_xform(Place1, Mode1)` to C'
345347
* Minimization
346348
* Until no rules apply:
347-
* For each two places (M1, P1), (M2, P2) where P1 is a prefix of P2:
349+
* For each two places (P1, M1), (P2, M2) where P1 is a prefix of P2:
348350
* Remove both places from the set
349-
* Add (max(M1, M2), P1) into the set
351+
* Add (P1, max(M1, M2)) into the set
350352
* 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)`
353+
* `unsafe_check(Place, Mode) -> (Place, Mode)`
358354
* "Ensure unsafe accesses occur within the closure"
359355
* If Place contains a deref of a raw pointer:
360356
* Let Place1 = Place truncated just before the deref
361-
* Return (Mode, Place1)
357+
* Return (Place1, Mode)
362358
* If Mode is `ref *` and the place contains a field of a packed struct:
363359
* Let Place1 = Place truncated just before the field
364-
* Return (Mode, Place1)
360+
* Return (Place1, Mode)
365361
* Else
366-
* Return (Mode, Place1)
367-
* `move_xform(Mode, Place) -> (Mode, Place)` (For move closures)
362+
* Return (Place, Mode)
363+
* `move_xform(Place, Mode) -> (Place, Mode)` (For move closures)
368364
* "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)."
369365
* "When taking ownership, only capture data found on the stack."
370366
* "Otherwise, reborrow the reference."
371367
* If Mode is `ref mut` and the place contains a deref of an `&mut`:
372-
* Return (Mode, Place)
368+
* Return (Place, Mode)
373369
* Else if Mode is `ref *` and the place contains a deref of an `&`:
374-
* Return (Mode, Place)
370+
* Return (Place, Mode)
375371
* Else if place contains a deref:
376372
* Let Place1 = Place truncated just before the deref
377-
* Return (ByValue, Place1)
373+
* Return (Place1, ByValue)
378374
* Else:
379-
* Return (ByValue, Place)
380-
* `ref_xform(Mode, Place) -> (Mode, Place)` (for ref closures)
375+
* Return (Place, ByValue)
376+
* `ref_xform(Place, Mode) -> (Place, Mode)` (for ref closures)
381377
* "If taking ownership of data, only move data from enclosing stack frame."
382378
* Generate C' by mapping each (Mode, Place) in C
383379
* If Mode is ByValue and place contains a deref:
384380
* Let Place1 = Place truncated just before the deref
385-
* Return (ByValue, Place1)
381+
* Return (Place1, ByValue)
386382
* Else:
387-
* Return (Mode, Place)
388-
* `ref_opt(Mode, Place) -> (Mode, Place)` (for ref closures)
383+
* Return (Place, Mode)
384+
* `ref_opt(Place, Mode) -> (Place, Mode)`
389385
* "Optimization: borrow the ref, not data owned by ref."
390-
* If Place contains a deref of an `&`...
391-
* ...or something
386+
* Disjoint capture over immutable reference doesn't add too much value because the fields can either be borrowed immutably or copied.
387+
* Edge case: Field that is accessed via the referece lives longer than the reference.
388+
* Resolution: Only consider the last Deref
389+
* If Place is (Base, Projections), where Projections is a list of size N.
390+
* For all `i, 0 <= i < N`, Projections[i] != Deref
391+
* Return (Place, Mode)
392+
* If `l, 0 <= l < N` is the last/rightmost Deref Projection i.e. for any `i, l < i < N` Projection[i] != Deref,
393+
and `Place.type_before_projection(l) = ty::Ref(.., Mutability::Not)`
394+
* Let Place1 = (Base, Projections[0..=l])
395+
* Return (Place1, Ref)
392396

393397
## Key examples
394398

395399
### box-mut
396400

397401
```rust
402+
struct Foo { x: i32 }
403+
398404
fn box_mut() {
399405
let mut s = Foo { x: 0 } ;
400406

401407
let px = &mut s;
402408
let bx = Box::new(px);
403409

404410

405-
let c = #[rustc_capture_analysis] move || bx.x += 10;
411+
let c = move || bx.x += 10;
406412
// Mutable reference to this place:
407413
// (*(*bx)).x
408414
// ^ ^
@@ -411,7 +417,8 @@ fn box_mut() {
411417
}
412418
```
413419

414-
```
420+
<!-- ignore: Omit error about unterminated string literal when representing c_prime -->
421+
```ignore
415422
Closure mode = move
416423
C = {
417424
(ref mut, (*(*bx)).x)
@@ -426,37 +433,57 @@ Output is the same: `C' = C`
426433
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:
427434

428435
```rust
429-
print(&packed.x);
430-
move_value(packed.x);
436+
#[repr(packed)]
437+
struct Packed { x: String }
438+
439+
# fn use_ref<T>(_: &T) {}
440+
# fn move_value<T>(_: T) {}
441+
442+
fn main() {
443+
let packed = Packed { x: String::new() };
444+
445+
let c = || {
446+
use_ref(&packed.x);
447+
move_value(packed.x);
448+
};
449+
450+
c();
451+
}
452+
```
453+
454+
<!-- ignore: Omit error about unterminated string literal when representing c_prime -->
455+
```ignore
456+
Closure mode = ref
457+
C = {
458+
(ref mut, packed)
459+
}
460+
C' = C
431461
```
432462

463+
### Optimization-Edge-Case
464+
```edition2021
465+
struct Int(i32);
466+
struct B<'a>(&'a i32);
433467
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
468+
struct MyStruct<'a> {
469+
a: &'static Int,
470+
b: B<'a>,
471+
}
443472
473+
fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
474+
let c = || drop(&m.a.0);
444475
c
445-
}
476+
}
446477
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-
478+
```
479+
480+
<!-- ignore: Omit error about unterminated string literal when reprenting c_prime -->
481+
```ignore
482+
Closure mode = ref
483+
C = {
484+
(ref mut, *m.a)
485+
}
486+
C' = C
460487
```
461488

462489
# Edition 2018 and before

0 commit comments

Comments
 (0)