Skip to content

Commit 3488082

Browse files
committed
Compute mutability of closure captures
When `capture_disjoint_fields` is not enabled, checking if the root variable binding is mutable would suffice. However with the feature enabled, the captured place might be mutable because it dereferences a mutable reference. This PR computes the mutability of each capture after capture analysis in rustc_typeck. We store this in `ty::CapturedPlace` and then use `ty::CapturedPlace::mutability` in mir_build and borrow_check.
1 parent b421cd5 commit 3488082

File tree

4 files changed

+60
-22
lines changed

4 files changed

+60
-22
lines changed

compiler/rustc_middle/src/ty/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -661,11 +661,17 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis
661661
/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
662662
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
663663

664-
/// A `Place` and the corresponding `CaptureInfo`.
664+
/// A composite describing a `Place` that is captured by a closure.
665665
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
666666
pub struct CapturedPlace<'tcx> {
667+
/// The `Place` that is captured.
667668
pub place: HirPlace<'tcx>,
669+
670+
/// `CaptureKind` and expression(s) that resulted in such capture of `place`.
668671
pub info: CaptureInfo<'tcx>,
672+
673+
/// Represents if `place` can be mutated or not.
674+
pub mutability: hir::Mutability,
669675
}
670676

671677
pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {

compiler/rustc_mir/src/borrow_check/mod.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,12 @@ fn do_mir_borrowck<'a, 'tcx>(
170170
ty::UpvarCapture::ByValue(_) => false,
171171
ty::UpvarCapture::ByRef(..) => true,
172172
};
173-
let mut upvar = Upvar {
173+
Upvar {
174174
name: tcx.hir().name(var_hir_id),
175175
var_hir_id,
176176
by_ref,
177-
mutability: Mutability::Not,
178-
};
179-
let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
180-
if bm == ty::BindByValue(hir::Mutability::Mut) {
181-
upvar.mutability = Mutability::Mut;
177+
mutability: captured_place.mutability,
182178
}
183-
upvar
184179
})
185180
.collect();
186181

compiler/rustc_mir_build/src/build/mod.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -851,22 +851,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
851851
_ => bug!("Expected an upvar")
852852
};
853853

854-
let mut mutability = Mutability::Not;
854+
let mutability = captured_place.mutability;
855855

856856
// FIXME(project-rfc-2229#8): Store more precise information
857857
let mut name = kw::Empty;
858858
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
859859
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
860860
name = ident.name;
861-
match hir_typeck_results
862-
.extract_binding_mode(tcx.sess, pat.hir_id, pat.span)
863-
{
864-
Some(ty::BindByValue(hir::Mutability::Mut)) => {
865-
mutability = Mutability::Mut;
866-
}
867-
Some(_) => mutability = Mutability::Not,
868-
_ => {}
869-
}
870861
}
871862
}
872863

compiler/rustc_typeck/src/check/upvar.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
252252
let capture = captured_place.info.capture_kind;
253253

254254
debug!(
255-
"place={:?} upvar_ty={:?} capture={:?}",
256-
captured_place.place, upvar_ty, capture
255+
"final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}",
256+
captured_place.place, upvar_ty, capture, captured_place.mutability,
257257
);
258258

259259
match capture {
@@ -423,7 +423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
423423

424424
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
425425
None => {
426-
let min_cap_list = vec![ty::CapturedPlace { place, info: capture_info }];
426+
let mutability = self.determine_capture_mutability(&place);
427+
let min_cap_list =
428+
vec![ty::CapturedPlace { place, info: capture_info, mutability }];
427429
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
428430
continue;
429431
}
@@ -486,8 +488,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
486488

487489
// Only need to insert when we don't have an ancestor in the existing min capture list
488490
if !ancestor_found {
491+
let mutability = self.determine_capture_mutability(&place);
489492
let captured_place =
490-
ty::CapturedPlace { place: place.clone(), info: updated_capture_info };
493+
ty::CapturedPlace { place, info: updated_capture_info, mutability };
491494
min_cap_list.push(captured_place);
492495
}
493496
}
@@ -607,6 +610,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
607610
}
608611
}
609612
}
613+
614+
/// A captured place is mutable if
615+
/// 1. Projections don't include a Deref of an immut-borrow, **and**
616+
/// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
617+
fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability {
618+
let var_hir_id = match place.base {
619+
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
620+
_ => unreachable!(),
621+
};
622+
623+
let bm = *self
624+
.typeck_results
625+
.borrow()
626+
.pat_binding_modes()
627+
.get(var_hir_id)
628+
.expect("missing binding mode");
629+
630+
let mut is_mutbl = match bm {
631+
ty::BindByValue(mutability) => mutability,
632+
ty::BindByReference(_) => hir::Mutability::Not,
633+
};
634+
635+
for pointer_ty in place.deref_tys() {
636+
match pointer_ty.kind() {
637+
// We don't capture derefs of raw ptrs
638+
ty::RawPtr(_) => unreachable!(),
639+
640+
// Derefencing a mut-ref allows us to mut the Place if we don't deref
641+
// an immut-ref after on top of this.
642+
ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut,
643+
644+
// The place isn't mutable once we dereference a immutable reference.
645+
ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not,
646+
647+
// Dereferencing a box doesn't change mutability
648+
ty::Adt(def, ..) if def.is_box() => {}
649+
650+
unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty),
651+
}
652+
}
653+
654+
is_mutbl
655+
}
610656
}
611657

612658
struct InferBorrowKind<'a, 'tcx> {

0 commit comments

Comments
 (0)