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(crate) fn locals_state_at_exit(&self) -> &LocalsStateAtExit {
91 &self.locals_state_at_exit
92 }
93
94 pub(crate) fn len(&self) -> usize {
95 self.borrows.len()
96 }
97
98 pub(crate) fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> {
99 self.borrows.iter_enumerated()
100 }
101
102 pub(crate) fn activations_at_location(&self, location: &Location) -> &[BorrowIndex] {
103 self.activation_map.get(&location).map_or(&[], |activations| &activations[..])
104 }
105
106 pub(crate) fn borrows_at_location(&self, location: &Location) -> Option<&[BorrowIndex]> {
107 self.location_map.get(location).map(|v| v.as_slice())
108 }
109
110 pub(crate) fn borrows_on_local(&self, local: Local) -> Option<&IndexSet<BorrowIndex>> {
111 self.local_map.get(&local)
112 }
113}
114
115impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
116 type Output = BorrowData<'tcx>;
117
118 fn index(&self, index: BorrowIndex) -> &BorrowData<'tcx> {
119 &self.borrows[index]
120 }
121}
122
123#[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)]
126pub enum TwoPhaseActivation {
127 NotTwoPhase,
128 NotActivated,
129 ActivatedAt(Location),
130}
131
132#[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)]
133pub struct BorrowData<'tcx> {
134 pub(crate) reserve_location: Location,
137 pub(crate) activation_location: TwoPhaseActivation,
139 pub(crate) kind: mir::BorrowKind,
141 pub(crate) region: RegionVid,
143 pub(crate) borrowed_place: mir::Place<'tcx>,
145 pub(crate) assigned_place: mir::Place<'tcx>,
147}
148
149impl<'tcx> BorrowData<'tcx> {
151 pub fn reserve_location(&self) -> Location {
152 self.reserve_location
153 }
154
155 pub fn activation_location(&self) -> TwoPhaseActivation {
156 self.activation_location
157 }
158
159 pub fn kind(&self) -> mir::BorrowKind {
160 self.kind
161 }
162
163 pub fn region(&self) -> RegionVid {
164 self.region
165 }
166
167 pub fn borrowed_place(&self) -> mir::Place<'tcx> {
168 self.borrowed_place
169 }
170
171 pub fn assigned_place(&self) -> mir::Place<'tcx> {
172 self.assigned_place
173 }
174}
175
176impl<'tcx> fmt::Display for BorrowData<'tcx> {
177 fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
178 let kind = match self.kind {
179 mir::BorrowKind::Shared => "",
180 mir::BorrowKind::Fake(mir::FakeBorrowKind::Deep) => "fake ",
181 mir::BorrowKind::Fake(mir::FakeBorrowKind::Shallow) => "fake shallow ",
182 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::ClosureCapture } => "uniq ",
183 mir::BorrowKind::Mut {
185 kind: mir::MutBorrowKind::Default | mir::MutBorrowKind::TwoPhaseBorrow,
186 } => "mut ",
187 };
188 w.write_fmt(format_args!("&{0:?} {1}{2:?}", self.region, kind,
self.borrowed_place))write!(w, "&{:?} {}{:?}", self.region, kind, self.borrowed_place)
189 }
190}
191
192pub enum LocalsStateAtExit {
193 AllAreInvalidated,
194 SomeAreInvalidated { has_storage_dead_or_moved: DenseBitSet<Local> },
195}
196
197impl LocalsStateAtExit {
198 fn build<'tcx>(
199 locals_are_invalidated_at_exit: bool,
200 body: &Body<'tcx>,
201 move_data: &MoveData<'tcx>,
202 ) -> Self {
203 struct HasStorageDead(DenseBitSet<Local>);
204
205 impl<'tcx> Visitor<'tcx> for HasStorageDead {
206 fn visit_local(&mut self, local: Local, ctx: PlaceContext, _: Location) {
207 if ctx == PlaceContext::NonUse(NonUseContext::StorageDead) {
208 self.0.insert(local);
209 }
210 }
211 }
212
213 if locals_are_invalidated_at_exit {
214 LocalsStateAtExit::AllAreInvalidated
215 } else {
216 let mut has_storage_dead =
217 HasStorageDead(DenseBitSet::new_empty(body.local_decls.len()));
218 has_storage_dead.visit_body(body);
219 let mut has_storage_dead_or_moved = has_storage_dead.0;
220 for move_out in &move_data.moves {
221 has_storage_dead_or_moved.insert(move_data.base_local(move_out.path));
222 }
223 LocalsStateAtExit::SomeAreInvalidated { has_storage_dead_or_moved }
224 }
225 }
226}
227
228struct GatherBorrows<'a, 'tcx> {
229 tcx: TyCtxt<'tcx>,
230 body: &'a Body<'tcx>,
231 borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
232 location_map: FxHashMap<Location, SmallVec<[BorrowIndex; 1]>>,
233 activation_map: FxHashMap<Location, SmallVec<[BorrowIndex; 1]>>,
234 local_map: FxIndexMap<mir::Local, FxIndexSet<BorrowIndex>>,
235
236 pending_activations: FxIndexMap<mir::Local, BorrowIndex>,
245
246 locals_state_at_exit: LocalsStateAtExit,
247}
248
249impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
250 fn insert_borrow(&mut self, location: Location, borrow: BorrowData<'tcx>) -> BorrowIndex {
251 let idx = self.borrows.push(borrow);
252 match self.location_map.entry(location) {
253 Entry::Occupied(entry) => {
254 ::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!(
255 "Inserting a borrow {idx:?} at {location:?} attempted to override an existing list {entry:?}"
256 );
257 }
258 Entry::Vacant(entry) => {
259 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]);
260 }
261 }
262 idx
263 }
264}
265
266impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
267 fn visit_assign(
268 &mut self,
269 assigned_place: &mir::Place<'tcx>,
270 rvalue: &mir::Rvalue<'tcx>,
271 location: mir::Location,
272 ) {
273 if let &mir::Rvalue::Ref(region, kind, borrowed_place) = rvalue {
274 if borrowed_place.ignore_borrow(self.tcx, self.body, &self.locals_state_at_exit) {
275 {
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:275",
"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(275u32),
::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);
276 return;
277 }
278
279 let region = region.as_var();
280 let borrow = |activation_location| BorrowData {
281 kind,
282 region,
283 reserve_location: location,
284 activation_location,
285 borrowed_place,
286 assigned_place: *assigned_place,
287 };
288
289 let idx = if !kind.is_two_phase_borrow() {
290 {
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:290",
"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(290u32),
::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);
291 self.insert_borrow(location, borrow(TwoPhaseActivation::NotTwoPhase))
292 } else {
293 let Some(temp) = assigned_place.as_local() else {
300 ::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!(
301 self.body.source_info(location).span,
302 "expected 2-phase borrow to assign to a local, not `{:?}`",
303 assigned_place,
304 );
305 };
306
307 let idx = self.insert_borrow(location, borrow(TwoPhaseActivation::NotActivated));
310
311 let prev = self.pending_activations.insert(temp, idx);
316 {
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");
317
318 idx
319 };
320
321 self.local_map.entry(borrowed_place.local).or_default().insert(idx);
322 } else if let &mir::Rvalue::Reborrow(target, mutability, borrowed_place) = rvalue {
323 let borrowed_place_ty = borrowed_place.ty(self.body, self.tcx).ty;
324 let &ty::Adt(reborrowed_adt, _reborrowed_args) = borrowed_place_ty.kind() else {
325 ::core::panicking::panic("internal error: entered unreachable code")unreachable!()
326 };
327 let &ty::Adt(target_adt, assigned_args) = target.kind() else { ::core::panicking::panic("internal error: entered unreachable code")unreachable!() };
328 let Some(ty::GenericArgKind::Lifetime(region)) = assigned_args.get(0).map(|r| r.kind())
329 else {
330 ::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!(
331 "hir-typeck passed but {} does not have a lifetime argument",
332 if mutability == Mutability::Mut { "Reborrow" } else { "CoerceShared" }
333 );
334 };
335 let region = region.as_var();
336 let kind = if mutability == Mutability::Mut {
337 if target_adt.did() != reborrowed_adt.did() {
339 ::rustc_middle::util::bug::bug_fmt(format_args!("hir-typeck passed but Reborrow involves mismatching types at {0:?}",
location))bug!(
340 "hir-typeck passed but Reborrow involves mismatching types at {location:?}"
341 )
342 }
343
344 mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }
345 } else {
346 if target_adt.did() == reborrowed_adt.did() {
348 ::rustc_middle::util::bug::bug_fmt(format_args!("hir-typeck passed but CoerceShared involves matching types at {0:?}",
location))bug!(
349 "hir-typeck passed but CoerceShared involves matching types at {location:?}"
350 )
351 }
352 mir::BorrowKind::Shared
353 };
354 let borrow = BorrowData {
355 kind,
356 region,
357 reserve_location: location,
358 activation_location: TwoPhaseActivation::NotTwoPhase,
359 borrowed_place,
360 assigned_place: *assigned_place,
361 };
362 let idx = self.insert_borrow(location, borrow);
363
364 self.local_map.entry(borrowed_place.local).or_default().insert(idx);
365 }
366
367 self.super_assign(assigned_place, rvalue, location)
368 }
369
370 fn visit_local(&mut self, temp: Local, context: PlaceContext, location: Location) {
371 if !context.is_use() {
372 return;
373 }
374
375 let Some(&borrow_index) = self.pending_activations.get(&temp) else {
380 return;
381 };
382 let borrow_data = &mut self.borrows[borrow_index];
383
384 if borrow_data.reserve_location == location
387 && context == PlaceContext::MutatingUse(MutatingUseContext::Store)
388 {
389 return;
390 }
391
392 if let TwoPhaseActivation::ActivatedAt(other_location) = borrow_data.activation_location {
393 ::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!(
394 self.body.source_info(location).span,
395 "found two uses for 2-phase borrow temporary {:?}: \
396 {:?} and {:?}",
397 temp,
398 location,
399 other_location,
400 );
401 }
402
403 {
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!(
408 borrow_data.activation_location,
409 TwoPhaseActivation::NotActivated,
410 "never found an activation for this borrow!",
411 );
412 self.activation_map.entry(location).or_default().push(borrow_index);
413
414 borrow_data.activation_location = TwoPhaseActivation::ActivatedAt(location);
415 }
416
417 fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: mir::Location) {
418 if let &mir::Rvalue::Ref(region, kind, place) = rvalue {
419 let idxs = &self.location_map[&location];
422 for idx in idxs {
423 let borrow_data = &self.borrows[*idx];
424 {
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);
425 {
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);
426 {
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());
427 {
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);
428 }
429 }
430
431 self.super_rvalue(rvalue, location)
432 }
433}