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
@@ -55,6 +58,11 @@ enum PlaceAncestryRelation {
5558 Divergent ,
5659}
5760
61+ /// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo`
62+ /// during capture analysis. Information in this map feeds into the minimum capture
63+ /// analysis pass.
64+ type InferredCaptureInformation < ' tcx > = FxIndexMap < Place < ' tcx > , ty:: CaptureInfo < ' tcx > > ;
65+
5866impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
5967 pub fn closure_analyze ( & self , body : & ' tcx hir:: Body < ' tcx > ) {
6068 InferBorrowKindVisitor { fcx : self } . visit_body ( body) ;
@@ -92,7 +100,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
92100 & self ,
93101 closure_hir_id : hir:: HirId ,
94102 span : Span ,
95- body : & hir:: Body < ' _ > ,
103+ body : & ' tcx hir:: Body < ' tcx > ,
96104 capture_clause : hir:: CaptureBy ,
97105 ) {
98106 debug ! ( "analyze_closure(id={:?}, body.id={:?})" , closure_hir_id, body. id( ) ) ;
@@ -124,28 +132,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
124132
125133 let local_def_id = closure_def_id. expect_local ( ) ;
126134
127- let mut capture_information: FxIndexMap < Place < ' tcx > , ty:: CaptureInfo < ' tcx > > =
128- Default :: default ( ) ;
129- if !self . tcx . features ( ) . capture_disjoint_fields {
130- if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
131- for ( & var_hir_id, _) in upvars. iter ( ) {
132- let place = self . place_for_root_variable ( local_def_id, var_hir_id) ;
133-
134- debug ! ( "seed place {:?}" , place) ;
135-
136- let upvar_id = ty:: UpvarId :: new ( var_hir_id, local_def_id) ;
137- let capture_kind = self . init_capture_kind ( capture_clause, upvar_id, span) ;
138- let info = ty:: CaptureInfo {
139- capture_kind_expr_id : None ,
140- path_expr_id : None ,
141- capture_kind,
142- } ;
143-
144- capture_information. insert ( place, info) ;
145- }
146- }
147- }
148-
149135 let body_owner_def_id = self . tcx . hir ( ) . body_owner_def_id ( body. id ( ) ) ;
150136 assert_eq ! ( body_owner_def_id. to_def_id( ) , closure_def_id) ;
151137 let mut delegate = InferBorrowKind {
@@ -155,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
155141 capture_clause,
156142 current_closure_kind : ty:: ClosureKind :: LATTICE_BOTTOM ,
157143 current_origin : None ,
158- capture_information,
144+ capture_information : Default :: default ( ) ,
159145 } ;
160146 euv:: ExprUseVisitor :: new (
161147 & mut delegate,
@@ -172,6 +158,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
172158 ) ;
173159 self . log_capture_analysis_first_pass ( closure_def_id, & delegate. capture_information , span) ;
174160
161+ self . compute_min_captures ( closure_def_id, delegate. capture_information ) ;
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+ self . perform_2229_migration_anaysis ( closure_def_id, capture_clause, span, body) ;
166+ }
167+
168+ // We now fake capture information for all variables that are mentioned within the closure
169+ // We do this after handling migrations so that min_captures computes before
170+ if !self . tcx . features ( ) . capture_disjoint_fields {
171+ let mut capture_information: InferredCaptureInformation < ' tcx > = Default :: default ( ) ;
172+
173+ if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
174+ for var_hir_id in upvars. keys ( ) {
175+ let place = self . place_for_root_variable ( local_def_id, * var_hir_id) ;
176+
177+ debug ! ( "seed place {:?}" , place) ;
178+
179+ let upvar_id = ty:: UpvarId :: new ( * var_hir_id, local_def_id) ;
180+ let capture_kind = self . init_capture_kind ( capture_clause, upvar_id, span) ;
181+ let fake_info = ty:: CaptureInfo {
182+ capture_kind_expr_id : None ,
183+ path_expr_id : None ,
184+ capture_kind,
185+ } ;
186+
187+ capture_information. insert ( place, fake_info) ;
188+ }
189+ }
190+
191+ // This will update the min captures based on this new fake information.
192+ self . compute_min_captures ( closure_def_id, capture_information) ;
193+ }
194+
175195 if let Some ( closure_substs) = infer_kind {
176196 // Unify the (as yet unbound) type variable in the closure
177197 // substs with the kind we inferred.
@@ -197,7 +217,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
197217 }
198218 }
199219
200- self . compute_min_captures ( closure_def_id, delegate) ;
201220 self . log_closure_min_capture_info ( closure_def_id, span) ;
202221
203222 self . min_captures_to_closure_captures_bridge ( closure_def_id) ;
@@ -344,6 +363,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
344363 /// Places (and corresponding capture kind) that we need to keep track of to support all
345364 /// the required captured paths.
346365 ///
366+ ///
367+ /// Note: If this function is called multiple times for the same closure, it will update
368+ /// the existing min_capture map that is stored in TypeckResults.
369+ ///
347370 /// Eg:
348371 /// ```rust,no_run
349372 /// struct Point { x: i32, y: i32 }
@@ -408,11 +431,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
408431 fn compute_min_captures (
409432 & self ,
410433 closure_def_id : DefId ,
411- inferred_info : InferBorrowKind < ' _ , ' tcx > ,
434+ capture_information : InferredCaptureInformation < ' tcx > ,
412435 ) {
413- let mut root_var_min_capture_list: ty:: RootVariableMinCaptureList < ' _ > = Default :: default ( ) ;
436+ if capture_information. is_empty ( ) {
437+ return ;
438+ }
414439
415- for ( place, capture_info) in inferred_info. capture_information . into_iter ( ) {
440+ let mut typeck_results = self . typeck_results . borrow_mut ( ) ;
441+
442+ let root_var_min_capture_list =
443+ typeck_results. closure_min_captures . entry ( closure_def_id) . or_insert ( Default :: default ( ) ) ;
444+
445+ for ( place, capture_info) in capture_information. into_iter ( ) {
416446 let var_hir_id = match place. base {
417447 PlaceBase :: Upvar ( upvar_id) => upvar_id. var_path . hir_id ,
418448 base => bug ! ( "Expected upvar, found={:?}" , base) ,
@@ -495,15 +525,122 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
495525 }
496526
497527 debug ! ( "For closure={:?}, min_captures={:#?}" , closure_def_id, root_var_min_capture_list) ;
528+ }
498529
499- if !root_var_min_capture_list. is_empty ( ) {
500- self . typeck_results
501- . borrow_mut ( )
502- . closure_min_captures
503- . insert ( closure_def_id, root_var_min_capture_list) ;
530+ /// Perform the migration analysis for RFC 2229, and emit lint
531+ /// `disjoint_capture_drop_reorder` if needed.
532+ fn perform_2229_migration_anaysis (
533+ & self ,
534+ closure_def_id : DefId ,
535+ capture_clause : hir:: CaptureBy ,
536+ span : Span ,
537+ body : & ' tcx hir:: Body < ' tcx > ,
538+ ) {
539+ let need_migrations = self . compute_2229_migrations_first_pass (
540+ closure_def_id,
541+ span,
542+ capture_clause,
543+ body,
544+ self . typeck_results . borrow ( ) . closure_min_captures . get ( & closure_def_id) ,
545+ ) ;
546+
547+ if !need_migrations. is_empty ( ) {
548+ let need_migrations_hir_id = need_migrations. iter ( ) . map ( |m| m. 0 ) . collect :: < Vec < _ > > ( ) ;
549+
550+ let migrations_text = migration_suggestion_for_2229 ( self . tcx , & need_migrations_hir_id) ;
551+
552+ let local_def_id = closure_def_id. expect_local ( ) ;
553+ let closure_hir_id = self . tcx . hir ( ) . local_def_id_to_hir_id ( local_def_id) ;
554+ self . tcx . struct_span_lint_hir (
555+ lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER ,
556+ closure_hir_id,
557+ span,
558+ |lint| {
559+ let mut diagnostics_builder = lint. build (
560+ "Drop order affected for closure because of `capture_disjoint_fields`" ,
561+ ) ;
562+ diagnostics_builder. note ( & migrations_text) ;
563+ diagnostics_builder. emit ( ) ;
564+ } ,
565+ ) ;
504566 }
505567 }
506568
569+ /// Figures out the list of root variables (and their types) that aren't completely
570+ /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of
571+ /// some path starting at that root variable **might** be affected.
572+ ///
573+ /// The output list would include a root variable if:
574+ /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't
575+ /// enabled, **and**
576+ /// - It wasn't completely captured by the closure, **and**
577+ /// - The type of the root variable needs Drop.
578+ fn compute_2229_migrations_first_pass (
579+ & self ,
580+ closure_def_id : DefId ,
581+ closure_span : Span ,
582+ closure_clause : hir:: CaptureBy ,
583+ body : & ' tcx hir:: Body < ' tcx > ,
584+ min_captures : Option < & ty:: RootVariableMinCaptureList < ' tcx > > ,
585+ ) -> Vec < ( hir:: HirId , Ty < ' tcx > ) > {
586+ fn resolve_ty < T : TypeFoldable < ' tcx > > (
587+ fcx : & FnCtxt < ' _ , ' tcx > ,
588+ span : Span ,
589+ body : & ' tcx hir:: Body < ' tcx > ,
590+ ty : T ,
591+ ) -> T {
592+ let mut resolver = Resolver :: new ( fcx, & span, body) ;
593+ ty. fold_with ( & mut resolver)
594+ }
595+
596+ let upvars = if let Some ( upvars) = self . tcx . upvars_mentioned ( closure_def_id) {
597+ upvars
598+ } else {
599+ return vec ! [ ] ;
600+ } ;
601+
602+ let mut need_migrations = Vec :: new ( ) ;
603+
604+ for ( & var_hir_id, _) in upvars. iter ( ) {
605+ let ty = resolve_ty ( self , closure_span, body, self . node_ty ( var_hir_id) ) ;
606+
607+ if !ty. needs_drop ( self . tcx , self . tcx . param_env ( closure_def_id. expect_local ( ) ) ) {
608+ continue ;
609+ }
610+
611+ let root_var_min_capture_list = if let Some ( root_var_min_capture_list) =
612+ min_captures. and_then ( |m| m. get ( & var_hir_id) )
613+ {
614+ root_var_min_capture_list
615+ } else {
616+ // The upvar is mentioned within the closure but no path starting from it is
617+ // used.
618+
619+ match closure_clause {
620+ // Only migrate if closure is a move closure
621+ hir:: CaptureBy :: Value => need_migrations. push ( ( var_hir_id, ty) ) ,
622+
623+ hir:: CaptureBy :: Ref => { }
624+ }
625+
626+ continue ;
627+ } ;
628+
629+ let is_moved = root_var_min_capture_list
630+ . iter ( )
631+ . any ( |capture| matches ! ( capture. info. capture_kind, ty:: UpvarCapture :: ByValue ( _) ) ) ;
632+
633+ let is_not_completely_captured =
634+ root_var_min_capture_list. iter ( ) . any ( |capture| capture. place . projections . len ( ) > 0 ) ;
635+
636+ if is_moved && is_not_completely_captured {
637+ need_migrations. push ( ( var_hir_id, ty) ) ;
638+ }
639+ }
640+
641+ need_migrations
642+ }
643+
507644 fn init_capture_kind (
508645 & self ,
509646 capture_clause : hir:: CaptureBy ,
@@ -698,9 +835,11 @@ struct InferBorrowKind<'a, 'tcx> {
698835 ///
699836 /// For closure `fix_s`, (at a high level) the map contains
700837 ///
838+ /// ```
701839 /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow }
702840 /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow }
703- capture_information : FxIndexMap < Place < ' tcx > , ty:: CaptureInfo < ' tcx > > ,
841+ /// ```
842+ capture_information : InferredCaptureInformation < ' tcx > ,
704843}
705844
706845impl < ' a , ' tcx > InferBorrowKind < ' a , ' tcx > {
@@ -1119,6 +1258,21 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol {
11191258 tcx. hir ( ) . name ( var_hir_id)
11201259}
11211260
1261+ fn should_do_migration_analysis ( tcx : TyCtxt < ' _ > , closure_id : hir:: HirId ) -> bool {
1262+ let ( level, _) =
1263+ tcx. lint_level_at_node ( lint:: builtin:: DISJOINT_CAPTURE_DROP_REORDER , closure_id) ;
1264+
1265+ !matches ! ( level, lint:: Level :: Allow )
1266+ }
1267+
1268+ fn migration_suggestion_for_2229 ( tcx : TyCtxt < ' _ > , need_migrations : & Vec < hir:: HirId > ) -> String {
1269+ let need_migrations_strings =
1270+ need_migrations. iter ( ) . map ( |v| format ! ( "{}" , var_name( tcx, * v) ) ) . collect :: < Vec < _ > > ( ) ;
1271+ let migrations_list_concat = need_migrations_strings. join ( ", " ) ;
1272+
1273+ format ! ( "drop(&({}));" , migrations_list_concat)
1274+ }
1275+
11221276/// Helper function to determine if we need to escalate CaptureKind from
11231277/// CaptureInfo A to B and returns the escalated CaptureInfo.
11241278/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way)
0 commit comments