mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-17 01:13:11 +00:00
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.
|
/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
|
||||||
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
|
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)]
|
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
|
||||||
pub struct CapturedPlace<'tcx> {
|
pub struct CapturedPlace<'tcx> {
|
||||||
|
/// The `Place` that is captured.
|
||||||
pub place: HirPlace<'tcx>,
|
pub place: HirPlace<'tcx>,
|
||||||
|
|
||||||
|
/// `CaptureKind` and expression(s) that resulted in such capture of `place`.
|
||||||
pub info: CaptureInfo<'tcx>,
|
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 {
|
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::ByValue(_) => false,
|
||||||
ty::UpvarCapture::ByRef(..) => true,
|
ty::UpvarCapture::ByRef(..) => true,
|
||||||
};
|
};
|
||||||
let mut upvar = Upvar {
|
Upvar {
|
||||||
name: tcx.hir().name(var_hir_id),
|
name: tcx.hir().name(var_hir_id),
|
||||||
var_hir_id,
|
var_hir_id,
|
||||||
by_ref,
|
by_ref,
|
||||||
mutability: Mutability::Not,
|
mutability: captured_place.mutability,
|
||||||
};
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
upvar
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
@ -851,22 +851,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
_ => bug!("Expected an upvar")
|
_ => bug!("Expected an upvar")
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut mutability = Mutability::Not;
|
let mutability = captured_place.mutability;
|
||||||
|
|
||||||
// FIXME(project-rfc-2229#8): Store more precise information
|
// FIXME(project-rfc-2229#8): Store more precise information
|
||||||
let mut name = kw::Empty;
|
let mut name = kw::Empty;
|
||||||
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
|
if let Some(Node::Binding(pat)) = tcx_hir.find(var_id) {
|
||||||
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
|
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
|
||||||
name = ident.name;
|
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;
|
let capture = captured_place.info.capture_kind;
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"place={:?} upvar_ty={:?} capture={:?}",
|
"final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}",
|
||||||
captured_place.place, upvar_ty, capture
|
captured_place.place, upvar_ty, capture, captured_place.mutability,
|
||||||
);
|
);
|
||||||
|
|
||||||
match capture {
|
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) {
|
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
|
||||||
None => {
|
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);
|
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
|
||||||
continue;
|
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
|
// Only need to insert when we don't have an ancestor in the existing min capture list
|
||||||
if !ancestor_found {
|
if !ancestor_found {
|
||||||
|
let mutability = self.determine_capture_mutability(&place);
|
||||||
let captured_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);
|
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> {
|
struct InferBorrowKind<'a, 'tcx> {
|
||||||
|
Loading…
Reference in New Issue
Block a user