Skip to content

Commit 0c5ae17

Browse files
committed
Update copy type, optimization, make algorithm (place, mode)
1 parent c5ca446 commit 0c5ae17

File tree

1 file changed

+74
-52
lines changed

1 file changed

+74
-52
lines changed

src/types/closure.md

+74-52
Original file line numberDiff line numberDiff line change
@@ -315,65 +315,69 @@ If a closure captures a field of a composite types such as structs, tuples, and
315315
## Overall Capture analysis algorithm
316316

317317
* Input:
318-
* Analyzing the closure C yields a set of `(Mode, Place)` pairs that are accessed
318+
* Analyzing the closure C yields a mapping of `Place -> Mode` that are accessed
319319
* Access mode is `ref`, `ref uniq`, `ref mut`, or `by-value` (ordered least to max)
320+
* 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.
321+
* Note: `ByValue` use of a `Copy` type is seen as a `ref` access mode.
320322
* Closure mode is `ref` or `move`
321323
* Output:
322-
* Minimal `(Mode, Place)` pairs that are actually captured
324+
* Minimal `(Place, Mode)` pairs that are actually captured
323325
* Cleanup and truncation
324326
* Generate C' by mapping each (Mode, Place) in C:
325-
* `(Mode1, Place1) = ref_opt(unsafe_check(copy_type(Mode, Place)))`
327+
* `(Place1, Mode1) = ref_opt(unsafe_check(Place, Mode))`
326328
* if this is a ref closure:
327-
* Add `ref_xform(Mode1, Place1)` to C'
329+
* Add `ref_xform(Place1, Mode1)` to C'
328330
* else:
329-
* Add `move_xform(Mode1, Place1)` to C'
331+
* Add `move_xform(Place1, Mode1)` to C'
330332
* Minimization
331333
* Until no rules apply:
332-
* For each two places (M1, P1), (M2, P2) where P1 is a prefix of P2:
334+
* For each two places (P1, M1), (P2, M2) where P1 is a prefix of P2:
333335
* Remove both places from the set
334-
* Add (max(M1, M2), P1) into the set
336+
* Add (P1, max(M1, M2)) into the set
335337
* 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)`
338+
* `unsafe_check(Place, Mode) -> (Place, Mode)`
343339
* "Ensure unsafe accesses occur within the closure"
344340
* If Place contains a deref of a raw pointer:
345341
* Let Place1 = Place truncated just before the deref
346-
* Return (Mode, Place1)
342+
* Return (Place1, Mode)
347343
* If Mode is `ref *` and the place contains a field of a packed struct:
348344
* Let Place1 = Place truncated just before the field
349-
* Return (Mode, Place1)
345+
* Return (Place1, Mode)
350346
* Else
351-
* Return (Mode, Place1)
352-
* `move_xform(Mode, Place) -> (Mode, Place)` (For move closures)
347+
* Return (Place, Mode)
348+
* `move_xform(Place, Mode) -> (Place, Mode)` (For move closures)
353349
* "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)."
354350
* "When taking ownership, only capture data found on the stack."
355351
* "Otherwise, reborrow the reference."
356352
* If Mode is `ref mut` and the place contains a deref of an `&mut`:
357-
* Return (Mode, Place)
353+
* Return (Place, Mode)
358354
* Else if Mode is `ref *` and the place contains a deref of an `&`:
359-
* Return (Mode, Place)
355+
* Return (Place, Mode)
360356
* Else if place contains a deref:
361357
* Let Place1 = Place truncated just before the deref
362-
* Return (ByValue, Place1)
358+
* Return (Place1, ByValue)
363359
* Else:
364-
* Return (ByValue, Place)
365-
* `ref_xform(Mode, Place) -> (Mode, Place)` (for ref closures)
360+
* Return (Place, ByValue)
361+
* `ref_xform(Place, Mode) -> (Place, Mode)` (for ref closures)
366362
* "If taking ownership of data, only move data from enclosing stack frame."
367363
* Generate C' by mapping each (Mode, Place) in C
368364
* If Mode is ByValue and place contains a deref:
369365
* Let Place1 = Place truncated just before the deref
370-
* Return (ByValue, Place1)
366+
* Return (Place1, ByValue)
371367
* Else:
372-
* Return (Mode, Place)
373-
* `ref_opt(Mode, Place) -> (Mode, Place)` (for ref closures)
368+
* Return (Place, Mode)
369+
* `ref_opt(Place, Mode) -> (Place, Mode)`
374370
* "Optimization: borrow the ref, not data owned by ref."
375-
* If Place contains a deref of an `&`...
376-
* ...or something
371+
* Disjoint capture over immutable reference doesn't add too much value because the fields can either be borrowed immutably or copied.
372+
* Edge case: Field that is accessed via the referece lives longer than the reference.
373+
* Resolution: Only consider the last Deref
374+
* If Place is (Base, Projections), where Projections is a list of size N.
375+
* For all `i, 0 <= i < N`, Projections[i] != Deref
376+
* Return (Place, Mode)
377+
* If `l, 0 <= l < N` is the last/rightmost Deref Projection i.e. for any `i, l < i < N` Projection[i] != Deref,
378+
and `Place.type_before_projection(l) = ty::Ref(.., Mutability::Not)`
379+
* Let Place1 = (Base, Projections[0..=l])
380+
* Return (Place1, Ref)
377381

378382
## Key examples
379383

@@ -411,37 +415,55 @@ Output is the same: `C' = C`
411415
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:
412416

413417
```rust
414-
print(&packed.x);
415-
move_value(packed.x);
418+
#[repr(packed)]
419+
struct Packed { x: String }
420+
421+
# fn use_ref<T>(_: &T) {}
422+
# fn move_value<T>(_: T) {}
423+
424+
fn main() {
425+
let packed = Packed { x: String::new() };
426+
427+
let c = || {
428+
use_ref(&packed.x);
429+
move_value(packed.x);
430+
};
431+
432+
c();
433+
}
416434
```
417435

436+
```
437+
Closure mode = ref
438+
C = {
439+
(ref mut, packed)
440+
}
441+
C' = C
442+
```
418443

444+
### Optimization-Edge-Case
419445
```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
446+
struct Int(i32);
447+
struct B<'a>(&'a i32);
428448

449+
struct MyStruct<'a> {
450+
a: &'static Int,
451+
b: B<'a>,
452+
}
453+
454+
fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static {
455+
let c = || drop(&m.a.0);
429456
c
430-
}
457+
}
431458

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-
459+
```
460+
461+
```
462+
Closure mode = ref
463+
C = {
464+
(ref mut, *m.a)
465+
}
466+
C' = C
445467
```
446468

447469
# Edition 2018 and before

0 commit comments

Comments
 (0)