Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2251b42
refactor perform_access to suppoert wildcard roots
royAmmerschuber Nov 10, 2025
6c5f166
fix access pattern
royAmmerschuber Nov 11, 2025
04780bb
fix only_foreign & normal wildcard access
royAmmerschuber Nov 11, 2025
3455c18
add tests
royAmmerschuber Nov 11, 2025
ba50563
fix strong-protector
royAmmerschuber Nov 13, 2025
2caf2ee
add wildcard subtree support to print_state
royAmmerschuber Nov 15, 2025
1de4072
fine grained tag ordering checks
royAmmerschuber Nov 15, 2025
2f1530b
fix protector release & make more precise
royAmmerschuber Nov 15, 2025
c53b037
add formatting test
royAmmerschuber Nov 15, 2025
3fe046b
fix strict check & tests
royAmmerschuber Nov 17, 2025
10d452e
add comments
royAmmerschuber Nov 17, 2025
fff256b
formatting & remove outdated comments
royAmmerschuber Nov 18, 2025
214e8ba
unify root and wildcard_roots into single Vec
royAmmerschuber Nov 18, 2025
5347312
fix formatting
royAmmerschuber Nov 18, 2025
0beebdc
report correct accessed tag on protector violation
royAmmerschuber Nov 20, 2025
b412011
start improving test documentation
royAmmerschuber Nov 20, 2025
8edaf3d
move stacked borrows tests to both_borrows
royAmmerschuber Nov 24, 2025
720294d
make tree visitor return root index
royAmmerschuber Nov 24, 2025
08bc656
factor out root f_continue & get_relatedness in wildcard_access
royAmmerschuber Nov 24, 2025
9a5b2fd
other changes from review
royAmmerschuber Nov 24, 2025
d8089dd
remove skip child logic
royAmmerschuber Nov 24, 2025
4097b0a
expand roots comment & refactor check_strong_protector
royAmmerschuber Nov 25, 2025
d40d674
further factor out get_relatedness
royAmmerschuber Nov 25, 2025
40ed9a1
update tests & make iteration order clearer
royAmmerschuber Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 84 additions & 25 deletions src/borrow_tracker/tree_borrows/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,8 @@ struct DisplayFmtPadding {
indent_middle: S,
/// Indentation for the last child.
indent_last: S,
/// Replaces `join_last` for a wildcard root.
wildcard_root: S,
}
/// How to show whether a location has been accessed
///
Expand Down Expand Up @@ -561,6 +563,11 @@ impl DisplayFmt {
})
.unwrap_or("")
}

/// Print extra text if the tag is exposed.
fn print_exposed(&self, exposed: bool) -> S {
if exposed { " (exposed)" } else { "" }
}
}

