Skip to content

Commit b421cd5

Browse files
committed
Restrict precision of captures with capture_disjoint_fields set
- No Derefs in move closure, this will result in value behind a reference getting moved. - No projections are applied to raw pointers, since these require unsafe blocks. We capture them completely. Motivations for these are recorded here: https://hackmd.io/71qq-IOpTNqzMkPpAI1dVg?view
1 parent b122908 commit b421cd5

File tree

1 file changed

+61
-9
lines changed

1 file changed

+61
-9
lines changed

compiler/rustc_typeck/src/check/upvar.rs

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
9631015
fn 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

Comments
 (0)