@@ -419,15 +419,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
419
419
base => bug ! ( "Expected upvar, found={:?}" , base) ,
420
420
} ;
421
421
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 ) ;
431
423
432
424
let min_cap_list = match root_var_min_capture_list. get_mut ( & var_hir_id) {
433
425
None => {
@@ -960,6 +952,66 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
960
952
}
961
953
}
962
954
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
+
963
1015
fn construct_place_string ( tcx : TyCtxt < ' _ > , place : & Place < ' tcx > ) -> String {
964
1016
let variable_name = match place. base {
965
1017
PlaceBase :: Upvar ( upvar_id) => var_name ( tcx, upvar_id. var_path . hir_id ) . to_string ( ) ,
0 commit comments