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
@@ -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 {
@@ -525,6 +560,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
525
560
debug ! ( "For closure={:?}, min_captures={:#?}" , closure_def_id, root_var_min_capture_list) ;
526
561
}
527
562
563
+ /// Figures out the list of root variables (and their types) that aren't completely
564
+ /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
565
+ /// some path starting at that root variable **might** be affected.
566
+ ///
567
+ /// The output list would include a root variable if:
568
+ /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
569
+ /// enabled, **and**
570
+ /// - It wasn't completely captured by the closure, **and**
571
+ /// - The type of the root variable needs Drop.
572
+ fn compute_2229_migrations_first_pass (
573
+ & self ,
574
+ closure_def_id : DefId ,
575
+ closure_span : Span ,
576
+ closure_clause : hir:: CaptureBy ,
577
+ body : & ' tcx hir:: Body < ' tcx > ,
578
+ min_captures : Option < & ty:: RootVariableMinCaptureList < ' tcx > > ,
579
+ ) -> Vec < ( hir:: HirId , Ty < ' tcx > ) > {
580
+ fn resolve_ty < T : TypeFoldable < ' tcx > > (
581
+ fcx : & FnCtxt < ' _ , ' tcx > ,
582
+ span : Span ,
583
+ body : & ' tcx hir:: Body < ' tcx > ,
584
+ ty : T ,
585
+ ) -> T {
586
+ let mut resolver = Resolver :: new ( fcx, & span, body) ;
587
+ ty. fold_with ( & mut resolver)
588
+ }
589
+
590
+ let upvars = if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
591
+ upvars
592
+ } else {
593
+ return vec ! [ ] ;
594
+ } ;
595
+
596
+ let mut need_migrations = Vec :: new ( ) ;
597
+
598
+ for ( & var_hir_id, _) in upvars. iter ( ) {
599
+ let ty = resolve_ty ( self , closure_span, body, self . node_ty ( var_hir_id) ) ;
600
+
601
+ if !ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) ) {
602
+ continue ;
603
+ }
604
+
605
+ let root_var_min_capture_list = if let Some ( root_var_min_capture_list) =
606
+ min_captures. and_then ( |m| m. get ( & var_hir_id) )
607
+ {
608
+ root_var_min_capture_list
609
+ } else {
610
+ // The upvar is mentioned within the closure but no path starting from it is
611
+ // used.
612
+
613
+ match closure_clause {
614
+ // Only migrate if closure is a move closure
615
+ hir:: CaptureBy :: Value => need_migrations. push ( ( var_hir_id, ty) ) ,
616
+
617
+ hir:: CaptureBy :: Ref => { }
618
+ }
619
+
620
+ continue ;
621
+ } ;
622
+
623
+ let is_moved = root_var_min_capture_list
624
+ . iter ( )
625
+ . find ( |capture| matches ! ( capture. info. capture_kind, ty:: UpvarCapture :: ByValue ( _) ) )
626
+ . is_some ( ) ;
627
+
628
+ // 1. If we capture more than one path starting at the root variabe then the root variable
629
+ // isn't being captured in its entirety
630
+ // 2. If we only capture one path starting at the root variable, it's still possible
631
+ // that it isn't the root variable completely.
632
+ if is_moved
633
+ && ( ( root_var_min_capture_list. len ( ) > 1 )
634
+ || ( root_var_min_capture_list[ 0 ] . place . projections . len ( ) > 0 ) )
635
+ {
636
+ need_migrations. push ( ( var_hir_id, ty) ) ;
637
+ }
638
+ }
639
+
640
+ need_migrations
641
+ }
642
+
528
643
fn init_capture_kind (
529
644
& self ,
530
645
capture_clause : hir:: CaptureBy ,
@@ -1039,6 +1154,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
1039
1154
tcx. hir ( ) . name ( var_hir_id)
1040
1155
}
1041
1156
1157
+ fn should_do_migration_analysis ( tcx : TyCtxt < ' _ > , closure_id : hir:: HirId ) -> bool {
1158
+ let ( level, _) =
1159
+ tcx. lint_level_at_node ( lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER , closure_id) ;
1160
+
1161
+ !matches ! ( level, lint:: Level :: Allow )
1162
+ }
1163
+
1164
+ fn migration_suggestion_for_2229 ( tcx : TyCtxt < ' _ > , need_migrations : & Vec < hir:: HirId > ) -> String {
1165
+ let need_migrations_strings =
1166
+ need_migrations. iter ( ) . map ( |v| format ! ( "{}" , var_name( tcx, * v) ) ) . collect :: < Vec < _ > > ( ) ;
1167
+ let migrations_list_concat = need_migrations_strings. join ( ", " ) ;
1168
+
1169
+ format ! ( "let ({}) = ({});" , migrations_list_concat, migrations_list_concat)
1170
+ }
1171
+
1042
1172
/// Helper function to determine if we need to escalate CaptureKind from
1043
1173
/// CaptureInfo A to B and returns the escalated CaptureInfo.
1044
1174
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
0 commit comments