1use std::ops::ControlFlow;
23use rustc_abi::FieldIdx;
4use rustc_data_structures::graph::dominators::Dominators;
5use rustc_middle::mir::{BasicBlock, Body, Location, Place, PlaceRef, ProjectionElem};
6use rustc_middle::ty::TyCtxt;
7use tracing::debug;
89use crate::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
10use crate::{AccessDepth, BorrowIndex, places_conflict};
1112/// Encapsulates the idea of iterating over every borrow that involves a particular path
13pub(super) fn each_borrow_involving_path<'tcx, F, I, S>(
14 s: &mut S,
15 tcx: TyCtxt<'tcx>,
16 body: &Body<'tcx>,
17 access_place: (AccessDepth, Place<'tcx>),
18 borrow_set: &BorrowSet<'tcx>,
19 is_candidate: I,
20mut op: F,
21) where
22F: FnMut(&mut S, BorrowIndex, &BorrowData<'tcx>) -> ControlFlow<()>,
23 I: Fn(BorrowIndex) -> bool,
24{
25let (access, place) = access_place;
2627// The number of candidates can be large, but borrows for different locals cannot conflict with
28 // each other, so we restrict the working set a priori.
29let Some(borrows_for_place_base) = borrow_set.local_map.get(&place.local) else { return };
3031// check for loan restricting path P being used. Accounts for
32 // borrows of P, P.a.b, etc.
33for &i in borrows_for_place_base {
34if !is_candidate(i) {
35continue;
36 }
37let borrowed = &borrow_set[i];
3839if places_conflict::borrow_conflicts_with_place(
40 tcx,
41 body,
42 borrowed.borrowed_place,
43 borrowed.kind,
44 place.as_ref(),
45 access,
46 places_conflict::PlaceConflictBias::Overlap,
47 ) {
48{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/path_utils.rs:48",
"rustc_borrowck::path_utils", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/path_utils.rs"),
::tracing_core::__macro_support::Option::Some(48u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::path_utils"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("each_borrow_involving_path: {0:?} @ {1:?} vs. {2:?}/{3:?}",
i, borrowed, place, access) as &dyn Value))])
});
} else { ; }
};debug!(
49"each_borrow_involving_path: {:?} @ {:?} vs. {:?}/{:?}",
50 i, borrowed, place, access
51 );
52let ctrl = op(s, i, borrowed);
53if #[allow(non_exhaustive_omitted_patterns)] match ctrl {
ControlFlow::Break(_) => true,
_ => false,
}matches!(ctrl, ControlFlow::Break(_)) {
54return;
55 }
56 }
57 }
58}
5960pub(super) fn is_active<'tcx>(
61 dominators: &Dominators<BasicBlock>,
62 borrow_data: &BorrowData<'tcx>,
63 location: Location,
64) -> bool {
65{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_borrowck/src/path_utils.rs:65",
"rustc_borrowck::path_utils", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/path_utils.rs"),
::tracing_core::__macro_support::Option::Some(65u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::path_utils"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
let mut iter = __CALLSITE.metadata().fields().iter();
__CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&format_args!("is_active(borrow_data={0:?}, location={1:?})",
borrow_data, location) as &dyn Value))])
});
} else { ; }
};debug!("is_active(borrow_data={:?}, location={:?})", borrow_data, location);
6667let activation_location = match borrow_data.activation_location {
68// If this is not a 2-phase borrow, it is always active.
69TwoPhaseActivation::NotTwoPhase => return true,
70// And if the unique 2-phase use is not an activation, then it is *never* active.
71TwoPhaseActivation::NotActivated => return false,
72// Otherwise, we derive info from the activation point `loc`:
73TwoPhaseActivation::ActivatedAt(loc) => loc,
74 };
7576// Otherwise, it is active for every location *except* in between
77 // the reservation and the activation:
78 //
79 // X
80 // /
81 // R <--+ Except for this
82 // / \ | diamond
83 // \ / |
84 // A <------+
85 // |
86 // Z
87 //
88 // Note that we assume that:
89 // - the reservation R dominates the activation A
90 // - the activation A post-dominates the reservation R (ignoring unwinding edges).
91 //
92 // This means that there can't be an edge that leaves A and
93 // comes back into that diamond unless it passes through R.
94 //
95 // Suboptimal: In some cases, this code walks the dominator
96 // tree twice when it only has to be walked once. I am
97 // lazy. -nmatsakis
9899 // If dominated by the activation A, then it is active. The
100 // activation occurs upon entering the point A, so this is
101 // also true if location == activation_location.
102if activation_location.dominates(location, dominators) {
103return true;
104 }
105106// The reservation starts *on exiting* the reservation block,
107 // so check if the location is dominated by R.successor. If so,
108 // this point falls in between the reservation and location.
109let reserve_location = borrow_data.reserve_location.successor_within_block();
110if reserve_location.dominates(location, dominators) {
111false
112} else {
113// Otherwise, this point is outside the diamond, so
114 // consider the borrow active. This could happen for
115 // example if the borrow remains active around a loop (in
116 // which case it would be active also for the point R,
117 // which would generate an error).
118true
119}
120}
121122/// Determines if a given borrow is borrowing local data
123/// This is called for all Yield expressions on movable coroutines
124pub(super) fn borrow_of_local_data(place: Place<'_>) -> bool {
125// Reborrow of already borrowed data is ignored
126 // Any errors will be caught on the initial borrow
127!place.is_indirect()
128}
129130/// If `place` is a field projection, and the field is being projected from a closure type,
131/// then returns the index of the field being projected. Note that this closure will always
132/// be `self` in the current MIR, because that is the only time we directly access the fields
133/// of a closure type.
134pub(crate) fn is_upvar_field_projection<'tcx>(
135 tcx: TyCtxt<'tcx>,
136 upvars: &[&rustc_middle::ty::CapturedPlace<'tcx>],
137 place_ref: PlaceRef<'tcx>,
138 body: &Body<'tcx>,
139) -> Option<FieldIdx> {
140let mut place_ref = place_ref;
141let mut by_ref = false;
142143if let Some((place_base, ProjectionElem::Deref)) = place_ref.last_projection() {
144place_ref = place_base;
145by_ref = true;
146 }
147148match place_ref.last_projection() {
149Some((place_base, ProjectionElem::Field(field, _ty))) => {
150let base_ty = place_base.ty(body, tcx).ty;
151if (base_ty.is_closure() || base_ty.is_coroutine() || base_ty.is_coroutine_closure())
152 && (!by_ref || upvars[field.index()].is_by_ref())
153 {
154Some(field)
155 } else {
156None157 }
158 }
159_ => None,
160 }
161}