@@ -316,65 +316,69 @@ If a closure captures a field of a composite types such as structs, tuples, and
316
316
## Overall Capture analysis algorithm
317
317
318
318
* 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
320
320
* 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.
321
323
* Closure mode is ` ref ` or ` move `
322
324
* Output:
323
- * Minimal ` (Mode, Place ) ` pairs that are actually captured
325
+ * Minimal ` (Place, Mode ) ` pairs that are actually captured
324
326
* Cleanup and truncation
325
327
* 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 )) `
327
329
* if this is a ref closure:
328
- * Add ` ref_xform(Mode1, Place1 ) ` to C'
330
+ * Add ` ref_xform(Place1, Mode1 ) ` to C'
329
331
* else:
330
- * Add ` move_xform(Mode1, Place1 ) ` to C'
332
+ * Add ` move_xform(Place1, Mode1 ) ` to C'
331
333
* Minimization
332
334
* 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:
334
336
* Remove both places from the set
335
- * Add (max(M1, M2), P1 ) into the set
337
+ * Add (P1, max(M1, M2)) into the set
336
338
* 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) `
344
340
* "Ensure unsafe accesses occur within the closure"
345
341
* If Place contains a deref of a raw pointer:
346
342
* Let Place1 = Place truncated just before the deref
347
- * Return (Mode, Place1 )
343
+ * Return (Place1, Mode )
348
344
* If Mode is ` ref * ` and the place contains a field of a packed struct:
349
345
* Let Place1 = Place truncated just before the field
350
- * Return (Mode, Place1 )
346
+ * Return (Place1, Mode )
351
347
* 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)
354
350
* "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)."
355
351
* "When taking ownership, only capture data found on the stack."
356
352
* "Otherwise, reborrow the reference."
357
353
* If Mode is ` ref mut ` and the place contains a deref of an ` &mut ` :
358
- * Return (Mode, Place )
354
+ * Return (Place, Mode )
359
355
* Else if Mode is ` ref * ` and the place contains a deref of an ` & ` :
360
- * Return (Mode, Place )
356
+ * Return (Place, Mode )
361
357
* Else if place contains a deref:
362
358
* Let Place1 = Place truncated just before the deref
363
- * Return (ByValue, Place1 )
359
+ * Return (Place1, ByValue )
364
360
* 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)
367
363
* "If taking ownership of data, only move data from enclosing stack frame."
368
364
* Generate C' by mapping each (Mode, Place) in C
369
365
* If Mode is ByValue and place contains a deref:
370
366
* Let Place1 = Place truncated just before the deref
371
- * Return (ByValue, Place1 )
367
+ * Return (Place1, ByValue )
372
368
* 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) `
375
371
* "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)
378
382
379
383
## Key examples
380
384
@@ -412,37 +416,55 @@ Output is the same: `C' = C`
412
416
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:
413
417
414
418
``` rust
415
- print (& packed . x);
416
- move_value (packed . x);
419
+ #[repr(packed)]
420
+ struct Packed { x : String }
421
+
422
+ # fn use_ref <T >(_ : & T ) {}
423
+ # fn move_value <T >(_ : T ) {}
424
+
425
+ fn main () {
426
+ let packed = Packed { x : String :: new () };
427
+
428
+ let c = || {
429
+ use_ref (& packed . x);
430
+ move_value (packed . x);
431
+ };
432
+
433
+ c ();
434
+ }
417
435
```
418
436
437
+ ```
438
+ Closure mode = ref
439
+ C = {
440
+ (ref mut, packed)
441
+ }
442
+ C' = C
443
+ ```
419
444
445
+ ### Optimization-Edge-Case
420
446
``` 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
447
+ struct Int (i32 );
448
+ struct B <'a >(& 'a i32 );
429
449
450
+ struct MyStruct <'a > {
451
+ a : & 'static Int ,
452
+ b : B <'a >,
453
+ }
454
+
455
+ fn foo <'a , 'b >(m : & 'a MyStruct <'b >) -> impl FnMut () + 'static {
456
+ let c = || drop (& m . a. 0 );
430
457
c
431
- }
458
+ }
432
459
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
-
460
+ ```
461
+
462
+ ```
463
+ Closure mode = ref
464
+ C = {
465
+ (ref mut, *m.a)
466
+ }
467
+ C' = C
446
468
```
447
469
448
470
# Edition 2018 and before
0 commit comments