1use std::collections::hash_map::Entry;
2use std::fmt;
3use std::ops::Index;
4
5use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
6use rustc_hir::Mutability;
7use rustc_index::IndexVec;
8use rustc_index::bit_set::DenseBitSet;
9use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor};
10use rustc_middle::mir::{self, Body, Local, Location, traversal};
11use rustc_middle::ty::data_structures::IndexSet;
12use rustc_middle::ty::{RegionVid, TyCtxt};
13use rustc_middle::{bug, span_bug, ty};
14use rustc_mir_dataflow::move_paths::MoveData;
15use smallvec::{SmallVec, smallvec};
16use tracing::debug;
17
18use crate::BorrowIndex;
19use crate::place_ext::PlaceExt;
20
21pub struct BorrowSet<'tcx> {
22 borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
24
25 location_map: FxHashMap<Location, SmallVec<[BorrowIndex; 1]>>,
37
38 activation_map: FxHashMap<Location, SmallVec<[BorrowIndex; 1]>>,
40
41 local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
43
44 locals_state_at_exit: LocalsStateAtExit,
45}
46
47impl<'tcx> BorrowSet<'tcx> {
48 pub fn build(
50 tcx: TyCtxt<'tcx>,
51 body: &Body<'tcx>,
52 locals_are_invalidated_at_exit: bool,
53 move_data: &MoveData<'tcx>,
54 ) -> Self {
55 let mut visitor = GatherBorrows {
56 tcx,
57 body,
58 borrows: Default::default(),
59 location_map: Default::default(),
60 activation_map: Default::default(),
61 local_map: Default::default(),
62 pending_activations: Default::default(),
63 locals_state_at_exit: LocalsStateAtExit::build(
64 locals_are_invalidated_at_exit,
65 body,
66 move_data,
67 ),
68 };
69
70 for (block, block_data) in traversal::preorder(body) {
71 visitor.visit_basic_block_data(block, block_data);
72 }
73
74 BorrowSet {
75 borrows: visitor.borrows,
76 location_map: visitor.location_map,
77 activation_map: visitor.activation_map,
78 local_map: visitor.local_map,
79 locals_state_at_exit: visitor.locals_state_at_exit,
80 }
81 }
82
83 pub fn iter(&self) -> impl Iterator<Item = &BorrowData<'tcx>> {
86 self.borrows.iter()
87 }
88
89 pub fn locals_state_at_exit(&self) -> &LocalsStateAtExit {
91 &self.locals_state_at_exit
92 }
93
94 pub fn len(&self) -> usize {
96 self.borrows.len()
97 }
98
99 pub fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> {
100 self.borrows.iter_enumerated()
101 }
102
103 pub fn activations_at_location(&self, location: &Location) -> &[BorrowIndex] {
105 self.activation_map.get(&location).map_or(&[], |activations| &activations[..])
106 }
107
108 pub fn borrows_at_location(&self, location: &Location) -> Option<&[BorrowIndex]> {
110 self.location_map.get(location).map(|v| v.as_slice())
111 }
112
113 pub fn borrows_on_local(&self, local: Local) -> Option<&IndexSet<BorrowIndex>> {
115 self.local_map.get(&local)
116 }
117}
118
119impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
120 type Output = BorrowData<'tcx>;
121
122 fn index(&self, index: BorrowIndex) -> &BorrowData<'tcx> {
123 &self.borrows[index]
124 }
125}
126
127#[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)]
130pub enum TwoPhaseActivation {
131 NotTwoPhase,
132 NotActivated,
133 ActivatedAt(Location),
134}
135
136#[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)]
137pub struct BorrowData<'tcx> {
138 pub(crate) reserve_location: Location,
141 pub(crate) activation_location: TwoPhaseActivation,
143 pub(crate) kind: mir::BorrowKind,
145 pub(crate) region: RegionVid,
147 pub(crate) borrowed_place: mir::Place<'tcx>,
149 pub(crate) assigned_place: mir::Place<'tcx>,
151}
152
153impl<'tcx> BorrowData<'tcx> {
155 pub fn reserve_location(&self) -> Location {
156 self.reserve_location
157 }
158
159 pub fn activation_location(&self) -> TwoPhaseActivation {
160 self.activation_location
161 }
162
163 pub fn kind(&self) -> mir::BorrowKind {
164 self.kind
165 }
166
167 pub fn region(&self) -> RegionVid {
168 self.region
169 }
170
171 pub fn borrowed_place(&self) -> mir::Place<'tcx> {
172 self.borrowed_place
173 }
174
175 pub fn assigned_place(&self) -> mir::Place<'tcx> {
176 self.assigned_place
177 }
178}
179
180impl<'tcx> fmt::Display for BorrowData<'tcx> {
181 fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
182 let kind = match self.kind {
183 mir::BorrowKind::Shared => "",
184 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => "fake ",
185 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => "fake shallow ",
186 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ",
187 mir::BorrowKind::Mut {
189 kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow,
190 } => "mut ",
191 };
192 w.write_fmt(format_args!("&{0:?} {1}{2:?}", self.region, kind,
self.borrowed_place))write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place)
193 }
194}
195
196pub enum LocalsStateAtExit {
197 AllAreInvalidated,
198 SomeAreInvalidated { has_storage_dead_or_moved: DenseBitSet<Local> },
199}
200
201impl LocalsStateAtExit {
202 fn build<'tcx>(
203 locals_are_invalidated_at_exit: bool,
204 body: &Body<'tcx>,
205 move_data: &MoveData<'tcx>,
206 ) -> Self {
207 struct HasStorageDead(DenseBitSet<Local>);
208
209 impl<'tcx> Visitor<'tcx> for HasStorageDead {
210 fn visit_local(&mut self, local: Local, ctx: PlaceContext, _: Location) {
211 if ctx == PlaceContext::NonUse(NonUseContext::StorageDead) {
212 self.0.insert(local);
213 }
214 }
215 }
216
217 if locals_are_invalidated_at_exit {
218 LocalsStateAtExit::AllAreInvalidated
219 } else {
220 let mut has_storage_dead =
221 HasStorageDead(DenseBitSet::new_empty(body.local_decls.len()));
222 has_storage_dead.visit_body(body);
223 let mut has_storage_dead_or_moved = has_storage_dead.0;
224 for move_out in &move_data.move_outs {
225 has_storage_dead_or_moved.insert(move_data.base_local(move_out.path));
226 }
227 LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved }
228 }
229 }
230}
231
232struct GatherBorrows<'a, 'tcx> {
233 tcx: TyCtxt<'tcx>,
234 body: &'a Body<'tcx>,
235 borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
236 location_map: FxHashMap<Location, SmallVec<[BorrowIndex; 1]>>,
237 activation_map: FxHashMap<Location, SmallVec<[BorrowIndex; 1]>>,
238 local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
239
240 pending_activations: FxIndexMap<mir::Local, BorrowIndex>,
249
250 locals_state_at_exit: LocalsStateAtExit,
251}
252
253impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
254 fn insert_borrow(&mut self, location: Location, borrow: BorrowData<'tcx>) -> BorrowIndex {
255 let idx = self.borrows.push(borrow);
256 match self.location_map.entry(location) {
257 Entry::Occupied(entry) => {
258 ::rustc_middle::util::bug::bug_fmt(format_args!("Inserting a borrow {0:?} at {1:?} attempted to override an existing list {2:?}",
idx, location, entry));bug!(
259 "Inserting a borrow {idx:?} at {location:?} attempted to override an existing list {entry:?}"
260 );
261 }
262 Entry::Vacant(entry) => {
263 entry.insert({
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(idx);
vec
} else {
::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[idx])))
}
}smallvec![idx]);
264 }
265 }
266 idx
267 }
268}
269
270impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
271 fn visit_assign(
272 &mut self,
273 assigned_place: &mir::Place<'tcx>,
274 rvalue: &mir::Rvalue<'tcx>,
275 location: mir::Location,
276 ) {
277 if let &mir::Rvalue::Ref(region, kind, borrowed_place) = rvalue {
278 if borrowed_place.ignore_borrow(self.tcx, self.body, &self.locals_state_at_exit) {
279 {
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:279",
"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(279u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("ignoring_borrow of {0:?}",
borrowed_place) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("ignoring_borrow of {:?}", borrowed_place);
280 return;
281 }
282
283 let region = region.as_var();
284 let borrow = |activation_location| BorrowData {
285 kind,
286 region,
287 reserve_location: location,
288 activation_location,
289 borrowed_place,
290 assigned_place: *assigned_place,
291 };
292
293 let idx = if !kind.is_two_phase_borrow() {
294 {
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:294",
"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(294u32),
::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};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!(" -> {0:?}",
location) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(" -> {:?}", location);
295 self.insert_borrow(location, borrow(TwoPhaseActivation::NotTwoPhase))
296 } else {
297 let Some(temp) = assigned_place.as_local() else {
304 ::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!(
305 self.body.source_info(location).span,
306 "expected 2-phase borrow to assign to a local, not `{:?}`",
307 assigned_place,
308 );
309 };
310
311 let idx = self.insert_borrow(location, borrow(TwoPhaseActivation::NotActivated));
314
315 let prev = self.pending_activations.insert(temp, idx);
320 {
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");
321
322 idx
323 };
324
325 self.local_map.entry(borrowed_place.local).or_default().insert(idx);
326 } else if let &mir::Rvalue::Reborrow(target, mutability, borrowed_place) = rvalue {
327 let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty;
328 let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else {
329 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
330 };
331 let &ty::Adt(target_adt, assigned_args) = target.kind() else { ::core::panicking::panic("internal error: entered unreachable code")unreachable!() };
332 let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind())
333 else {
334 ::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!(
335 "hir-typeck passed but {} does not have a lifetime argument",
336 if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" }
337 );
338 };
339 let region = region.as_var();
340 let kind = if mutability == Mutability::Mut {
341 if target_adt.did() != reborrowed_adt.did() {
343 ::rustc_middle::util::bug::bug_fmt(format_args!("hir-typeck passed but Reborrow involves mismatching types at {0:?}",
location))bug!(
344 "hir-typeck passed but Reborrow involves mismatching types at {location:?}"
345 )
346 }
347
348 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }
349 } else {
350 if target_adt.did() == reborrowed_adt.did() {
352 ::rustc_middle::util::bug::bug_fmt(format_args!("hir-typeck passed but CoerceShared involves matching types at {0:?}",
location))bug!(
353 "hir-typeck passed but CoerceShared involves matching types at {location:?}"
354 )
355 }
356 mir::BorrowKind::Shared
357 };
358 let borrow = BorrowData {
359 kind,
360 region,
361 reserve_location: location,
362 activation_location: TwoPhaseActivation::NotTwoPhase,
363 borrowed_place,
364 assigned_place: *assigned_place,
365 };
366 let idx = self.insert_borrow(location, borrow);
367
368 self.local_map.entry(borrowed_place.local).or_default().insert(idx);
369 }
370
371 self.super_assign(assigned_place, rvalue, location)
372 }
373
374 fn visit_local(&mut self, temp: Local, context: PlaceContext, location: Location) {
375 if !context.is_use() {
376 return;
377 }
378
379 let Some(&borrow_index) = self.pending_activations.get(&temp) else {
384 return;
385 };
386 let borrow_data = &mut self.borrows[borrow_index];
387
388 if borrow_data.reserve_location == location
391 && context == PlaceContext::MutatingUse(MutatingUseContext::Store)
392 {
393 return;
394 }
395
396 if let TwoPhaseActivation::ActivatedAt(other_location) = borrow_data.activation_location {
397 ::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!(
398 self.body.source_info(location).span,
399 "found two uses for 2-phase borrow temporary {:?}: \
400 {:?} and {:?}",
401 temp,
402 location,
403 other_location,
404 );
405 }
406
407 {
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!(
412 borrow_data.activation_location,
413 TwoPhaseActivation::NotActivated,
414 "never found an activation for this borrow!",
415 );
416 self.activation_map.entry(location).or_default().push(borrow_index);
417
418 borrow_data.activation_location = TwoPhaseActivation::ActivatedAt(location);
419 }
420
421 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
422 if let &mir::Rvalue::Ref(region, kind, place) = rvalue {
423 let idxs = &self.location_map[&location];
426 for idx in idxs {
427 let borrow_data = &self.borrows[*idx];
428 {
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);
429 {
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);
430 {
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());
431 {
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);
432 }
433 }
434
435 self.super_rvalue(rvalue, location)
436 }
437}