diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index f039445bf77..56b91ee4bcb 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -40,7 +40,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, ProjectionKind}; -use rustc_middle::ty::{self, Ty, TyCtxt, UpvarSubsts}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults, UpvarSubsts}; use rustc_span::sym; use rustc_span::{MultiSpan, Span, Symbol}; @@ -55,6 +55,11 @@ enum PlaceAncestryRelation { Divergent, } +/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo` +/// during capture analysis. Information in this map feeds into the minimum capture +/// analysis pass. +type InferredCaptureInformation<'tcx> = FxIndexMap, ty::CaptureInfo<'tcx>>; + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { InferBorrowKindVisitor { fcx: self }.visit_body(body); @@ -124,28 +129,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); - let mut capture_information: FxIndexMap, ty::CaptureInfo<'tcx>> = - Default::default(); - if !self.tcx.features().capture_disjoint_fields { - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - for (&var_hir_id, _) in upvars.iter() { - let place = self.place_for_root_variable(local_def_id, var_hir_id); - - debug!("seed place {:?}", place); - - let upvar_id = ty::UpvarId::new(var_hir_id, local_def_id); - let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span); - let info = ty::CaptureInfo { - capture_kind_expr_id: None, - path_expr_id: None, - capture_kind, - }; - - capture_information.insert(place, info); - } - } - } - let body_owner_def_id = self.tcx.hir().body_owner_def_id(body.id()); assert_eq!(body_owner_def_id.to_def_id(), closure_def_id); let mut delegate = InferBorrowKind { @@ -155,7 +138,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause, current_closure_kind: ty::ClosureKind::LATTICE_BOTTOM, current_origin: None, - capture_information, + capture_information: Default::default(), }; euv::ExprUseVisitor::new( &mut delegate, @@ -172,6 +155,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); + self.compute_min_captures(closure_def_id, delegate.capture_information); + + // We now fake capture information for all variables that are mentioned within the closure + // We do this after handling migrations so that min_captures computes before + if !self.tcx.features().capture_disjoint_fields { + let mut capture_information: InferredCaptureInformation<'tcx> = Default::default(); + + if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + for var_hir_id in upvars.keys() { + let place = self.place_for_root_variable(local_def_id, *var_hir_id); + + debug!("seed place {:?}", place); + + let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id); + let capture_kind = self.init_capture_kind(capture_clause, upvar_id, span); + let fake_info = ty::CaptureInfo { + capture_kind_expr_id: None, + path_expr_id: None, + capture_kind, + }; + + capture_information.insert(place, fake_info); + } + } + + // This will update the min captures based on this new fake information. + self.compute_min_captures(closure_def_id, capture_information); + } + if let Some(closure_substs) = infer_kind { // Unify the (as yet unbound) type variable in the closure // substs with the kind we inferred. @@ -197,7 +209,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - self.compute_min_captures(closure_def_id, delegate); self.log_closure_min_capture_info(closure_def_id, span); self.min_captures_to_closure_captures_bridge(closure_def_id); @@ -344,6 +355,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Places (and corresponding capture kind) that we need to keep track of to support all /// the required captured paths. /// + /// + /// Note: If this function is called multiple times for the same closure, it will update + /// the existing min_capture map that is stored in TypeckResults. + /// /// Eg: /// ```rust,no_run /// struct Point { x: i32, y: i32 } @@ -408,11 +423,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn compute_min_captures( &self, closure_def_id: DefId, - inferred_info: InferBorrowKind<'_, 'tcx>, + capture_information: InferredCaptureInformation<'tcx>, ) { - let mut root_var_min_capture_list: ty::RootVariableMinCaptureList<'_> = Default::default(); + if capture_information.is_empty() { + return; + } - for (place, capture_info) in inferred_info.capture_information.into_iter() { + let mut typeck_results = self.typeck_results.borrow_mut(); + + let mut root_var_min_capture_list = + typeck_results.closure_min_captures.remove(&closure_def_id).unwrap_or_default(); + + for (place, capture_info) in capture_information.into_iter() { let var_hir_id = match place.base { PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, base => bug!("Expected upvar, found={:?}", base), @@ -422,7 +444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) { None => { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(&typeck_results, &place); let min_cap_list = vec![ty::CapturedPlace { place, info: capture_info, mutability }]; root_var_min_capture_list.insert(var_hir_id, min_cap_list); @@ -487,7 +509,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Only need to insert when we don't have an ancestor in the existing min capture list if !ancestor_found { - let mutability = self.determine_capture_mutability(&place); + let mutability = self.determine_capture_mutability(&typeck_results, &place); let captured_place = ty::CapturedPlace { place, info: updated_capture_info, mutability }; min_cap_list.push(captured_place); @@ -495,13 +517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } debug!("For closure={:?}, min_captures={:#?}", closure_def_id, root_var_min_capture_list); - - if !root_var_min_capture_list.is_empty() { - self.typeck_results - .borrow_mut() - .closure_min_captures - .insert(closure_def_id, root_var_min_capture_list); - } + typeck_results.closure_min_captures.insert(closure_def_id, root_var_min_capture_list); } fn init_capture_kind( @@ -613,18 +629,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// A captured place is mutable if /// 1. Projections don't include a Deref of an immut-borrow, **and** /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. - fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability { + fn determine_capture_mutability( + &self, + typeck_results: &'a TypeckResults<'tcx>, + place: &Place<'tcx>, + ) -> hir::Mutability { let var_hir_id = match place.base { PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, _ => unreachable!(), }; - let bm = *self - .typeck_results - .borrow() - .pat_binding_modes() - .get(var_hir_id) - .expect("missing binding mode"); + let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); let mut is_mutbl = match bm { ty::BindByValue(mutability) => mutability, @@ -698,9 +713,11 @@ struct InferBorrowKind<'a, 'tcx> { /// /// For closure `fix_s`, (at a high level) the map contains /// + /// ``` /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } - capture_information: FxIndexMap, ty::CaptureInfo<'tcx>>, + /// ``` + capture_information: InferredCaptureInformation<'tcx>, } impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> { diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr index 1a825837614..e1b446fc61f 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-nested.stderr @@ -7,10 +7,10 @@ LL | let mut closure1 = || p = &y; = note: defining type: test::{closure#0}::{closure#0} with closure substs [ i16, extern "rust-call" fn(()), - (&'_#1r i32, &'_#2r mut &'_#3r i32), + (&'_#1r mut &'_#2r i32, &'_#3r i32), ] = note: number of external vids: 4 - = note: where '_#1r: '_#3r + = note: where '_#3r: '_#2r note: external requirements --> $DIR/escape-upvar-nested.rs:20:27 @@ -25,10 +25,10 @@ LL | | }; = note: defining type: test::{closure#0} with closure substs [ i16, extern "rust-call" fn(()), - (&'_#1r i32, &'_#2r mut &'_#3r i32), + (&'_#1r mut &'_#2r i32, &'_#3r i32), ] = note: number of external vids: 4 - = note: where '_#1r: '_#3r + = note: where '_#3r: '_#2r note: no external requirements --> $DIR/escape-upvar-nested.rs:13:1 diff --git a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr index 29fd796882b..0ea1076c32e 100644 --- a/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr +++ b/src/test/ui/nll/closure-requirements/escape-upvar-ref.stderr @@ -7,10 +7,10 @@ LL | let mut closure = || p = &y; = note: defining type: test::{closure#0} with closure substs [ i16, extern "rust-call" fn(()), - (&'_#1r i32, &'_#2r mut &'_#3r i32), + (&'_#1r mut &'_#2r i32, &'_#3r i32), ] = note: number of external vids: 4 - = note: where '_#1r: '_#3r + = note: where '_#3r: '_#2r note: no external requirements --> $DIR/escape-upvar-ref.rs:17:1