30
30
//! then mean that all later passes would have to check for these figments
31
31
//! and report an error, and it just seems like more mess in the end.)
32
32
33
+ use super :: writeback:: Resolver ;
33
34
use super :: FnCtxt ;
34
35
35
36
use crate :: expr_use_visitor as euv;
@@ -40,7 +41,9 @@ use rustc_hir::def_id::LocalDefId;
40
41
use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
41
42
use rustc_infer:: infer:: UpvarRegion ;
42
43
use rustc_middle:: hir:: place:: { Place , PlaceBase , PlaceWithHirId , ProjectionKind } ;
43
- use rustc_middle:: ty:: { self , Ty , TyCtxt , UpvarSubsts } ;
44
+ use rustc_middle:: ty:: fold:: TypeFoldable ;
45
+ use rustc_middle:: ty:: { self , Ty , TyCtxt , TypeckResults , UpvarSubsts } ;
46
+ use rustc_session:: lint;
44
47
use rustc_span:: sym;
45
48
use rustc_span:: { MultiSpan , Span , Symbol } ;
46
49
@@ -55,6 +58,11 @@ enum PlaceAncestryRelation {
55
58
Divergent ,
56
59
}
57
60
61
+ /// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo`
62
+ /// during capture analysis. Information in this map feeds into the minimum capture
63
+ /// analysis pass.
64
+ type InferredCaptureInformation < ' tcx > = FxIndexMap < Place < ' tcx > , ty:: CaptureInfo < ' tcx > > ;
65
+
58
66
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
59
67
pub fn closure_analyze ( & self , body : & ' tcx hir:: Body < ' tcx > ) {
60
68
InferBorrowKindVisitor { fcx : self } . visit_body ( body) ;
@@ -92,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
92
100
& self ,
93
101
closure_hir_id : hir:: HirId ,
94
102
span : Span ,
95
- body : & hir:: Body < ' _ > ,
103
+ body : & ' tcx hir:: Body < ' tcx > ,
96
104
capture_clause : hir:: CaptureBy ,
97
105
) {
98
106
debug ! ( "analyze_closure(id={:?}, body.id={:?})" , closure_hir_id, body. id( ) ) ;
@@ -124,28 +132,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
124
132
125
133
let local_def_id = closure_def_id. expect_local ( ) ;
126
134
127
- let mut capture_information: FxIndexMap < Place < ' tcx > , ty:: CaptureInfo < ' tcx > > =
128
- Default :: default ( ) ;
129
- if !self . tcx . features ( ) . capture_disjoint_fields {
130
- if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
131
- for ( & var_hir_id, _) in upvars. iter ( ) {
132
- let place = self . place_for_root_variable ( local_def_id, var_hir_id) ;
133
-
134
- debug ! ( "seed place {:?}" , place) ;
135
-
136
- let upvar_id = ty:: UpvarId :: new ( var_hir_id, local_def_id) ;
137
- let capture_kind = self . init_capture_kind ( capture_clause, upvar_id, span) ;
138
- let info = ty:: CaptureInfo {
139
- capture_kind_expr_id : None ,
140
- path_expr_id : None ,
141
- capture_kind,
142
- } ;
143
-
144
- capture_information. insert ( place, info) ;
145
- }
146
- }
147
- }
148
-
149
135
let body_owner_def_id = self . tcx . hir ( ) . body_owner_def_id ( body. id ( ) ) ;
150
136
assert_eq ! ( body_owner_def_id. to_def_id( ) , closure_def_id) ;
151
137
let mut delegate = InferBorrowKind {
@@ -155,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
155
141
capture_clause,
156
142
current_closure_kind : ty:: ClosureKind :: LATTICE_BOTTOM ,
157
143
current_origin : None ,
158
- capture_information,
144
+ capture_information : Default :: default ( ) ,
159
145
} ;
160
146
euv:: ExprUseVisitor :: new (
161
147
& mut delegate,
@@ -172,6 +158,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
172
158
) ;
173
159
self . log_capture_analysis_first_pass ( closure_def_id, & delegate. capture_information , span) ;
174
160
161
+ self . compute_min_captures ( closure_def_id, delegate. capture_information ) ;
162
+
163
+ let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
164
+ if should_do_migration_analysis ( self . tcx , closure_hir_id) {
165
+ self . perform_2229_migration_anaysis ( closure_def_id, capture_clause, span, body) ;
166
+ }
167
+
168
+ // We now fake capture information for all variables that are mentioned within the closure
169
+ // We do this after handling migrations so that min_captures computes before
170
+ if !self . tcx . features ( ) . capture_disjoint_fields {
171
+ let mut capture_information: InferredCaptureInformation < ' tcx > = Default :: default ( ) ;
172
+
173
+ if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
174
+ for var_hir_id in upvars. keys ( ) {
175
+ let place = self . place_for_root_variable ( local_def_id, * var_hir_id) ;
176
+
177
+ debug ! ( "seed place {:?}" , place) ;
178
+
179
+ let upvar_id = ty:: UpvarId :: new ( * var_hir_id, local_def_id) ;
180
+ let capture_kind = self . init_capture_kind ( capture_clause, upvar_id, span) ;
181
+ let fake_info = ty:: CaptureInfo {
182
+ capture_kind_expr_id : None ,
183
+ path_expr_id : None ,
184
+ capture_kind,
185
+ } ;
186
+
187
+ capture_information. insert ( place, fake_info) ;
188
+ }
189
+ }
190
+
191
+ // This will update the min captures based on this new fake information.
192
+ self . compute_min_captures ( closure_def_id, capture_information) ;
193
+ }
194
+
175
195
if let Some ( closure_substs) = infer_kind {
176
196
// Unify the (as yet unbound) type variable in the closure
177
197
// substs with the kind we inferred.
@@ -197,7 +217,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
197
217
}
198
218
}
199
219
200
- self . compute_min_captures ( closure_def_id, delegate) ;
201
220
self . log_closure_min_capture_info ( closure_def_id, span) ;
202
221
203
222
self . min_captures_to_closure_captures_bridge ( closure_def_id) ;
@@ -344,6 +363,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
344
363
/// Places (and corresponding capture kind) that we need to keep track of to support all
345
364
/// the required captured paths.
346
365
///
366
+ ///
367
+ /// Note: If this function is called multiple times for the same closure, it will update
368
+ /// the existing min_capture map that is stored in TypeckResults.
369
+ ///
347
370
/// Eg:
348
371
/// ```rust,no_run
349
372
/// struct Point { x: i32, y: i32 }
@@ -408,11 +431,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
408
431
fn compute_min_captures (
409
432
& self ,
410
433
closure_def_id : DefId ,
411
- inferred_info : InferBorrowKind < ' _ , ' tcx > ,
434
+ capture_information : InferredCaptureInformation < ' tcx > ,
412
435
) {
413
- let mut root_var_min_capture_list: ty:: RootVariableMinCaptureList < ' _ > = Default :: default ( ) ;
436
+ if capture_information. is_empty ( ) {
437
+ return ;
438
+ }
439
+
440
+ let mut typeck_results = self . typeck_results . borrow_mut ( ) ;
414
441
415
- for ( place, capture_info) in inferred_info. capture_information . into_iter ( ) {
442
+ let mut root_var_min_capture_list =
443
+ typeck_results. closure_min_captures . remove ( & closure_def_id) . unwrap_or_default ( ) ;
444
+
445
+ for ( place, capture_info) in capture_information. into_iter ( ) {
416
446
let var_hir_id = match place. base {
417
447
PlaceBase :: Upvar ( upvar_id) => upvar_id. var_path . hir_id ,
418
448
base => bug ! ( "Expected upvar, found={:?}" , base) ,
@@ -422,7 +452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
422
452
423
453
let min_cap_list = match root_var_min_capture_list. get_mut ( & var_hir_id) {
424
454
None => {
425
- let mutability = self . determine_capture_mutability ( & place) ;
455
+ let mutability = self . determine_capture_mutability ( & typeck_results , & place) ;
426
456
let min_cap_list =
427
457
vec ! [ ty:: CapturedPlace { place, info: capture_info, mutability } ] ;
428
458
root_var_min_capture_list. insert ( var_hir_id, min_cap_list) ;
@@ -487,21 +517,129 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
487
517
488
518
// Only need to insert when we don't have an ancestor in the existing min capture list
489
519
if !ancestor_found {
490
- let mutability = self . determine_capture_mutability ( & place) ;
520
+ let mutability = self . determine_capture_mutability ( & typeck_results , & place) ;
491
521
let captured_place =
492
522
ty:: CapturedPlace { place, info : updated_capture_info, mutability } ;
493
523
min_cap_list. push ( captured_place) ;
494
524
}
495
525
}
496
526
497
527
debug ! ( "For closure={:?}, min_captures={:#?}" , closure_def_id, root_var_min_capture_list) ;
528
+ typeck_results. closure_min_captures . insert ( closure_def_id, root_var_min_capture_list) ;
529
+ }
498
530
499
- if !root_var_min_capture_list. is_empty ( ) {
500
- self . typeck_results
501
- . borrow_mut ( )
502
- . closure_min_captures
503
- . insert ( closure_def_id, root_var_min_capture_list) ;
531
+ /// Perform the migration analysis for RFC 2229, and emit lint
532
+ /// `disjoint_capture_drop_reorder` if needed.
533
+ fn perform_2229_migration_anaysis (
534
+ & self ,
535
+ closure_def_id : DefId ,
536
+ capture_clause : hir:: CaptureBy ,
537
+ span : Span ,
538
+ body : & ' tcx hir:: Body < ' tcx > ,
539
+ ) {
540
+ let need_migrations = self . compute_2229_migrations_first_pass (
541
+ closure_def_id,
542
+ span,
543
+ capture_clause,
544
+ body,
545
+ self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id) ,
546
+ ) ;
547
+
548
+ if !need_migrations. is_empty ( ) {
549
+ let need_migrations_hir_id = need_migrations. iter ( ) . map ( |m| m. 0 ) . collect :: < Vec < _ > > ( ) ;
550
+
551
+ let migrations_text = migration_suggestion_for_2229 ( self . tcx , & need_migrations_hir_id) ;
552
+
553
+ let local_def_id = closure_def_id. expect_local ( ) ;
554
+ let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
555
+ self . tcx . struct_span_lint_hir (
556
+ lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER ,
557
+ closure_hir_id,
558
+ span,
559
+ |lint| {
560
+ let mut diagnostics_builder = lint. build (
561
+ "drop order affected for closure because of `capture_disjoint_fields`" ,
562
+ ) ;
563
+ diagnostics_builder. note ( & migrations_text) ;
564
+ diagnostics_builder. emit ( ) ;
565
+ } ,
566
+ ) ;
567
+ }
568
+ }
569
+
570
+ /// Figures out the list of root variables (and their types) that aren't completely
571
+ /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
572
+ /// some path starting at that root variable **might** be affected.
573
+ ///
574
+ /// The output list would include a root variable if:
575
+ /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
576
+ /// enabled, **and**
577
+ /// - It wasn't completely captured by the closure, **and**
578
+ /// - The type of the root variable needs Drop.
579
+ fn compute_2229_migrations_first_pass (
580
+ & self ,
581
+ closure_def_id : DefId ,
582
+ closure_span : Span ,
583
+ closure_clause : hir:: CaptureBy ,
584
+ body : & ' tcx hir:: Body < ' tcx > ,
585
+ min_captures : Option < & ty:: RootVariableMinCaptureList < ' tcx > > ,
586
+ ) -> Vec < ( hir:: HirId , Ty < ' tcx > ) > {
587
+ fn resolve_ty < T : TypeFoldable < ' tcx > > (
588
+ fcx : & FnCtxt < ' _ , ' tcx > ,
589
+ span : Span ,
590
+ body : & ' tcx hir:: Body < ' tcx > ,
591
+ ty : T ,
592
+ ) -> T {
593
+ let mut resolver = Resolver :: new ( fcx, & span, body) ;
594
+ ty. fold_with ( & mut resolver)
595
+ }
596
+
597
+ let upvars = if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
598
+ upvars
599
+ } else {
600
+ return vec ! [ ] ;
601
+ } ;
602
+
603
+ let mut need_migrations = Vec :: new ( ) ;
604
+
605
+ for ( & var_hir_id, _) in upvars. iter ( ) {
606
+ let ty = resolve_ty ( self , closure_span, body, self . node_ty ( var_hir_id) ) ;
607
+
608
+ if !ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) ) {
609
+ continue ;
610
+ }
611
+
612
+ let root_var_min_capture_list = if let Some ( root_var_min_capture_list) =
613
+ min_captures. and_then ( |m| m. get ( & var_hir_id) )
614
+ {
615
+ root_var_min_capture_list
616
+ } else {
617
+ // The upvar is mentioned within the closure but no path starting from it is
618
+ // used.
619
+
620
+ match closure_clause {
621
+ // Only migrate if closure is a move closure
622
+ hir:: CaptureBy :: Value => need_migrations. push ( ( var_hir_id, ty) ) ,
623
+
624
+ hir:: CaptureBy :: Ref => { }
625
+ }
626
+
627
+ continue ;
628
+ } ;
629
+
630
+ let is_moved = root_var_min_capture_list
631
+ . iter ( )
632
+ . any ( |capture| matches ! ( capture. info. capture_kind, ty:: UpvarCapture :: ByValue ( _) ) ) ;
633
+
634
+ let is_not_completely_captured =
635
+ root_var_min_capture_list. iter ( ) . any ( |capture| capture. place . projections . len ( ) > 0 ) ;
636
+
637
+ if is_moved && is_not_completely_captured {
638
+ need_migrations. push ( ( var_hir_id, ty) ) ;
639
+ }
504
640
}
641
+
642
+ need_migrations
505
643
}
506
644
507
645
fn init_capture_kind (
@@ -613,18 +751,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
613
751
/// A captured place is mutable if
614
752
/// 1. Projections don't include a Deref of an immut-borrow, **and**
615
753
/// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
616
- fn determine_capture_mutability ( & self , place : & Place < ' tcx > ) -> hir:: Mutability {
754
+ fn determine_capture_mutability (
755
+ & self ,
756
+ typeck_results : & ' a TypeckResults < ' tcx > ,
757
+ place : & Place < ' tcx > ,
758
+ ) -> hir:: Mutability {
617
759
let var_hir_id = match place. base {
618
760
PlaceBase :: Upvar ( upvar_id) => upvar_id. var_path . hir_id ,
619
761
_ => unreachable ! ( ) ,
620
762
} ;
621
763
622
- let bm = * self
623
- . typeck_results
624
- . borrow ( )
625
- . pat_binding_modes ( )
626
- . get ( var_hir_id)
627
- . expect ( "missing binding mode" ) ;
764
+ let bm = * typeck_results. pat_binding_modes ( ) . get ( var_hir_id) . expect ( "missing binding mode" ) ;
628
765
629
766
let mut is_mutbl = match bm {
630
767
ty:: BindByValue ( mutability) => mutability,
@@ -698,9 +835,11 @@ struct InferBorrowKind<'a, 'tcx> {
698
835
///
699
836
/// For closure `fix_s`, (at a high level) the map contains
700
837
///
838
+ /// ```
701
839
/// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
702
840
/// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
703
- capture_information : FxIndexMap < Place < ' tcx > , ty:: CaptureInfo < ' tcx > > ,
841
+ /// ```
842
+ capture_information : InferredCaptureInformation < ' tcx > ,
704
843
}
705
844
706
845
impl < ' a , ' tcx > InferBorrowKind < ' a , ' tcx > {
@@ -1119,6 +1258,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
1119
1258
tcx. hir ( ) . name ( var_hir_id)
1120
1259
}
1121
1260
1261
+ fn should_do_migration_analysis ( tcx : TyCtxt < ' _ > , closure_id : hir:: HirId ) -> bool {
1262
+ let ( level, _) =
1263
+ tcx. lint_level_at_node ( lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER , closure_id) ;
1264
+
1265
+ !matches ! ( level, lint:: Level :: Allow )
1266
+ }
1267
+
1268
+ fn migration_suggestion_for_2229 ( tcx : TyCtxt < ' _ > , need_migrations : & Vec < hir:: HirId > ) -> String {
1269
+ let need_migrations_strings =
1270
+ need_migrations. iter ( ) . map ( |v| format ! ( "{}" , var_name( tcx, * v) ) ) . collect :: < Vec < _ > > ( ) ;
1271
+ let migrations_list_concat = need_migrations_strings. join ( ", " ) ;
1272
+
1273
+ format ! ( "drop(&({}));" , migrations_list_concat)
1274
+ }
1275
+
1122
1276
/// Helper function to determine if we need to escalate CaptureKind from
1123
1277
/// CaptureInfo A to B and returns the escalated CaptureInfo.
1124
1278
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
0 commit comments