/// Track the indentation of the tree.
Expand Down Expand Up @@ -607,23 +614,21 @@ fn char_repeat(c: char, n: usize) -> String {
struct DisplayRepr {
tag: BorTag,
name: Option<String>,
exposed: bool,
rperm: Vec<Option<LocationState>>,
children: Vec<DisplayRepr>,
}

impl DisplayRepr {
fn from(tree: &Tree, show_unnamed: bool) -> Option<Self> {
fn from(tree: &Tree, root: UniIndex, show_unnamed: bool) -> Option<Self> {
let mut v = Vec::new();
extraction_aux(tree, tree.root, show_unnamed, &mut v);
extraction_aux(tree, root, show_unnamed, &mut v);
let Some(root) = v.pop() else {
if show_unnamed {
unreachable!(
"This allocation contains no tags, not even a root. This should not happen."
);
}
eprintln!(
"This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."
);
return None;
};
assert!(v.is_empty());
Expand All @@ -637,6 +642,7 @@ impl DisplayRepr {
) {
let node = tree.nodes.get(idx).unwrap();
let name = node.debug_info.name.clone();
let exposed = node.is_exposed;
let children_sorted = {
let mut children = node.children.iter().cloned().collect::<Vec<_>>();
children.sort_by_key(|idx| tree.nodes.get(*idx).unwrap().tag);
Expand All @@ -661,12 +667,13 @@ impl DisplayRepr {
for child_idx in children_sorted {
extraction_aux(tree, child_idx, show_unnamed, &mut children);
}
acc.push(DisplayRepr { tag: node.tag, name, rperm, children });
acc.push(DisplayRepr { tag: node.tag, name, rperm, children, exposed });
}
}
}
fn print(
&self,
main_root: &Option<DisplayRepr>,
wildcard_subtrees: &[DisplayRepr],
fmt: &DisplayFmt,
indenter: &mut DisplayIndent,
protected_tags: &FxHashMap<BorTag, ProtectorKind>,
Expand Down Expand Up @@ -703,15 +710,41 @@ impl DisplayRepr {
block.push(s);
}
// This is the actual work
print_aux(
self,
&range_padding,
fmt,
indenter,
protected_tags,
true, /* root _is_ the last child */
&mut block,
);
if let Some(root) = main_root {
print_aux(
root,
&range_padding,
fmt,
indenter,
protected_tags,
true, /* root _is_ the last child */
false, /* not a wildcard_root*/
&mut block,
);
}
for tree in wildcard_subtrees.iter() {
let mut gap_line = String::new();
gap_line.push_str(fmt.perm.open);
for (i, &pad) in range_padding.iter().enumerate() {
if i > 0 {
gap_line.push_str(fmt.perm.sep);
}
gap_line.push_str(&format!("{}{}", char_repeat(' ', pad), " "));
}
gap_line.push_str(fmt.perm.close);
block.push(gap_line);

print_aux(
tree,
&range_padding,
fmt,
indenter,
protected_tags,
true, /* root _is_ the last child */
true, /* wildcard_root*/
&mut block,
);
}
// Then it's just prettifying it with a border of dashes.
{
let wr = &fmt.wrapper;
Expand Down Expand Up @@ -741,6 +774,7 @@ impl DisplayRepr {
indent: &mut DisplayIndent,
protected_tags: &FxHashMap<BorTag, ProtectorKind>,
is_last_child: bool,
is_wildcard_root: bool,
acc: &mut Vec<String>,
) {
let mut line = String::new();
Expand All @@ -760,7 +794,9 @@ impl DisplayRepr {
indent.write(&mut line);
{
// padding
line.push_str(if is_last_child {
line.push_str(if is_wildcard_root {
fmt.padding.wildcard_root
} else if is_last_child {
fmt.padding.join_last
} else {
fmt.padding.join_middle
Expand All @@ -777,12 +813,22 @@ impl DisplayRepr {
line.push_str(&fmt.print_tag(tree.tag, &tree.name));
let protector = protected_tags.get(&tree.tag);
line.push_str(fmt.print_protector(protector));
line.push_str(fmt.print_exposed(tree.exposed));
// Push the line to the accumulator then recurse.
acc.push(line);
let nb_children = tree.children.len();
for (i, child) in tree.children.iter().enumerate() {
indent.increment(fmt, is_last_child);
print_aux(child, padding, fmt, indent, protected_tags, i + 1 == nb_children, acc);
print_aux(
child,
padding,
fmt,
indent,
protected_tags,
/* is_last_child */ i + 1 == nb_children,
/* is_wildcard_root */ false,
acc,
);
indent.decrement(fmt);
}
}
Expand All @@ -803,6 +849,7 @@ const DEFAULT_FORMATTER: DisplayFmt = DisplayFmt {
indent_last: " ",
join_haschild: "┬",
join_default: "─",
wildcard_root: "*",
},
accessed: DisplayFmtAccess { yes: " ", no: "?", meh: "-" },
};
Expand All @@ -816,15 +863,27 @@ impl<'tcx> Tree {
) -> InterpResult<'tcx> {
let mut indenter = DisplayIndent::new();
let ranges = self.locations.iter_all().map(|(range, _loc)| range).collect::<Vec<_>>();
if let Some(repr) = DisplayRepr::from(self, show_unnamed) {
repr.print(
&DEFAULT_FORMATTER,
&mut indenter,
protected_tags,
ranges,
/* print warning message about tags not shown */ !show_unnamed,
let main_tree = DisplayRepr::from(self, self.roots[0], show_unnamed);
let wildcard_subtrees = self.roots[1..]
.iter()
.filter_map(|root| DisplayRepr::from(self, *root, show_unnamed))
.collect::<Vec<_>>();

if main_tree.is_none() && wildcard_subtrees.is_empty() {
eprintln!(
"This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."
);
}

DisplayRepr::print(
&main_tree,
wildcard_subtrees.as_slice(),
&DEFAULT_FORMATTER,
&mut indenter,
protected_tags,
ranges,
/* print warning message about tags not shown */ !show_unnamed,
);
interp_ok(())
}
}
15 changes: 5 additions & 10 deletions src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,18 +239,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(new_prov);
}
};
let new_prov = Provenance::Concrete { alloc_id, tag: new_tag };

log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;

let orig_tag = match parent_prov {
ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle retagging wildcard pointers
ProvenanceExtra::Concrete(tag) => tag,
};

trace!(
"reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
new_tag,
orig_tag,
parent_prov,
place.layout.ty,
interpret::Pointer::new(alloc_id, base_offset),
ptr_size.bytes()
Expand Down Expand Up @@ -281,7 +277,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
// There's not actually any bytes here where accesses could even be tracked.
// Just produce the new provenance, nothing else to do.
return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
return interp_ok(Some(new_prov));
}

let protected = new_perm.protector.is_some();
Expand Down Expand Up @@ -367,11 +363,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
}
}

// Record the parent-child pair in the tree.
tree_borrows.new_child(
base_offset,
orig_tag,
parent_prov,
new_tag,
inside_perms,
new_perm.outside_perm,
Expand All @@ -380,7 +375,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
)?;
drop(tree_borrows);

interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
interp_ok(Some(new_prov))
}

fn tb_retag_place(
Expand Down
Loading