diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 5fbb7b927768d..6aba61527d467 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -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 { +struct TreeVisitorStack { /// 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 { /// 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 TreeVisitorStack +impl + TreeVisitorStack where - NodeApp: Fn(NodeAppArgs<'_>) -> Result, + 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, 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 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)?; + 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(()) } - /// 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( + // `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( mut self, start: BorTag, - f_propagate: impl Fn(NodeAppArgs<'_>) -> Result, + 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( mut self, start: BorTag, - f_propagate: impl Fn(NodeAppArgs<'_>) -> Result, + 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 { + // 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, access_kind: AccessKind, access_cause: diagnostics::AccessCause, args: NodeAppArgs<'_>| - -> Result { + -> 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), )?; diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs index 92bae6203b3c0..cbc25724cb685 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/unimap.rs @@ -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 { diff --git a/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr b/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr index f26e8444a9aaf..0bd196eab1b83 100644 --- a/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr @@ -5,24 +5,18 @@ LL | let _val = *target_alias; | ^^^^^^^^^^^^^ read access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child read access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child read access +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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 diff --git a/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr index ee5ef0c5ea8b2..27d987feb574d 100644 --- a/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr @@ -5,19 +5,13 @@ LL | *LEAK = 7; | ^^^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed 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 was created here, in the initial state Frozen - --> $DIR/box_exclusive_violation1.rs:LL:CC - | -LL | unknown_code_1(&*our); - | ^^^^^ -help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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; diff --git a/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr b/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr index 5593db899710d..16e87c4d4ebc2 100644 --- a/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr @@ -5,19 +5,13 @@ LL | v2[1] = 7; | ^^^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] +help: the accessed 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; diff --git a/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr b/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr index 7d3725d611a2c..aaf7c2256a78a 100644 --- a/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr @@ -5,19 +5,13 @@ LL | b[1] = 6; | ^^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8] +help: the accessed 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; diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr index 1f3d6e387d437..dec2bb6880f87 100644 --- a/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr @@ -5,19 +5,13 @@ LL | let _val = *xref; | ^^^^^ read access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child read access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child read access +help: the accessed 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 was created here, in the initial state Reserved - --> $DIR/illegal_write5.rs:LL:CC - | -LL | let xref = unsafe { &mut *xraw }; - | ^^^^^^^^^^ -help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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 }; diff --git a/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr b/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr index 292dc8150f17f..0fcfc20e43696 100644 --- a/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr @@ -5,19 +5,13 @@ LL | let _val = *xref_in_mem; | ^^^^^^^^^^^^ reborrow through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) +help: the accessed 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 was created here, in the initial state Frozen - --> $DIR/load_invalid_shr.rs:LL:CC - | -LL | let xref = unsafe { &*xraw }; - | ^^^^^^ -help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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 diff --git a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr index a5e62e9ea3381..5b4ee4a891302 100644 --- a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr @@ -5,19 +5,13 @@ LL | *LEAK = 7; | ^^^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed 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 was created here, in the initial state Frozen - --> $DIR/mut_exclusive_violation1.rs:LL:CC - | -LL | unknown_code_1(&*our); - | ^^^^^ -help: the conflicting tag later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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; diff --git a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr index 6b6eecbe41362..d3bc54b7ef3c9 100644 --- a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr @@ -5,19 +5,13 @@ LL | *raw1 = 3; | ^^^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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; diff --git a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr index f2eaf767a47af..f687b3f867fc3 100644 --- a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr @@ -5,19 +5,13 @@ LL | foo(some_xref); | ^^^^^^^^^ reborrow through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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 diff --git a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr index 9cfd4239c85f8..845838f93d57d 100644 --- a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr @@ -5,19 +5,13 @@ LL | foo(pair_xref); | ^^^^^^^^^ reborrow through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4] +help: the accessed 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 diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr index 1169200b57c1f..e770372925130 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr @@ -5,19 +5,13 @@ LL | ret | ^^^ reborrow through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed 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 diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr index af410534140fe..a5c6be3f13aa8 100644 --- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr @@ -5,19 +5,13 @@ LL | ret | ^^^ reborrow through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this reborrow (acting as a child read access) -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this reborrow (acting as a child read access) +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8] +help: the accessed 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 diff --git a/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr index 97cd64c03be8c..45b3bceb7287e 100644 --- a/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr @@ -5,18 +5,12 @@ LL | *(x as *const i32 as *mut i32) = 7; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Frozen which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Frozen which forbids this child write access +help: the accessed 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 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` diff --git a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr index 98371cbe7a262..bd969d089c37a 100644 --- a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr @@ -5,25 +5,19 @@ LL | *y += 1; // Failure | ^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Frozen which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Frozen which forbids this child write access +help: the accessed 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 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 later transitioned to Active due to a child write access at offsets [0x0..0x1] +help: the accessed 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 later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1] +help: the accessed 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; diff --git a/src/tools/miri/tests/fail/tree_borrows/children-can-alias.uniq.stderr b/src/tools/miri/tests/fail/tree_borrows/children-can-alias.uniq.stderr index cc1fea30f53e4..cdfa8a74238a4 100644 --- a/src/tools/miri/tests/fail/tree_borrows/children-can-alias.uniq.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/children-can-alias.uniq.stderr @@ -5,19 +5,13 @@ LL | child2.write(2); | ^^^^^^^^^^^^^^^ write access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child write access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child write access +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x0..0x1] +help: the accessed 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); diff --git a/src/tools/miri/tests/fail/tree_borrows/error-range.stderr b/src/tools/miri/tests/fail/tree_borrows/error-range.stderr index 37f0b9b62b037..090ae4507bd75 100644 --- a/src/tools/miri/tests/fail/tree_borrows/error-range.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/error-range.stderr @@ -5,19 +5,13 @@ LL | rmut[5] += 1; | ^^^^^^^^^^^^ read access through 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 is a child of the conflicting tag - = help: the conflicting tag has state Disabled which forbids this child read access -help: the accessed tag was created here + = help: the accessed tag has state Disabled which forbids this child read access +help: the accessed 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 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 later transitioned to Disabled due to a foreign write access at offsets [0x5..0x6] +help: the accessed 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; diff --git a/src/tools/miri/tests/fail/tree_borrows/strongly-protected.rs b/src/tools/miri/tests/fail/tree_borrows/strongly-protected.rs index 484c7c3bbff78..037031d0345c4 100644 --- a/src/tools/miri/tests/fail/tree_borrows/strongly-protected.rs +++ b/src/tools/miri/tests/fail/tree_borrows/strongly-protected.rs @@ -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) }); }); } diff --git a/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr b/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr index f0afcc7b3ff1e..9da43055af240 100644 --- a/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr @@ -15,7 +15,7 @@ LL | drop(unsafe { Box::from_raw(raw) }); help: the strongly protected 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 | | }); | |______^