1use std::cell::RefCell;
2use std::fmt;
3use std::num::NonZero;
4
5use rustc_abi::Size;
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_middle::mir::RetagKind;
8use smallvec::SmallVec;
9
10use crate::*;
11pub mod stacked_borrows;
12pub mod tree_borrows;
13
14#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
16pub enum AccessKind {
17 Read,
18 Write,
19}
20
21impl fmt::Display for AccessKind {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 match self {
24 AccessKind::Read => write!(f, "read access"),
25 AccessKind::Write => write!(f, "write access"),
26 }
27 }
28}
29
30#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
32pub struct BorTag(NonZero<u64>);
33
34impl BorTag {
35 pub fn new(i: u64) -> Option<Self> {
36 NonZero::new(i).map(BorTag)
37 }
38
39 pub fn get(&self) -> u64 {
40 self.0.get()
41 }
42
43 pub fn inner(&self) -> NonZero<u64> {
44 self.0
45 }
46
47 pub fn succ(self) -> Option<Self> {
48 self.0.checked_add(1).map(Self)
49 }
50
51 pub fn one() -> Self {
53 Self::new(1).unwrap()
54 }
55}
56
57impl std::default::Default for BorTag {
58 fn default() -> Self {
60 Self::one()
61 }
62}
63
64impl fmt::Debug for BorTag {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 write!(f, "<{}>", self.0)
67 }
68}
69
70#[derive(Debug)]
72pub struct FrameState {
73 protected_tags: SmallVec<[(AllocId, BorTag); 2]>,
85}
86
87impl VisitProvenance for FrameState {
88 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
89 for (id, tag) in &self.protected_tags {
96 visit(Some(*id), Some(*tag));
97 }
98 }
99}
100
101#[derive(Debug)]
103pub struct GlobalStateInner {
104 borrow_tracker_method: BorrowTrackerMethod,
106 next_ptr_tag: BorTag,
108 root_ptr_tags: FxHashMap<AllocId, BorTag>,
112 protected_tags: FxHashMap<BorTag, ProtectorKind>,
117 tracked_pointer_tags: FxHashSet<BorTag>,
119 retag_fields: RetagFields,
121}
122
123impl VisitProvenance for GlobalStateInner {
124 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
125 }
129}
130
131pub type GlobalState = RefCell<GlobalStateInner>;
133
134#[derive(Copy, Clone, Debug)]
136pub enum RetagFields {
137 No,
139 Yes,
141 OnlyScalar,
144}
145
146#[derive(Copy, Clone, Debug, PartialEq, Eq)]
148pub enum ProtectorKind {
149 WeakProtector,
156
157 StrongProtector,
164}
165
166impl GlobalStateInner {
168 pub fn new(
169 borrow_tracker_method: BorrowTrackerMethod,
170 tracked_pointer_tags: FxHashSet<BorTag>,
171 retag_fields: RetagFields,
172 ) -> Self {
173 GlobalStateInner {
174 borrow_tracker_method,
175 next_ptr_tag: BorTag::one(),
176 root_ptr_tags: FxHashMap::default(),
177 protected_tags: FxHashMap::default(),
178 tracked_pointer_tags,
179 retag_fields,
180 }
181 }
182
183 fn new_ptr(&mut self) -> BorTag {
185 let id = self.next_ptr_tag;
186 self.next_ptr_tag = id.succ().unwrap();
187 id
188 }
189
190 pub fn new_frame(&mut self) -> FrameState {
191 FrameState { protected_tags: SmallVec::new() }
192 }
193
194 fn end_call(&mut self, frame: &machine::FrameExtra<'_>) {
195 for (_, tag) in &frame
196 .borrow_tracker
197 .as_ref()
198 .expect("we should have borrow tracking data")
199 .protected_tags
200 {
201 self.protected_tags.remove(tag);
202 }
203 }
204
205 pub fn root_ptr_tag(&mut self, id: AllocId, machine: &MiriMachine<'_>) -> BorTag {
206 self.root_ptr_tags.get(&id).copied().unwrap_or_else(|| {
207 let tag = self.new_ptr();
208 if self.tracked_pointer_tags.contains(&tag) {
209 machine.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
210 tag.inner(),
211 None,
212 None,
213 ));
214 }
215 trace!("New allocation {:?} has rpot tag {:?}", id, tag);
216 self.root_ptr_tags.try_insert(id, tag).unwrap();
217 tag
218 })
219 }
220
221 pub fn remove_unreachable_allocs(&mut self, allocs: &LiveAllocs<'_, '_>) {
222 self.root_ptr_tags.retain(|id, _| allocs.is_live(*id));
223 }
224
225 pub fn borrow_tracker_method(&self) -> BorrowTrackerMethod {
226 self.borrow_tracker_method
227 }
228}
229
230#[derive(Debug, Copy, Clone, PartialEq, Eq)]
232pub enum BorrowTrackerMethod {
233 StackedBorrows,
235 TreeBorrows(TreeBorrowsParams),
237}
238
239#[derive(Debug, Copy, Clone, PartialEq, Eq)]
241pub struct TreeBorrowsParams {
242 pub precise_interior_mut: bool,
243}
244
245impl BorrowTrackerMethod {
246 pub fn instantiate_global_state(self, config: &MiriConfig) -> GlobalState {
247 RefCell::new(GlobalStateInner::new(
248 self,
249 config.tracked_pointer_tags.clone(),
250 config.retag_fields,
251 ))
252 }
253
254 pub fn get_tree_borrows_params(self) -> TreeBorrowsParams {
255 match self {
256 BorrowTrackerMethod::TreeBorrows(params) => params,
257 _ => panic!("can only be called when `BorrowTrackerMethod` is `TreeBorrows`"),
258 }
259 }
260}
261
262impl GlobalStateInner {
263 pub fn new_allocation(
264 &mut self,
265 id: AllocId,
266 alloc_size: Size,
267 kind: MemoryKind,
268 machine: &MiriMachine<'_>,
269 ) -> AllocState {
270 let _trace = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
271 match self.borrow_tracker_method {
272 BorrowTrackerMethod::StackedBorrows =>
273 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
274 id, alloc_size, self, kind, machine,
275 )))),
276 BorrowTrackerMethod::TreeBorrows { .. } =>
277 AllocState::TreeBorrows(Box::new(RefCell::new(Tree::new_allocation(
278 id, alloc_size, self, kind, machine,
279 )))),
280 }
281 }
282}
283
284impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
285pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
286 fn retag_ptr_value(
287 &mut self,
288 kind: RetagKind,
289 val: &ImmTy<'tcx>,
290 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
291 let _trace = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout);
292 let this = self.eval_context_mut();
293 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
294 match method {
295 BorrowTrackerMethod::StackedBorrows => this.sb_retag_ptr_value(kind, val),
296 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_retag_ptr_value(kind, val),
297 }
298 }
299
300 fn retag_place_contents(
301 &mut self,
302 kind: RetagKind,
303 place: &PlaceTy<'tcx>,
304 ) -> InterpResult<'tcx> {
305 let _trace = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place);
306 let this = self.eval_context_mut();
307 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
308 match method {
309 BorrowTrackerMethod::StackedBorrows => this.sb_retag_place_contents(kind, place),
310 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_retag_place_contents(kind, place),
311 }
312 }
313
314 fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
315 let _trace = enter_trace_span!(borrow_tracker::protect_place, ?place);
316 let this = self.eval_context_mut();
317 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
318 match method {
319 BorrowTrackerMethod::StackedBorrows => this.sb_protect_place(place),
320 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_protect_place(place),
321 }
322 }
323
324 fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
325 let _trace =
326 enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0);
327 let this = self.eval_context_ref();
328 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
329 match method {
330 BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),
331 BorrowTrackerMethod::TreeBorrows { .. } => this.tb_expose_tag(alloc_id, tag),
332 }
333 }
334
335 fn give_pointer_debug_name(
336 &mut self,
337 ptr: Pointer,
338 nth_parent: u8,
339 name: &str,
340 ) -> InterpResult<'tcx> {
341 let this = self.eval_context_mut();
342 let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
343 match method {
344 BorrowTrackerMethod::StackedBorrows => {
345 this.tcx.tcx.dcx().warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op");
346 interp_ok(())
347 }
348 BorrowTrackerMethod::TreeBorrows { .. } =>
349 this.tb_give_pointer_debug_name(ptr, nth_parent, name),
350 }
351 }
352
353 fn print_borrow_state(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
354 let this = self.eval_context_mut();
355 let Some(borrow_tracker) = &this.machine.borrow_tracker else {
356 eprintln!("attempted to print borrow state, but no borrow state is being tracked");
357 return interp_ok(());
358 };
359 let method = borrow_tracker.borrow().borrow_tracker_method;
360 match method {
361 BorrowTrackerMethod::StackedBorrows => this.print_stacks(alloc_id),
362 BorrowTrackerMethod::TreeBorrows { .. } => this.print_tree(alloc_id, show_unnamed),
363 }
364 }
365
366 fn on_stack_pop(
367 &self,
368 frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
369 ) -> InterpResult<'tcx> {
370 let _trace = enter_trace_span!(borrow_tracker::on_stack_pop);
371 let this = self.eval_context_ref();
372 let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
373 for (alloc_id, tag) in &frame
376 .extra
377 .borrow_tracker
378 .as_ref()
379 .expect("we should have borrow tracking data")
380 .protected_tags
381 {
382 let kind = this.get_alloc_info(*alloc_id).kind;
389 if matches!(kind, AllocKind::LiveData) {
390 let alloc_extra = this.get_alloc_extra(*alloc_id)?; let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
392 alloc_borrow_tracker.release_protector(
393 &this.machine,
394 borrow_tracker,
395 *tag,
396 *alloc_id,
397 )?;
398 }
399 }
400 borrow_tracker.borrow_mut().end_call(&frame.extra);
401 interp_ok(())
402 }
403}
404
405#[derive(Debug, Clone)]
407pub enum AllocState {
408 StackedBorrows(Box<RefCell<stacked_borrows::AllocState>>),
410 TreeBorrows(Box<RefCell<tree_borrows::AllocState>>),
412}
413
414impl machine::AllocExtra<'_> {
415 #[track_caller]
416 pub fn borrow_tracker_sb(&self) -> &RefCell<stacked_borrows::AllocState> {
417 match self.borrow_tracker {
418 Some(AllocState::StackedBorrows(ref sb)) => sb,
419 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
420 }
421 }
422
423 #[track_caller]
424 pub fn borrow_tracker_sb_mut(&mut self) -> &mut RefCell<stacked_borrows::AllocState> {
425 match self.borrow_tracker {
426 Some(AllocState::StackedBorrows(ref mut sb)) => sb,
427 _ => panic!("expected Stacked Borrows borrow tracking, got something else"),
428 }
429 }
430
431 #[track_caller]
432 pub fn borrow_tracker_tb(&self) -> &RefCell<tree_borrows::AllocState> {
433 match self.borrow_tracker {
434 Some(AllocState::TreeBorrows(ref tb)) => tb,
435 _ => panic!("expected Tree Borrows borrow tracking, got something else"),
436 }
437 }
438}
439
440impl AllocState {
441 pub fn before_memory_read<'tcx>(
442 &self,
443 alloc_id: AllocId,
444 prov_extra: ProvenanceExtra,
445 range: AllocRange,
446 machine: &MiriMachine<'tcx>,
447 ) -> InterpResult<'tcx> {
448 let _trace = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
449 match self {
450 AllocState::StackedBorrows(sb) =>
451 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
452 AllocState::TreeBorrows(tb) =>
453 tb.borrow_mut().before_memory_access(
454 AccessKind::Read,
455 alloc_id,
456 prov_extra,
457 range,
458 machine,
459 ),
460 }
461 }
462
463 pub fn before_memory_write<'tcx>(
464 &mut self,
465 alloc_id: AllocId,
466 prov_extra: ProvenanceExtra,
467 range: AllocRange,
468 machine: &MiriMachine<'tcx>,
469 ) -> InterpResult<'tcx> {
470 let _trace = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
471 match self {
472 AllocState::StackedBorrows(sb) =>
473 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
474 AllocState::TreeBorrows(tb) =>
475 tb.get_mut().before_memory_access(
476 AccessKind::Write,
477 alloc_id,
478 prov_extra,
479 range,
480 machine,
481 ),
482 }
483 }
484
485 pub fn before_memory_deallocation<'tcx>(
486 &mut self,
487 alloc_id: AllocId,
488 prov_extra: ProvenanceExtra,
489 size: Size,
490 machine: &MiriMachine<'tcx>,
491 ) -> InterpResult<'tcx> {
492 let _trace =
493 enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0);
494 match self {
495 AllocState::StackedBorrows(sb) =>
496 sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
497 AllocState::TreeBorrows(tb) =>
498 tb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
499 }
500 }
501
502 pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
503 let _trace = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
504 match self {
505 AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
506 AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags),
507 }
508 }
509
510 pub fn release_protector<'tcx>(
512 &self,
513 machine: &MiriMachine<'tcx>,
514 global: &GlobalState,
515 tag: BorTag,
516 alloc_id: AllocId, ) -> InterpResult<'tcx> {
518 let _trace = enter_trace_span!(
519 borrow_tracker::release_protector,
520 alloc_id = alloc_id.0,
521 tag = tag.0
522 );
523 match self {
524 AllocState::StackedBorrows(_sb) => interp_ok(()),
525 AllocState::TreeBorrows(tb) =>
526 tb.borrow_mut().release_protector(machine, global, tag, alloc_id),
527 }
528 }
529}
530
531impl VisitProvenance for AllocState {
532 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
533 let _trace = enter_trace_span!(borrow_tracker::visit_provenance);
534 match self {
535 AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
536 AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
537 }
538 }
539}