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:: { 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 {
@@ -475,6 +510,86 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
475510 debug ! ( "For closure={:?}, min_captures={:#?}" , closure_def_id, root_var_min_capture_list) ;
476511 }
477512
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+
478593 fn init_capture_kind (
479594 & self ,
480595 capture_clause : hir:: CaptureBy ,
@@ -932,6 +1047,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
9321047 tcx. hir ( ) . name ( var_hir_id)
9331048}
9341049
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+
9351065/// Helper function to determine if we need to escalate CaptureKind from
9361066/// CaptureInfo A to B and returns the escalated CaptureInfo.
9371067/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
0 commit comments