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.
This commit is contained in:
parent
b421cd56d9
commit
3488082582
|
@ -661,11 +661,17 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis
|
|||
/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
|
||||
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
|
||||
|
||||
/// A `Place` and the corresponding `CaptureInfo`.
|
||||
/// A composite describing a `Place` that is captured by a closure.
|
||||
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
|
||||
pub struct CapturedPlace<'tcx> {
|
||||
/// The `Place` that is captured.
|
||||
pub place: HirPlace<'tcx>,
|
||||
|
||||
/// `CaptureKind` and expression(s) that resulted in such capture of `place`.
|
||||
pub info: CaptureInfo<'tcx>,
|
||||
|
||||
/// Represents if `place` can be mutated or not.
|
||||
pub mutability: hir::Mutability,
|
||||
}
|
||||
|
||||
pub fn place_to_string_for_capture(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {
|
||||
|
|
|
@ -170,17 +170,12 @@ fn do_mir_borrowck<'a, 'tcx>(
|
|||
ty::UpvarCapture::ByValue(_) => false,
|
||||
ty::UpvarCapture::ByRef(..) => true,
|
||||
};
|
||||
let mut upvar = Upvar {
|
||||
Upvar {
|
||||
name: tcx.hir().name(var_hir_id),
|
||||
var_hir_id,
|
||||
by_ref,
|
||||
mutability: Mutability::Not,
|
||||
};
|
||||
let bm = *tables.pat_binding_modes().get(var_hir_id).expect("missing binding mode");
|
||||
if bm == ty::BindByValue(hir::Mutability::Mut) {
|
||||
upvar.mutability = Mutability::Mut;
|
||||
mutability: captured_place.mutability,
|
||||
}
|
||||
upvar
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -851,22 +851,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
_ => bug!("Expected an upvar")
|
||||
};
|
||||
|
||||
let mut mutability = Mutability::Not;
|
||||
let mutability = captured_place.mutability;
|
||||
|
||||
// FIXME(project-rfc-2229#8): Store more precise information
|
||||
let mut name = kw::Empty;
|
||||
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||
name = ident.name;
|
||||
match hir_typeck_results
|
||||
.extract_binding_mode(tcx.sess, pat.hir_id, pat.span)
|
||||
{
|
||||
Some(ty::BindByValue(hir::Mutability::Mut)) => {
|
||||
mutability = Mutability::Mut;
|
||||
}
|
||||
Some(_) => mutability = Mutability::Not,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -252,8 +252,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let capture = captured_place.info.capture_kind;
|
||||
|
||||
debug!(
|
||||
"place={:?} upvar_ty={:?} capture={:?}",
|
||||
captured_place.place, upvar_ty, capture
|
||||
"final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}",
|
||||
captured_place.place, upvar_ty, capture, captured_place.mutability,
|
||||
);
|
||||
|
||||
match capture {
|
||||
|
@ -423,7 +423,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
|
||||
None => {
|
||||
let min_cap_list = vec![ty::CapturedPlace { place, info: capture_info }];
|
||||
let mutability = self.determine_capture_mutability(&place);
|
||||
let min_cap_list =
|
||||
vec![ty::CapturedPlace { place, info: capture_info, mutability }];
|
||||
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
|
||||
continue;
|
||||
}
|
||||
|
@ -486,8 +488,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
// Only need to insert when we don't have an ancestor in the existing min capture list
|
||||
if !ancestor_found {
|
||||
let mutability = self.determine_capture_mutability(&place);
|
||||
let captured_place =
|
||||
ty::CapturedPlace { place: place.clone(), info: updated_capture_info };
|
||||
ty::CapturedPlace { place, info: updated_capture_info, mutability };
|
||||
min_cap_list.push(captured_place);
|
||||
}
|
||||
}
|
||||
|
@ -607,6 +610,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A captured place is mutable if
|
||||
/// 1. Projections don't include a Deref of an immut-borrow, **and**
|
||||
/// 2. PlaceBase is mut or projections include a Deref of a mut-borrow.
|
||||
fn determine_capture_mutability(&self, place: &Place<'tcx>) -> hir::Mutability {
|
||||
let var_hir_id = match place.base {
|
||||
PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let bm = *self
|
||||
.typeck_results
|
||||
.borrow()
|
||||
.pat_binding_modes()
|
||||
.get(var_hir_id)
|
||||
.expect("missing binding mode");
|
||||
|
||||
let mut is_mutbl = match bm {
|
||||
ty::BindByValue(mutability) => mutability,
|
||||
ty::BindByReference(_) => hir::Mutability::Not,
|
||||
};
|
||||
|
||||
for pointer_ty in place.deref_tys() {
|
||||
match pointer_ty.kind() {
|
||||
// We don't capture derefs of raw ptrs
|
||||
ty::RawPtr(_) => unreachable!(),
|
||||
|
||||
// Derefencing a mut-ref allows us to mut the Place if we don't deref
|
||||
// an immut-ref after on top of this.
|
||||
ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut,
|
||||
|
||||
// The place isn't mutable once we dereference a immutable reference.
|
||||
ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not,
|
||||
|
||||
// Dereferencing a box doesn't change mutability
|
||||
ty::Adt(def, ..) if def.is_box() => {}
|
||||
|
||||
unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty),
|
||||
}
|
||||
}
|
||||
|
||||
is_mutbl
|
||||
}
|
||||
}
|
||||
|
||||
struct InferBorrowKind<'a, 'tcx> {
|
||||
|
|
Loading…
Reference in New Issue