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:: { 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 {
@@ -475,6 +510,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
475
510
debug ! ( "For closure={:?}, min_captures={:#?}" , closure_def_id, root_var_min_capture_list) ;
476
511
}
477
512
513
+ /// Figures out the list of root variables (and their types) that aren't completely
514
+ /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
515
+ /// some path starting at that root variable **might** be affected.
516
+ ///
517
+ /// The output list would include a root variable if:
518
+ /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
519
+ /// enabled, **and**
520
+ /// - It wasn't completely captured by the closure, **and**
521
+ /// - The type of the root variable needs Drop.
522
+ fn compute_2229_migrations_first_pass (
523
+ & self ,
524
+ closure_def_id : DefId ,
525
+ closure_span : Span ,
526
+ closure_clause : hir:: CaptureBy ,
527
+ body : & ' tcx hir:: Body < ' tcx > ,
528
+ min_captures : Option < & ty:: RootVariableMinCaptureList < ' tcx > > ,
529
+ ) -> Vec < ( hir:: HirId , Ty < ' tcx > ) > {
530
+ fn resolve_ty < T : TypeFoldable < ' tcx > > (
531
+ fcx : & FnCtxt < ' _ , ' tcx > ,
532
+ span : Span ,
533
+ body : & ' tcx hir:: Body < ' tcx > ,
534
+ ty : T ,
535
+ ) -> T {
536
+ let mut resolver = Resolver :: new ( fcx, & span, body) ;
537
+ ty. fold_with ( & mut resolver)
538
+ }
539
+
540
+ let upvars = if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
541
+ upvars
542
+ } else {
543
+ return vec ! [ ] ;
544
+ } ;
545
+
546
+ let mut need_migrations = Vec :: new ( ) ;
547
+
548
+ for ( & var_hir_id, _) in upvars. iter ( ) {
549
+ let ty = resolve_ty ( self , closure_span, body, self . node_ty ( var_hir_id) ) ;
550
+
551
+ if !ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) ) {
552
+ continue ;
553
+ }
554
+
555
+ let root_var_min_capture_list = if let Some ( root_var_min_capture_list) =
556
+ min_captures. and_then ( |m| m. get ( & var_hir_id) )
557
+ {
558
+ root_var_min_capture_list
559
+ } else {
560
+ // The upvar is mentioned within the closure but no path starting from it is
561
+ // used.
562
+
563
+ match closure_clause {
564
+ // Only migrate if closure is a move closure
565
+ hir:: CaptureBy :: Value => need_migrations. push ( ( var_hir_id, ty) ) ,
566
+
567
+ hir:: CaptureBy :: Ref => { }
568
+ }
569
+
570
+ continue ;
571
+ } ;
572
+
573
+ let is_moved = root_var_min_capture_list
574
+ . iter ( )
575
+ . find ( |capture| matches ! ( capture. info. capture_kind, ty:: UpvarCapture :: ByValue ( _) ) )
576
+ . is_some ( ) ;
577
+
578
+ // 1. If we capture more than one path starting at the root variabe then the root variable
579
+ // isn't being captured in its entirety
580
+ // 2. If we only capture one path starting at the root variable, it's still possible
581
+ // that it isn't the root variable completely.
582
+ if is_moved
583
+ && ( ( root_var_min_capture_list. len ( ) > 1 )
584
+ || ( root_var_min_capture_list[ 0 ] . place . projections . len ( ) > 0 ) )
585
+ {
586
+ need_migrations. push ( ( var_hir_id, ty) ) ;
587
+ }
588
+ }
589
+
590
+ need_migrations
591
+ }
592
+
478
593
fn init_capture_kind (
479
594
& self ,
480
595
capture_clause : hir:: CaptureBy ,
@@ -932,6 +1047,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
932
1047
tcx. hir ( ) . name ( var_hir_id)
933
1048
}
934
1049
1050
+ fn should_do_migration_analysis ( tcx : TyCtxt < ' _ > , closure_id : hir:: HirId ) -> bool {
1051
+ let ( level, _) =
1052
+ tcx. lint_level_at_node ( lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER , closure_id) ;
1053
+
1054
+ !matches ! ( level, lint:: Level :: Allow )
1055
+ }
1056
+
1057
+ fn migration_suggestion_for_2229 ( tcx : TyCtxt < ' _ > , need_migrations : & Vec < hir:: HirId > ) -> String {
1058
+ let need_migrations_strings =
1059
+ need_migrations. iter ( ) . map ( |v| format ! ( "{}" , var_name( tcx, * v) ) ) . collect :: < Vec < _ > > ( ) ;
1060
+ let migrations_list_concat = need_migrations_strings. join ( ", " ) ;
1061
+
1062
+ format ! ( "let ({}) = ({});" , migrations_list_concat, migrations_list_concat)
1063
+ }
1064
+
935
1065
/// Helper function to determine if we need to escalate CaptureKind from
936
1066
/// CaptureInfo A to B and returns the escalated CaptureInfo.
937
1067
/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
0 commit comments