Make TB tree traversal bottom-up

In preparation for #3837, the tree traversal needs to be made bottom-up,
because the current top-down tree traversal, coupled with that PR's
changes to the garbage collector, can introduce non-deterministic error
messages if the GC removes a parent tag of the accessed tag that would
have triggered the error first.

This is a breaking change for the diagnostics emitted by TB. The
implemented semantics stay the same.
This commit is contained in:
Johannes Hostert 2024-08-25 17:33:01 +02:00
parent 9b82f3b729
commit 2765444c15
No known key found for this signature in database
GPG Key ID: 0BA6032B5A38D049
20 changed files with 241 additions and 274 deletions

View File

@ -149,12 +149,11 @@ impl LocationState {
// is possible, it also updates the internal state to keep track of whether
// the propagation can be skipped next time.
// It is a performance loss not to call this function when a foreign access occurs.
// It is unsound not to call this function when a child access occurs.
// FIXME: This optimization is wrong, and is currently disabled (by ignoring the
// result returned here). Since we presumably want an optimization like this,
// we should add it back. See #3864 for more information.
fn update_last_foreign_access(
&mut self,
fn skip_if_known_noop(
&self,
access_kind: AccessKind,
rel_pos: AccessRelatedness,
) -> ContinueTraversal {
@ -177,22 +176,37 @@ impl LocationState {
// No need to update `self.latest_foreign_access`,
// the type of the current streak among nonempty read-only
// or nonempty with at least one write has not changed.
ContinueTraversal::SkipChildren
ContinueTraversal::SkipSelfAndChildren
} else {
// Otherwise propagate this time, and also record the
// access that just occurred so that we can skip the propagation
// next time.
self.latest_foreign_access = Some(access_kind);
ContinueTraversal::Recurse
}
} else {
// A child access occurred, this breaks the streak of foreign
// accesses in a row and the sequence since the previous child access
// is now empty.
self.latest_foreign_access = None;
ContinueTraversal::Recurse
}
}
/// Records a new access, so that future access can potentially be skipped
/// by `skip_if_known_noop`.
/// The invariants for this function are closely coupled to the function above:
/// It MUST be called on child accesses, and on foreign accesses MUST be called
/// when `skip_if_know_noop` returns `Recurse`, and MUST NOT be called otherwise.
/// FIXME: This optimization is wrong, and is currently disabled (by ignoring the
/// result returned here). Since we presumably want an optimization like this,
/// we should add it back. See #3864 for more information.
#[allow(unused)]
fn record_new_access(&mut self, access_kind: AccessKind, rel_pos: AccessRelatedness) {
if rel_pos.is_foreign() {
self.latest_foreign_access = Some(access_kind);
} else {
self.latest_foreign_access = None;
}
}
}
impl fmt::Display for LocationState {
@ -285,13 +299,16 @@ struct TreeVisitor<'tree> {
/// Whether to continue exploring the children recursively or not.
enum ContinueTraversal {
Recurse,
SkipChildren,
SkipSelfAndChildren,
}
/// Stack of nodes left to explore in a tree traversal.
struct TreeVisitorStack<NodeApp, ErrHandler> {
struct TreeVisitorStack<NodeContinue, NodeApp, ErrHandler> {
/// Identifier of the original access.
initial: UniIndex,
/// Function describing whether to continue at a tag.
/// This is only invoked for foreign accesses.
f_continue: NodeContinue,
/// Function to apply to each tag.
f_propagate: NodeApp,
/// Handler to add the required context to diagnostics.
@ -299,152 +316,178 @@ struct TreeVisitorStack<NodeApp, ErrHandler> {
/// Mutable state of the visit: the tags left to handle.
/// Every tag pushed should eventually be handled,
/// and the precise order is relevant for diagnostics.
stack: Vec<(UniIndex, AccessRelatedness)>,
/// Since the traversal is bottom-up, we need to remember
/// whether we're here initially (false) or after visiting all
/// children (true). The bool indicates this.
stack: Vec<(UniIndex, AccessRelatedness, bool)>,
}
impl<NodeApp, InnErr, OutErr, ErrHandler> TreeVisitorStack<NodeApp, ErrHandler>
impl<NodeContinue, NodeApp, InnErr, OutErr, ErrHandler>
TreeVisitorStack<NodeContinue, NodeApp, ErrHandler>
where
NodeApp: Fn(NodeAppArgs<'_>) -> Result<ContinueTraversal, InnErr>,
NodeContinue: Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
NodeApp: Fn(NodeAppArgs<'_>) -> Result<(), InnErr>,
ErrHandler: Fn(ErrHandlerArgs<'_, InnErr>) -> OutErr,
{
/// Apply the function to the current `tag`, and push its children
/// to the stack of future tags to visit.
fn exec_and_visit(
fn should_continue_at(
&self,
this: &mut TreeVisitor<'_>,
idx: UniIndex,
rel_pos: AccessRelatedness,
) -> ContinueTraversal {
let node = this.nodes.get_mut(idx).unwrap();
let args = NodeAppArgs { node, perm: this.perms.entry(idx), rel_pos };
(self.f_continue)(&args)
}
fn propagate_at(
&mut self,
this: &mut TreeVisitor<'_>,
idx: UniIndex,
exclude: Option<UniIndex>,
rel_pos: AccessRelatedness,
) -> Result<(), OutErr> {
// 1. apply the propagation function
let node = this.nodes.get_mut(idx).unwrap();
let recurse =
(self.f_propagate)(NodeAppArgs { node, perm: this.perms.entry(idx), rel_pos })
.map_err(|error_kind| {
(self.err_builder)(ErrHandlerArgs {
error_kind,
conflicting_info: &this.nodes.get(idx).unwrap().debug_info,
accessed_info: &this.nodes.get(self.initial).unwrap().debug_info,
})
})?;
let node = this.nodes.get(idx).unwrap();
// 2. add the children to the stack for future traversal
if matches!(recurse, ContinueTraversal::Recurse) {
let general_child_rel = rel_pos.for_child();
for &child in node.children.iter() {
// Some child might be excluded from here and handled separately,
// e.g. the initially accessed tag.
if Some(child) != exclude {
// We should still ensure that if we don't skip the initially accessed
// it will receive the proper `AccessRelatedness`.
let this_child_rel = if child == self.initial {
AccessRelatedness::This
} else {
general_child_rel
};
self.stack.push((child, this_child_rel));
(self.f_propagate)(NodeAppArgs { node, perm: this.perms.entry(idx), rel_pos }).map_err(
|error_kind| {
(self.err_builder)(ErrHandlerArgs {
error_kind,
conflicting_info: &this.nodes.get(idx).unwrap().debug_info,
accessed_info: &this.nodes.get(self.initial).unwrap().debug_info,
})
},
)
}
fn go_upwards_from_accessed(
&mut self,
this: &mut TreeVisitor<'_>,
accessed_node: UniIndex,
push_children_of_accessed: bool,
) -> Result<(), OutErr> {
assert!(self.stack.is_empty());
// First, handle accessed node. A bunch of things need to
// be handled differently here compared to the further parents
// of `accesssed_node`.
{
self.propagate_at(this, accessed_node, AccessRelatedness::This)?;
if push_children_of_accessed {
let accessed_node = this.nodes.get(accessed_node).unwrap();
for &child in accessed_node.children.iter().rev() {
self.stack.push((child, AccessRelatedness::AncestorAccess, false));
}
}
}
// Then, handle the accessed node's parent. Here, we need to
// make sure we only mark the "cousin" subtrees for later visitation,
// not the subtree that contains the accessed node.
let mut last_node = accessed_node;
while let Some(current) = this.nodes.get(last_node).unwrap().parent {
self.propagate_at(this, current, AccessRelatedness::StrictChildAccess)?;
let node = this.nodes.get(current).unwrap();
for &child in node.children.iter().rev() {
if last_node == child {
continue;
}
self.stack.push((child, AccessRelatedness::DistantAccess, false));
}
last_node = current;
}
self.stack.reverse();
Ok(())
}
fn finish_foreign_accesses(&mut self, this: &mut TreeVisitor<'_>) -> Result<(), OutErr> {
while let Some((idx, rel_pos, is_final)) = self.stack.last_mut() {
let idx = *idx;
let rel_pos = *rel_pos;
if *is_final {
self.stack.pop();
self.propagate_at(this, idx, rel_pos)?;
} else {
*is_final = true;
let handle_children = self.should_continue_at(this, idx, rel_pos);
match handle_children {
ContinueTraversal::Recurse => {
// add all children, and also leave the node itself
// on the stack so that it can be visited later.
let node = this.nodes.get(idx).unwrap();
for &child in node.children.iter() {
self.stack.push((child, rel_pos, false));
}
}
ContinueTraversal::SkipSelfAndChildren => {
// skip self
self.stack.pop();
continue;
}
}
}
}
Ok(())
}
fn new(initial: UniIndex, f_propagate: NodeApp, err_builder: ErrHandler) -> Self {
Self { initial, f_propagate, err_builder, stack: Vec::new() }
}
/// Finish the exploration by applying `exec_and_visit` until
/// the stack is empty.
fn finish(&mut self, visitor: &mut TreeVisitor<'_>) -> Result<(), OutErr> {
while let Some((idx, rel_pos)) = self.stack.pop() {
self.exec_and_visit(visitor, idx, /* no children to exclude */ None, rel_pos)?;
}
Ok(())
}
/// Push all ancestors to the exploration stack in order of nearest ancestor
/// towards the top.
fn push_and_visit_strict_ancestors(
&mut self,
visitor: &mut TreeVisitor<'_>,
) -> Result<(), OutErr> {
let mut path_ascend = Vec::new();
// First climb to the root while recording the path
let mut curr = self.initial;
while let Some(ancestor) = visitor.nodes.get(curr).unwrap().parent {
path_ascend.push((ancestor, curr));
curr = ancestor;
}
// Then descend:
// - execute f_propagate on each node
// - record children in visit
while let Some((ancestor, next_in_path)) = path_ascend.pop() {
// Explore ancestors in descending order.
// `next_in_path` is excluded from the recursion because it
// will be the `ancestor` of the next iteration.
// It also needs a different `AccessRelatedness` than the other
// children of `ancestor`.
self.exec_and_visit(
visitor,
ancestor,
Some(next_in_path),
AccessRelatedness::StrictChildAccess,
)?;
}
Ok(())
fn new(
initial: UniIndex,
f_continue: NodeContinue,
f_propagate: NodeApp,
err_builder: ErrHandler,
) -> Self {
Self { initial, f_continue, f_propagate, err_builder, stack: Vec::new() }
}
}
impl<'tree> TreeVisitor<'tree> {
// Applies `f_propagate` to every vertex of the tree top-down in the following order: first
// all ancestors of `start`, then `start` itself, then children of `start`, then the rest.
// Applies `f_propagate` to every vertex of the tree bottom-up in the following order: first
// all ancestors of `start` (starting with `start` itself), then children of `start`, then the rest,
// always going bottom-up.
// This ensures that errors are triggered in the following order
// - first invalid accesses with insufficient permissions, closest to the root first,
// - then protector violations, closest to `start` first.
// - first invalid accesses with insufficient permissions, closest to the accessed node first,
// - then protector violations, bottom-up, starting with the children of the accessed node, and then
// going upwards and outwards.
//
// `f_propagate` should follow the following format: for a given `Node` it updates its
// `Permission` depending on the position relative to `start` (given by an
// `AccessRelatedness`).
// It outputs whether the tree traversal for this subree should continue or not.
fn traverse_parents_this_children_others<InnErr, OutErr>(
// `f_continue` is called before on foreign nodes, and describes whether to continue
// with the subtree at that node.
fn traverse_this_parents_children_other<InnErr, OutErr>(
mut self,
start: BorTag,
f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<ContinueTraversal, InnErr>,
f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), InnErr>,
err_builder: impl Fn(ErrHandlerArgs<'_, InnErr>) -> OutErr,
) -> Result<(), OutErr> {
let start_idx = self.tag_mapping.get(&start).unwrap();
let mut stack = TreeVisitorStack::new(start_idx, f_propagate, err_builder);
stack.push_and_visit_strict_ancestors(&mut self)?;
// All (potentially zero) ancestors have been explored,
// it's time to explore the `start` tag.
stack.exec_and_visit(
&mut self,
start_idx,
/* no children to exclude */ None,
AccessRelatedness::This,
)?;
// Then finish with a normal DFS.
stack.finish(&mut self)
let mut stack = TreeVisitorStack::new(start_idx, f_continue, f_propagate, err_builder);
// Visits the accessed node itself, and all its parents, i.e. all nodes
// undergoing a child access. Also pushes the children and the other
// cousin nodes (i.e. all nodes undergoing a foreign access) to the stack
// to be processed later.
stack.go_upwards_from_accessed(&mut self, start_idx, true)?;
// Now visit all the foreign nodes we remembered earlier.
// For this we go bottom-up, but also allow f_continue to skip entire
// subtrees from being visited if it would be a NOP.
stack.finish_foreign_accesses(&mut self)
}
// Applies `f_propagate` to every non-child vertex of the tree (ancestors first).
//
// `f_propagate` should follow the following format: for a given `Node` it updates its
// `Permission` depending on the position relative to `start` (given by an
// `AccessRelatedness`).
// It outputs whether the tree traversal for this subree should continue or not.
// Like `traverse_this_parents_children_other`, but skips the children of `start`.
fn traverse_nonchildren<InnErr, OutErr>(
mut self,
start: BorTag,
f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<ContinueTraversal, InnErr>,
f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), InnErr>,
err_builder: impl Fn(ErrHandlerArgs<'_, InnErr>) -> OutErr,
) -> Result<(), OutErr> {
let start_idx = self.tag_mapping.get(&start).unwrap();
let mut stack = TreeVisitorStack::new(start_idx, f_propagate, err_builder);
stack.push_and_visit_strict_ancestors(&mut self)?;
// We *don't* visit the `start` tag, and we don't push its children.
// Only finish the DFS with the cousins.
stack.finish(&mut self)
let mut stack = TreeVisitorStack::new(start_idx, f_continue, f_propagate, err_builder);
// Visits the accessed node itself, and all its parents, i.e. all nodes
// undergoing a child access. Also pushes the other cousin nodes to the
// stack, but not the children of the accessed node.
stack.go_upwards_from_accessed(&mut self, start_idx, false)?;
// Now visit all the foreign nodes we remembered earlier.
// For this we go bottom-up, but also allow f_continue to skip entire
// subtrees from being visited if it would be a NOP.
stack.finish_foreign_accesses(&mut self)
}
}
@ -541,16 +584,18 @@ impl<'tcx> Tree {
)?;
for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size) {
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
.traverse_parents_this_children_others(
.traverse_this_parents_children_other(
tag,
|args: NodeAppArgs<'_>| -> Result<ContinueTraversal, TransitionError> {
// visit all children, skipping none
|_| ContinueTraversal::Recurse,
|args: NodeAppArgs<'_>| -> Result<(), TransitionError> {
let NodeAppArgs { node, .. } = args;
if global.borrow().protected_tags.get(&node.tag)
== Some(&ProtectorKind::StrongProtector)
{
Err(TransitionError::ProtectedDealloc)
} else {
Ok(ContinueTraversal::Recurse)
Ok(())
}
},
|args: ErrHandlerArgs<'_, TransitionError>| -> InterpError<'tcx> {
@ -607,16 +652,28 @@ impl<'tcx> Tree {
//
// `perms_range` is only for diagnostics (it is the range of
// the `RangeMap` on which we are currently working).
let node_skipper = |access_kind: AccessKind, args: &NodeAppArgs<'_>| -> ContinueTraversal {
let NodeAppArgs { node, perm, rel_pos } = args;
let old_state = perm
.get()
.copied()
.unwrap_or_else(|| LocationState::new_uninit(node.default_initial_perm));
// FIXME: See #3684
let _would_skip_if_not_for_fixme = old_state.skip_if_known_noop(access_kind, *rel_pos);
ContinueTraversal::Recurse
};
let node_app = |perms_range: Range<u64>,
access_kind: AccessKind,
access_cause: diagnostics::AccessCause,
args: NodeAppArgs<'_>|
-> Result<ContinueTraversal, TransitionError> {
-> Result<(), TransitionError> {
let NodeAppArgs { node, mut perm, rel_pos } = args;
let old_state = perm.or_insert(LocationState::new_uninit(node.default_initial_perm));
old_state.update_last_foreign_access(access_kind, rel_pos);
// FIXME: See #3684
// old_state.record_new_access(access_kind, rel_pos);
let protected = global.borrow().protected_tags.contains_key(&node.tag);
let transition = old_state.perform_access(access_kind, rel_pos, protected)?;
@ -631,7 +688,7 @@ impl<'tcx> Tree {
span,
});
}
Ok(ContinueTraversal::Recurse)
Ok(())
};
// Error handler in case `node_app` goes wrong.
@ -658,8 +715,9 @@ impl<'tcx> Tree {
for (perms_range, perms) in self.rperms.iter_mut(access_range.start, access_range.size)
{
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
.traverse_parents_this_children_others(
.traverse_this_parents_children_other(
tag,
|args| node_skipper(access_kind, args),
|args| node_app(perms_range.clone(), access_kind, access_cause, args),
|args| err_handler(perms_range.clone(), access_cause, args),
)?;
@ -686,6 +744,7 @@ impl<'tcx> Tree {
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
.traverse_nonchildren(
tag,
|args| node_skipper(access_kind, args),
|args| node_app(perms_range.clone(), access_kind, access_cause, args),
|args| err_handler(perms_range.clone(), access_cause, args),
)?;

View File

@ -221,6 +221,10 @@ impl<'a, V> UniEntry<'a, V> {
}
self.inner.as_mut().unwrap()
}
pub fn get(&self) -> Option<&V> {
self.inner.as_ref()
}
}
mod tests {

View File

@ -5,24 +5,18 @@ LL | let _val = *target_alias;
| ^^^^^^^^^^^^^ read access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/alias_through_mutation.rs:LL:CC
|
LL | *x = &mut *(target as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/alias_through_mutation.rs:LL:CC
|
LL | retarget(&mut target_alias, target);
| ^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/alias_through_mutation.rs:LL:CC
|
LL | *target = 13;
| ^^^^^^^^^^^^
= help: this transition corresponds to a loss of read and write permissions
= help: this transition corresponds to a loss of read permissions
= note: BACKTRACE (of the first span):
= note: inside `main` at $DIR/alias_through_mutation.rs:LL:CC

View File

@ -5,19 +5,13 @@ LL | *LEAK = 7;
| ^^^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | fn unknown_code_1(x: &i32) {
| ^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | unknown_code_1(&*our);
| ^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/box_exclusive_violation1.rs:LL:CC
|
LL | *our = 5;

View File

@ -5,19 +5,13 @@ LL | v2[1] = 7;
| ^^^^^^^^^ write access through <TAG> at ALLOC[0x4] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> $DIR/buggy_as_mut_slice.rs:LL:CC
|
LL | let v2 = safe::as_mut_slice(&v);
| ^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/buggy_as_mut_slice.rs:LL:CC
|
LL | unsafe { from_raw_parts_mut(self_.as_ptr() as *mut T, self_.len()) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8]
--> $DIR/buggy_as_mut_slice.rs:LL:CC
|
LL | v1[1] = 5;

View File

@ -5,19 +5,13 @@ LL | b[1] = 6;
| ^^^^^^^^ write access through <TAG> at ALLOC[0x4] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> $DIR/buggy_split_at_mut.rs:LL:CC
|
LL | let (a, b) = safe::split_at_mut(&mut array, 0);
| ^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/buggy_split_at_mut.rs:LL:CC
|
LL | from_raw_parts_mut(ptr.offset(mid as isize), len - mid),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8]
--> $DIR/buggy_split_at_mut.rs:LL:CC
|
LL | a[1] = 5;

View File

@ -5,19 +5,13 @@ LL | let _val = *xref;
| ^^^^^ read access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> $DIR/illegal_write5.rs:LL:CC
|
LL | let xref = unsafe { &mut *xraw };
| ^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/illegal_write5.rs:LL:CC
|
LL | let xref = unsafe { &mut *xraw };
| ^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/illegal_write5.rs:LL:CC
|
LL | unsafe { *xraw = 15 };

View File

@ -5,19 +5,13 @@ LL | let _val = *xref_in_mem;
| ^^^^^^^^^^^^ reborrow through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/load_invalid_shr.rs:LL:CC
|
LL | let xref_in_mem = Box::new(xref);
| ^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/load_invalid_shr.rs:LL:CC
|
LL | let xref = unsafe { &*xraw };
| ^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/load_invalid_shr.rs:LL:CC
|
LL | unsafe { *xraw = 42 }; // unfreeze

View File

@ -5,19 +5,13 @@ LL | *LEAK = 7;
| ^^^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | fn unknown_code_1(x: &i32) {
| ^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | unknown_code_1(&*our);
| ^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/mut_exclusive_violation1.rs:LL:CC
|
LL | *our = 5;

View File

@ -5,19 +5,13 @@ LL | *raw1 = 3;
| ^^^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> $DIR/mut_exclusive_violation2.rs:LL:CC
|
LL | let raw1 = ptr1.as_mut();
| ^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/mut_exclusive_violation2.rs:LL:CC
|
LL | let raw1 = ptr1.as_mut();
| ^^^^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/mut_exclusive_violation2.rs:LL:CC
|
LL | *raw2 = 2;

View File

@ -5,19 +5,13 @@ LL | foo(some_xref);
| ^^^^^^^^^ reborrow through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/pass_invalid_shr_option.rs:LL:CC
|
LL | let some_xref = unsafe { Some(&*xraw) };
| ^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/pass_invalid_shr_option.rs:LL:CC
|
LL | let some_xref = unsafe { Some(&*xraw) };
| ^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/pass_invalid_shr_option.rs:LL:CC
|
LL | unsafe { *xraw = 42 }; // unfreeze

View File

@ -5,19 +5,13 @@ LL | foo(pair_xref);
| ^^^^^^^^^ reborrow through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/pass_invalid_shr_tuple.rs:LL:CC
|
LL | let pair_xref = unsafe { (&*xraw0, &*xraw1) };
| ^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/pass_invalid_shr_tuple.rs:LL:CC
|
LL | let pair_xref = unsafe { (&*xraw0, &*xraw1) };
| ^^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
--> $DIR/pass_invalid_shr_tuple.rs:LL:CC
|
LL | unsafe { *xraw0 = 42 }; // unfreeze

View File

@ -5,19 +5,13 @@ LL | ret
| ^^^ reborrow through <TAG> at ALLOC[0x4] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/return_invalid_shr_option.rs:LL:CC
|
LL | let ret = Some(unsafe { &(*xraw).1 });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/return_invalid_shr_option.rs:LL:CC
|
LL | let ret = Some(unsafe { &(*xraw).1 });
| ^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
--> $DIR/return_invalid_shr_option.rs:LL:CC
|
LL | unsafe { *xraw = (42, 23) }; // unfreeze

View File

@ -5,19 +5,13 @@ LL | ret
| ^^^ reborrow through <TAG> at ALLOC[0x4] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
|
LL | let ret = (unsafe { &(*xraw).1 },);
| ^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
|
LL | let ret = (unsafe { &(*xraw).1 },);
| ^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
--> $DIR/return_invalid_shr_tuple.rs:LL:CC
|
LL | unsafe { *xraw = (42, 23) }; // unfreeze

View File

@ -5,18 +5,12 @@ LL | *(x as *const i32 as *mut i32) = 7;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Frozen
--> $DIR/shr_frozen_violation1.rs:LL:CC
|
LL | fn unknown_code(x: &i32) {
| ^
help: the conflicting tag <TAG> was created here, in the initial state Frozen
--> $DIR/shr_frozen_violation1.rs:LL:CC
|
LL | unknown_code(&*x);
| ^^^
= note: BACKTRACE (of the first span):
= note: inside `unknown_code` at $DIR/shr_frozen_violation1.rs:LL:CC
note: inside `foo`

View File

@ -5,25 +5,19 @@ LL | *y += 1; // Failure
| ^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> $DIR/alternate-read-write.rs:LL:CC
|
LL | let y = unsafe { &mut *(x as *mut u8) };
| ^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/alternate-read-write.rs:LL:CC
|
LL | let y = unsafe { &mut *(x as *mut u8) };
| ^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
--> $DIR/alternate-read-write.rs:LL:CC
|
LL | *y += 1; // Success
| ^^^^^^^
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
help: the conflicting tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
--> $DIR/alternate-read-write.rs:LL:CC
|
LL | let _val = *x;

View File

@ -5,19 +5,13 @@ LL | child2.write(2);
| ^^^^^^^^^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> $DIR/children-can-alias.rs:LL:CC
|
LL | let child2 = x.as_ptr();
| ^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/children-can-alias.rs:LL:CC
|
LL | let child2 = x.as_ptr();
| ^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x1]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x1]
--> $DIR/children-can-alias.rs:LL:CC
|
LL | child1.write(1);

View File

@ -5,19 +5,13 @@ LL | rmut[5] += 1;
| ^^^^^^^^^^^^ read access through <TAG> at ALLOC[0x5] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
= help: the conflicting tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here
= help: the accessed tag <TAG> has state Disabled which forbids this child read access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> $DIR/error-range.rs:LL:CC
|
LL | let rmut = &mut *addr_of_mut!(data[0..6]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the conflicting tag <TAG> was created here, in the initial state Reserved
--> $DIR/error-range.rs:LL:CC
|
LL | let rmut = &mut *addr_of_mut!(data[0..6]);
| ^^^^
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x5..0x6]
help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x5..0x6]
--> $DIR/error-range.rs:LL:CC
|
LL | data[5] = 1;

View File

@ -1,14 +1,15 @@
//@compile-flags: -Zmiri-tree-borrows
//@error-in-other-file: /deallocation through .* is forbidden/
fn inner(x: &mut i32, f: fn(&mut i32)) {
fn inner(x: &mut i32, f: fn(*mut i32)) {
// `f` may mutate, but it may not deallocate!
// `f` takes a raw pointer so that the only protector
// is that on `x`
f(x)
}
fn main() {
inner(Box::leak(Box::new(0)), |x| {
let raw = x as *mut _;
inner(Box::leak(Box::new(0)), |raw| {
drop(unsafe { Box::from_raw(raw) });
});
}

View File

@ -15,7 +15,7 @@ LL | drop(unsafe { Box::from_raw(raw) });
help: the strongly protected tag <TAG> was created here, in the initial state Reserved
--> $DIR/strongly-protected.rs:LL:CC
|
LL | fn inner(x: &mut i32, f: fn(&mut i32)) {
LL | fn inner(x: &mut i32, f: fn(*mut i32)) {
| ^
= note: BACKTRACE (of the first span):
= note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
@ -28,7 +28,7 @@ note: inside closure
|
LL | drop(unsafe { Box::from_raw(raw) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: inside `<{closure@$DIR/strongly-protected.rs:LL:CC} as std::ops::FnOnce<(&mut i32,)>>::call_once - shim` at RUSTLIB/core/src/ops/function.rs:LL:CC
= note: inside `<{closure@$DIR/strongly-protected.rs:LL:CC} as std::ops::FnOnce<(*mut i32,)>>::call_once - shim` at RUSTLIB/core/src/ops/function.rs:LL:CC
note: inside `inner`
--> $DIR/strongly-protected.rs:LL:CC
|
@ -37,8 +37,7 @@ LL | f(x)
note: inside `main`
--> $DIR/strongly-protected.rs:LL:CC
|
LL | / inner(Box::leak(Box::new(0)), |x| {
LL | | let raw = x as *mut _;
LL | / inner(Box::leak(Box::new(0)), |raw| {
LL | | drop(unsafe { Box::from_raw(raw) });
LL | | });
| |______^