@@ -419,15 +419,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
419419 base => bug ! ( "Expected upvar, found={:?}" , base) ,
420420 } ;
421421
422- // Arrays are captured in entirety, drop Index projections and projections
423- // after Index projections.
424- let first_index_projection =
425- place. projections . split ( |proj| ProjectionKind :: Index == proj. kind ) . next ( ) ;
426- let place = Place {
427- base_ty : place. base_ty ,
428- base : place. base ,
429- projections : first_index_projection. map_or ( Vec :: new ( ) , |p| p. to_vec ( ) ) ,
430- } ;
422+ let place = restrict_capture_precision ( place, capture_info. capture_kind ) ;
431423
432424 let min_cap_list = match root_var_min_capture_list. get_mut ( & var_hir_id) {
433425 None => {
@@ -960,6 +952,66 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
960952 }
961953}
962954
955+ /// Truncate projections so that following rules are obeyed by the captured `place`:
956+ ///
957+ /// - No Derefs in move closure, this will result in value behind a reference getting moved.
958+ /// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
959+ /// them completely.
960+ /// - No Index projections are captured, since arrays are captured completely.
961+ fn restrict_capture_precision < ' tcx > (
962+ mut place : Place < ' tcx > ,
963+ capture_kind : ty:: UpvarCapture < ' tcx > ,
964+ ) -> Place < ' tcx > {
965+ if place. projections . is_empty ( ) {
966+ // Nothing to do here
967+ return place;
968+ }
969+
970+ if place. base_ty . is_unsafe_ptr ( ) {
971+ place. projections . truncate ( 0 ) ;
972+ return place;
973+ }
974+
975+ let mut truncated_length = usize:: MAX ;
976+ let mut first_deref_projection = usize:: MAX ;
977+
978+ for ( i, proj) in place. projections . iter ( ) . enumerate ( ) {
979+ if proj. ty . is_unsafe_ptr ( ) {
980+ // Don't apply any projections on top of an unsafe ptr
981+ truncated_length = truncated_length. min ( i + 1 ) ;
982+ break ;
983+ }
984+ match proj. kind {
985+ ProjectionKind :: Index => {
986+ // Arrays are completely captured, so we drop Index projections
987+ truncated_length = truncated_length. min ( i) ;
988+ break ;
989+ }
990+ ProjectionKind :: Deref => {
991+ // We only drop Derefs in case of move closures
992+ // There might be an index projection or raw ptr ahead, so we don't stop here.
993+ first_deref_projection = first_deref_projection. min ( i) ;
994+ }
995+ ProjectionKind :: Field ( ..) => { } // ignore
996+ ProjectionKind :: Subslice => { } // We never capture this
997+ }
998+ }
999+
1000+ let length = place
1001+ . projections
1002+ . len ( )
1003+ . min ( truncated_length)
1004+ // In case of capture `ByValue` we want to not capture derefs
1005+ . min ( match capture_kind {
1006+ ty:: UpvarCapture :: ByValue ( ..) => first_deref_projection,
1007+ ty:: UpvarCapture :: ByRef ( ..) => usize:: MAX ,
1008+ } ) ;
1009+
1010+ place. projections . truncate ( length) ;
1011+
1012+ place
1013+ }
1014+
9631015fn construct_place_string ( tcx : TyCtxt < ' _ > , place : & Place < ' tcx > ) -> String {
9641016 let variable_name = match place. base {
9651017 PlaceBase :: Upvar ( upvar_id) => var_name ( tcx, upvar_id. var_path . hir_id ) . to_string ( ) ,
0 commit comments