Auto merge of #89861 - nbdd0121:closure, r=wesleywiser

Closure capture cleanup & refactor

Follow up of #89648

Each commit is self-contained and the rationale/changes are documented in the commit message, so it's advisable to review commit by commit.

The code is significantly cleaner (at least IMO), but that could have some perf implication, so I'd suggest a perf run.

r? `@wesleywiser`
cc `@arora-aman`
This commit is contained in:
bors 2022-01-13 18:51:07 +00:00
commit 22e491ac7e
23 changed files with 324 additions and 419 deletions

View File

@ -706,13 +706,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
&origin_projection,
) {
match captured_place.info.capture_kind {
ty::UpvarCapture::ByRef(ty::UpvarBorrow {
kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
..
}) => {
ty::UpvarCapture::ByRef(
ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
) => {
capture_reason = format!("mutable borrow of `{}`", upvar);
}
ty::UpvarCapture::ByValue(_) => {
ty::UpvarCapture::ByValue => {
capture_reason = format!("possible mutation of `{}`", upvar);
}
_ => bug!("upvar `{}` borrowed, but not mutably", upvar),

View File

@ -186,7 +186,7 @@ fn do_mir_borrowck<'a, 'tcx>(
.map(|captured_place| {
let capture = captured_place.info.capture_kind;
let by_ref = match capture {
ty::UpvarCapture::ByValue(_) => false,
ty::UpvarCapture::ByValue => false,
ty::UpvarCapture::ByRef(..) => true,
};
Upvar { place: captured_place.clone(), by_ref }

View File

@ -52,35 +52,18 @@ impl UpvarId {
/// Information describing the capture of an upvar. This is computed
/// during `typeck`, specifically by `regionck`.
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub enum UpvarCapture<'tcx> {
pub enum UpvarCapture {
/// Upvar is captured by value. This is always true when the
/// closure is labeled `move`, but can also be true in other cases
/// depending on inference.
///
/// If the upvar was inferred to be captured by value (e.g. `move`
/// was not used), then the `Span` points to a usage that
/// required it. There may be more than one such usage
/// (e.g. `|| { a; a; }`), in which case we pick an
/// arbitrary one.
ByValue(Option<Span>),
ByValue,
/// Upvar is captured by reference.
ByRef(UpvarBorrow<'tcx>),
}
#[derive(PartialEq, Clone, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct UpvarBorrow<'tcx> {
/// The kind of borrow: by-ref upvars have access to shared
/// immutable borrows, which are not part of the normal language
/// syntax.
pub kind: BorrowKind,
/// Region of the resulting reference.
pub region: ty::Region<'tcx>,
ByRef(BorrowKind),
}
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>;
pub type UpvarCaptureMap<'tcx> = FxHashMap<UpvarId, UpvarCapture<'tcx>>;
pub type UpvarCaptureMap = FxHashMap<UpvarId, UpvarCapture>;
/// Given the closure DefId this map provides a map of root variables to minimum
/// set of `CapturedPlace`s that need to be tracked to support all captures of that closure.
@ -150,10 +133,13 @@ pub struct CapturedPlace<'tcx> {
pub place: HirPlace<'tcx>,
/// `CaptureKind` and expression(s) that resulted in such capture of `place`.
pub info: CaptureInfo<'tcx>,
pub info: CaptureInfo,
/// Represents if `place` can be mutated or not.
pub mutability: hir::Mutability,
/// Region of the resulting reference if the upvar is captured by ref.
pub region: Option<ty::Region<'tcx>>,
}
impl<'tcx> CapturedPlace<'tcx> {
@ -287,7 +273,7 @@ pub fn is_ancestor_or_same_capture(
/// for a particular capture as well as identifying the part of the source code
/// that triggered this capture to occur.
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)]
pub struct CaptureInfo<'tcx> {
pub struct CaptureInfo {
/// Expr Id pointing to use that resulted in selecting the current capture kind
///
/// Eg:
@ -325,7 +311,7 @@ pub struct CaptureInfo<'tcx> {
pub path_expr_id: Option<hir::HirId>,
/// Capture mode that was selected
pub capture_kind: UpvarCapture<'tcx>,
pub capture_kind: UpvarCapture,
}
pub fn place_to_string_for_capture<'tcx>(tcx: TyCtxt<'tcx>, place: &HirPlace<'tcx>) -> String {

View File

@ -57,8 +57,8 @@ pub use self::binding::BindingMode::*;
pub use self::closure::{
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList,
RootVariableMinCaptureList, UpvarBorrow, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap,
UpvarPath, CAPTURE_STRUCT_LOCAL,
RootVariableMinCaptureList, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, UpvarPath,
CAPTURE_STRUCT_LOCAL,
};
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree};
pub use self::context::{

View File

@ -47,12 +47,6 @@ impl fmt::Debug for ty::UpvarId {
}
}
impl<'tcx> fmt::Debug for ty::UpvarBorrow<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UpvarBorrow({:?}, {:?})", self.kind, self.region)
}
}
impl<'tcx> fmt::Debug for ty::ExistentialTraitRef<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_no_trimmed_paths(|| fmt::Display::fmt(self, f))

View File

@ -266,7 +266,7 @@ fn to_upvars_resolved_place_builder<'a, 'tcx>(
// we need to deref it
upvar_resolved_place_builder = match capture.info.capture_kind {
ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(),
ty::UpvarCapture::ByValue(_) => upvar_resolved_place_builder,
ty::UpvarCapture::ByValue => upvar_resolved_place_builder,
};
let next_projection = capture.place.projections.len();

View File

@ -930,7 +930,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let mut projs = closure_env_projs.clone();
projs.push(ProjectionElem::Field(Field::new(i), ty));
match capture {
ty::UpvarCapture::ByValue(_) => {}
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByRef(..) => {
projs.push(ProjectionElem::Deref);
}

View File

@ -1108,9 +1108,9 @@ impl<'tcx> Cx<'tcx> {
let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
match upvar_capture {
ty::UpvarCapture::ByValue(_) => captured_place_expr,
ty::UpvarCapture::ByValue => captured_place_expr,
ty::UpvarCapture::ByRef(upvar_borrow) => {
let borrow_kind = match upvar_borrow.kind {
let borrow_kind = match upvar_borrow {
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false },

View File

@ -726,7 +726,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
);
self.acc(self.exit_ln, var, ACC_READ | ACC_USE);
}
ty::UpvarCapture::ByValue(_) => {}
ty::UpvarCapture::ByValue => {}
}
}
}
@ -1481,7 +1481,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
for (&var_hir_id, min_capture_list) in closure_min_captures {
for captured_place in min_capture_list {
match captured_place.info.capture_kind {
ty::UpvarCapture::ByValue(_) => {}
ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByRef(..) => continue,
};
let span = captured_place.get_capture_kind_span(self.ir.tcx);

View File

@ -859,15 +859,15 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
self.sub_regions(
infer::ReborrowUpvar(span, upvar_id),
borrow_region,
upvar_borrow.region,
captured_place.region.unwrap(),
);
if let ty::ImmBorrow = upvar_borrow.kind {
if let ty::ImmBorrow = upvar_borrow {
debug!("link_upvar_region: capture by shared ref");
} else {
all_captures_are_imm_borrow = false;
}
}
ty::UpvarCapture::ByValue(_) => {
ty::UpvarCapture::ByValue => {
all_captures_are_imm_borrow = false;
}
}

View File

@ -33,7 +33,6 @@
use super::FnCtxt;
use crate::expr_use_visitor as euv;
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
@ -72,7 +71,7 @@ enum PlaceAncestryRelation {
/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo`
/// during capture analysis. Information in this map feeds into the minimum capture
/// analysis pass.
type InferredCaptureInformation<'tcx> = FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>;
type InferredCaptureInformation<'tcx> = Vec<(Place<'tcx>, ty::CaptureInfo)>;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) {
@ -207,8 +206,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
assert_eq!(body_owner_def_id.to_def_id(), closure_def_id);
let mut delegate = InferBorrowKind {
fcx: self,
closure_def_id,
closure_span: span,
closure_def_id: local_def_id,
capture_information: Default::default(),
fake_reads: Default::default(),
};
@ -231,7 +229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (capture_information, closure_kind, origin) = self
.process_collected_capture_information(capture_clause, delegate.capture_information);
self.compute_min_captures(closure_def_id, capture_information);
self.compute_min_captures(closure_def_id, capture_information, span);
let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id);
@ -252,21 +250,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
debug!("seed place {:?}", place);
let upvar_id = ty::UpvarId::new(*var_hir_id, local_def_id);
let capture_kind =
self.init_capture_kind_for_place(&place, capture_clause, upvar_id, span);
let capture_kind = self.init_capture_kind_for_place(&place, capture_clause);
let fake_info = ty::CaptureInfo {
capture_kind_expr_id: None,
path_expr_id: None,
capture_kind,
};
capture_information.insert(place, fake_info);
capture_information.push((place, fake_info));
}
}
// This will update the min captures based on this new fake information.
self.compute_min_captures(closure_def_id, capture_information);
self.compute_min_captures(closure_def_id, capture_information, span);
}
let before_feature_tys = self.final_upvar_tys(closure_def_id);
@ -362,7 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
captured_place.place, upvar_ty, capture, captured_place.mutability,
);
apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture)
apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region)
})
.collect()
}
@ -387,77 +383,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
capture_clause: hir::CaptureBy,
capture_information: InferredCaptureInformation<'tcx>,
) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) {
let mut processed: InferredCaptureInformation<'tcx> = Default::default();
let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM;
let mut origin: Option<(Span, Place<'tcx>)> = None;
for (place, mut capture_info) in capture_information {
// Apply rules for safety before inferring closure kind
let (place, capture_kind) =
restrict_capture_precision(place, capture_info.capture_kind);
capture_info.capture_kind = capture_kind;
let processed = capture_information
.into_iter()
.map(|(place, mut capture_info)| {
// Apply rules for safety before inferring closure kind
let (place, capture_kind) =
restrict_capture_precision(place, capture_info.capture_kind);
let (place, capture_kind) =
truncate_capture_for_optimization(place, capture_info.capture_kind);
capture_info.capture_kind = capture_kind;
let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind);
let usage_span = if let Some(usage_expr) = capture_info.path_expr_id {
self.tcx.hir().span(usage_expr)
} else {
unreachable!()
};
let usage_span = if let Some(usage_expr) = capture_info.path_expr_id {
self.tcx.hir().span(usage_expr)
} else {
unreachable!()
};
let updated = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(..) => match closure_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
(ty::ClosureKind::FnOnce, Some((usage_span, place.clone())))
}
// If closure is already FnOnce, don't update
ty::ClosureKind::FnOnce => (closure_kind, origin),
},
ty::UpvarCapture::ByRef(ty::UpvarBorrow {
kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
..
}) => {
match closure_kind {
ty::ClosureKind::Fn => {
(ty::ClosureKind::FnMut, Some((usage_span, place.clone())))
let updated = match capture_kind {
ty::UpvarCapture::ByValue => match closure_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
(ty::ClosureKind::FnOnce, Some((usage_span, place.clone())))
}
// If closure is already FnOnce, don't update
ty::ClosureKind::FnOnce => (closure_kind, origin.take()),
},
ty::UpvarCapture::ByRef(
ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
) => {
match closure_kind {
ty::ClosureKind::Fn => {
(ty::ClosureKind::FnMut, Some((usage_span, place.clone())))
}
// Don't update the origin
ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => {
(closure_kind, origin.take())
}
}
// Don't update the origin
ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => (closure_kind, origin),
}
}
_ => (closure_kind, origin),
};
_ => (closure_kind, origin.take()),
};
closure_kind = updated.0;
origin = updated.1;
closure_kind = updated.0;
origin = updated.1;
let (place, capture_kind) = match capture_clause {
hir::CaptureBy::Value => adjust_for_move_closure(place, capture_info.capture_kind),
hir::CaptureBy::Ref => {
adjust_for_non_move_closure(place, capture_info.capture_kind)
}
};
let (place, capture_kind) = match capture_clause {
hir::CaptureBy::Value => adjust_for_move_closure(place, capture_kind),
hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind),
};
// This restriction needs to be applied after we have handled adjustments for `move`
// closures. We want to make sure any adjustment that might make us move the place into
// the closure gets handled.
let (place, capture_kind) =
restrict_precision_for_drop_types(self, place, capture_kind, usage_span);
// This restriction needs to be applied after we have handled adjustments for `move`
// closures. We want to make sure any adjustment that might make us move the place into
// the closure gets handled.
let (place, capture_kind) =
restrict_precision_for_drop_types(self, place, capture_kind, usage_span);
capture_info.capture_kind = capture_kind;
let capture_info = if let Some(existing) = processed.get(&place) {
determine_capture_info(*existing, capture_info)
} else {
capture_info
};
processed.insert(place, capture_info);
}
capture_info.capture_kind = capture_kind;
(place, capture_info)
})
.collect();
(processed, closure_kind, origin)
}
@ -535,6 +522,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
closure_def_id: DefId,
capture_information: InferredCaptureInformation<'tcx>,
closure_span: Span,
) {
if capture_information.is_empty() {
return;
@ -554,8 +542,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
None => {
let mutability = self.determine_capture_mutability(&typeck_results, &place);
let min_cap_list =
vec![ty::CapturedPlace { place, info: capture_info, mutability }];
let min_cap_list = vec![ty::CapturedPlace {
place,
info: capture_info,
mutability,
region: None,
}];
root_var_min_capture_list.insert(var_hir_id, min_cap_list);
continue;
}
@ -608,8 +600,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !descendant_found {
for possible_ancestor in min_cap_list.iter_mut() {
match determine_place_ancestry_relation(&place, &possible_ancestor.place) {
PlaceAncestryRelation::SamePlace => {
ancestor_found = true;
possible_ancestor.info = determine_capture_info(
possible_ancestor.info,
updated_capture_info,
);
// Only one related place will be in the list.
break;
}
// current place is descendant of possible_ancestor
PlaceAncestryRelation::Descendant | PlaceAncestryRelation::SamePlace => {
PlaceAncestryRelation::Descendant => {
ancestor_found = true;
let backup_path_expr_id = possible_ancestor.info.path_expr_id;
@ -629,7 +631,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// we need to keep the ancestor's `path_expr_id`
possible_ancestor.info.path_expr_id = backup_path_expr_id;
// Only one ancestor of the current place will be in the list.
// Only one related place will be in the list.
break;
}
_ => {}
@ -640,12 +642,31 @@ 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(&typeck_results, &place);
let captured_place =
ty::CapturedPlace { place, info: updated_capture_info, mutability };
let captured_place = ty::CapturedPlace {
place,
info: updated_capture_info,
mutability,
region: None,
};
min_cap_list.push(captured_place);
}
}
// For each capture that is determined to be captured by ref, add region info.
for (_, captures) in &mut root_var_min_capture_list {
for capture in captures {
match capture.info.capture_kind {
ty::UpvarCapture::ByRef(_) => {
let PlaceBase::Upvar(upvar_id) = capture.place.base else { bug!("expected upvar") };
let origin = UpvarRegion(upvar_id, closure_span);
let upvar_region = self.next_region_var(origin);
capture.region = Some(upvar_region);
}
_ => (),
}
}
}
debug!(
"For closure={:?}, min_captures before sorting={:?}",
closure_def_id, root_var_min_capture_list
@ -947,7 +968,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
max_capture_info = determine_capture_info(max_capture_info, capture.info);
}
apply_capture_kind_on_capture_ty(self.tcx, ty, max_capture_info.capture_kind)
apply_capture_kind_on_capture_ty(
self.tcx,
ty,
max_capture_info.capture_kind,
Some(&ty::ReErased),
)
}
};
@ -977,6 +1003,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx,
capture.place.ty(),
capture.info.capture_kind,
Some(&ty::ReErased),
);
// Checks if a capture implements any of the auto traits
@ -1086,7 +1113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
for captured_place in root_var_min_capture_list.iter() {
match captured_place.info.capture_kind {
// Only care about captures that are moved into the closure
ty::UpvarCapture::ByValue(..) => {
ty::UpvarCapture::ByValue => {
projections_list.push(captured_place.place.projections.as_slice());
diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise {
source_expr: captured_place.info.path_expr_id,
@ -1470,9 +1497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
place: &Place<'tcx>,
capture_clause: hir::CaptureBy,
upvar_id: ty::UpvarId,
closure_span: Span,
) -> ty::UpvarCapture<'tcx> {
) -> ty::UpvarCapture {
match capture_clause {
// In case of a move closure if the data is accessed through a reference we
// want to capture by ref to allow precise capture using reborrows.
@ -1481,14 +1506,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
// the closure.
hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => {
ty::UpvarCapture::ByValue(None)
}
hir::CaptureBy::Value | hir::CaptureBy::Ref => {
let origin = UpvarRegion(upvar_id, closure_span);
let upvar_region = self.next_region_var(origin);
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
ty::UpvarCapture::ByRef(upvar_borrow)
ty::UpvarCapture::ByValue
}
hir::CaptureBy::Value | hir::CaptureBy::Ref => ty::UpvarCapture::ByRef(ty::ImmBorrow),
}
}
@ -1513,7 +1533,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn log_capture_analysis_first_pass(
&self,
closure_def_id: rustc_hir::def_id::DefId,
capture_information: &FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>,
capture_information: &InferredCaptureInformation<'tcx>,
closure_span: Span,
) {
if self.should_log_capture_analysis(closure_def_id) {
@ -1629,9 +1649,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn restrict_repr_packed_field_ref_capture<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
place: &Place<'tcx>,
mut curr_borrow_kind: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
mut place: Place<'tcx>,
mut curr_borrow_kind: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture) {
let pos = place.projections.iter().enumerate().position(|(i, p)| {
let ty = place.ty_before_projection(i);
@ -1662,8 +1682,6 @@ fn restrict_repr_packed_field_ref_capture<'tcx>(
}
});
let mut place = place.clone();
if let Some(pos) = pos {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_borrow_kind, pos);
}
@ -1675,12 +1693,14 @@ fn restrict_repr_packed_field_ref_capture<'tcx>(
fn apply_capture_kind_on_capture_ty<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
capture_kind: UpvarCapture<'tcx>,
capture_kind: UpvarCapture,
region: Option<ty::Region<'tcx>>,
) -> Ty<'tcx> {
match capture_kind {
ty::UpvarCapture::ByValue(_) => ty,
ty::UpvarCapture::ByRef(borrow) => tcx
.mk_ref(borrow.region, ty::TypeAndMut { ty: ty, mutbl: borrow.kind.to_mutbl_lossy() }),
ty::UpvarCapture::ByValue => ty,
ty::UpvarCapture::ByRef(kind) => {
tcx.mk_ref(region.unwrap(), ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() })
}
}
}
@ -1708,9 +1728,7 @@ struct InferBorrowKind<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
// The def-id of the closure whose kind and upvar accesses are being inferred.
closure_def_id: DefId,
closure_span: Span,
closure_def_id: LocalDefId,
/// For each Place that is captured by the closure, we track the minimal kind of
/// access we need (ref, ref mut, move, etc) and the expression that resulted in such access.
@ -1742,184 +1760,38 @@ struct InferBorrowKind<'a, 'tcx> {
fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>,
}
impl<'a, 'tcx> InferBorrowKind<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind_for_consume(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
let tcx = self.fcx.tcx;
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else {
return;
};
debug!(?upvar_id);
let usage_span = tcx.hir().span(diag_expr_id);
let capture_info = ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
capture_kind: ty::UpvarCapture::ByValue(Some(usage_span)),
};
let curr_info = self.capture_information[&place_with_id.place];
let updated_info = determine_capture_info(curr_info, capture_info);
self.capture_information[&place_with_id.place] = updated_info;
}
/// Indicates that `place_with_id` is being directly mutated (e.g., assigned
/// to). If the place is based on a by-ref upvar, this implies that
/// the upvar must be borrowed using an `&mut` borrow.
#[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind_for_mut(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
if let PlaceBase::Upvar(_) = place_with_id.place.base {
// Raw pointers don't inherit mutability
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
return;
}
self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::MutBorrow);
}
}
#[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind_for_unique(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
if let PlaceBase::Upvar(_) = place_with_id.place.base {
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
// Raw pointers don't inherit mutability.
return;
}
// for a borrowed pointer to be unique, its base must be unique
self.adjust_upvar_deref(place_with_id, diag_expr_id, ty::UniqueImmBorrow);
}
}
fn adjust_upvar_deref(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
borrow_kind: ty::BorrowKind,
) {
assert!(match borrow_kind {
ty::MutBorrow => true,
ty::UniqueImmBorrow => true,
// imm borrows never require adjusting any kinds, so we don't wind up here
ty::ImmBorrow => false,
});
// if this is an implicit deref of an
// upvar, then we need to modify the
// borrow_kind of the upvar to make sure it
// is inferred to mutable if necessary
self.adjust_upvar_borrow_kind(place_with_id, diag_expr_id, borrow_kind);
}
/// We infer the borrow_kind with which to borrow upvars in a stack closure.
/// The borrow_kind basically follows a lattice of `imm < unique-imm < mut`,
/// moving from left to right as needed (but never right to left).
/// Here the argument `mutbl` is the borrow_kind that is required by
/// some particular use.
#[instrument(skip(self), level = "debug")]
fn adjust_upvar_borrow_kind(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
kind: ty::BorrowKind,
) {
let curr_capture_info = self.capture_information[&place_with_id.place];
debug!(?curr_capture_info);
if let ty::UpvarCapture::ByValue(_) = curr_capture_info.capture_kind {
// It's already captured by value, we don't need to do anything here
return;
} else if let ty::UpvarCapture::ByRef(curr_upvar_borrow) = curr_capture_info.capture_kind {
// Use the same region as the current capture information
// Doesn't matter since only one of the UpvarBorrow will be used.
let new_upvar_borrow = ty::UpvarBorrow { kind, region: curr_upvar_borrow.region };
let capture_info = ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
capture_kind: ty::UpvarCapture::ByRef(new_upvar_borrow),
};
let updated_info = determine_capture_info(curr_capture_info, capture_info);
self.capture_information[&place_with_id.place] = updated_info;
};
}
#[instrument(skip(self, diag_expr_id), level = "debug")]
fn init_capture_info_for_place(
&mut self,
place_with_id: &PlaceWithHirId<'tcx>,
diag_expr_id: hir::HirId,
) {
if let PlaceBase::Upvar(upvar_id) = place_with_id.place.base {
assert_eq!(self.closure_def_id.expect_local(), upvar_id.closure_expr_id);
// Initialize to ImmBorrow
// We will escalate the CaptureKind based on any uses we see or in `process_collected_capture_information`.
let origin = UpvarRegion(upvar_id, self.closure_span);
let upvar_region = self.fcx.next_region_var(origin);
let upvar_borrow = ty::UpvarBorrow { kind: ty::ImmBorrow, region: upvar_region };
let capture_kind = ty::UpvarCapture::ByRef(upvar_borrow);
let expr_id = Some(diag_expr_id);
let capture_info = ty::CaptureInfo {
capture_kind_expr_id: expr_id,
path_expr_id: expr_id,
capture_kind,
};
debug!("Capturing new place {:?}, capture_info={:?}", place_with_id, capture_info);
self.capture_information.insert(place_with_id.place.clone(), capture_info);
} else {
debug!("Not upvar");
}
}
}
impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) {
if let PlaceBase::Upvar(_) = place.base {
// We need to restrict Fake Read precision to avoid fake reading unsafe code,
// such as deref of a raw pointer.
let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::UpvarBorrow {
kind: ty::BorrowKind::ImmBorrow,
region: &ty::ReErased,
});
let PlaceBase::Upvar(_) = place.base else { return };
let (place, _) = restrict_capture_precision(place, dummy_capture_kind);
// We need to restrict Fake Read precision to avoid fake reading unsafe code,
// such as deref of a raw pointer.
let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow);
let (place, _) = restrict_repr_packed_field_ref_capture(
self.fcx.tcx,
self.fcx.param_env,
&place,
dummy_capture_kind,
);
self.fake_reads.push((place, cause, diag_expr_id));
}
let (place, _) = restrict_capture_precision(place, dummy_capture_kind);
let (place, _) = restrict_repr_packed_field_ref_capture(
self.fcx.tcx,
self.fcx.param_env,
place,
dummy_capture_kind,
);
self.fake_reads.push((place, cause, diag_expr_id));
}
#[instrument(skip(self), level = "debug")]
fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(place_with_id, diag_expr_id);
}
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
assert_eq!(self.closure_def_id, upvar_id.closure_expr_id);
self.adjust_upvar_borrow_kind_for_consume(place_with_id, diag_expr_id);
self.capture_information.push((
place_with_id.place.clone(),
ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
capture_kind: ty::UpvarCapture::ByValue,
},
));
}
#[instrument(skip(self), level = "debug")]
@ -1929,40 +1801,35 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
diag_expr_id: hir::HirId,
bk: ty::BorrowKind,
) {
let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
assert_eq!(self.closure_def_id, upvar_id.closure_expr_id);
// The region here will get discarded/ignored
let dummy_capture_kind =
ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: bk, region: &ty::ReErased });
let capture_kind = ty::UpvarCapture::ByRef(bk);
// We only want repr packed restriction to be applied to reading references into a packed
// struct, and not when the data is being moved. Therefore we call this method here instead
// of in `restrict_capture_precision`.
let (place, updated_kind) = restrict_repr_packed_field_ref_capture(
let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture(
self.fcx.tcx,
self.fcx.param_env,
&place_with_id.place,
dummy_capture_kind,
place_with_id.place.clone(),
capture_kind,
);
let place_with_id = PlaceWithHirId { place, ..*place_with_id };
if !self.capture_information.contains_key(&place_with_id.place) {
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
// Raw pointers don't inherit mutability
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow);
}
match updated_kind {
ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind, .. }) => match kind {
ty::ImmBorrow => {}
ty::UniqueImmBorrow => {
self.adjust_upvar_borrow_kind_for_unique(&place_with_id, diag_expr_id);
}
ty::MutBorrow => {
self.adjust_upvar_borrow_kind_for_mut(&place_with_id, diag_expr_id);
}
self.capture_information.push((
place,
ty::CaptureInfo {
capture_kind_expr_id: Some(diag_expr_id),
path_expr_id: Some(diag_expr_id),
capture_kind,
},
// Just truncating the place will never cause capture kind to be updated to ByValue
ty::UpvarCapture::ByValue(..) => unreachable!(),
}
));
}
#[instrument(skip(self), level = "debug")]
@ -1975,12 +1842,12 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
fn restrict_precision_for_drop_types<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
mut place: Place<'tcx>,
mut curr_mode: ty::UpvarCapture<'tcx>,
mut curr_mode: ty::UpvarCapture,
span: Span,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
) -> (Place<'tcx>, ty::UpvarCapture) {
let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty(), span);
if let (false, UpvarCapture::ByValue(..)) = (is_copy_type, curr_mode) {
if let (false, UpvarCapture::ByValue) = (is_copy_type, curr_mode) {
for i in 0..place.projections.len() {
match place.ty_before_projection(i).kind() {
ty::Adt(def, _) if def.destructor(fcx.tcx).is_some() => {
@ -2001,8 +1868,8 @@ fn restrict_precision_for_drop_types<'a, 'tcx>(
/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
fn restrict_precision_for_unsafe<'tcx>(
mut place: Place<'tcx>,
mut curr_mode: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
mut curr_mode: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture) {
if place.base_ty.is_unsafe_ptr() {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0);
}
@ -2034,8 +1901,8 @@ fn restrict_precision_for_unsafe<'tcx>(
/// Returns the truncated place and updated cature mode.
fn restrict_capture_precision<'tcx>(
place: Place<'tcx>,
curr_mode: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
curr_mode: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture) {
let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode);
if place.projections.is_empty() {
@ -2062,30 +1929,28 @@ fn restrict_capture_precision<'tcx>(
/// Truncate deref of any reference.
fn adjust_for_move_closure<'tcx>(
mut place: Place<'tcx>,
mut kind: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
mut kind: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture) {
let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
if let Some(idx) = first_deref {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx);
}
// AMAN: I think we don't need the span inside the ByValue anymore
// we have more detailed span in CaptureInfo
(place, ty::UpvarCapture::ByValue(None))
(place, ty::UpvarCapture::ByValue)
}
/// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame.
fn adjust_for_non_move_closure<'tcx>(
mut place: Place<'tcx>,
mut kind: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
mut kind: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture) {
let contains_deref =
place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
match kind {
ty::UpvarCapture::ByValue(..) => {
ty::UpvarCapture::ByValue => {
if let Some(idx) = contains_deref {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx);
}
@ -2123,13 +1988,13 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String
fn construct_capture_kind_reason_string<'tcx>(
tcx: TyCtxt<'_>,
place: &Place<'tcx>,
capture_info: &ty::CaptureInfo<'tcx>,
capture_info: &ty::CaptureInfo,
) -> String {
let place_str = construct_place_string(tcx, place);
let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => "ByValue".into(),
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
ty::UpvarCapture::ByValue => "ByValue".into(),
ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind),
};
format!("{} captured as {} here", place_str, capture_kind_str)
@ -2144,13 +2009,13 @@ fn construct_path_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String {
fn construct_capture_info_string<'tcx>(
tcx: TyCtxt<'_>,
place: &Place<'tcx>,
capture_info: &ty::CaptureInfo<'tcx>,
capture_info: &ty::CaptureInfo,
) -> String {
let place_str = construct_place_string(tcx, place);
let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => "ByValue".into(),
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind),
ty::UpvarCapture::ByValue => "ByValue".into(),
ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind),
};
format!("{} -> {}", place_str, capture_kind_str)
}
@ -2233,25 +2098,16 @@ fn migration_suggestion_for_2229(
/// would've already handled `E1`, and have an existing capture_information for it.
/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return
/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics.
fn determine_capture_info<'tcx>(
capture_info_a: ty::CaptureInfo<'tcx>,
capture_info_b: ty::CaptureInfo<'tcx>,
) -> ty::CaptureInfo<'tcx> {
fn determine_capture_info(
capture_info_a: ty::CaptureInfo,
capture_info_b: ty::CaptureInfo,
) -> ty::CaptureInfo {
// If the capture kind is equivalent then, we don't need to escalate and can compare the
// expressions.
let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => {
// We don't need to worry about the spans being ignored here.
//
// The expr_id in capture_info corresponds to the span that is stored within
// ByValue(span) and therefore it gets handled with priortizing based on
// expressions below.
true
}
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
ref_a.kind == ref_b.kind
}
(ty::UpvarCapture::ByValue(_), _) | (ty::UpvarCapture::ByRef(_), _) => false,
(ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true,
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b,
(ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false,
};
if eq_capture_kind {
@ -2263,10 +2119,10 @@ fn determine_capture_info<'tcx>(
// We select the CaptureKind which ranks higher based the following priority order:
// ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow
match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue(_), _) => capture_info_a,
(_, ty::UpvarCapture::ByValue(_)) => capture_info_b,
(ty::UpvarCapture::ByValue, _) => capture_info_a,
(_, ty::UpvarCapture::ByValue) => capture_info_b,
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => {
match (ref_a.kind, ref_b.kind) {
match (ref_a, ref_b) {
// Take LHS:
(ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow)
| (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a,
@ -2294,7 +2150,7 @@ fn determine_capture_info<'tcx>(
/// contained `Deref` of `&mut`.
fn truncate_place_to_len_and_update_capture_kind<'tcx>(
place: &mut Place<'tcx>,
curr_mode: &mut ty::UpvarCapture<'tcx>,
curr_mode: &mut ty::UpvarCapture,
len: usize,
) {
let is_mut_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Mut));
@ -2304,22 +2160,19 @@ fn truncate_place_to_len_and_update_capture_kind<'tcx>(
// Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so
// we don't need to worry about that case here.
match curr_mode {
ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: ty::BorrowKind::MutBorrow, region }) => {
ty::UpvarCapture::ByRef(ty::BorrowKind::MutBorrow) => {
for i in len..place.projections.len() {
if place.projections[i].kind == ProjectionKind::Deref
&& is_mut_ref(place.ty_before_projection(i))
{
*curr_mode = ty::UpvarCapture::ByRef(ty::UpvarBorrow {
kind: ty::BorrowKind::UniqueImmBorrow,
region,
});
*curr_mode = ty::UpvarCapture::ByRef(ty::BorrowKind::UniqueImmBorrow);
break;
}
}
}
ty::UpvarCapture::ByRef(..) => {}
ty::UpvarCapture::ByValue(..) => {}
ty::UpvarCapture::ByValue => {}
}
place.projections.truncate(len);
@ -2390,8 +2243,8 @@ fn determine_place_ancestry_relation<'tcx>(
/// ```
fn truncate_capture_for_optimization<'tcx>(
mut place: Place<'tcx>,
mut curr_mode: ty::UpvarCapture<'tcx>,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) {
mut curr_mode: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture) {
let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not));
// Find the right-most deref (if any). All the projections that come after this

View File

@ -796,14 +796,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
);
match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => {
ty::UpvarCapture::ByValue => {
self.delegate_consume(&place_with_id, place_with_id.hir_id);
}
ty::UpvarCapture::ByRef(upvar_borrow) => {
self.delegate.borrow(
&place_with_id,
place_with_id.hir_id,
upvar_borrow.kind,
upvar_borrow,
);
}
}

View File

@ -15,6 +15,7 @@ fn main() {
//~^ NOTE: Capturing m[] -> MutBorrow
//~| NOTE: Min Capture m[] -> MutBorrow
m[1] += 40;
//~^ NOTE: Capturing m[] -> MutBorrow
};
c();

View File

@ -15,7 +15,7 @@ LL | |
LL | |
LL | | m[0] += 10;
... |
LL | | m[1] += 40;
LL | |
LL | | };
| |_____^
|
@ -24,6 +24,11 @@ note: Capturing m[] -> MutBorrow
|
LL | m[0] += 10;
| ^
note: Capturing m[] -> MutBorrow
--> $DIR/arrays-completely-captured.rs:17:9
|
LL | m[1] += 40;
| ^
error: Min Capture analysis includes:
--> $DIR/arrays-completely-captured.rs:11:5
@ -33,7 +38,7 @@ LL | |
LL | |
LL | | m[0] += 10;
... |
LL | | m[1] += 40;
LL | |
LL | | };
| |_____^
|

View File

@ -15,6 +15,8 @@ fn arrays() {
//~| ERROR: Min Capture analysis includes:
let [a, b, .., e] = arr;
//~^ NOTE: Capturing arr[Index] -> ByValue
//~| NOTE: Capturing arr[Index] -> ByValue
//~| NOTE: Capturing arr[Index] -> ByValue
//~| NOTE: Min Capture arr[] -> ByValue
assert_eq!(a, "A");
assert_eq!(b, "B");

View File

@ -8,7 +8,7 @@ LL | let c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure_patterns.rs:36:13
--> $DIR/destructure_patterns.rs:38:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -17,7 +17,7 @@ LL | let c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/destructure_patterns.rs:56:13
--> $DIR/destructure_patterns.rs:58:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -42,6 +42,16 @@ note: Capturing arr[Index] -> ByValue
|
LL | let [a, b, .., e] = arr;
| ^^^
note: Capturing arr[Index] -> ByValue
--> $DIR/destructure_patterns.rs:16:29
|
LL | let [a, b, .., e] = arr;
| ^^^
note: Capturing arr[Index] -> ByValue
--> $DIR/destructure_patterns.rs:16:29
|
LL | let [a, b, .., e] = arr;
| ^^^
error: Min Capture analysis includes:
--> $DIR/destructure_patterns.rs:13:5
@ -62,7 +72,7 @@ LL | let [a, b, .., e] = arr;
| ^^^
error: First Pass analysis includes:
--> $DIR/destructure_patterns.rs:39:5
--> $DIR/destructure_patterns.rs:41:5
|
LL | / || {
LL | |
@ -74,18 +84,18 @@ LL | | };
| |_____^
|
note: Capturing p[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:42:58
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
note: Capturing p[(2, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:42:58
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
error: Min Capture analysis includes:
--> $DIR/destructure_patterns.rs:39:5
--> $DIR/destructure_patterns.rs:41:5
|
LL | / || {
LL | |
@ -97,18 +107,18 @@ LL | | };
| |_____^
|
note: Min Capture p[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:42:58
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
note: Min Capture p[(2, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:42:58
--> $DIR/destructure_patterns.rs:44:58
|
LL | let Point { x: ref mut x, y: _, id: moved_id } = p;
| ^
error: First Pass analysis includes:
--> $DIR/destructure_patterns.rs:59:5
--> $DIR/destructure_patterns.rs:61:5
|
LL | / || {
LL | |
@ -120,23 +130,23 @@ LL | | };
| |_____^
|
note: Capturing t[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:62:54
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Capturing t[(1, 0)] -> ImmBorrow
--> $DIR/destructure_patterns.rs:62:54
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Capturing t[(2, 0),(0, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:62:54
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
error: Min Capture analysis includes:
--> $DIR/destructure_patterns.rs:59:5
--> $DIR/destructure_patterns.rs:61:5
|
LL | / || {
LL | |
@ -148,17 +158,17 @@ LL | | };
| |_____^
|
note: Min Capture t[(0, 0)] -> MutBorrow
--> $DIR/destructure_patterns.rs:62:54
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Min Capture t[(1, 0)] -> ImmBorrow
--> $DIR/destructure_patterns.rs:62:54
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^
note: Min Capture t[(2, 0),(0, 0)] -> ByValue
--> $DIR/destructure_patterns.rs:62:54
--> $DIR/destructure_patterns.rs:64:54
|
LL | let (ref mut x, ref ref_str, (moved_s, _)) = t;
| ^

View File

@ -0,0 +1,25 @@
// edition:2021
// Test that we point to the correct location that results a union being captured.
// Union is special because it can't be disjointly captured.
union A {
y: u32,
x: (),
}
fn main() {
let mut a = A { y: 1 };
let mut c = || {
//~^ borrow of `a.y` occurs here
let _ = unsafe { &a.y };
let _ = &mut a;
//~^ borrow occurs due to use in closure
let _ = unsafe { &mut a.y };
};
a.y = 1;
//~^ cannot assign to `a.y` because it is borrowed [E0506]
//~| assignment to borrowed `a.y` occurs here
c();
//~^ borrow later used here
}

View File

@ -0,0 +1,18 @@
error[E0506]: cannot assign to `a.y` because it is borrowed
--> $DIR/union.rs:20:5
|
LL | let mut c = || {
| -- borrow of `a.y` occurs here
...
LL | let _ = &mut a;
| - borrow occurs due to use in closure
...
LL | a.y = 1;
| ^^^^^^^ assignment to borrowed `a.y` occurs here
...
LL | c();
| - borrow later used here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0506`.

View File

@ -40,6 +40,7 @@ fn main() {
//~| NOTE: Min Capture p[(1, 0)] -> MutBorrow
c2();
println!("{}", p.y);
//~^ NOTE: Capturing p[(1, 0)] -> ImmBorrow
};
c1();

View File

@ -58,7 +58,7 @@ LL | |
LL | |
LL | | println!("{}", p.x);
... |
LL | | println!("{}", p.y);
LL | |
LL | | };
| |_____^
|
@ -72,6 +72,11 @@ note: Capturing p[(1, 0)] -> MutBorrow
|
LL | || p.y += incr;
| ^^^
note: Capturing p[(1, 0)] -> ImmBorrow
--> $DIR/nested-closure.rs:42:24
|
LL | println!("{}", p.y);
| ^^^
error: Min Capture analysis includes:
--> $DIR/nested-closure.rs:22:5
@ -81,7 +86,7 @@ LL | |
LL | |
LL | | println!("{}", p.x);
... |
LL | | println!("{}", p.y);
LL | |
LL | | };
| |_____^
|

View File

@ -48,6 +48,7 @@ fn test_alignment_affected() {
//~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes:
let z1: &String = &foo.x;
//~^ NOTE: Capturing foo[] -> ImmBorrow
let z2: &mut u16 = &mut foo.y;
//~^ NOTE: Capturing foo[] -> MutBorrow
//~| NOTE: Min Capture foo[] -> MutBorrow

View File

@ -17,7 +17,7 @@ LL | let mut c = #[rustc_capture_analysis]
= help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental
--> $DIR/repr_packed.rs:78:13
--> $DIR/repr_packed.rs:79:13
|
LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -83,8 +83,13 @@ LL | | println!("({}, {})", z1, z2);
LL | | };
| |_____^
|
note: Capturing foo[] -> ImmBorrow
--> $DIR/repr_packed.rs:50:28
|
LL | let z1: &String = &foo.x;
| ^^^^^
note: Capturing foo[] -> MutBorrow
--> $DIR/repr_packed.rs:51:33
--> $DIR/repr_packed.rs:52:33
|
LL | let z2: &mut u16 = &mut foo.y;
| ^^^^^
@ -102,13 +107,13 @@ LL | | };
| |_____^
|
note: Min Capture foo[] -> MutBorrow
--> $DIR/repr_packed.rs:51:33
--> $DIR/repr_packed.rs:52:33
|
LL | let z2: &mut u16 = &mut foo.y;
| ^^^^^
error: First Pass analysis includes:
--> $DIR/repr_packed.rs:81:5
--> $DIR/repr_packed.rs:82:5
|
LL | / || {
LL | |
@ -120,18 +125,18 @@ LL | | };
| |_____^
|
note: Capturing foo[] -> ImmBorrow
--> $DIR/repr_packed.rs:84:24
--> $DIR/repr_packed.rs:85:24
|
LL | println!("{}", foo.x);
| ^^^^^
note: Capturing foo[(0, 0)] -> ByValue
--> $DIR/repr_packed.rs:88:18
--> $DIR/repr_packed.rs:89:18
|
LL | let _z = foo.x;
| ^^^^^
error: Min Capture analysis includes:
--> $DIR/repr_packed.rs:81:5
--> $DIR/repr_packed.rs:82:5
|
LL | / || {
LL | |
@ -143,7 +148,7 @@ LL | | };
| |_____^
|
note: Min Capture foo[] -> ByValue
--> $DIR/repr_packed.rs:84:24
--> $DIR/repr_packed.rs:85:24
|
LL | println!("{}", foo.x);
| ^^^^^ foo[] used here

View File

@ -976,8 +976,8 @@ pub fn can_move_expr_to_closure(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) ->
};
if !self.locals.contains(&local_id) {
let capture = match capture.info.capture_kind {
UpvarCapture::ByValue(_) => CaptureKind::Value,
UpvarCapture::ByRef(borrow) => match borrow.kind {
UpvarCapture::ByValue => CaptureKind::Value,
UpvarCapture::ByRef(kind) => match kind {
BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
CaptureKind::Ref(Mutability::Mut)