3030//! then mean that all later passes would have to check for these figments
3131//! and report an error, and it just seems like more mess in the end.)
3232
33+ use super :: writeback:: Resolver ;
3334use super :: FnCtxt ;
3435
3536use crate :: expr_use_visitor as euv;
@@ -40,7 +41,9 @@ use rustc_hir::def_id::LocalDefId;
4041use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
4142use rustc_infer:: infer:: UpvarRegion ;
4243use rustc_middle:: hir:: place:: { Place , PlaceBase , PlaceWithHirId , ProjectionKind } ;
44+ use rustc_middle:: ty:: fold:: TypeFoldable ;
4345use rustc_middle:: ty:: { self , Ty , TyCtxt , UpvarSubsts } ;
46+ use rustc_session:: lint;
4447use rustc_span:: sym;
4548use rustc_span:: { MultiSpan , Span , Symbol } ;
4649
@@ -97,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
97100 & self ,
98101 closure_hir_id : hir:: HirId ,
99102 span : Span ,
100- body : & hir:: Body < ' _ > ,
103+ body : & ' tcx hir:: Body < ' tcx > ,
101104 capture_clause : hir:: CaptureBy ,
102105 ) {
103106 debug ! ( "analyze_closure(id={:?}, body.id={:?})" , closure_hir_id, body. id( ) ) ;
@@ -157,6 +160,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
157160
158161 self . compute_min_captures ( closure_def_id, delegate. capture_information ) ;
159162
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+
160195 // We now fake capture information for all variables that are mentioned within the closure
161196 // We do this after handling migrations so that min_captures computes before
162197 if !self . tcx . features ( ) . capture_disjoint_fields {
@@ -525,6 +560,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
525560 debug ! ( "For closure={:?}, min_captures={:#?}" , closure_def_id, root_var_min_capture_list) ;
526561 }
527562
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+
528643 fn init_capture_kind (
529644 & self ,
530645 capture_clause : hir:: CaptureBy ,
@@ -1039,6 +1154,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
10391154 tcx. hir ( ) . name ( var_hir_id)
10401155}
10411156
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+
10421172/// Helper function to determine if we need to escalate CaptureKind from
10431173/// CaptureInfo A to B and returns the escalated CaptureInfo.
10441174/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
0 commit comments