1 use log::trace;
2
3 use rustc_target::abi::{Abi, Size};
4
5 use crate::borrow_tracker::{AccessKind, GlobalStateInner, ProtectorKind, RetagFields};
6 use rustc_middle::{
7 mir::{Mutability, RetagKind},
8 ty::{
9 self,
10 layout::{HasParamEnv, LayoutOf},
11 Ty,
12 },
13 };
14 use rustc_span::def_id::DefId;
15
16 use crate::*;
17
18 pub mod diagnostics;
19 mod perms;
20 mod tree;
21 mod unimap;
22 use perms::Permission;
23 pub use tree::Tree;
24
25 pub type AllocState = Tree;
26
27 impl<'tcx> Tree {
28 /// Create a new allocation, i.e. a new tree
new_allocation( id: AllocId, size: Size, state: &mut GlobalStateInner, _kind: MemoryKind<machine::MiriMemoryKind>, machine: &MiriMachine<'_, 'tcx>, ) -> Self29 pub fn new_allocation(
30 id: AllocId,
31 size: Size,
32 state: &mut GlobalStateInner,
33 _kind: MemoryKind<machine::MiriMemoryKind>,
34 machine: &MiriMachine<'_, 'tcx>,
35 ) -> Self {
36 let tag = state.base_ptr_tag(id, machine); // Fresh tag for the root
37 let span = machine.current_span();
38 Tree::new(tag, size, span)
39 }
40
41 /// Check that an access on the entire range is permitted, and update
42 /// the tree.
before_memory_access( &mut self, access_kind: AccessKind, alloc_id: AllocId, prov: ProvenanceExtra, range: AllocRange, machine: &MiriMachine<'_, 'tcx>, ) -> InterpResult<'tcx>43 pub fn before_memory_access(
44 &mut self,
45 access_kind: AccessKind,
46 alloc_id: AllocId,
47 prov: ProvenanceExtra,
48 range: AllocRange,
49 machine: &MiriMachine<'_, 'tcx>,
50 ) -> InterpResult<'tcx> {
51 trace!(
52 "{} with tag {:?}: {:?}, size {}",
53 access_kind,
54 prov,
55 Pointer::new(alloc_id, range.start),
56 range.size.bytes(),
57 );
58 // TODO: for now we bail out on wildcard pointers. Eventually we should
59 // handle them as much as we can.
60 let tag = match prov {
61 ProvenanceExtra::Concrete(tag) => tag,
62 ProvenanceExtra::Wildcard => return Ok(()),
63 };
64 let global = machine.borrow_tracker.as_ref().unwrap();
65 let span = machine.current_span();
66 self.perform_access(
67 access_kind,
68 tag,
69 range,
70 global,
71 span,
72 diagnostics::AccessCause::Explicit(access_kind),
73 )
74 }
75
76 /// Check that this pointer has permission to deallocate this range.
before_memory_deallocation( &mut self, _alloc_id: AllocId, prov: ProvenanceExtra, range: AllocRange, machine: &MiriMachine<'_, 'tcx>, ) -> InterpResult<'tcx>77 pub fn before_memory_deallocation(
78 &mut self,
79 _alloc_id: AllocId,
80 prov: ProvenanceExtra,
81 range: AllocRange,
82 machine: &MiriMachine<'_, 'tcx>,
83 ) -> InterpResult<'tcx> {
84 // TODO: for now we bail out on wildcard pointers. Eventually we should
85 // handle them as much as we can.
86 let tag = match prov {
87 ProvenanceExtra::Concrete(tag) => tag,
88 ProvenanceExtra::Wildcard => return Ok(()),
89 };
90 let global = machine.borrow_tracker.as_ref().unwrap();
91 let span = machine.current_span();
92 self.dealloc(tag, range, global, span)
93 }
94
expose_tag(&mut self, _tag: BorTag)95 pub fn expose_tag(&mut self, _tag: BorTag) {
96 // TODO
97 }
98 }
99
100 /// Policy for a new borrow.
101 #[derive(Debug, Clone, Copy)]
102 struct NewPermission {
103 /// Optionally ignore the actual size to do a zero-size reborrow.
104 /// If this is set then `dereferenceable` is not enforced.
105 zero_size: bool,
106 /// Which permission should the pointer start with.
107 initial_state: Permission,
108 /// Whether this pointer is part of the arguments of a function call.
109 /// `protector` is `Some(_)` for all pointers marked `noalias`.
110 protector: Option<ProtectorKind>,
111 }
112
113 impl<'tcx> NewPermission {
114 /// Determine NewPermission of the reference from the type of the pointee.
from_ref_ty( pointee: Ty<'tcx>, mutability: Mutability, kind: RetagKind, cx: &crate::MiriInterpCx<'_, 'tcx>, ) -> Option<Self>115 fn from_ref_ty(
116 pointee: Ty<'tcx>,
117 mutability: Mutability,
118 kind: RetagKind,
119 cx: &crate::MiriInterpCx<'_, 'tcx>,
120 ) -> Option<Self> {
121 let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.param_env());
122 let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.param_env());
123 let initial_state = match mutability {
124 Mutability::Mut if ty_is_unpin => Permission::new_unique_2phase(ty_is_freeze),
125 Mutability::Not if ty_is_freeze => Permission::new_frozen(),
126 // Raw pointers never enter this function so they are not handled.
127 // However raw pointers are not the only pointers that take the parent
128 // tag, this also happens for `!Unpin` `&mut`s and interior mutable
129 // `&`s, which are excluded above.
130 _ => return None,
131 };
132
133 let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
134 Some(Self { zero_size: false, initial_state, protector })
135 }
136
137 /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled).
138 /// These pointers allow deallocation so need a different kind of protector not handled
139 /// by `from_ref_ty`.
from_unique_ty( ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'_, 'tcx>, zero_size: bool, ) -> Option<Self>140 fn from_unique_ty(
141 ty: Ty<'tcx>,
142 kind: RetagKind,
143 cx: &crate::MiriInterpCx<'_, 'tcx>,
144 zero_size: bool,
145 ) -> Option<Self> {
146 let pointee = ty.builtin_deref(true).unwrap().ty;
147 pointee.is_unpin(*cx.tcx, cx.param_env()).then_some(()).map(|()| {
148 // Regular `Unpin` box, give it `noalias` but only a weak protector
149 // because it is valid to deallocate it within the function.
150 let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.param_env());
151 Self {
152 zero_size,
153 initial_state: Permission::new_unique_2phase(ty_is_freeze),
154 protector: (kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
155 }
156 })
157 }
158 }
159
160 /// Retagging/reborrowing.
161 /// Policy on which permission to grant to each pointer should be left to
162 /// the implementation of NewPermission.
163 impl<'mir: 'ecx, 'tcx: 'mir, 'ecx> EvalContextPrivExt<'mir, 'tcx, 'ecx>
164 for crate::MiriInterpCx<'mir, 'tcx>
165 {
166 }
167 trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'mir, 'tcx> {
168 /// Returns the `AllocId` the reborrow was done in, if there is some actual
169 /// memory associated with this pointer. Returns `None` if there is no actual
170 /// memory allocated. Also checks that the reborrow of size `ptr_size` is
171 /// within bounds of the allocation.
172 ///
173 /// Also returns the tag that the pointer should get, which is essentially
174 /// `if new_perm.is_some() { new_tag } else { parent_tag }` along with
175 /// some logging (always) and fake reads (if `new_perm` is
176 /// `Some(NewPermission { perform_read_access: true }`).
tb_reborrow( &mut self, place: &MPlaceTy<'tcx, Provenance>, ptr_size: Size, new_perm: Option<NewPermission>, new_tag: BorTag, ) -> InterpResult<'tcx, Option<(AllocId, BorTag)>>177 fn tb_reborrow(
178 &mut self,
179 place: &MPlaceTy<'tcx, Provenance>, // parent tag extracted from here
180 ptr_size: Size,
181 new_perm: Option<NewPermission>,
182 new_tag: BorTag,
183 ) -> InterpResult<'tcx, Option<(AllocId, BorTag)>> {
184 let this = self.eval_context_mut();
185
186 // It is crucial that this gets called on all code paths, to ensure we track tag creation.
187 let log_creation = |this: &MiriInterpCx<'mir, 'tcx>,
188 loc: Option<(AllocId, Size, ProvenanceExtra)>| // alloc_id, base_offset, orig_tag
189 -> InterpResult<'tcx> {
190 let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
191 let ty = place.layout.ty;
192 if global.tracked_pointer_tags.contains(&new_tag) {
193 let kind_str = format!("{new_perm:?} (pointee type {ty})");
194 this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
195 new_tag.inner(),
196 Some(kind_str),
197 loc.map(|(alloc_id, base_offset, orig_tag)| (alloc_id, alloc_range(base_offset, ptr_size), orig_tag)),
198 ));
199 }
200 drop(global); // don't hold that reference any longer than we have to
201 Ok(())
202 };
203
204 trace!("Reborrow of size {:?}", ptr_size);
205 let (alloc_id, base_offset, parent_prov) = if ptr_size > Size::ZERO {
206 this.ptr_get_alloc_id(place.ptr)?
207 } else {
208 match this.ptr_try_get_alloc_id(place.ptr) {
209 Ok(data) => data,
210 Err(_) => {
211 // This pointer doesn't come with an AllocId, so there's no
212 // memory to do retagging in.
213 trace!(
214 "reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
215 new_tag,
216 place.ptr,
217 place.layout.ty,
218 );
219 log_creation(this, None)?;
220 return Ok(None);
221 }
222 }
223 };
224 let orig_tag = match parent_prov {
225 ProvenanceExtra::Wildcard => return Ok(None), // TODO: handle wildcard pointers
226 ProvenanceExtra::Concrete(tag) => tag,
227 };
228
229 // Protection against trying to get a reference to a vtable:
230 // vtables do not have an alloc_extra so the call to
231 // `get_alloc_extra` that follows fails.
232 let (alloc_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
233 if ptr_size == Size::ZERO && !matches!(alloc_kind, AllocKind::LiveData) {
234 return Ok(Some((alloc_id, orig_tag)));
235 }
236
237 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
238
239 // Ensure we bail out if the pointer goes out-of-bounds (see miri#1050).
240 if base_offset + ptr_size > alloc_size {
241 throw_ub!(PointerOutOfBounds {
242 alloc_id,
243 alloc_size,
244 ptr_offset: this.target_usize_to_isize(base_offset.bytes()),
245 ptr_size,
246 msg: CheckInAllocMsg::InboundsTest
247 });
248 }
249
250 trace!(
251 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
252 new_tag,
253 orig_tag,
254 place.layout.ty,
255 Pointer::new(alloc_id, base_offset),
256 ptr_size.bytes()
257 );
258
259 let Some(new_perm) = new_perm else { return Ok(Some((alloc_id, orig_tag))); };
260
261 if let Some(protect) = new_perm.protector {
262 // We register the protection in two different places.
263 // This makes creating a protector slower, but checking whether a tag
264 // is protected faster.
265 this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
266 this.machine
267 .borrow_tracker
268 .as_mut()
269 .expect("We should have borrow tracking data")
270 .get_mut()
271 .protected_tags
272 .insert(new_tag, protect);
273 }
274
275 let span = this.machine.current_span();
276 let alloc_extra = this.get_alloc_extra(alloc_id)?;
277 let range = alloc_range(base_offset, ptr_size);
278 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
279
280 // All reborrows incur a (possibly zero-sized) read access to the parent
281 {
282 let global = &this.machine.borrow_tracker.as_ref().unwrap();
283 let span = this.machine.current_span();
284 tree_borrows.perform_access(
285 AccessKind::Read,
286 orig_tag,
287 range,
288 global,
289 span,
290 diagnostics::AccessCause::Reborrow,
291 )?;
292 if let Some(data_race) = alloc_extra.data_race.as_ref() {
293 data_race.read(alloc_id, range, &this.machine)?;
294 }
295 }
296
297 // Record the parent-child pair in the tree.
298 tree_borrows.new_child(orig_tag, new_tag, new_perm.initial_state, range, span)?;
299 Ok(Some((alloc_id, new_tag)))
300 }
301
302 /// Retags an individual pointer, returning the retagged version.
tb_retag_reference( &mut self, val: &ImmTy<'tcx, Provenance>, new_perm: Option<NewPermission>, ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>>303 fn tb_retag_reference(
304 &mut self,
305 val: &ImmTy<'tcx, Provenance>,
306 new_perm: Option<NewPermission>,
307 ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
308 let this = self.eval_context_mut();
309 // We want a place for where the ptr *points to*, so we get one.
310 let place = this.ref_to_mplace(val)?;
311
312 // Determine the size of the reborrow.
313 // For most types this is the entire size of the place, however
314 // - when `extern type` is involved we use the size of the known prefix,
315 // - if the pointer is not reborrowed (raw pointer) or if `zero_size` is set
316 // then we override the size to do a zero-length reborrow.
317 let reborrow_size = match new_perm {
318 Some(NewPermission { zero_size: false, .. }) =>
319 this.size_and_align_of_mplace(&place)?
320 .map(|(size, _)| size)
321 .unwrap_or(place.layout.size),
322 _ => Size::from_bytes(0),
323 };
324 trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
325
326 // This new tag is not guaranteed to actually be used.
327 //
328 // If you run out of tags, consider the following optimization: adjust `tb_reborrow`
329 // so that rather than taking as input a fresh tag and deciding whether it uses this
330 // one or the parent it instead just returns whether a new tag should be created.
331 // This will avoid creating tags than end up never being used.
332 let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
333
334 // Compute the actual reborrow.
335 let reborrowed = this.tb_reborrow(&place, reborrow_size, new_perm, new_tag)?;
336
337 // Adjust pointer.
338 let new_place = place.map_provenance(|p| {
339 p.map(|prov| {
340 match reborrowed {
341 Some((alloc_id, actual_tag)) => {
342 // If `reborrow` could figure out the AllocId of this ptr, hard-code it into the new one.
343 // Even if we started out with a wildcard, this newly retagged pointer is tied to that allocation.
344 Provenance::Concrete { alloc_id, tag: actual_tag }
345 }
346 None => {
347 // Looks like this has to stay a wildcard pointer.
348 assert!(matches!(prov, Provenance::Wildcard));
349 Provenance::Wildcard
350 }
351 }
352 })
353 });
354
355 // Return new pointer.
356 Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
357 }
358 }
359
360 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
361 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
362 /// Retag a pointer. References are passed to `from_ref_ty` and
363 /// raw pointers are never reborrowed.
tb_retag_ptr_value( &mut self, kind: RetagKind, val: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>>364 fn tb_retag_ptr_value(
365 &mut self,
366 kind: RetagKind,
367 val: &ImmTy<'tcx, Provenance>,
368 ) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
369 let this = self.eval_context_mut();
370 let new_perm = match val.layout.ty.kind() {
371 &ty::Ref(_, pointee, mutability) =>
372 NewPermission::from_ref_ty(pointee, mutability, kind, this),
373 _ => None,
374 };
375 this.tb_retag_reference(val, new_perm)
376 }
377
378 /// Retag all pointers that are stored in this place.
tb_retag_place_contents( &mut self, kind: RetagKind, place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx>379 fn tb_retag_place_contents(
380 &mut self,
381 kind: RetagKind,
382 place: &PlaceTy<'tcx, Provenance>,
383 ) -> InterpResult<'tcx> {
384 let this = self.eval_context_mut();
385 let options = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
386 let retag_fields = options.retag_fields;
387 let unique_did =
388 options.unique_is_unique.then(|| this.tcx.lang_items().ptr_unique()).flatten();
389 let mut visitor = RetagVisitor { ecx: this, kind, retag_fields, unique_did };
390 return visitor.visit_value(place);
391
392 // The actual visitor.
393 struct RetagVisitor<'ecx, 'mir, 'tcx> {
394 ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
395 kind: RetagKind,
396 retag_fields: RetagFields,
397 unique_did: Option<DefId>,
398 }
399 impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> {
400 #[inline(always)] // yes this helps in our benchmarks
401 fn retag_ptr_inplace(
402 &mut self,
403 place: &PlaceTy<'tcx, Provenance>,
404 new_perm: Option<NewPermission>,
405 ) -> InterpResult<'tcx> {
406 let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
407 let val = self.ecx.tb_retag_reference(&val, new_perm)?;
408 self.ecx.write_immediate(*val, place)?;
409 Ok(())
410 }
411 }
412 impl<'ecx, 'mir, 'tcx> MutValueVisitor<'mir, 'tcx, MiriMachine<'mir, 'tcx>>
413 for RetagVisitor<'ecx, 'mir, 'tcx>
414 {
415 type V = PlaceTy<'tcx, Provenance>;
416
417 #[inline(always)]
418 fn ecx(&mut self) -> &mut MiriInterpCx<'mir, 'tcx> {
419 self.ecx
420 }
421
422 /// Regardless of how `Unique` is handled, Boxes are always reborrowed.
423 /// When `Unique` is also reborrowed, then it behaves exactly like `Box`
424 /// except for the fact that `Box` has a non-zero-sized reborrow.
425 fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
426 let new_perm = NewPermission::from_unique_ty(
427 place.layout.ty,
428 self.kind,
429 self.ecx,
430 /* zero_size */ false,
431 );
432 self.retag_ptr_inplace(place, new_perm)
433 }
434
435 fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
436 // If this place is smaller than a pointer, we know that it can't contain any
437 // pointers we need to retag, so we can stop recursion early.
438 // This optimization is crucial for ZSTs, because they can contain way more fields
439 // than we can ever visit.
440 if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
441 return Ok(());
442 }
443
444 // Check the type of this value to see what to do with it (retag, or recurse).
445 match place.layout.ty.kind() {
446 &ty::Ref(_, pointee, mutability) => {
447 let new_perm =
448 NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx);
449 self.retag_ptr_inplace(place, new_perm)?;
450 }
451 ty::RawPtr(_) => {
452 // We definitely do *not* want to recurse into raw pointers -- wide raw
453 // pointers have fields, and for dyn Trait pointees those can have reference
454 // type!
455 // We also do not want to reborrow them.
456 }
457 ty::Adt(adt, _) if adt.is_box() => {
458 // Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.
459 // (Yes this means we technically also recursively retag the allocator itself
460 // even if field retagging is not enabled. *shrug*)
461 self.walk_value(place)?;
462 }
463 ty::Adt(adt, _) if self.unique_did == Some(adt.did()) => {
464 let place = inner_ptr_of_unique(self.ecx, place)?;
465 let new_perm = NewPermission::from_unique_ty(
466 place.layout.ty,
467 self.kind,
468 self.ecx,
469 /* zero_size */ true,
470 );
471 self.retag_ptr_inplace(&place, new_perm)?;
472 }
473 _ => {
474 // Not a reference/pointer/box. Only recurse if configured appropriately.
475 let recurse = match self.retag_fields {
476 RetagFields::No => false,
477 RetagFields::Yes => true,
478 RetagFields::OnlyScalar => {
479 // Matching `ArgAbi::new` at the time of writing, only fields of
480 // `Scalar` and `ScalarPair` ABI are considered.
481 matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..))
482 }
483 };
484 if recurse {
485 self.walk_value(place)?;
486 }
487 }
488 }
489 Ok(())
490 }
491 }
492 }
493
494 /// After a stack frame got pushed, retag the return place so that we are sure
495 /// it does not alias with anything.
496 ///
497 /// This is a HACK because there is nothing in MIR that would make the retag
498 /// explicit. Also see <https://github.com/rust-lang/rust/issues/71117>.
tb_retag_return_place(&mut self) -> InterpResult<'tcx>499 fn tb_retag_return_place(&mut self) -> InterpResult<'tcx> {
500 let this = self.eval_context_mut();
501 //this.debug_hint_location();
502 let return_place = &this.frame().return_place;
503 if return_place.layout.is_zst() {
504 // There may not be any memory here, nothing to do.
505 return Ok(());
506 }
507 // We need this to be in-memory to use tagged pointers.
508 let return_place = this.force_allocation(&return_place.clone())?;
509
510 // We have to turn the place into a pointer to use the existing code.
511 // (The pointer type does not matter, so we use a raw pointer.)
512 let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?;
513 let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
514 // Reborrow it. With protection! That is part of the point.
515 // FIXME: do we truly want a 2phase borrow here?
516 let new_perm = Some(NewPermission {
517 initial_state: Permission::new_unique_2phase(/*freeze*/ false),
518 zero_size: false,
519 protector: Some(ProtectorKind::StrongProtector),
520 });
521 let val = this.tb_retag_reference(&val, new_perm)?;
522 // And use reborrowed pointer for return place.
523 let return_place = this.ref_to_mplace(&val)?;
524 this.frame_mut().return_place = return_place.into();
525
526 Ok(())
527 }
528
529 /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
tb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx>530 fn tb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
531 let this = self.eval_context_mut();
532
533 // Function pointers and dead objects don't have an alloc_extra so we ignore them.
534 // This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
535 // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
536 let (_size, _align, kind) = this.get_alloc_info(alloc_id);
537 match kind {
538 AllocKind::LiveData => {
539 // This should have alloc_extra data, but `get_alloc_extra` can still fail
540 // if converting this alloc_id from a global to a local one
541 // uncovers a non-supported `extern static`.
542 let alloc_extra = this.get_alloc_extra(alloc_id)?;
543 trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
544 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
545 }
546 AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
547 // No tree borrows on these allocations.
548 }
549 }
550 Ok(())
551 }
552
553 /// Display the tree.
print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx>554 fn print_tree(&mut self, alloc_id: AllocId, show_unnamed: bool) -> InterpResult<'tcx> {
555 let this = self.eval_context_mut();
556 let alloc_extra = this.get_alloc_extra(alloc_id)?;
557 let tree_borrows = alloc_extra.borrow_tracker_tb().borrow();
558 let borrow_tracker = &this.machine.borrow_tracker.as_ref().unwrap().borrow();
559 tree_borrows.print_tree(&borrow_tracker.protected_tags, show_unnamed)
560 }
561
562 /// Give a name to the pointer, usually the name it has in the source code (for debugging).
563 /// The name given is `name` and the pointer that receives it is the `nth_parent`
564 /// of `ptr` (with 0 representing `ptr` itself)
tb_give_pointer_debug_name( &mut self, ptr: Pointer<Option<Provenance>>, nth_parent: u8, name: &str, ) -> InterpResult<'tcx>565 fn tb_give_pointer_debug_name(
566 &mut self,
567 ptr: Pointer<Option<Provenance>>,
568 nth_parent: u8,
569 name: &str,
570 ) -> InterpResult<'tcx> {
571 let this = self.eval_context_mut();
572 let (tag, alloc_id) = match ptr.provenance {
573 Some(Provenance::Concrete { tag, alloc_id }) => (tag, alloc_id),
574 _ => {
575 eprintln!("Can't give the name {name} to Wildcard pointer");
576 return Ok(());
577 }
578 };
579 let alloc_extra = this.get_alloc_extra(alloc_id)?;
580 let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
581 tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
582 }
583 }
584
585 /// Takes a place for a `Unique` and turns it into a place with the inner raw pointer.
586 /// I.e. input is what you get from the visitor upon encountering an `adt` that is `Unique`,
587 /// and output can be used by `retag_ptr_inplace`.
inner_ptr_of_unique<'tcx>( ecx: &mut MiriInterpCx<'_, 'tcx>, place: &PlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>>588 fn inner_ptr_of_unique<'tcx>(
589 ecx: &mut MiriInterpCx<'_, 'tcx>,
590 place: &PlaceTy<'tcx, Provenance>,
591 ) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> {
592 // Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in
593 // `rustc_const_eval`, just with one fewer layer.
594 // Here we have a `Unique(NonNull(*mut), PhantomData)`
595 assert_eq!(place.layout.fields.count(), 2, "Unique must have exactly 2 fields");
596 let (nonnull, phantom) = (ecx.place_field(place, 0)?, ecx.place_field(place, 1)?);
597 assert!(
598 phantom.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
599 "2nd field of `Unique` should be `PhantomData` but is `{:?}`",
600 phantom.layout.ty,
601 );
602 // Now down to `NonNull(*mut)`
603 assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field");
604 let ptr = ecx.place_field(&nonnull, 0)?;
605 // Finally a plain `*mut`
606 Ok(ptr)
607 }
608