@@ -180,7 +180,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
180180 debug ! ( "seed place {:?}" , place) ;
181181
182182 let upvar_id = ty:: UpvarId :: new ( * var_hir_id, local_def_id) ;
183- let capture_kind = self . init_capture_kind ( capture_clause, upvar_id, span) ;
183+ let capture_kind =
184+ self . init_capture_kind_for_place ( & place, capture_clause, upvar_id, span) ;
184185 let fake_info = ty:: CaptureInfo {
185186 capture_kind_expr_id : None ,
186187 path_expr_id : None ,
@@ -205,11 +206,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
205206 // If we have an origin, store it.
206207 if let Some ( origin) = delegate. current_origin . clone ( ) {
207208 let origin = if self . tcx . features ( ) . capture_disjoint_fields {
208- origin
209+ ( origin. 0 , restrict_capture_precision ( origin . 1 ) )
209210 } else {
210- // FIXME(project-rfc-2229#31): Once the changes to support reborrowing are
211- // made, make sure we are selecting and restricting
212- // the origin correctly.
213211 ( origin. 0 , Place { projections : vec ! [ ] , ..origin. 1 } )
214212 } ;
215213
@@ -449,7 +447,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
449447 base => bug ! ( "Expected upvar, found={:?}" , base) ,
450448 } ;
451449
452- let place = restrict_capture_precision ( place, capture_info . capture_kind ) ;
450+ let place = restrict_capture_precision ( place) ;
453451
454452 let min_cap_list = match root_var_min_capture_list. get_mut ( & var_hir_id) {
455453 None => {
@@ -897,15 +895,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
897895 }
898896 }
899897
900- fn init_capture_kind (
898+ fn init_capture_kind_for_place (
901899 & self ,
900+ place : & Place < ' tcx > ,
902901 capture_clause : hir:: CaptureBy ,
903902 upvar_id : ty:: UpvarId ,
904903 closure_span : Span ,
905904 ) -> ty:: UpvarCapture < ' tcx > {
906905 match capture_clause {
907- hir:: CaptureBy :: Value => ty:: UpvarCapture :: ByValue ( None ) ,
908- hir:: CaptureBy :: Ref => {
906+ // In case of a move closure if the data is accessed through a reference we
907+ // want to capture by ref to allow precise capture using reborrows.
908+ //
909+ // If the data will be moved out of this place, then the place will be truncated
910+ // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
911+ // the closure.
912+ hir:: CaptureBy :: Value if !place. deref_tys ( ) . any ( ty:: TyS :: is_ref) => {
913+ ty:: UpvarCapture :: ByValue ( None )
914+ }
915+ hir:: CaptureBy :: Value | hir:: CaptureBy :: Ref => {
909916 let origin = UpvarRegion ( upvar_id, closure_span) ;
910917 let upvar_region = self . next_region_var ( origin) ;
911918 let upvar_borrow = ty:: UpvarBorrow { kind : ty:: ImmBorrow , region : upvar_region } ;
@@ -1109,12 +1116,25 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
11091116 place_with_id, diag_expr_id, mode
11101117 ) ;
11111118
1112- // we only care about moves
1113- match mode {
1114- euv:: Copy => {
1119+ match ( self . capture_clause , mode) {
1120+ // In non-move closures, we only care about moves
1121+ ( hir:: CaptureBy :: Ref , euv:: Copy ) => return ,
1122+
1123+ // We want to capture Copy types that read through a ref via a reborrow
1124+ ( hir:: CaptureBy :: Value , euv:: Copy )
1125+ if place_with_id. place . deref_tys ( ) . any ( ty:: TyS :: is_ref) =>
1126+ {
11151127 return ;
11161128 }
1117- euv:: Move => { }
1129+
1130+ ( hir:: CaptureBy :: Ref , euv:: Move ) | ( hir:: CaptureBy :: Value , euv:: Move | euv:: Copy ) => { }
1131+ } ;
1132+
1133+ let place = truncate_capture_for_move ( place_with_id. place . clone ( ) ) ;
1134+ let place_with_id = PlaceWithHirId { place : place. clone ( ) , hir_id : place_with_id. hir_id } ;
1135+
1136+ if !self . capture_information . contains_key ( & place) {
1137+ self . init_capture_info_for_place ( & place_with_id, diag_expr_id) ;
11181138 }
11191139
11201140 let tcx = self . fcx . tcx ;
@@ -1128,13 +1148,15 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
11281148
11291149 let usage_span = tcx. hir ( ) . span ( diag_expr_id) ;
11301150
1131- // To move out of an upvar, this must be a FnOnce closure
1132- self . adjust_closure_kind (
1133- upvar_id. closure_expr_id ,
1134- ty:: ClosureKind :: FnOnce ,
1135- usage_span,
1136- place_with_id. place . clone ( ) ,
1137- ) ;
1151+ if matches ! ( mode, euv:: Move ) {
1152+ // To move out of an upvar, this must be a FnOnce closure
1153+ self . adjust_closure_kind (
1154+ upvar_id. closure_expr_id ,
1155+ ty:: ClosureKind :: FnOnce ,
1156+ usage_span,
1157+ place. clone ( ) ,
1158+ ) ;
1159+ }
11381160
11391161 let capture_info = ty:: CaptureInfo {
11401162 capture_kind_expr_id : Some ( diag_expr_id) ,
@@ -1317,8 +1339,12 @@ impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
13171339 if let PlaceBase :: Upvar ( upvar_id) = place_with_id. place . base {
13181340 assert_eq ! ( self . closure_def_id. expect_local( ) , upvar_id. closure_expr_id) ;
13191341
1320- let capture_kind =
1321- self . fcx . init_capture_kind ( self . capture_clause , upvar_id, self . closure_span ) ;
1342+ let capture_kind = self . fcx . init_capture_kind_for_place (
1343+ & place_with_id. place ,
1344+ self . capture_clause ,
1345+ upvar_id,
1346+ self . closure_span ,
1347+ ) ;
13221348
13231349 let expr_id = Some ( diag_expr_id) ;
13241350 let capture_info = ty:: CaptureInfo {
@@ -1392,15 +1418,10 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
13921418}
13931419
13941420/// Truncate projections so that following rules are obeyed by the captured `place`:
1395- ///
1396- /// - No Derefs in move closure, this will result in value behind a reference getting moved.
13971421/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
13981422/// them completely.
13991423/// - No Index projections are captured, since arrays are captured completely.
1400- fn restrict_capture_precision < ' tcx > (
1401- mut place : Place < ' tcx > ,
1402- capture_kind : ty:: UpvarCapture < ' tcx > ,
1403- ) -> Place < ' tcx > {
1424+ fn restrict_capture_precision < ' tcx > ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
14041425 if place. projections . is_empty ( ) {
14051426 // Nothing to do here
14061427 return place;
@@ -1412,7 +1433,6 @@ fn restrict_capture_precision<'tcx>(
14121433 }
14131434
14141435 let mut truncated_length = usize:: MAX ;
1415- let mut first_deref_projection = usize:: MAX ;
14161436
14171437 for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
14181438 if proj. ty . is_unsafe_ptr ( ) {
@@ -1426,31 +1446,30 @@ fn restrict_capture_precision<'tcx>(
14261446 truncated_length = truncated_length. min ( i) ;
14271447 break ;
14281448 }
1429- ProjectionKind :: Deref => {
1430- // We only drop Derefs in case of move closures
1431- // There might be an index projection or raw ptr ahead, so we don't stop here.
1432- first_deref_projection = first_deref_projection. min ( i) ;
1433- }
1449+ ProjectionKind :: Deref => { }
14341450 ProjectionKind :: Field ( ..) => { } // ignore
14351451 ProjectionKind :: Subslice => { } // We never capture this
14361452 }
14371453 }
14381454
1439- let length = place
1440- . projections
1441- . len ( )
1442- . min ( truncated_length)
1443- // In case of capture `ByValue` we want to not capture derefs
1444- . min ( match capture_kind {
1445- ty:: UpvarCapture :: ByValue ( ..) => first_deref_projection,
1446- ty:: UpvarCapture :: ByRef ( ..) => usize:: MAX ,
1447- } ) ;
1455+ let length = place. projections . len ( ) . min ( truncated_length) ;
14481456
14491457 place. projections . truncate ( length) ;
14501458
14511459 place
14521460}
14531461
1462+ /// Truncates a place so that the resultant capture doesn't move data out of a reference
1463+ fn truncate_capture_for_move ( mut place : Place < ' tcx > ) -> Place < ' tcx > {
1464+ if let Some ( i) = place. projections . iter ( ) . position ( |proj| proj. kind == ProjectionKind :: Deref ) {
1465+ // We only drop Derefs in case of move closures
1466+ // There might be an index projection or raw ptr ahead, so we don't stop here.
1467+ place. projections . truncate ( i) ;
1468+ }
1469+
1470+ place
1471+ }
1472+
14541473fn construct_place_string ( tcx : TyCtxt < ' _ > , place : & Place < ' tcx > ) -> String {
14551474 let variable_name = match place. base {
14561475 PlaceBase :: Upvar ( upvar_id) => var_name ( tcx, upvar_id. var_path . hir_id ) . to_string ( ) ,
0 commit comments