1use std::fmt;
2use std::ops::Index;
3
4use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
5use rustc_hir::Mutability;
6use rustc_index::bit_set::DenseBitSet;
7use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor};
8use rustc_middle::mir::{self, Body, Local, Location, traversal};
9use rustc_middle::ty::{RegionVid, TyCtxt};
10use rustc_middle::{bug, span_bug, ty};
11use rustc_mir_dataflow::move_paths::MoveData;
12use tracing::debug;
13
14use crate::BorrowIndex;
15use crate::place_ext::PlaceExt;
16
17pub struct BorrowSet<'tcx> {
18 pub(crate) location_map: FxIndexMap<Location, BorrowData<'tcx>>,
24
25 pub(crate) activation_map: FxIndexMap<Location, Vec<BorrowIndex>>,
30
31 pub(crate) local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
33
34 pub(crate) locals_state_at_exit: LocalsStateAtExit,
35}
36
37impl<'tcx> BorrowSet<'tcx> {
39 pub fn location_map(&self) -> &FxIndexMap<Location, BorrowData<'tcx>> {
40 &self.location_map
41 }
42
43 pub fn activation_map(&self) -> &FxIndexMap<Location, Vec<BorrowIndex>> {
44 &self.activation_map
45 }
46
47 pub fn local_map(&self) -> &FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>> {
48 &self.local_map
49 }
50
51 pub fn locals_state_at_exit(&self) -> &LocalsStateAtExit {
52 &self.locals_state_at_exit
53 }
54}
55
56impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
57 type Output = BorrowData<'tcx>;
58
59 fn index(&self, index: BorrowIndex) -> &BorrowData<'tcx> {
60 &self.location_map[index.as_usize()]
61 }
62}
63
64#[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_fields_are_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)]
67pub enum TwoPhaseActivation {
68 NotTwoPhase,
69 NotActivated,
70 ActivatedAt(Location),
71}
72
73#[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)]
74pub struct BorrowData<'tcx> {
75 pub(crate) reserve_location: Location,
78 pub(crate) activation_location: TwoPhaseActivation,
80 pub(crate) kind: mir::BorrowKind,
82 pub(crate) region: RegionVid,
84 pub(crate) borrowed_place: mir::Place<'tcx>,
86 pub(crate) assigned_place: mir::Place<'tcx>,
88}
89
90impl<'tcx> BorrowData<'tcx> {
92 pub fn reserve_location(&self) -> Location {
93 self.reserve_location
94 }
95
96 pub fn activation_location(&self) -> TwoPhaseActivation {
97 self.activation_location
98 }
99
100 pub fn kind(&self) -> mir::BorrowKind {
101 self.kind
102 }
103
104 pub fn region(&self) -> RegionVid {
105 self.region
106 }
107
108 pub fn borrowed_place(&self) -> mir::Place<'tcx> {
109 self.borrowed_place
110 }
111
112 pub fn assigned_place(&self) -> mir::Place<'tcx> {
113 self.assigned_place
114 }
115}
116
117impl<'tcx> fmt::Display for BorrowData<'tcx> {
118 fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let kind = match self.kind {
120 mir::BorrowKind::Shared => "",
121 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => "fake ",
122 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => "fake shallow ",
123 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ",
124 mir::BorrowKind::Mut {
126 kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow,
127 } => "mut ",
128 };
129 w.write_fmt(format_args!("&{0:?} {1}{2:?}", self.region, kind,
self.borrowed_place))write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place)
130 }
131}
132
133pub enum LocalsStateAtExit {
134 AllAreInvalidated,
135 SomeAreInvalidated { has_storage_dead_or_moved: DenseBitSet<Local> },
136}
137
138impl LocalsStateAtExit {
139 fn build<'tcx>(
140 locals_are_invalidated_at_exit: bool,
141 body: &Body<'tcx>,
142 move_data: &MoveData<'tcx>,
143 ) -> Self {
144 struct HasStorageDead(DenseBitSet<Local>);
145
146 impl<'tcx> Visitor<'tcx> for HasStorageDead {
147 fn visit_local(&mut self, local: Local, ctx: PlaceContext, _: Location) {
148 if ctx == PlaceContext::NonUse(NonUseContext::StorageDead) {
149 self.0.insert(local);
150 }
151 }
152 }
153
154 if locals_are_invalidated_at_exit {
155 LocalsStateAtExit::AllAreInvalidated
156 } else {
157 let mut has_storage_dead =
158 HasStorageDead(DenseBitSet::new_empty(body.local_decls.len()));
159 has_storage_dead.visit_body(body);
160 let mut has_storage_dead_or_moved = has_storage_dead.0;
161 for move_out in &move_data.moves {
162 has_storage_dead_or_moved.insert(move_data.base_local(move_out.path));
163 }
164 LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved }
165 }
166 }
167}
168
169impl<'tcx> BorrowSet<'tcx> {
170 pub fn build(
171 tcx: TyCtxt<'tcx>,
172 body: &Body<'tcx>,
173 locals_are_invalidated_at_exit: bool,
174 move_data: &MoveData<'tcx>,
175 ) -> Self {
176 let mut visitor = GatherBorrows {
177 tcx,
178 body,
179 location_map: Default::default(),
180 activation_map: Default::default(),
181 local_map: Default::default(),
182 pending_activations: Default::default(),
183 locals_state_at_exit: LocalsStateAtExit::build(
184 locals_are_invalidated_at_exit,
185 body,
186 move_data,
187 ),
188 };
189
190 for (block, block_data) in traversal::preorder(body) {
191 visitor.visit_basic_block_data(block, block_data);
192 }
193
194 BorrowSet {
195 location_map: visitor.location_map,
196 activation_map: visitor.activation_map,
197 local_map: visitor.local_map,
198 locals_state_at_exit: visitor.locals_state_at_exit,
199 }
200 }
201
202 pub(crate) fn activations_at_location(&self, location: Location) -> &[BorrowIndex] {
203 self.activation_map.get(&location).map_or(&[], |activations| &activations[..])
204 }
205
206 pub(crate) fn len(&self) -> usize {
207 self.location_map.len()
208 }
209
210 pub(crate) fn indices(&self) -> impl Iterator<Item = BorrowIndex> {
211 BorrowIndex::ZERO..BorrowIndex::from_usize(self.len())
212 }
213
214 pub(crate) fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> {
215 self.indices().zip(self.location_map.values())
216 }
217
218 pub(crate) fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> {
219 self.location_map.get_index_of(location).map(BorrowIndex::from)
220 }
221}
222
223struct GatherBorrows<'a, 'tcx> {
224 tcx: TyCtxt<'tcx>,
225 body: &'a Body<'tcx>,
226 location_map: FxIndexMap<Location, BorrowData<'tcx>>,
227 activation_map: FxIndexMap<Location, Vec<BorrowIndex>>,
228 local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
229
230 pending_activations: FxIndexMap<mir::Local, BorrowIndex>,
239
240 locals_state_at_exit: LocalsStateAtExit,
241}
242
243impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
244 fn visit_assign(
245 &mut self,
246 assigned_place: &mir::Place<'tcx>,
247 rvalue: &mir::Rvalue<'tcx>,
248 location: mir::Location,
249 ) {
250 if let &mir::Rvalue::Ref(region, kind, borrowed_place) = rvalue {
251 if borrowed_place.ignore_borrow(self.tcx, self.body, &self.locals_state_at_exit) {
252 {
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:252",
"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(252u32),
::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);
253 return;
254 }
255
256 let region = region.as_var();
257 let borrow = |activation_location| BorrowData {
258 kind,
259 region,
260 reserve_location: location,
261 activation_location,
262 borrowed_place,
263 assigned_place: *assigned_place,
264 };
265
266 let idx = if !kind.is_two_phase_borrow() {
267 {
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:267",
"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(267u32),
::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);
268 let (idx, _) = self
269 .location_map
270 .insert_full(location, borrow(TwoPhaseActivation::NotTwoPhase));
271 BorrowIndex::from(idx)
272 } else {
273 let Some(temp) = assigned_place.as_local() else {
280 ::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!(
281 self.body.source_info(location).span,
282 "expected 2-phase borrow to assign to a local, not `{:?}`",
283 assigned_place,
284 );
285 };
286
287 let (idx, _) = self
290 .location_map
291 .insert_full(location, borrow(TwoPhaseActivation::NotActivated));
292 let idx = BorrowIndex::from(idx);
293
294 let prev = self.pending_activations.insert(temp, idx);
299 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");
300
301 idx
302 };
303
304 self.local_map.entry(borrowed_place.local).or_default().insert(idx);
305 } else if let &mir::Rvalue::Reborrow(target, mutability, borrowed_place) = rvalue {
306 let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty;
307 let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else {
308 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
309 };
310 let &ty::Adt(target_adt, assigned_args) = target.kind() else { ::core::panicking::panic("internal error: entered unreachable code")unreachable!() };
311 let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind())
312 else {
313 ::rustc_middle::util::bug::bug_fmt(format_args!("hir-typeck passed but {0} does not have a lifetime argument",
if mutability == Mutability::Mut {
"Reborrow"
} else { "CoerceShared" }));bug!(
314 "hir-typeck passed but {} does not have a lifetime argument",
315 if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" }
316 );
317 };
318 let region = region.as_var();
319 let kind = if mutability == Mutability::Mut {
320 if target_adt.did() != reborrowed_adt.did() {
322 ::rustc_middle::util::bug::bug_fmt(format_args!("hir-typeck passed but Reborrow involves mismatching types at {0:?}",
location))bug!(
323 "hir-typeck passed but Reborrow involves mismatching types at {location:?}"
324 )
325 }
326
327 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }
328 } else {
329 if target_adt.did() == reborrowed_adt.did() {
331 ::rustc_middle::util::bug::bug_fmt(format_args!("hir-typeck passed but CoerceShared involves matching types at {0:?}",
location))bug!(
332 "hir-typeck passed but CoerceShared involves matching types at {location:?}"
333 )
334 }
335 mir::BorrowKind::Shared
336 };
337 let borrow = BorrowData {
338 kind,
339 region,
340 reserve_location: location,
341 activation_location: TwoPhaseActivation::NotTwoPhase,
342 borrowed_place,
343 assigned_place: *assigned_place,
344 };
345 let (idx, _) = self.location_map.insert_full(location, borrow);
346 let idx = BorrowIndex::from(idx);
347
348 self.local_map.entry(borrowed_place.local).or_default().insert(idx);
349 }
350
351 self.super_assign(assigned_place, rvalue, location)
352 }
353
354 fn visit_local(&mut self, temp: Local, context: PlaceContext, location: Location) {
355 if !context.is_use() {
356 return;
357 }
358
359 if let Some(&borrow_index) = self.pending_activations.get(&temp) {
364 let borrow_data = &mut self.location_map[borrow_index.as_usize()];
365
366 if borrow_data.reserve_location == location
369 && context == PlaceContext::MutatingUse(MutatingUseContext::Store)
370 {
371 return;
372 }
373
374 if let TwoPhaseActivation::ActivatedAt(other_location) = borrow_data.activation_location
375 {
376 ::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!(
377 self.body.source_info(location).span,
378 "found two uses for 2-phase borrow temporary {:?}: \
379 {:?} and {:?}",
380 temp,
381 location,
382 other_location,
383 );
384 }
385
386 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!(
391 borrow_data.activation_location,
392 TwoPhaseActivation::NotActivated,
393 "never found an activation for this borrow!",
394 );
395 self.activation_map.entry(location).or_default().push(borrow_index);
396
397 borrow_data.activation_location = TwoPhaseActivation::ActivatedAt(location);
398 }
399 }
400
401 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
402 if let &mir::Rvalue::Ref(region, kind, place) = rvalue {
403 let borrow_data = &self.location_map[&location];
406 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);
407 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);
408 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());
409 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);
410 }
411
412 self.super_rvalue(rvalue, location)
413 }
414}