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 , TypeckResults , UpvarSubsts } ;
46
+ use rustc_session:: lint;
44
47
use rustc_span:: sym;
45
48
use rustc_span:: { MultiSpan , Span , Symbol } ;
46
49
@@ -97,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
97
100
& self ,
98
101
closure_hir_id : hir:: HirId ,
99
102
span : Span ,
100
- body : & hir:: Body < ' _ > ,
103
+ body : & ' tcx hir:: Body < ' tcx > ,
101
104
capture_clause : hir:: CaptureBy ,
102
105
) {
103
106
debug ! ( "analyze_closure(id={:?}, body.id={:?})" , closure_hir_id, body. id( ) ) ;
@@ -157,6 +160,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
157
160
158
161
self . compute_min_captures ( closure_def_id, delegate. capture_information ) ;
159
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
+ let need_migrations = self . compute_2229_migrations_first_pass (
166
+ closure_def_id,
167
+ span,
168
+ capture_clause,
169
+ body,
170
+ self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id) ,
171
+ ) ;
172
+
173
+ if !need_migrations. is_empty ( ) {
174
+ let need_migrations_hir_id =
175
+ need_migrations. iter ( ) . map ( |m| m. 0 ) . collect :: < Vec < _ > > ( ) ;
176
+
177
+ let migrations_text =
178
+ migration_suggestion_for_2229 ( self . tcx , & need_migrations_hir_id) ;
179
+
180
+ self . tcx . struct_span_lint_hir (
181
+ lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER ,
182
+ closure_hir_id,
183
+ span,
184
+ |lint| {
185
+ let mut diagnostics_builder = lint. build (
186
+ "drop order affected for closure because of `capture_disjoint_fields`" ,
187
+ ) ;
188
+ diagnostics_builder. note ( & migrations_text) ;
189
+ diagnostics_builder. emit ( ) ;
190
+ } ,
191
+ ) ;
192
+ }
193
+ }
194
+
160
195
// We now fake capture information for all variables that are mentioned within the closure
161
196
// We do this after handling migrations so that min_captures computes before
162
197
if !self . tcx . features ( ) . capture_disjoint_fields {
@@ -520,6 +555,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
520
555
typeck_results. closure_min_captures . insert ( closure_def_id, root_var_min_capture_list) ;
521
556
}
522
557
558
+ /// Figures out the list of root variables (and their types) that aren't completely
559
+ /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
560
+ /// some path starting at that root variable **might** be affected.
561
+ ///
562
+ /// The output list would include a root variable if:
563
+ /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
564
+ /// enabled, **and**
565
+ /// - It wasn't completely captured by the closure, **and**
566
+ /// - The type of the root variable needs Drop.
567
+ fn compute_2229_migrations_first_pass (
568
+ & self ,
569
+ closure_def_id : DefId ,
570
+ closure_span : Span ,
571
+ closure_clause : hir:: CaptureBy ,
572
+ body : & ' tcx hir:: Body < ' tcx > ,
573
+ min_captures : Option < & ty:: RootVariableMinCaptureList < ' tcx > > ,
574
+ ) -> Vec < ( hir:: HirId , Ty < ' tcx > ) > {
575
+ fn resolve_ty < T : TypeFoldable < ' tcx > > (
576
+ fcx : & FnCtxt < ' _ , ' tcx > ,
577
+ span : Span ,
578
+ body : & ' tcx hir:: Body < ' tcx > ,
579
+ ty : T ,
580
+ ) -> T {
581
+ let mut resolver = Resolver :: new ( fcx, & span, body) ;
582
+ ty. fold_with ( & mut resolver)
583
+ }
584
+
585
+ let upvars = if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
586
+ upvars
587
+ } else {
588
+ return vec ! [ ] ;
589
+ } ;
590
+
591
+ let mut need_migrations = Vec :: new ( ) ;
592
+
593
+ for ( & var_hir_id, _) in upvars. iter ( ) {
594
+ let ty = resolve_ty ( self , closure_span, body, self . node_ty ( var_hir_id) ) ;
595
+
596
+ if !ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) ) {
597
+ continue ;
598
+ }
599
+
600
+ let root_var_min_capture_list = if let Some ( root_var_min_capture_list) =
601
+ min_captures. and_then ( |m| m. get ( & var_hir_id) )
602
+ {
603
+ root_var_min_capture_list
604
+ } else {
605
+ // The upvar is mentioned within the closure but no path starting from it is
606
+ // used.
607
+
608
+ match closure_clause {
609
+ // Only migrate if closure is a move closure
610
+ hir:: CaptureBy :: Value => need_migrations. push ( ( var_hir_id, ty) ) ,
611
+
612
+ hir:: CaptureBy :: Ref => { }
613
+ }
614
+
615
+ continue ;
616
+ } ;
617
+
618
+ let is_moved = root_var_min_capture_list
619
+ . iter ( )
620
+ . find ( |capture| matches ! ( capture. info. capture_kind, ty:: UpvarCapture :: ByValue ( _) ) )
621
+ . is_some ( ) ;
622
+
623
+ // 1. If we capture more than one path starting at the root variabe then the root variable
624
+ // isn't being captured in its entirety
625
+ // 2. If we only capture one path starting at the root variable, it's still possible
626
+ // that it isn't the root variable completely.
627
+ if is_moved
628
+ && ( ( root_var_min_capture_list. len ( ) > 1 )
629
+ || ( root_var_min_capture_list[ 0 ] . place . projections . len ( ) > 0 ) )
630
+ {
631
+ need_migrations. push ( ( var_hir_id, ty) ) ;
632
+ }
633
+ }
634
+
635
+ need_migrations
636
+ }
637
+
523
638
fn init_capture_kind (
524
639
& self ,
525
640
capture_clause : hir:: CaptureBy ,
@@ -1136,6 +1251,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
1136
1251
tcx. hir ( ) . name ( var_hir_id)
1137
1252
}
1138
1253
1254
+ fn should_do_migration_analysis ( tcx : TyCtxt < ' _ > , closure_id : hir:: HirId ) -> bool {
1255
+ let ( level, _) =
1256
+ tcx. lint_level_at_node ( lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER , closure_id) ;
1257
+
1258
+ !matches ! ( level, lint:: Level :: Allow )
1259
+ }
1260
+
1261
+ fn migration_suggestion_for_2229 ( tcx : TyCtxt < ' _ > , need_migrations : & Vec < hir:: HirId > ) -> String {
1262
+ let need_migrations_strings =
1263
+ need_migrations. iter ( ) . map ( |v| format ! ( "{}" , var_name( tcx, * v) ) ) . collect :: < Vec < _ > > ( ) ;
1264
+ let migrations_list_concat = need_migrations_strings. join ( ", " ) ;
1265
+
1266
+ format ! ( "let ({}) = ({});" , migrations_list_concat, migrations_list_concat)
1267
+ }
1268
+
1139
1269
/// Helper function to determine if we need to escalate CaptureKind from
1140
1270
/// CaptureInfo A to B and returns the escalated CaptureInfo.
1141
1271
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
0 commit comments