1
Fork 0

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, &origin_projection,
) { ) {
match captured_place.info.capture_kind { match captured_place.info.capture_kind {
ty::UpvarCapture::ByRef(ty::UpvarBorrow { ty::UpvarCapture::ByRef(
kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
.. ) => {
}) => {
capture_reason = format!("mutable borrow of `{}`", upvar); capture_reason = format!("mutable borrow of `{}`", upvar);
} }
ty::UpvarCapture::ByValue(_) => { ty::UpvarCapture::ByValue => {
capture_reason = format!("possible mutation of `{}`", upvar); capture_reason = format!("possible mutation of `{}`", upvar);
} }
_ => bug!("upvar `{}` borrowed, but not mutably", upvar), _ => bug!("upvar `{}` borrowed, but not mutably", upvar),

View file

@ -186,7 +186,7 @@ fn do_mir_borrowck<'a, 'tcx>(
.map(|captured_place| { .map(|captured_place| {
let capture = captured_place.info.capture_kind; let capture = captured_place.info.capture_kind;
let by_ref = match capture { let by_ref = match capture {
ty::UpvarCapture::ByValue(_) => false, ty::UpvarCapture::ByValue => false,
ty::UpvarCapture::ByRef(..) => true, ty::UpvarCapture::ByRef(..) => true,
}; };
Upvar { place: captured_place.clone(), by_ref } 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 /// Information describing the capture of an upvar. This is computed
/// during `typeck`, specifically by `regionck`. /// during `typeck`, specifically by `regionck`.
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] #[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 /// Upvar is captured by value. This is always true when the
/// closure is labeled `move`, but can also be true in other cases /// closure is labeled `move`, but can also be true in other cases
/// depending on inference. /// depending on inference.
/// ByValue,
/// 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>),
/// Upvar is captured by reference. /// Upvar is captured by reference.
ByRef(UpvarBorrow<'tcx>), ByRef(BorrowKind),
}
#[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>,
} }
pub type UpvarListMap = FxHashMap<DefId, FxIndexMap<hir::HirId, UpvarId>>; 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 /// 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. /// 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>, pub place: HirPlace<'tcx>,
/// `CaptureKind` and expression(s) that resulted in such capture of `place`. /// `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. /// Represents if `place` can be mutated or not.
pub mutability: hir::Mutability, 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> { 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 /// for a particular capture as well as identifying the part of the source code
/// that triggered this capture to occur. /// that triggered this capture to occur.
#[derive(PartialEq, Clone, Debug, Copy, TyEncodable, TyDecodable, TypeFoldable, HashStable)] #[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 /// Expr Id pointing to use that resulted in selecting the current capture kind
/// ///
/// Eg: /// Eg:
@ -325,7 +311,7 @@ pub struct CaptureInfo<'tcx> {
pub path_expr_id: Option<hir::HirId>, pub path_expr_id: Option<hir::HirId>,
/// Capture mode that was selected /// 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 { 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::{ pub use self::closure::{
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo, is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList, CapturedPlace, ClosureKind, MinCaptureInformationMap, MinCaptureList,
RootVariableMinCaptureList, UpvarBorrow, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, RootVariableMinCaptureList, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, UpvarPath,
UpvarPath, CAPTURE_STRUCT_LOCAL, CAPTURE_STRUCT_LOCAL,
}; };
pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree}; pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree};
pub use self::context::{ 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> { impl<'tcx> fmt::Debug for ty::ExistentialTraitRef<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_no_trimmed_paths(|| fmt::Display::fmt(self, f)) 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 // we need to deref it
upvar_resolved_place_builder = match capture.info.capture_kind { upvar_resolved_place_builder = match capture.info.capture_kind {
ty::UpvarCapture::ByRef(_) => upvar_resolved_place_builder.deref(), 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(); 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(); let mut projs = closure_env_projs.clone();
projs.push(ProjectionElem::Field(Field::new(i), ty)); projs.push(ProjectionElem::Field(Field::new(i), ty));
match capture { match capture {
ty::UpvarCapture::ByValue(_) => {} ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByRef(..) => { ty::UpvarCapture::ByRef(..) => {
projs.push(ProjectionElem::Deref); 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); let temp_lifetime = self.region_scope_tree.temporary_scope(closure_expr.hir_id.local_id);
match upvar_capture { match upvar_capture {
ty::UpvarCapture::ByValue(_) => captured_place_expr, ty::UpvarCapture::ByValue => captured_place_expr,
ty::UpvarCapture::ByRef(upvar_borrow) => { 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::ImmBorrow => BorrowKind::Shared,
ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique, ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
ty::BorrowKind::MutBorrow => BorrowKind::Mut { allow_two_phase_borrow: false }, 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); 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 (&var_hir_id, min_capture_list) in closure_min_captures {
for captured_place in min_capture_list { for captured_place in min_capture_list {
match captured_place.info.capture_kind { match captured_place.info.capture_kind {
ty::UpvarCapture::ByValue(_) => {} ty::UpvarCapture::ByValue => {}
ty::UpvarCapture::ByRef(..) => continue, ty::UpvarCapture::ByRef(..) => continue,
}; };
let span = captured_place.get_capture_kind_span(self.ir.tcx); 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( self.sub_regions(
infer::ReborrowUpvar(span, upvar_id), infer::ReborrowUpvar(span, upvar_id),
borrow_region, 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"); debug!("link_upvar_region: capture by shared ref");
} else { } else {
all_captures_are_imm_borrow = false; all_captures_are_imm_borrow = false;
} }
} }
ty::UpvarCapture::ByValue(_) => { ty::UpvarCapture::ByValue => {
all_captures_are_imm_borrow = false; all_captures_are_imm_borrow = false;
} }
} }

View file

@ -33,7 +33,6 @@
use super::FnCtxt; use super::FnCtxt;
use crate::expr_use_visitor as euv; use crate::expr_use_visitor as euv;
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -72,7 +71,7 @@ enum PlaceAncestryRelation {
/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo` /// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo`
/// during capture analysis. Information in this map feeds into the minimum capture /// during capture analysis. Information in this map feeds into the minimum capture
/// analysis pass. /// 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> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn closure_analyze(&self, body: &'tcx hir::Body<'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); assert_eq!(body_owner_def_id.to_def_id(), closure_def_id);
let mut delegate = InferBorrowKind { let mut delegate = InferBorrowKind {
fcx: self, fcx: self,
closure_def_id, closure_def_id: local_def_id,
closure_span: span,
capture_information: Default::default(), capture_information: Default::default(),
fake_reads: Default::default(), fake_reads: Default::default(),
}; };
@ -231,7 +229,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let (capture_information, closure_kind, origin) = self let (capture_information, closure_kind, origin) = self
.process_collected_capture_information(capture_clause, delegate.capture_information); .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); 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); 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);
let capture_kind =
self.init_capture_kind_for_place(&place, capture_clause, upvar_id, span);
let fake_info = ty::CaptureInfo { let fake_info = ty::CaptureInfo {
capture_kind_expr_id: None, capture_kind_expr_id: None,
path_expr_id: None, path_expr_id: None,
capture_kind, 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. // 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); 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, 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() .collect()
} }
@ -387,77 +383,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
capture_clause: hir::CaptureBy, capture_clause: hir::CaptureBy,
capture_information: InferredCaptureInformation<'tcx>, capture_information: InferredCaptureInformation<'tcx>,
) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'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 closure_kind = ty::ClosureKind::LATTICE_BOTTOM;
let mut origin: Option<(Span, Place<'tcx>)> = None; let mut origin: Option<(Span, Place<'tcx>)> = None;
for (place, mut capture_info) in capture_information { let processed = capture_information
// Apply rules for safety before inferring closure kind .into_iter()
let (place, capture_kind) = .map(|(place, mut capture_info)| {
restrict_capture_precision(place, capture_info.capture_kind); // Apply rules for safety before inferring closure kind
capture_info.capture_kind = capture_kind; let (place, capture_kind) =
restrict_capture_precision(place, capture_info.capture_kind);
let (place, capture_kind) = let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind);
truncate_capture_for_optimization(place, capture_info.capture_kind);
capture_info.capture_kind = capture_kind;
let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { let usage_span = if let Some(usage_expr) = capture_info.path_expr_id {
self.tcx.hir().span(usage_expr) self.tcx.hir().span(usage_expr)
} else { } else {
unreachable!() unreachable!()
}; };
let updated = match capture_info.capture_kind { let updated = match capture_kind {
ty::UpvarCapture::ByValue(..) => match closure_kind { ty::UpvarCapture::ByValue => match closure_kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
(ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) (ty::ClosureKind::FnOnce, Some((usage_span, place.clone())))
} }
// If closure is already FnOnce, don't update // If closure is already FnOnce, don't update
ty::ClosureKind::FnOnce => (closure_kind, origin), ty::ClosureKind::FnOnce => (closure_kind, origin.take()),
}, },
ty::UpvarCapture::ByRef(ty::UpvarBorrow { ty::UpvarCapture::ByRef(
kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
.. ) => {
}) => { match closure_kind {
match closure_kind { ty::ClosureKind::Fn => {
ty::ClosureKind::Fn => { (ty::ClosureKind::FnMut, Some((usage_span, place.clone())))
(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; closure_kind = updated.0;
origin = updated.1; origin = updated.1;
let (place, capture_kind) = match capture_clause { let (place, capture_kind) = match capture_clause {
hir::CaptureBy::Value => adjust_for_move_closure(place, capture_info.capture_kind), hir::CaptureBy::Value => adjust_for_move_closure(place, capture_kind),
hir::CaptureBy::Ref => { hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind),
adjust_for_non_move_closure(place, capture_info.capture_kind) };
}
};
// This restriction needs to be applied after we have handled adjustments for `move` // 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 // closures. We want to make sure any adjustment that might make us move the place into
// the closure gets handled. // the closure gets handled.
let (place, capture_kind) = let (place, capture_kind) =
restrict_precision_for_drop_types(self, place, capture_kind, usage_span); restrict_precision_for_drop_types(self, place, capture_kind, usage_span);
capture_info.capture_kind = capture_kind; capture_info.capture_kind = capture_kind;
(place, capture_info)
let capture_info = if let Some(existing) = processed.get(&place) { })
determine_capture_info(*existing, capture_info) .collect();
} else {
capture_info
};
processed.insert(place, capture_info);
}
(processed, closure_kind, origin) (processed, closure_kind, origin)
} }
@ -535,6 +522,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self, &self,
closure_def_id: DefId, closure_def_id: DefId,
capture_information: InferredCaptureInformation<'tcx>, capture_information: InferredCaptureInformation<'tcx>,
closure_span: Span,
) { ) {
if capture_information.is_empty() { if capture_information.is_empty() {
return; 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) { let min_cap_list = match root_var_min_capture_list.get_mut(&var_hir_id) {
None => { None => {
let mutability = self.determine_capture_mutability(&typeck_results, &place); let mutability = self.determine_capture_mutability(&typeck_results, &place);
let min_cap_list = let min_cap_list = vec![ty::CapturedPlace {
vec![ty::CapturedPlace { place, info: capture_info, mutability }]; place,
info: capture_info,
mutability,
region: None,
}];
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;
} }
@ -608,8 +600,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if !descendant_found { if !descendant_found {
for possible_ancestor in min_cap_list.iter_mut() { for possible_ancestor in min_cap_list.iter_mut() {
match determine_place_ancestry_relation(&place, &possible_ancestor.place) { 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 // current place is descendant of possible_ancestor
PlaceAncestryRelation::Descendant | PlaceAncestryRelation::SamePlace => { PlaceAncestryRelation::Descendant => {
ancestor_found = true; ancestor_found = true;
let backup_path_expr_id = possible_ancestor.info.path_expr_id; 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` // we need to keep the ancestor's `path_expr_id`
possible_ancestor.info.path_expr_id = backup_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; 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 // 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(&typeck_results, &place); let mutability = self.determine_capture_mutability(&typeck_results, &place);
let captured_place = let captured_place = ty::CapturedPlace {
ty::CapturedPlace { place, info: updated_capture_info, mutability }; place,
info: updated_capture_info,
mutability,
region: None,
};
min_cap_list.push(captured_place); 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!( debug!(
"For closure={:?}, min_captures before sorting={:?}", "For closure={:?}, min_captures before sorting={:?}",
closure_def_id, root_var_min_capture_list 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); 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, self.tcx,
capture.place.ty(), capture.place.ty(),
capture.info.capture_kind, capture.info.capture_kind,
Some(&ty::ReErased),
); );
// Checks if a capture implements any of the auto traits // 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() { for captured_place in root_var_min_capture_list.iter() {
match captured_place.info.capture_kind { match captured_place.info.capture_kind {
// Only care about captures that are moved into the closure // 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()); projections_list.push(captured_place.place.projections.as_slice());
diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise { diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise {
source_expr: captured_place.info.path_expr_id, source_expr: captured_place.info.path_expr_id,
@ -1470,9 +1497,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self, &self,
place: &Place<'tcx>, place: &Place<'tcx>,
capture_clause: hir::CaptureBy, capture_clause: hir::CaptureBy,
upvar_id: ty::UpvarId, ) -> ty::UpvarCapture {
closure_span: Span,
) -> ty::UpvarCapture<'tcx> {
match capture_clause { match capture_clause {
// In case of a move closure if the data is accessed through a reference we // 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. // 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 // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
// the closure. // the closure.
hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => { hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => {
ty::UpvarCapture::ByValue(None) ty::UpvarCapture::ByValue
}
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)
} }
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( fn log_capture_analysis_first_pass(
&self, &self,
closure_def_id: rustc_hir::def_id::DefId, closure_def_id: rustc_hir::def_id::DefId,
capture_information: &FxIndexMap<Place<'tcx>, ty::CaptureInfo<'tcx>>, capture_information: &InferredCaptureInformation<'tcx>,
closure_span: Span, closure_span: Span,
) { ) {
if self.should_log_capture_analysis(closure_def_id) { 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>( fn restrict_repr_packed_field_ref_capture<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>, param_env: ty::ParamEnv<'tcx>,
place: &Place<'tcx>, mut place: Place<'tcx>,
mut curr_borrow_kind: ty::UpvarCapture<'tcx>, mut curr_borrow_kind: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { ) -> (Place<'tcx>, ty::UpvarCapture) {
let pos = place.projections.iter().enumerate().position(|(i, p)| { let pos = place.projections.iter().enumerate().position(|(i, p)| {
let ty = place.ty_before_projection(i); 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 { if let Some(pos) = pos {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_borrow_kind, 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>( fn apply_capture_kind_on_capture_ty<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>, ty: Ty<'tcx>,
capture_kind: UpvarCapture<'tcx>, capture_kind: UpvarCapture,
region: Option<ty::Region<'tcx>>,
) -> Ty<'tcx> { ) -> Ty<'tcx> {
match capture_kind { match capture_kind {
ty::UpvarCapture::ByValue(_) => ty, ty::UpvarCapture::ByValue => ty,
ty::UpvarCapture::ByRef(borrow) => tcx ty::UpvarCapture::ByRef(kind) => {
.mk_ref(borrow.region, ty::TypeAndMut { ty: ty, mutbl: borrow.kind.to_mutbl_lossy() }), 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>, fcx: &'a FnCtxt<'a, 'tcx>,
// The def-id of the closure whose kind and upvar accesses are being inferred. // The def-id of the closure whose kind and upvar accesses are being inferred.
closure_def_id: DefId, closure_def_id: LocalDefId,
closure_span: Span,
/// For each Place that is captured by the closure, we track the minimal kind of /// 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. /// 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)>, 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> { 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) { fn fake_read(&mut self, place: Place<'tcx>, cause: FakeReadCause, diag_expr_id: hir::HirId) {
if let PlaceBase::Upvar(_) = place.base { let PlaceBase::Upvar(_) = place.base else { return };
// 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 (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( let (place, _) = restrict_capture_precision(place, dummy_capture_kind);
self.fcx.tcx,
self.fcx.param_env, let (place, _) = restrict_repr_packed_field_ref_capture(
&place, self.fcx.tcx,
dummy_capture_kind, self.fcx.param_env,
); place,
self.fake_reads.push((place, cause, diag_expr_id)); dummy_capture_kind,
} );
self.fake_reads.push((place, cause, diag_expr_id));
} }
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) {
if !self.capture_information.contains_key(&place_with_id.place) { let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return };
self.init_capture_info_for_place(place_with_id, diag_expr_id); 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")] #[instrument(skip(self), level = "debug")]
@ -1929,40 +1801,35 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> {
diag_expr_id: hir::HirId, diag_expr_id: hir::HirId,
bk: ty::BorrowKind, 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 // The region here will get discarded/ignored
let dummy_capture_kind = let capture_kind = ty::UpvarCapture::ByRef(bk);
ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind: bk, region: &ty::ReErased });
// We only want repr packed restriction to be applied to reading references into a packed // 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 // struct, and not when the data is being moved. Therefore we call this method here instead
// of in `restrict_capture_precision`. // 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.tcx,
self.fcx.param_env, self.fcx.param_env,
&place_with_id.place, place_with_id.place.clone(),
dummy_capture_kind, capture_kind,
); );
let place_with_id = PlaceWithHirId { place, ..*place_with_id }; // Raw pointers don't inherit mutability
if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
if !self.capture_information.contains_key(&place_with_id.place) { capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow);
self.init_capture_info_for_place(&place_with_id, diag_expr_id);
} }
match updated_kind { self.capture_information.push((
ty::UpvarCapture::ByRef(ty::UpvarBorrow { kind, .. }) => match kind { place,
ty::ImmBorrow => {} ty::CaptureInfo {
ty::UniqueImmBorrow => { capture_kind_expr_id: Some(diag_expr_id),
self.adjust_upvar_borrow_kind_for_unique(&place_with_id, diag_expr_id); path_expr_id: Some(diag_expr_id),
} capture_kind,
ty::MutBorrow => {
self.adjust_upvar_borrow_kind_for_mut(&place_with_id, diag_expr_id);
}
}, },
));
// Just truncating the place will never cause capture kind to be updated to ByValue
ty::UpvarCapture::ByValue(..) => unreachable!(),
}
} }
#[instrument(skip(self), level = "debug")] #[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>( fn restrict_precision_for_drop_types<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>, fcx: &'a FnCtxt<'a, 'tcx>,
mut place: Place<'tcx>, mut place: Place<'tcx>,
mut curr_mode: ty::UpvarCapture<'tcx>, mut curr_mode: ty::UpvarCapture,
span: Span, 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); 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() { for i in 0..place.projections.len() {
match place.ty_before_projection(i).kind() { match place.ty_before_projection(i).kind() {
ty::Adt(def, _) if def.destructor(fcx.tcx).is_some() => { 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. /// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
fn restrict_precision_for_unsafe<'tcx>( fn restrict_precision_for_unsafe<'tcx>(
mut place: Place<'tcx>, mut place: Place<'tcx>,
mut curr_mode: ty::UpvarCapture<'tcx>, mut curr_mode: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { ) -> (Place<'tcx>, ty::UpvarCapture) {
if place.base_ty.is_unsafe_ptr() { if place.base_ty.is_unsafe_ptr() {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0); 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. /// Returns the truncated place and updated cature mode.
fn restrict_capture_precision<'tcx>( fn restrict_capture_precision<'tcx>(
place: Place<'tcx>, place: Place<'tcx>,
curr_mode: ty::UpvarCapture<'tcx>, curr_mode: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { ) -> (Place<'tcx>, ty::UpvarCapture) {
let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode); let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode);
if place.projections.is_empty() { if place.projections.is_empty() {
@ -2062,30 +1929,28 @@ fn restrict_capture_precision<'tcx>(
/// Truncate deref of any reference. /// Truncate deref of any reference.
fn adjust_for_move_closure<'tcx>( fn adjust_for_move_closure<'tcx>(
mut place: Place<'tcx>, mut place: Place<'tcx>,
mut kind: ty::UpvarCapture<'tcx>, mut kind: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { ) -> (Place<'tcx>, ty::UpvarCapture) {
let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
if let Some(idx) = first_deref { if let Some(idx) = first_deref {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); 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 (place, ty::UpvarCapture::ByValue)
// we have more detailed span in CaptureInfo
(place, ty::UpvarCapture::ByValue(None))
} }
/// Adjust closure capture just that if taking ownership of data, only move data /// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame. /// from enclosing stack frame.
fn adjust_for_non_move_closure<'tcx>( fn adjust_for_non_move_closure<'tcx>(
mut place: Place<'tcx>, mut place: Place<'tcx>,
mut kind: ty::UpvarCapture<'tcx>, mut kind: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { ) -> (Place<'tcx>, ty::UpvarCapture) {
let contains_deref = let contains_deref =
place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
match kind { match kind {
ty::UpvarCapture::ByValue(..) => { ty::UpvarCapture::ByValue => {
if let Some(idx) = contains_deref { if let Some(idx) = contains_deref {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); 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>( fn construct_capture_kind_reason_string<'tcx>(
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
place: &Place<'tcx>, place: &Place<'tcx>,
capture_info: &ty::CaptureInfo<'tcx>, capture_info: &ty::CaptureInfo,
) -> String { ) -> String {
let place_str = construct_place_string(tcx, place); let place_str = construct_place_string(tcx, place);
let capture_kind_str = match capture_info.capture_kind { let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => "ByValue".into(), ty::UpvarCapture::ByValue => "ByValue".into(),
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind),
}; };
format!("{} captured as {} here", place_str, capture_kind_str) 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>( fn construct_capture_info_string<'tcx>(
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
place: &Place<'tcx>, place: &Place<'tcx>,
capture_info: &ty::CaptureInfo<'tcx>, capture_info: &ty::CaptureInfo,
) -> String { ) -> String {
let place_str = construct_place_string(tcx, place); let place_str = construct_place_string(tcx, place);
let capture_kind_str = match capture_info.capture_kind { let capture_kind_str = match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => "ByValue".into(), ty::UpvarCapture::ByValue => "ByValue".into(),
ty::UpvarCapture::ByRef(borrow) => format!("{:?}", borrow.kind), ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind),
}; };
format!("{} -> {}", place_str, capture_kind_str) 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. /// 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 /// 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. /// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics.
fn determine_capture_info<'tcx>( fn determine_capture_info(
capture_info_a: ty::CaptureInfo<'tcx>, capture_info_a: ty::CaptureInfo,
capture_info_b: ty::CaptureInfo<'tcx>, capture_info_b: ty::CaptureInfo,
) -> ty::CaptureInfo<'tcx> { ) -> ty::CaptureInfo {
// If the capture kind is equivalent then, we don't need to escalate and can compare the // If the capture kind is equivalent then, we don't need to escalate and can compare the
// expressions. // expressions.
let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue(_), ty::UpvarCapture::ByValue(_)) => { (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true,
// We don't need to worry about the spans being ignored here. (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b,
// (ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false,
// 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,
}; };
if eq_capture_kind { 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: // We select the CaptureKind which ranks higher based the following priority order:
// ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow
match (capture_info_a.capture_kind, capture_info_b.capture_kind) { match (capture_info_a.capture_kind, capture_info_b.capture_kind) {
(ty::UpvarCapture::ByValue(_), _) => capture_info_a, (ty::UpvarCapture::ByValue, _) => capture_info_a,
(_, ty::UpvarCapture::ByValue(_)) => capture_info_b, (_, ty::UpvarCapture::ByValue) => capture_info_b,
(ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_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: // Take LHS:
(ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow)
| (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a,
@ -2294,7 +2150,7 @@ fn determine_capture_info<'tcx>(
/// contained `Deref` of `&mut`. /// contained `Deref` of `&mut`.
fn truncate_place_to_len_and_update_capture_kind<'tcx>( fn truncate_place_to_len_and_update_capture_kind<'tcx>(
place: &mut Place<'tcx>, place: &mut Place<'tcx>,
curr_mode: &mut ty::UpvarCapture<'tcx>, curr_mode: &mut ty::UpvarCapture,
len: usize, len: usize,
) { ) {
let is_mut_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Mut)); 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 // 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. // we don't need to worry about that case here.
match curr_mode { 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() { for i in len..place.projections.len() {
if place.projections[i].kind == ProjectionKind::Deref if place.projections[i].kind == ProjectionKind::Deref
&& is_mut_ref(place.ty_before_projection(i)) && is_mut_ref(place.ty_before_projection(i))
{ {
*curr_mode = ty::UpvarCapture::ByRef(ty::UpvarBorrow { *curr_mode = ty::UpvarCapture::ByRef(ty::BorrowKind::UniqueImmBorrow);
kind: ty::BorrowKind::UniqueImmBorrow,
region,
});
break; break;
} }
} }
} }
ty::UpvarCapture::ByRef(..) => {} ty::UpvarCapture::ByRef(..) => {}
ty::UpvarCapture::ByValue(..) => {} ty::UpvarCapture::ByValue => {}
} }
place.projections.truncate(len); place.projections.truncate(len);
@ -2390,8 +2243,8 @@ fn determine_place_ancestry_relation<'tcx>(
/// ``` /// ```
fn truncate_capture_for_optimization<'tcx>( fn truncate_capture_for_optimization<'tcx>(
mut place: Place<'tcx>, mut place: Place<'tcx>,
mut curr_mode: ty::UpvarCapture<'tcx>, mut curr_mode: ty::UpvarCapture,
) -> (Place<'tcx>, ty::UpvarCapture<'tcx>) { ) -> (Place<'tcx>, ty::UpvarCapture) {
let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)); 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 // 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 { match capture_info.capture_kind {
ty::UpvarCapture::ByValue(_) => { ty::UpvarCapture::ByValue => {
self.delegate_consume(&place_with_id, place_with_id.hir_id); self.delegate_consume(&place_with_id, place_with_id.hir_id);
} }
ty::UpvarCapture::ByRef(upvar_borrow) => { ty::UpvarCapture::ByRef(upvar_borrow) => {
self.delegate.borrow( self.delegate.borrow(
&place_with_id, &place_with_id,
place_with_id.hir_id, place_with_id.hir_id,
upvar_borrow.kind, upvar_borrow,
); );
} }
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -48,6 +48,7 @@ fn test_alignment_affected() {
//~^ ERROR: First Pass analysis includes: //~^ ERROR: First Pass analysis includes:
//~| ERROR: Min Capture analysis includes: //~| ERROR: Min Capture analysis includes:
let z1: &String = &foo.x; let z1: &String = &foo.x;
//~^ NOTE: Capturing foo[] -> ImmBorrow
let z2: &mut u16 = &mut foo.y; let z2: &mut u16 = &mut foo.y;
//~^ NOTE: Capturing foo[] -> MutBorrow //~^ NOTE: Capturing foo[] -> MutBorrow
//~| NOTE: Min Capture 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 = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
error[E0658]: attributes on expressions are experimental 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] LL | let c = #[rustc_capture_analysis]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -83,8 +83,13 @@ LL | | println!("({}, {})", z1, z2);
LL | | }; LL | | };
| |_____^ | |_____^
| |
note: Capturing foo[] -> ImmBorrow
--> $DIR/repr_packed.rs:50:28
|
LL | let z1: &String = &foo.x;
| ^^^^^
note: Capturing foo[] -> MutBorrow note: Capturing foo[] -> MutBorrow
--> $DIR/repr_packed.rs:51:33 --> $DIR/repr_packed.rs:52:33
| |
LL | let z2: &mut u16 = &mut foo.y; LL | let z2: &mut u16 = &mut foo.y;
| ^^^^^ | ^^^^^
@ -102,13 +107,13 @@ LL | | };
| |_____^ | |_____^
| |
note: Min Capture foo[] -> MutBorrow 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; LL | let z2: &mut u16 = &mut foo.y;
| ^^^^^ | ^^^^^
error: First Pass analysis includes: error: First Pass analysis includes:
--> $DIR/repr_packed.rs:81:5 --> $DIR/repr_packed.rs:82:5
| |
LL | / || { LL | / || {
LL | | LL | |
@ -120,18 +125,18 @@ LL | | };
| |_____^ | |_____^
| |
note: Capturing foo[] -> ImmBorrow note: Capturing foo[] -> ImmBorrow
--> $DIR/repr_packed.rs:84:24 --> $DIR/repr_packed.rs:85:24
| |
LL | println!("{}", foo.x); LL | println!("{}", foo.x);
| ^^^^^ | ^^^^^
note: Capturing foo[(0, 0)] -> ByValue note: Capturing foo[(0, 0)] -> ByValue
--> $DIR/repr_packed.rs:88:18 --> $DIR/repr_packed.rs:89:18
| |
LL | let _z = foo.x; LL | let _z = foo.x;
| ^^^^^ | ^^^^^
error: Min Capture analysis includes: error: Min Capture analysis includes:
--> $DIR/repr_packed.rs:81:5 --> $DIR/repr_packed.rs:82:5
| |
LL | / || { LL | / || {
LL | | LL | |
@ -143,7 +148,7 @@ LL | | };
| |_____^ | |_____^
| |
note: Min Capture foo[] -> ByValue note: Min Capture foo[] -> ByValue
--> $DIR/repr_packed.rs:84:24 --> $DIR/repr_packed.rs:85:24
| |
LL | println!("{}", foo.x); LL | println!("{}", foo.x);
| ^^^^^ foo[] used here | ^^^^^ 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) { if !self.locals.contains(&local_id) {
let capture = match capture.info.capture_kind { let capture = match capture.info.capture_kind {
UpvarCapture::ByValue(_) => CaptureKind::Value, UpvarCapture::ByValue => CaptureKind::Value,
UpvarCapture::ByRef(borrow) => match borrow.kind { UpvarCapture::ByRef(kind) => match kind {
BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not), BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => { BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
CaptureKind::Ref(Mutability::Mut) CaptureKind::Ref(Mutability::Mut)