TB: rename Active → Unique to match paper

This commit is contained in:
Ralf Jung 2025-09-19 12:24:22 +02:00
parent 0dac2f06fa
commit 1c316023d6
25 changed files with 95 additions and 128 deletions

View File

@ -244,8 +244,8 @@ pub(super) enum TransitionError {
ChildAccessForbidden(Permission),
/// A protector was triggered due to an invalid transition that loses
/// too much permissions.
/// For example, if a protected tag goes from `Active` to `Disabled` due
/// to a foreign write this will produce a `ProtectedDisabled(Active)`.
/// For example, if a protected tag goes from `Unique` to `Disabled` due
/// to a foreign write this will produce a `ProtectedDisabled(Unique)`.
/// This kind of error can only occur on foreign accesses.
ProtectedDisabled(Permission),
/// Cannot deallocate because some tag in the allocation is strongly protected.

View File

@ -14,7 +14,7 @@ enum PermissionPriv {
Cell,
/// represents: a local mutable reference that has not yet been written to;
/// allows: child reads, foreign reads;
/// affected by: child writes (becomes Active),
/// affected by: child writes (becomes Unique),
/// rejects: foreign writes (Disabled).
///
/// `ReservedFrz` is mostly for types that are `Freeze` (no interior mutability).
@ -31,17 +31,17 @@ enum PermissionPriv {
/// This is so that the behavior of `Reserved` adheres to the rules of `noalias`:
/// - foreign-read then child-write is UB due to `conflicted`,
/// - child-write then foreign-read is UB since child-write will activate and then
/// foreign-read disables a protected `Active`, which is UB.
/// foreign-read disables a protected `Unique`, which is UB.
ReservedFrz { conflicted: bool },
/// Alternative version of `ReservedFrz` made for types with interior mutability.
/// allows: child reads, foreign reads, foreign writes (extra);
/// affected by: child writes (becomes Active);
/// affected by: child writes (becomes Unique);
/// rejects: nothing.
ReservedIM,
/// represents: a unique pointer;
/// allows: child reads, child writes;
/// rejects: foreign reads (Frozen), foreign writes (Disabled).
Active,
Unique,
/// represents: a shared pointer;
/// allows: all read accesses;
/// rejects child writes (UB), foreign writes (Disabled).
@ -56,7 +56,7 @@ use super::foreign_access_skipping::IdempotentForeignAccess;
impl PartialOrd for PermissionPriv {
/// PermissionPriv is ordered by the reflexive transitive closure of
/// `Reserved(conflicted=false) < Reserved(conflicted=true) < Active < Frozen < Disabled`.
/// `Reserved(conflicted=false) < Reserved(conflicted=true) < Unique < Frozen < Disabled`.
/// `Reserved` that have incompatible `ty_is_freeze` are incomparable to each other.
/// This ordering matches the reachability by transitions, as asserted by the exhaustive test
/// `permissionpriv_partialord_is_reachability`.
@ -76,8 +76,8 @@ impl PartialOrd for PermissionPriv {
(_, Disabled) => Less,
(Frozen, _) => Greater,
(_, Frozen) => Less,
(Active, _) => Greater,
(_, Active) => Less,
(Unique, _) => Greater,
(_, Unique) => Less,
(ReservedIM, ReservedIM) => Equal,
(ReservedFrz { conflicted: c1 }, ReservedFrz { conflicted: c2 }) => {
// `bool` is ordered such that `false <= true`, so this works as intended.
@ -115,8 +115,8 @@ impl PermissionPriv {
// Famously, ReservedIM survives foreign writes. It is never protected.
ReservedIM if prot => unreachable!("Protected ReservedIM should not exist!"),
ReservedIM => IdempotentForeignAccess::Write,
// Active changes on any foreign access (becomes Frozen/Disabled).
Active => IdempotentForeignAccess::None,
// Unique changes on any foreign access (becomes Frozen/Disabled).
Unique => IdempotentForeignAccess::None,
// Frozen survives foreign reads, but not writes.
Frozen => IdempotentForeignAccess::Read,
// Disabled survives foreign reads and writes. It survives them
@ -139,12 +139,12 @@ mod transition {
Disabled => return None,
// The inner data `ty_is_freeze` of `Reserved` is always irrelevant for Read
// accesses, since the data is not being mutated. Hence the `{ .. }`.
readable @ (Cell | ReservedFrz { .. } | ReservedIM | Active | Frozen) => readable,
readable @ (Cell | ReservedFrz { .. } | ReservedIM | Unique | Frozen) => readable,
})
}
/// A non-child node was read-accessed: keep `Reserved` but mark it as `conflicted` if it
/// is protected; invalidate `Active`.
/// is protected; invalidate `Unique`.
fn foreign_read(state: PermissionPriv, protected: bool) -> Option<PermissionPriv> {
Some(match state {
// Cell ignores foreign reads.
@ -167,10 +167,10 @@ mod transition {
assert!(!protected);
res
}
Active =>
Unique =>
if protected {
// We wrote, someone else reads -- that's bad.
// (Since Active is always initialized, this move-to-protected will mean insta-UB.)
// (Since Unique is always initialized, this move-to-protected will mean insta-UB.)
Disabled
} else {
// We don't want to disable here to allow read-read reordering: it is crucial
@ -180,7 +180,7 @@ mod transition {
})
}
/// A child node was write-accessed: `Reserved` must become `Active` to obtain
/// A child node was write-accessed: `Reserved` must become `Unique` to obtain
/// write permissions, `Frozen` and `Disabled` cannot obtain such permissions and produce UB.
fn child_write(state: PermissionPriv, protected: bool) -> Option<PermissionPriv> {
Some(match state {
@ -192,7 +192,7 @@ mod transition {
ReservedFrz { conflicted: true } if protected => return None,
// A write always activates the 2-phase borrow, even with interior
// mutability
ReservedFrz { .. } | ReservedIM | Active => Active,
ReservedFrz { .. } | ReservedIM | Unique => Unique,
Frozen | Disabled => return None,
})
}
@ -266,8 +266,8 @@ impl Permission {
/// Default initial permission of the root of a new tree at inbounds positions.
/// Must *only* be used for the root, this is not in general an "initial" permission!
pub fn new_active() -> Self {
Self { inner: Active }
pub fn new_unique() -> Self {
Self { inner: Unique }
}
/// Default initial permission of a reborrowed mutable reference that is either
@ -309,7 +309,7 @@ impl Permission {
// Do not do perform access if it is a `Cell`, as this
// can cause data races when using thread-safe data types.
Cell => None,
Active => Some(AccessKind::Write),
Unique => Some(AccessKind::Write),
_ => Some(AccessKind::Read),
}
}
@ -344,7 +344,7 @@ impl Permission {
(_, Cell) => false,
// ReservedIM can be replaced by anything besides Cell.
// ReservedIM allows all transitions, but unlike Cell, a local write
// to ReservedIM transitions to Active, while it is a no-op for Cell.
// to ReservedIM transitions to Unique, while it is a no-op for Cell.
(ReservedIM, _) => true,
(_, ReservedIM) => false,
// Reserved (as parent, where conflictedness does not matter)
@ -352,12 +352,12 @@ impl Permission {
// since ReservedIM and Cell alone would survive foreign writes
(ReservedFrz { .. }, _) => true,
(_, ReservedFrz { .. }) => false,
// Active can not be replaced by something surviving
// Unique can not be replaced by something surviving
// foreign reads and then remaining writable (i.e., Reserved*).
// Replacing a state by itself is always okay, even if the child state is protected.
// Active can be replaced by Frozen, since it is not protected.
(Active, Active | Frozen | Disabled) => true,
(_, Active) => false,
// Unique can be replaced by Frozen, since it is not protected.
(Unique, Unique | Frozen | Disabled) => true,
(_, Unique) => false,
// Frozen can only be replaced by Disabled (and itself).
(Frozen, Frozen | Disabled) => true,
(_, Frozen) => false,
@ -410,7 +410,7 @@ pub mod diagnostics {
ReservedFrz { conflicted: false } => "Reserved",
ReservedFrz { conflicted: true } => "Reserved (conflicted)",
ReservedIM => "Reserved (interior mutable)",
Active => "Active",
Unique => "Unique",
Frozen => "Frozen",
Disabled => "Disabled",
}
@ -441,7 +441,7 @@ pub mod diagnostics {
ReservedFrz { conflicted: false } => "Res ",
ReservedFrz { conflicted: true } => "ResC",
ReservedIM => "ReIM",
Active => "Act ",
Unique => "Act ",
Frozen => "Frz ",
Disabled => "Dis ",
}
@ -455,7 +455,7 @@ pub mod diagnostics {
assert!(self.is_possible());
assert!(!self.is_noop());
match (self.from, self.to) {
(_, Active) => "the first write to a 2-phase borrowed mutable reference",
(_, Unique) => "the first write to a 2-phase borrowed mutable reference",
(_, Frozen) => "a loss of write permissions",
(ReservedFrz { conflicted: false }, ReservedFrz { conflicted: true }) =>
"a temporary loss of write permissions until function exit",
@ -472,8 +472,8 @@ pub mod diagnostics {
///
/// Irrelevant events:
/// - modifications of write permissions when the error is related to read permissions
/// (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Active`,
/// `Reserved(conflicted=false) -> Reserved(conflicted=true)`, and `Active -> Frozen`)
/// (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Unique`,
/// `Reserved(conflicted=false) -> Reserved(conflicted=true)`, and `Unique -> Frozen`)
/// - all transitions for attempts to deallocate strongly protected tags
///
/// # Panics
@ -481,10 +481,10 @@ pub mod diagnostics {
/// This function assumes that its arguments apply to the same location
/// and that they were obtained during a normal execution. It will panic otherwise.
/// - all transitions involved in `self` and `err` should be increasing
/// (Reserved < Active < Frozen < Disabled);
/// (Reserved < Unique < Frozen < Disabled);
/// - between `self` and `err` the permission should also be increasing,
/// so all permissions inside `err` should be greater than `self.1`;
/// - `Active`, `Reserved(conflicted=false)`, and `Cell` cannot cause an error
/// - `Unique`, `Reserved(conflicted=false)`, and `Cell` cannot cause an error
/// due to insufficient permissions, so `err` cannot be a `ChildAccessForbidden(_)`
/// of either of them;
/// - `err` should not be `ProtectedDisabled(Disabled)`, because the protected
@ -500,11 +500,11 @@ pub mod diagnostics {
TransitionError::ChildAccessForbidden(insufficient) => {
// Show where the permission was gained then lost,
// but ignore unrelated permissions.
// This eliminates transitions like `Active -> Frozen`
// This eliminates transitions like `Unique -> Frozen`
// when the error is a failed `Read`.
match (self.to, insufficient.inner) {
(Frozen, Frozen) => true,
(Active, Frozen) => true,
(Unique, Frozen) => true,
(Disabled, Disabled) => true,
(
ReservedFrz { conflicted: true, .. },
@ -512,14 +512,14 @@ pub mod diagnostics {
) => true,
// A pointer being `Disabled` is a strictly stronger source of
// errors than it being `Frozen`. If we try to access a `Disabled`,
// then where it became `Frozen` (or `Active` or `Reserved`) is the least
// then where it became `Frozen` (or `Unique` or `Reserved`) is the least
// of our concerns for now.
(ReservedFrz { conflicted: true } | Active | Frozen, Disabled) => false,
(ReservedFrz { conflicted: true } | Unique | Frozen, Disabled) => false,
(ReservedFrz { conflicted: true }, Frozen) => false,
// `Active`, `Reserved`, and `Cell` have all permissions, so a
// `ChildAccessForbidden(Reserved | Active)` can never exist.
(_, Active) | (_, ReservedFrz { conflicted: false }) | (_, Cell) =>
// `Unique`, `Reserved`, and `Cell` have all permissions, so a
// `ChildAccessForbidden(Reserved | Unique)` can never exist.
(_, Unique) | (_, ReservedFrz { conflicted: false }) | (_, Cell) =>
unreachable!("this permission cannot cause an error"),
// No transition has `Reserved { conflicted: false }` or `ReservedIM`
// as its `.to` unless it's a noop. `Cell` cannot be in its `.to`
@ -527,11 +527,11 @@ pub mod diagnostics {
(ReservedFrz { conflicted: false } | ReservedIM | Cell, _) =>
unreachable!("self is a noop transition"),
// All transitions produced in normal executions (using `apply_access`)
// change permissions in the order `Reserved -> Active -> Frozen -> Disabled`.
// change permissions in the order `Reserved -> Unique -> Frozen -> Disabled`.
// We assume that the error was triggered on the same location that
// the transition `self` applies to, so permissions found must be increasing
// in the order `self.from < self.to <= insufficient.inner`
(Active | Frozen | Disabled, ReservedFrz { .. } | ReservedIM)
(Unique | Frozen | Disabled, ReservedFrz { .. } | ReservedIM)
| (Disabled, Frozen)
| (ReservedFrz { .. }, ReservedIM) =>
unreachable!("permissions between self and err must be increasing"),
@ -540,29 +540,29 @@ pub mod diagnostics {
TransitionError::ProtectedDisabled(before_disabled) => {
// Show how we got to the starting point of the forbidden transition,
// but ignore what came before.
// This eliminates transitions like `Reserved -> Active`
// This eliminates transitions like `Reserved -> Unique`
// when the error is a `Frozen -> Disabled`.
match (self.to, before_disabled.inner) {
// We absolutely want to know where it was activated/frozen/marked
// conflicted.
(Active, Active) => true,
(Unique, Unique) => true,
(Frozen, Frozen) => true,
(
ReservedFrz { conflicted: true, .. },
ReservedFrz { conflicted: true, .. },
) => true,
// If the error is a transition `Frozen -> Disabled`, then we don't really
// care whether before that was `Reserved -> Active -> Frozen` or
// care whether before that was `Reserved -> Unique -> Frozen` or
// `Frozen` directly.
// The error will only show either
// - created as Reserved { conflicted: false },
// then Reserved { .. } -> Disabled is forbidden
// - created as Reserved { conflicted: false },
// then Active -> Disabled is forbidden
// then Unique -> Disabled is forbidden
// A potential `Reserved { conflicted: false }
// -> Reserved { conflicted: true }` is inexistant or irrelevant,
// and so is the `Reserved { conflicted: false } -> Active`
(Active, Frozen) => false,
// and so is the `Reserved { conflicted: false } -> Unique`
(Unique, Frozen) => false,
(ReservedFrz { conflicted: true }, _) => false,
(_, Disabled) =>
@ -575,12 +575,12 @@ pub mod diagnostics {
(ReservedFrz { conflicted: false } | ReservedIM | Cell, _) =>
unreachable!("self is a noop transition"),
// Permissions only evolve in the order `Reserved -> Active -> Frozen -> Disabled`,
// Permissions only evolve in the order `Reserved -> Unique -> Frozen -> Disabled`,
// so permissions found must be increasing in the order
// `self.from < self.to <= forbidden.from < forbidden.to`.
(Disabled, Cell | ReservedFrz { .. } | ReservedIM | Active | Frozen)
| (Frozen, Cell | ReservedFrz { .. } | ReservedIM | Active)
| (Active, Cell | ReservedFrz { .. } | ReservedIM) =>
(Disabled, Cell | ReservedFrz { .. } | ReservedIM | Unique | Frozen)
| (Frozen, Cell | ReservedFrz { .. } | ReservedIM | Unique)
| (Unique, Cell | ReservedFrz { .. } | ReservedIM) =>
unreachable!("permissions between self and err must be increasing"),
}
}
@ -617,7 +617,7 @@ mod propagation_optimization_checks {
impl Exhaustive for PermissionPriv {
fn exhaustive() -> Box<dyn Iterator<Item = Self>> {
Box::new(
vec![Active, Frozen, Disabled, ReservedIM, Cell]
vec![Unique, Frozen, Disabled, ReservedIM, Cell]
.into_iter()
.chain(<bool>::exhaustive().map(|conflicted| ReservedFrz { conflicted })),
)
@ -730,7 +730,7 @@ mod propagation_optimization_checks {
#[test]
// Check that all transitions are consistent with the order on PermissionPriv,
// i.e. Reserved -> Active -> Frozen -> Disabled
// i.e. Reserved -> Unique -> Frozen -> Disabled
fn permissionpriv_partialord_is_reachability() {
let reach = {
let mut reach = rustc_data_structures::fx::FxHashSet::default();

View File

@ -57,7 +57,7 @@ pub(super) struct LocationState {
impl LocationState {
/// Constructs a new initial state. It has neither been accessed, nor been subjected
/// to any foreign access yet.
/// The permission is not allowed to be `Active`.
/// The permission is not allowed to be `Unique`.
/// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
assert!(permission.is_initial() || permission.is_disabled());
@ -80,7 +80,7 @@ impl LocationState {
/// Check if the state can exist as the initial permission of a pointer.
///
/// Do not confuse with `is_accessed`, the two are almost orthogonal
/// as apart from `Active` which is not initial and must be accessed,
/// as apart from `Unique` which is not initial and must be accessed,
/// any other permission can have an arbitrary combination of being
/// initial/accessed.
/// FIXME: when the corresponding `assert` in `tree_borrows/mod.rs` finally
@ -170,7 +170,7 @@ impl LocationState {
}
if self.permission.is_frozen() && access_kind == AccessKind::Read {
// A foreign read to a `Frozen` tag will have almost no observable effect.
// It's a theorem that `Frozen` nodes have no active children, so all children
// It's a theorem that `Frozen` nodes have no `Unique` children, so all children
// already survive foreign reads. Foreign reads in general have almost no
// effect, the only further thing they could do is make protected `Reserved`
// nodes become conflicted, i.e. make them reject child writes for the further
@ -265,7 +265,7 @@ pub(super) struct Node {
pub children: SmallVec<[UniIndex; 4]>,
/// Either `Reserved`, `Frozen`, or `Disabled`, it is the permission this tag will
/// lazily be initialized to on the first access.
/// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by
/// It is only ever `Disabled` for a tree root, since the root is initialized to `Unique` by
/// its own separate mechanism.
default_initial_perm: Permission,
/// The default initial (strongest) idempotent foreign access.
@ -598,14 +598,14 @@ impl Tree {
};
let rperms = {
let mut perms = UniValMap::default();
// We manually set it to `Active` on all in-bounds positions.
// We also ensure that it is accessed, so that no `Active` but
// We manually set it to `Unique` on all in-bounds positions.
// We also ensure that it is accessed, so that no `Unique` but
// not yet accessed nodes exist. Essentially, we pretend there
// was a write that initialized these to `Active`.
// was a write that initialized these to `Unique`.
perms.insert(
root_idx,
LocationState::new_accessed(
Permission::new_active(),
Permission::new_unique(),
IdempotentForeignAccess::None,
),
);
@ -790,7 +790,7 @@ impl<'tcx> Tree {
/// - the access will be applied only to accessed locations of the allocation,
/// - it will not be visible to children,
/// - it will be recorded as a `FnExit` diagnostic access
/// - and it will be a read except if the location is `Active`, i.e. has been written to,
/// - and it will be a read except if the location is `Unique`, i.e. has been written to,
/// in which case it will be a write.
///
/// `LocationState::perform_access` will take care of raising transition

View File

@ -420,7 +420,7 @@ mod spurious_read {
/// `(LocStateProt, LocStateProt)` where the two states are not guaranteed
/// to be updated at the same time.
/// Some `LocStateProtPair` may be unreachable through normal means
/// such as `x: Active, y: Active` in the case of mutually foreign pointers.
/// such as `x: Unique, y: Unique` in the case of mutually foreign pointers.
struct LocStateProtPair {
xy_rel: RelPosXY,
x: LocStateProt,
@ -709,7 +709,7 @@ mod spurious_read {
let mut err = 0;
for pat in Pattern::exhaustive() {
let Ok(initial_source) = pat.initial_state() else {
// Failed to retag `x` in the source (e.g. `y` was protected Active)
// Failed to retag `x` in the source (e.g. `y` was protected Unique)
continue;
};
// `x` must stay protected, but the function protecting `y` might return here

View File

@ -16,7 +16,7 @@ LL | | Poll::<()>::Pending
LL | | })
LL | | .await
| |______________^
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [OFFSET]
help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [OFFSET]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | *x = 1;

View File

@ -7,7 +7,7 @@ LL | *y
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/both_borrows/box_noalias_violation.rs:LL:CC
@ -19,7 +19,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | unsafe fn test(mut x: Box<i32>, y: *const i32) -> i32 {
| ^^^^^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/both_borrows/box_noalias_violation.rs:LL:CC
|
LL | *x = 5;

View File

@ -7,7 +7,7 @@ LL | unsafe { *y = 2 };
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/both_borrows/illegal_write6.rs:LL:CC
@ -19,7 +19,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | fn foo(a: &mut u32, y: *mut u32) -> u32 {
| ^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/both_borrows/illegal_write6.rs:LL:CC
|
LL | *a = 1;

View File

@ -7,7 +7,7 @@ LL | Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(a
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
@ -19,7 +19,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | y.0 = 0;
| ^^^^^^^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
|
LL | y.0 = 0;

View File

@ -7,7 +7,7 @@ LL | Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call),
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this reborrow (acting as a foreign read access) would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this reborrow (acting as a foreign read access) would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
@ -19,7 +19,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | x
| ^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
|
LL | x

View File

@ -7,7 +7,7 @@ LL | unsafe { ptr.write(S(0)) };
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
@ -24,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | unsafe { ptr.write(S(0)) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
|
LL | unsafe { ptr.write(S(0)) };

View File

@ -7,7 +7,7 @@ LL | unsafe { ptr.read() };
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
@ -24,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | x.0 = 0;
| ^^^^^^^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
|
LL | x.0 = 0;

View File

@ -7,7 +7,7 @@ LL | unsafe { ptr.read() };
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
@ -24,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | unsafe { ptr.read() };
| ^^^^^^^^^^^^^^^^^^^^^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
|
LL | unsafe { ptr.read() };

View File

@ -7,7 +7,7 @@ LL | unsafe { ptr.write(0) };
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
@ -24,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | unsafe { ptr.write(0) };
| ^^^^^^^^^^^^^^^^^^^^^^^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
|
LL | unsafe { ptr.write(0) };

View File

@ -7,7 +7,7 @@ LL | unsafe { ptr.write(0) };
= 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: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
= help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
= help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
= help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
= help: protected tags must never be Disabled
help: the accessed tag <TAG> was created here
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
@ -24,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
|
LL | unsafe { ptr.write(0) };
| ^^^^^^^^^^^^^^^^^^^^^^^
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
|
LL | unsafe { ptr.write(0) };

View File

@ -12,7 +12,7 @@ help: the accessed tag <TAG> was created here, in the initial state Reserved
|
LL | let y = unsafe { &mut *(x as *mut u8) };
| ^^^^^^^^^^^^^^^^^^^^
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x1]
--> tests/fail/tree_borrows/alternate-read-write.rs:LL:CC
|
LL | *y += 1; // Success

View File

@ -12,7 +12,7 @@ help: the accessed tag <TAG> was created here, in the initial state Reserved
|
LL | let z = &mut x as *mut i32;
| ^^^^^^
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/tree_borrows/fnentry_invalidation.rs:LL:CC
|
LL | *z = 1;

View File

@ -12,7 +12,7 @@ help: the accessed tag <TAG> was created here, in the initial state Reserved
|
LL | let mref = &mut root;
| ^^^^^^^^^
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x1]
--> tests/fail/tree_borrows/parent_read_freezes_raw_mut.rs:LL:CC
|
LL | *ptr = 0; // Write

View File

@ -18,7 +18,7 @@ help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
LL | let xref = unsafe { &mut *xraw };
| ^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
help: the conflicting tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
--> tests/fail/tree_borrows/pass_invalid_mut.rs:LL:CC
|
LL | *xref = 18; // activate xref

View File

@ -60,8 +60,7 @@ fn main() {
fn inner(x: &mut u8, b: IdxBarrier) {
*x = 42; // activate immediately
synchronized!(b, "[lazy] retag y (&mut, protect, IM)");
// A spurious write should be valid here because `x` is
// `Active` and protected.
// A spurious write should be valid here because `x` is `Unique` and protected.
if cfg!(with) {
synchronized!(b, "spurious write x (executed)");
*x = 64;

View File

@ -18,7 +18,7 @@ help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
LL | let ret = unsafe { &mut (*xraw).1 };
| ^^^^^^^^^^^^^^
help: the conflicting tag <TAG> later transitioned to Active due to a child write access at offsets [0x4..0x8]
help: the conflicting tag <TAG> later transitioned to Unique due to a child write access at offsets [0x4..0x8]
--> tests/fail/tree_borrows/return_invalid_mut.rs:LL:CC
|
LL | *ret = *ret; // activate

View File

@ -5,7 +5,7 @@
// When this method is called, the tree will be a single line and look like this,
// with other_ptr being the root at the top
// other_ptr = root : Active
// other_ptr = root : Unique
// intermediary : Frozen // an intermediary node
// m : Reserved
fn write_to_mut(m: &mut u8, other_ptr: *const u8) {

View File

@ -1,32 +0,0 @@
error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
--> tests/fail/tree_borrows/unique.rs:LL:CC
|
LL | *uniq.as_ptr() = 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> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> tests/fail/tree_borrows/unique.rs:LL:CC
|
LL | let refmut = &mut data;
| ^^^^^^^^^
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
--> tests/fail/tree_borrows/unique.rs:LL:CC
|
LL | *uniq.as_ptr() = 1; // activation
| ^^^^^^^^^^^^^^^^^^
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
--> tests/fail/tree_borrows/unique.rs:LL:CC
|
LL | let _definitely_parent = data; // definitely Frozen by now
| ^^^^
= help: this transition corresponds to a loss of write permissions
= note: BACKTRACE (of the first span):
= note: inside `main` at tests/fail/tree_borrows/unique.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -20,7 +20,7 @@ pub fn main() {
name!(ptr2);
// We perform a write through `x`.
// Because `ptr1` is ReservedIM, a child write will make it transition to Active.
// Because `ptr1` is ReservedIM, a child write will make it transition to Unique.
// Because `ptr2` is ReservedIM, a foreign write doesn't have any effect on it.
let x = (*ptr1).get();
*x = 1;

View File

@ -6,7 +6,7 @@
mod utils;
// To check that a reborrow is counted as a Read access, we use a reborrow
// with no additional Read to Freeze an Active pointer.
// with no additional Read to Freeze an Unique pointer.
fn main() {
unsafe {
@ -15,7 +15,7 @@ fn main() {
let alloc_id = alloc_id!(parent);
let x = &mut *parent;
name!(x);
*x = 0; // x is now Active
*x = 0; // x is now Unique
print_state!(alloc_id);
let y = &mut *parent;
name!(y);

View File

@ -68,7 +68,7 @@ unsafe fn cell_unprotected_read() {
}
// Foreign Write on an interior mutable pointer is a noop.
// Also y must become Active.
// Also y must become Unique.
unsafe fn cell_unprotected_write() {
print("[interior mut] Foreign Write: Re* -> Re*");
let base = &mut UnsafeCell::new(0u64);
@ -97,7 +97,7 @@ unsafe fn int_protected_read() {
}
// Foreign Read on a Reserved is a noop.
// Also y must become Active.
// Also y must become Unique.
unsafe fn int_unprotected_read() {
print("[] Foreign Read: Res -> Res");
let base = &mut 0u8;