1use std::fmt;
2use std::ops::Index;
3
4use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
5use rustc_index::bit_set::DenseBitSet;
6use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor};
7use rustc_middle::mir::{self, Body, Local, Location, traversal};
8use rustc_middle::span_bug;
9use rustc_middle::ty::{RegionVid, TyCtxt};
10use rustc_mir_dataflow::move_paths::MoveData;
11use tracing::debug;
12
13use crate::BorrowIndex;
14use crate::place_ext::PlaceExt;
15
16pub struct BorrowSet<'tcx> {
17 pub(crate) location_map: FxIndexMap<Location, BorrowData<'tcx>>,
23
24 pub(crate) activation_map: FxIndexMap<Location, Vec<BorrowIndex>>,
29
30 pub(crate) local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
32
33 pub(crate) locals_state_at_exit: LocalsStateAtExit,
34}
35
36impl<'tcx> BorrowSet<'tcx> {
38 pub fn location_map(&self) -> &FxIndexMap<Location, BorrowData<'tcx>> {
39 &self.location_map
40 }
41
42 pub fn activation_map(&self) -> &FxIndexMap<Location, Vec<BorrowIndex>> {
43 &self.activation_map
44 }
45
46 pub fn local_map(&self) -> &FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>> {
47 &self.local_map
48 }
49
50 pub fn locals_state_at_exit(&self) -> &LocalsStateAtExit {
51 &self.locals_state_at_exit
52 }
53}
54
55impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
56 type Output = BorrowData<'tcx>;
57
58 fn index(&self, index: BorrowIndex) -> &BorrowData<'tcx> {
59 &self.location_map[index.as_usize()]
60 }
61}
62
63#[derive(#[automatically_derived]
impl ::core::marker::Copy for TwoPhaseActivation { }Copy, #[automatically_derived]
impl ::core::clone::Clone for TwoPhaseActivation {
#[inline]
fn clone(&self) -> TwoPhaseActivation {
let _: ::core::clone::AssertParamIsClone<Location>;
*self
}
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for TwoPhaseActivation {
#[inline]
fn eq(&self, other: &TwoPhaseActivation) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(TwoPhaseActivation::ActivatedAt(__self_0),
TwoPhaseActivation::ActivatedAt(__arg1_0)) =>
__self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for TwoPhaseActivation {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_receiver_is_total_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Location>;
}
}Eq, #[automatically_derived]
impl ::core::fmt::Debug for TwoPhaseActivation {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
TwoPhaseActivation::NotTwoPhase =>
::core::fmt::Formatter::write_str(f, "NotTwoPhase"),
TwoPhaseActivation::NotActivated =>
::core::fmt::Formatter::write_str(f, "NotActivated"),
TwoPhaseActivation::ActivatedAt(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ActivatedAt", &__self_0),
}
}
}Debug)]
66pub enum TwoPhaseActivation {
67 NotTwoPhase,
68 NotActivated,
69 ActivatedAt(Location),
70}
71
72#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for BorrowData<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["reserve_location", "activation_location", "kind", "region",
"borrowed_place", "assigned_place"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self.reserve_location, &self.activation_location, &self.kind,
&self.region, &self.borrowed_place, &&self.assigned_place];
::core::fmt::Formatter::debug_struct_fields_finish(f, "BorrowData",
names, values)
}
}Debug, #[automatically_derived]
impl<'tcx> ::core::clone::Clone for BorrowData<'tcx> {
#[inline]
fn clone(&self) -> BorrowData<'tcx> {
BorrowData {
reserve_location: ::core::clone::Clone::clone(&self.reserve_location),
activation_location: ::core::clone::Clone::clone(&self.activation_location),
kind: ::core::clone::Clone::clone(&self.kind),
region: ::core::clone::Clone::clone(&self.region),
borrowed_place: ::core::clone::Clone::clone(&self.borrowed_place),
assigned_place: ::core::clone::Clone::clone(&self.assigned_place),
}
}
}Clone)]
73pub struct BorrowData<'tcx> {
74 pub(crate) reserve_location: Location,
77 pub(crate) activation_location: TwoPhaseActivation,
79 pub(crate) kind: mir::BorrowKind,
81 pub(crate) region: RegionVid,
83 pub(crate) borrowed_place: mir::Place<'tcx>,
85 pub(crate) assigned_place: mir::Place<'tcx>,
87}
88
89impl<'tcx> BorrowData<'tcx> {
91 pub fn reserve_location(&self) -> Location {
92 self.reserve_location
93 }
94
95 pub fn activation_location(&self) -> TwoPhaseActivation {
96 self.activation_location
97 }
98
99 pub fn kind(&self) -> mir::BorrowKind {
100 self.kind
101 }
102
103 pub fn region(&self) -> RegionVid {
104 self.region
105 }
106
107 pub fn borrowed_place(&self) -> mir::Place<'tcx> {
108 self.borrowed_place
109 }
110
111 pub fn assigned_place(&self) -> mir::Place<'tcx> {
112 self.assigned_place
113 }
114}
115
116impl<'tcx> fmt::Display for BorrowData<'tcx> {
117 fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
118 let kind = match self.kind {
119 mir::BorrowKind::Shared => "",
120 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => "fake ",
121 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => "fake shallow ",
122 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ",
123 mir::BorrowKind::Mut {
125 kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow,
126 } => "mut ",
127 };
128 w.write_fmt(format_args!("&{0:?} {1}{2:?}", self.region, kind,
self.borrowed_place))write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place)
129 }
130}
131
132pub enum LocalsStateAtExit {
133 AllAreInvalidated,
134 SomeAreInvalidated { has_storage_dead_or_moved: DenseBitSet<Local> },
135}
136
137impl LocalsStateAtExit {
138 fn build<'tcx>(
139 locals_are_invalidated_at_exit: bool,
140 body: &Body<'tcx>,
141 move_data: &MoveData<'tcx>,
142 ) -> Self {
143 struct HasStorageDead(DenseBitSet<Local>);
144
145 impl<'tcx> Visitor<'tcx> for HasStorageDead {
146 fn visit_local(&mut self, local: Local, ctx: PlaceContext, _: Location) {
147 if ctx == PlaceContext::NonUse(NonUseContext::StorageDead) {
148 self.0.insert(local);
149 }
150 }
151 }
152
153 if locals_are_invalidated_at_exit {
154 LocalsStateAtExit::AllAreInvalidated
155 } else {
156 let mut has_storage_dead =
157 HasStorageDead(DenseBitSet::new_empty(body.local_decls.len()));
158 has_storage_dead.visit_body(body);
159 let mut has_storage_dead_or_moved = has_storage_dead.0;
160 for move_out in &move_data.moves {
161 has_storage_dead_or_moved.insert(move_data.base_local(move_out.path));
162 }
163 LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved }
164 }
165 }
166}
167
168impl<'tcx> BorrowSet<'tcx> {
169 pub fn build(
170 tcx: TyCtxt<'tcx>,
171 body: &Body<'tcx>,
172 locals_are_invalidated_at_exit: bool,
173 move_data: &MoveData<'tcx>,
174 ) -> Self {
175 let mut visitor = GatherBorrows {
176 tcx,
177 body,
178 location_map: Default::default(),
179 activation_map: Default::default(),
180 local_map: Default::default(),
181 pending_activations: Default::default(),
182 locals_state_at_exit: LocalsStateAtExit::build(
183 locals_are_invalidated_at_exit,
184 body,
185 move_data,
186 ),
187 };
188
189 for (block, block_data) in traversal::preorder(body) {
190 visitor.visit_basic_block_data(block, block_data);
191 }
192
193 BorrowSet {
194 location_map: visitor.location_map,
195 activation_map: visitor.activation_map,
196 local_map: visitor.local_map,
197 locals_state_at_exit: visitor.locals_state_at_exit,
198 }
199 }
200
201 pub(crate) fn activations_at_location(&self, location: Location) -> &[BorrowIndex] {
202 self.activation_map.get(&location).map_or(&[], |activations| &activations[..])
203 }
204
205 pub(crate) fn len(&self) -> usize {
206 self.location_map.len()
207 }
208
209 pub(crate) fn indices(&self) -> impl Iterator<Item = BorrowIndex> {
210 BorrowIndex::ZERO..BorrowIndex::from_usize(self.len())
211 }
212
213 pub(crate) fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> {
214 self.indices().zip(self.location_map.values())
215 }
216
217 pub(crate) fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> {
218 self.location_map.get_index_of(location).map(BorrowIndex::from)
219 }
220}
221
222struct GatherBorrows<'a, 'tcx> {
223 tcx: TyCtxt<'tcx>,
224 body: &'a Body<'tcx>,
225 location_map: FxIndexMap<Location, BorrowData<'tcx>>,
226 activation_map: FxIndexMap<Location, Vec<BorrowIndex>>,
227 local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
228
229 pending_activations: FxIndexMap<mir::Local, BorrowIndex>,
238
239 locals_state_at_exit: LocalsStateAtExit,
240}
241
242impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
243 fn visit_assign(
244 &mut self,
245 assigned_place: &mir::Place<'tcx>,
246 rvalue: &mir::Rvalue<'tcx>,
247 location: mir::Location,
248 ) {
249 if let &mir::Rvalue::Ref(region, kind, borrowed_place) = rvalue {
250 if borrowed_place.ignore_borrow(self.tcx, self.body, &self.locals_state_at_exit) {
251 {
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/borrow_set.rs:251",
"rustc_borrowck::borrow_set", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/borrow_set.rs"),
::tracing_core::__macro_support::Option::Some(251u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::borrow_set"),
::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!("ignoring_borrow of {0:?}",
borrowed_place) as &dyn Value))])
});
} else { ; }
};debug!("ignoring_borrow of {:?}", borrowed_place);
252 return;
253 }
254
255 let region = region.as_var();
256 let borrow = |activation_location| BorrowData {
257 kind,
258 region,
259 reserve_location: location,
260 activation_location,
261 borrowed_place,
262 assigned_place: *assigned_place,
263 };
264
265 let idx = if !kind.is_two_phase_borrow() {
266 {
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/borrow_set.rs:266",
"rustc_borrowck::borrow_set", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_borrowck/src/borrow_set.rs"),
::tracing_core::__macro_support::Option::Some(266u32),
::tracing_core::__macro_support::Option::Some("rustc_borrowck::borrow_set"),
::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!(" -> {0:?}",
location) as &dyn Value))])
});
} else { ; }
};debug!(" -> {:?}", location);
267 let (idx, _) = self
268 .location_map
269 .insert_full(location, borrow(TwoPhaseActivation::NotTwoPhase));
270 BorrowIndex::from(idx)
271 } else {
272 let Some(temp) = assigned_place.as_local() else {
279 ::rustc_middle::util::bug::span_bug_fmt(self.body.source_info(location).span,
format_args!("expected 2-phase borrow to assign to a local, not `{0:?}`",
assigned_place));span_bug!(
280 self.body.source_info(location).span,
281 "expected 2-phase borrow to assign to a local, not `{:?}`",
282 assigned_place,
283 );
284 };
285
286 let (idx, _) = self
289 .location_map
290 .insert_full(location, borrow(TwoPhaseActivation::NotActivated));
291 let idx = BorrowIndex::from(idx);
292
293 let prev = self.pending_activations.insert(temp, idx);
298 match (&prev, &None) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::Some(format_args!("temporary associated with multiple two phase borrows")));
}
}
};assert_eq!(prev, None, "temporary associated with multiple two phase borrows");
299
300 idx
301 };
302
303 self.local_map.entry(borrowed_place.local).or_default().insert(idx);
304 }
305
306 self.super_assign(assigned_place, rvalue, location)
307 }
308
309 fn visit_local(&mut self, temp: Local, context: PlaceContext, location: Location) {
310 if !context.is_use() {
311 return;
312 }
313
314 if let Some(&borrow_index) = self.pending_activations.get(&temp) {
319 let borrow_data = &mut self.location_map[borrow_index.as_usize()];
320
321 if borrow_data.reserve_location == location
324 && context == PlaceContext::MutatingUse(MutatingUseContext::Store)
325 {
326 return;
327 }
328
329 if let TwoPhaseActivation::ActivatedAt(other_location) = borrow_data.activation_location
330 {
331 ::rustc_middle::util::bug::span_bug_fmt(self.body.source_info(location).span,
format_args!("found two uses for 2-phase borrow temporary {0:?}: {1:?} and {2:?}",
temp, location, other_location));span_bug!(
332 self.body.source_info(location).span,
333 "found two uses for 2-phase borrow temporary {:?}: \
334 {:?} and {:?}",
335 temp,
336 location,
337 other_location,
338 );
339 }
340
341 match (&borrow_data.activation_location, &TwoPhaseActivation::NotActivated) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::Some(format_args!("never found an activation for this borrow!")));
}
}
};assert_eq!(
346 borrow_data.activation_location,
347 TwoPhaseActivation::NotActivated,
348 "never found an activation for this borrow!",
349 );
350 self.activation_map.entry(location).or_default().push(borrow_index);
351
352 borrow_data.activation_location = TwoPhaseActivation::ActivatedAt(location);
353 }
354 }
355
356 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
357 if let &mir::Rvalue::Ref(region, kind, place) = rvalue {
358 let borrow_data = &self.location_map[&location];
361 match (&borrow_data.reserve_location, &location) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(borrow_data.reserve_location, location);
362 match (&borrow_data.kind, &kind) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(borrow_data.kind, kind);
363 match (&borrow_data.region, ®ion.as_var()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(borrow_data.region, region.as_var());
364 match (&borrow_data.borrowed_place, &place) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::None);
}
}
};assert_eq!(borrow_data.borrowed_place, place);
365 }
366
367 self.super_rvalue(rvalue, location)
368 }
369}