@@ -315,65 +315,69 @@ If a closure captures a field of a composite types such as structs, tuples, and
315
315
## Overall Capture analysis algorithm
316
316
317
317
* 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
319
319
* 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.
320
322
* Closure mode is ` ref ` or ` move `
321
323
* Output:
322
- * Minimal ` (Mode, Place ) ` pairs that are actually captured
324
+ * Minimal ` (Place, Mode ) ` pairs that are actually captured
323
325
* Cleanup and truncation
324
326
* 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 )) `
326
328
* if this is a ref closure:
327
- * Add ` ref_xform(Mode1, Place1 ) ` to C'
329
+ * Add ` ref_xform(Place1, Mode1 ) ` to C'
328
330
* else:
329
- * Add ` move_xform(Mode1, Place1 ) ` to C'
331
+ * Add ` move_xform(Place1, Mode1 ) ` to C'
330
332
* Minimization
331
333
* 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:
333
335
* Remove both places from the set
334
- * Add (max(M1, M2), P1 ) into the set
336
+ * Add (P1, max(M1, M2)) into the set
335
337
* 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) `
343
339
* "Ensure unsafe accesses occur within the closure"
344
340
* If Place contains a deref of a raw pointer:
345
341
* Let Place1 = Place truncated just before the deref
346
- * Return (Mode, Place1 )
342
+ * Return (Place1, Mode )
347
343
* If Mode is ` ref * ` and the place contains a field of a packed struct:
348
344
* Let Place1 = Place truncated just before the field
349
- * Return (Mode, Place1 )
345
+ * Return (Place1, Mode )
350
346
* 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)
353
349
* "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
350
* "When taking ownership, only capture data found on the stack."
355
351
* "Otherwise, reborrow the reference."
356
352
* If Mode is ` ref mut ` and the place contains a deref of an ` &mut ` :
357
- * Return (Mode, Place )
353
+ * Return (Place, Mode )
358
354
* Else if Mode is ` ref * ` and the place contains a deref of an ` & ` :
359
- * Return (Mode, Place )
355
+ * Return (Place, Mode )
360
356
* Else if place contains a deref:
361
357
* Let Place1 = Place truncated just before the deref
362
- * Return (ByValue, Place1 )
358
+ * Return (Place1, ByValue )
363
359
* 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)
366
362
* "If taking ownership of data, only move data from enclosing stack frame."
367
363
* Generate C' by mapping each (Mode, Place) in C
368
364
* If Mode is ByValue and place contains a deref:
369
365
* Let Place1 = Place truncated just before the deref
370
- * Return (ByValue, Place1 )
366
+ * Return (Place1, ByValue )
371
367
* 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) `
374
370
* "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)
377
381
378
382
## Key examples
379
383
@@ -411,37 +415,55 @@ Output is the same: `C' = C`
411
415
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
416
413
417
``` 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
+ }
416
434
```
417
435
436
+ ```
437
+ Closure mode = ref
438
+ C = {
439
+ (ref mut, packed)
440
+ }
441
+ C' = C
442
+ ```
418
443
444
+ ### Optimization-Edge-Case
419
445
``` 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 );
428
448
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 );
429
456
c
430
- }
457
+ }
431
458
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
445
467
```
446
468
447
469
# Edition 2018 and before
0 commit comments