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 } ;
44
+ use rustc_middle:: ty:: fold:: TypeFoldable ;
43
45
use rustc_middle:: ty:: { self , Ty , TyCtxt , 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
+ }
414
439
415
- for ( place, capture_info) in inferred_info. capture_information . into_iter ( ) {
440
+ let mut typeck_results = self . typeck_results . borrow_mut ( ) ;
441
+
442
+ let root_var_min_capture_list =
443
+ typeck_results. closure_min_captures . entry ( closure_def_id) . or_insert ( Default :: 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) ,
@@ -495,15 +525,122 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
495
525
}
496
526
497
527
debug ! ( "For closure={:?}, min_captures={:#?}" , closure_def_id, root_var_min_capture_list) ;
528
+ }
498
529
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) ;
530
+ /// Perform the migration analysis for RFC 2229, and emit lint
531
+ /// `disjoint_capture_drop_reorder` if needed.
532
+ fn perform_2229_migration_anaysis (
533
+ & self ,
534
+ closure_def_id : DefId ,
535
+ capture_clause : hir:: CaptureBy ,
536
+ span : Span ,
537
+ body : & ' tcx hir:: Body < ' tcx > ,
538
+ ) {
539
+ let need_migrations = self . compute_2229_migrations_first_pass (
540
+ closure_def_id,
541
+ span,
542
+ capture_clause,
543
+ body,
544
+ self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id) ,
545
+ ) ;
546
+
547
+ if !need_migrations. is_empty ( ) {
548
+ let need_migrations_hir_id = need_migrations. iter ( ) . map ( |m| m. 0 ) . collect :: < Vec < _ > > ( ) ;
549
+
550
+ let migrations_text = migration_suggestion_for_2229 ( self . tcx , & need_migrations_hir_id) ;
551
+
552
+ let local_def_id = closure_def_id. expect_local ( ) ;
553
+ let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
554
+ self . tcx . struct_span_lint_hir (
555
+ lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER ,
556
+ closure_hir_id,
557
+ span,
558
+ |lint| {
559
+ let mut diagnostics_builder = lint. build (
560
+ "Drop order affected for closure because of `capture_disjoint_fields`" ,
561
+ ) ;
562
+ diagnostics_builder. note ( & migrations_text) ;
563
+ diagnostics_builder. emit ( ) ;
564
+ } ,
565
+ ) ;
504
566
}
505
567
}
506
568
569
+ /// Figures out the list of root variables (and their types) that aren't completely
570
+ /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
571
+ /// some path starting at that root variable **might** be affected.
572
+ ///
573
+ /// The output list would include a root variable if:
574
+ /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
575
+ /// enabled, **and**
576
+ /// - It wasn't completely captured by the closure, **and**
577
+ /// - The type of the root variable needs Drop.
578
+ fn compute_2229_migrations_first_pass (
579
+ & self ,
580
+ closure_def_id : DefId ,
581
+ closure_span : Span ,
582
+ closure_clause : hir:: CaptureBy ,
583
+ body : & ' tcx hir:: Body < ' tcx > ,
584
+ min_captures : Option < & ty:: RootVariableMinCaptureList < ' tcx > > ,
585
+ ) -> Vec < ( hir:: HirId , Ty < ' tcx > ) > {
586
+ fn resolve_ty < T : TypeFoldable < ' tcx > > (
587
+ fcx : & FnCtxt < ' _ , ' tcx > ,
588
+ span : Span ,
589
+ body : & ' tcx hir:: Body < ' tcx > ,
590
+ ty : T ,
591
+ ) -> T {
592
+ let mut resolver = Resolver :: new ( fcx, & span, body) ;
593
+ ty. fold_with ( & mut resolver)
594
+ }
595
+
596
+ let upvars = if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
597
+ upvars
598
+ } else {
599
+ return vec ! [ ] ;
600
+ } ;
601
+
602
+ let mut need_migrations = Vec :: new ( ) ;
603
+
604
+ for ( & var_hir_id, _) in upvars. iter ( ) {
605
+ let ty = resolve_ty ( self , closure_span, body, self . node_ty ( var_hir_id) ) ;
606
+
607
+ if !ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) ) {
608
+ continue ;
609
+ }
610
+
611
+ let root_var_min_capture_list = if let Some ( root_var_min_capture_list) =
612
+ min_captures. and_then ( |m| m. get ( & var_hir_id) )
613
+ {
614
+ root_var_min_capture_list
615
+ } else {
616
+ // The upvar is mentioned within the closure but no path starting from it is
617
+ // used.
618
+
619
+ match closure_clause {
620
+ // Only migrate if closure is a move closure
621
+ hir:: CaptureBy :: Value => need_migrations. push ( ( var_hir_id, ty) ) ,
622
+
623
+ hir:: CaptureBy :: Ref => { }
624
+ }
625
+
626
+ continue ;
627
+ } ;
628
+
629
+ let is_moved = root_var_min_capture_list
630
+ . iter ( )
631
+ . any ( |capture| matches ! ( capture. info. capture_kind, ty:: UpvarCapture :: ByValue ( _) ) ) ;
632
+
633
+ let is_not_completely_captured =
634
+ root_var_min_capture_list. iter ( ) . any ( |capture| capture. place . projections . len ( ) > 0 ) ;
635
+
636
+ if is_moved && is_not_completely_captured {
637
+ need_migrations. push ( ( var_hir_id, ty) ) ;
638
+ }
639
+ }
640
+
641
+ need_migrations
642
+ }
643
+
507
644
fn init_capture_kind (
508
645
& self ,
509
646
capture_clause : hir:: CaptureBy ,
@@ -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