Skip to content

Commit 211d64c

Browse files
authored
Merge pull request #4595 from RalfJung/tb-terms
TB: update terminology to match paper & MiniRust
2 parents 1d79e99 + e7b0320 commit 211d64c

26 files changed

+128
-197
lines changed

src/borrow_tracker/tree_borrows/diagnostics.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,8 @@ pub(super) enum TransitionError {
244244
ChildAccessForbidden(Permission),
245245
/// A protector was triggered due to an invalid transition that loses
246246
/// too much permissions.
247-
/// For example, if a protected tag goes from `Active` to `Disabled` due
248-
/// to a foreign write this will produce a `ProtectedDisabled(Active)`.
247+
/// For example, if a protected tag goes from `Unique` to `Disabled` due
248+
/// to a foreign write this will produce a `ProtectedDisabled(Unique)`.
249249
/// This kind of error can only occur on foreign accesses.
250250
ProtectedDisabled(Permission),
251251
/// Cannot deallocate because some tag in the allocation is strongly protected.
@@ -504,7 +504,7 @@ impl DisplayFmt {
504504
if let Some(perm) = perm {
505505
format!(
506506
"{ac}{st}",
507-
ac = if perm.is_accessed() { self.accessed.yes } else { self.accessed.no },
507+
ac = if perm.accessed() { self.accessed.yes } else { self.accessed.no },
508508
st = perm.permission().short_name(),
509509
)
510510
} else {

src/borrow_tracker/tree_borrows/mod.rs

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -294,24 +294,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
294294
return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
295295
}
296296

297-
let span = this.machine.current_span();
298-
299-
// When adding a new node, the SIFA of its parents needs to be updated, potentially across
300-
// the entire memory range. For the parts that are being accessed below, the access itself
301-
// trivially takes care of that. However, we have to do some more work to also deal with the
302-
// parts that are not being accessed. Specifically what we do is that we call
303-
// `update_last_accessed_after_retag` on the SIFA of the permission set for the part of
304-
// memory outside `perm_map` -- so that part is definitely taken care of. The remaining
305-
// concern is the part of memory that is in the range of `perms_map`, but not accessed
306-
// below. There we have two cases:
307-
// * If the type is `!Freeze`, then the non-accessed part uses `nonfreeze_perm`, so the
308-
// `nonfreeze_perm` initialized parts are also fine. We enforce the `freeze_perm` parts to
309-
// be accessed via the assert below, and thus everything is taken care of.
310-
// * If the type is `Freeze`, then `freeze_perm` is used everywhere (both inside and outside
311-
// the initial range), and we update everything to have the `freeze_perm`'s SIFA, so there
312-
// are no issues. (And this assert below is not actually needed in this case).
313-
assert!(new_perm.freeze_access);
314-
315297
let protected = new_perm.protector.is_some();
316298
let precise_interior_mut = this
317299
.machine
@@ -337,7 +319,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
337319
LocationState::new_non_accessed(perm, sifa)
338320
}
339321
};
340-
let perms_map = if !precise_interior_mut {
322+
let inside_perms = if !precise_interior_mut {
341323
// For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`.
342324
let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
343325
let state = loc_state(ty_is_freeze);
@@ -364,8 +346,8 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
364346
let alloc_extra = this.get_alloc_extra(alloc_id)?;
365347
let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
366348

367-
for (perm_range, perm) in perms_map.iter_all() {
368-
if perm.is_accessed() {
349+
for (perm_range, perm) in inside_perms.iter_all() {
350+
if perm.accessed() {
369351
// Some reborrows incur a read access to the parent.
370352
// Adjust range to be relative to allocation start (rather than to `place`).
371353
let range_in_alloc = AllocRange {
@@ -401,10 +383,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
401383
base_offset,
402384
orig_tag,
403385
new_tag,
404-
perms_map,
386+
inside_perms,
405387
new_perm.outside_perm,
406388
protected,
407-
span,
389+
this.machine.current_span(),
408390
)?;
409391
drop(tree_borrows);
410392

src/borrow_tracker/tree_borrows/perms.rs

Lines changed: 51 additions & 51 deletions
Large diffs are not rendered by default.

src/borrow_tracker/tree_borrows/tree.rs

Lines changed: 33 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//! - idempotency properties asserted in `perms.rs` (for optimizations)
1212
1313
use std::ops::Range;
14-
use std::{fmt, mem};
14+
use std::{cmp, fmt, mem};
1515

1616
use rustc_abi::Size;
1717
use rustc_data_structures::fx::FxHashSet;
@@ -57,7 +57,7 @@ pub(super) struct LocationState {
5757
impl LocationState {
5858
/// Constructs a new initial state. It has neither been accessed, nor been subjected
5959
/// to any foreign access yet.
60-
/// The permission is not allowed to be `Active`.
60+
/// The permission is not allowed to be `Unique`.
6161
/// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
6262
pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
6363
assert!(permission.is_initial() || permission.is_disabled());
@@ -73,23 +73,10 @@ impl LocationState {
7373

7474
/// Check if the location has been accessed, i.e. if it has
7575
/// ever been accessed through a child pointer.
76-
pub fn is_accessed(&self) -> bool {
76+
pub fn accessed(&self) -> bool {
7777
self.accessed
7878
}
7979

80-
/// Check if the state can exist as the initial permission of a pointer.
81-
///
82-
/// Do not confuse with `is_accessed`, the two are almost orthogonal
83-
/// as apart from `Active` which is not initial and must be accessed,
84-
/// any other permission can have an arbitrary combination of being
85-
/// initial/accessed.
86-
/// FIXME: when the corresponding `assert` in `tree_borrows/mod.rs` finally
87-
/// passes and can be uncommented, remove this `#[allow(dead_code)]`.
88-
#[cfg_attr(not(test), allow(dead_code))]
89-
pub fn is_initial(&self) -> bool {
90-
self.permission.is_initial()
91-
}
92-
9380
pub fn permission(&self) -> Permission {
9481
self.permission
9582
}
@@ -170,7 +157,7 @@ impl LocationState {
170157
}
171158
if self.permission.is_frozen() && access_kind == AccessKind::Read {
172159
// A foreign read to a `Frozen` tag will have almost no observable effect.
173-
// It's a theorem that `Frozen` nodes have no active children, so all children
160+
// It's a theorem that `Frozen` nodes have no `Unique` children, so all children
174161
// already survive foreign reads. Foreign reads in general have almost no
175162
// effect, the only further thing they could do is make protected `Reserved`
176163
// nodes become conflicted, i.e. make them reject child writes for the further
@@ -265,7 +252,7 @@ pub(super) struct Node {
265252
pub children: SmallVec<[UniIndex; 4]>,
266253
/// Either `Reserved`, `Frozen`, or `Disabled`, it is the permission this tag will
267254
/// lazily be initialized to on the first access.
268-
/// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by
255+
/// It is only ever `Disabled` for a tree root, since the root is initialized to `Unique` by
269256
/// its own separate mechanism.
270257
default_initial_perm: Permission,
271258
/// The default initial (strongest) idempotent foreign access.
@@ -598,14 +585,14 @@ impl Tree {
598585
};
599586
let rperms = {
600587
let mut perms = UniValMap::default();
601-
// We manually set it to `Active` on all in-bounds positions.
602-
// We also ensure that it is accessed, so that no `Active` but
588+
// We manually set it to `Unique` on all in-bounds positions.
589+
// We also ensure that it is accessed, so that no `Unique` but
603590
// not yet accessed nodes exist. Essentially, we pretend there
604-
// was a write that initialized these to `Active`.
591+
// was a write that initialized these to `Unique`.
605592
perms.insert(
606593
root_idx,
607594
LocationState::new_accessed(
608-
Permission::new_active(),
595+
Permission::new_unique(),
609596
IdempotentForeignAccess::None,
610597
),
611598
);
@@ -618,67 +605,62 @@ impl Tree {
618605
impl<'tcx> Tree {
619606
/// Insert a new tag in the tree.
620607
///
621-
/// `initial_perms` defines the initial permissions for the part of memory
622-
/// that is already considered "initialized" immediately. The ranges in this
623-
/// map are relative to `base_offset`.
624-
/// `default_perm` defines the initial permission for the rest of the allocation.
625-
///
626-
/// For all non-accessed locations in the RangeMap (those that haven't had an
627-
/// implicit read), their SIFA must be weaker than or as weak as the SIFA of
628-
/// `default_perm`.
608+
/// `inside_perm` defines the initial permissions for a block of memory starting at
609+
/// `base_offset`. These may nor may not be already marked as "accessed".
610+
/// `outside_perm` defines the initial permission for the rest of the allocation.
611+
/// These are definitely not "accessed".
629612
pub(super) fn new_child(
630613
&mut self,
631614
base_offset: Size,
632615
parent_tag: BorTag,
633616
new_tag: BorTag,
634-
initial_perms: DedupRangeMap<LocationState>,
635-
default_perm: Permission,
617+
inside_perms: DedupRangeMap<LocationState>,
618+
outside_perm: Permission,
636619
protected: bool,
637620
span: Span,
638621
) -> InterpResult<'tcx> {
639622
let idx = self.tag_mapping.insert(new_tag);
640623
let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();
641-
assert!(default_perm.is_initial());
624+
assert!(outside_perm.is_initial());
642625

643626
let default_strongest_idempotent =
644-
default_perm.strongest_idempotent_foreign_access(protected);
627+
outside_perm.strongest_idempotent_foreign_access(protected);
645628
// Create the node
646629
self.nodes.insert(
647630
idx,
648631
Node {
649632
tag: new_tag,
650633
parent: Some(parent_idx),
651634
children: SmallVec::default(),
652-
default_initial_perm: default_perm,
635+
default_initial_perm: outside_perm,
653636
default_initial_idempotent_foreign_access: default_strongest_idempotent,
654-
debug_info: NodeDebugInfo::new(new_tag, default_perm, span),
637+
debug_info: NodeDebugInfo::new(new_tag, outside_perm, span),
655638
},
656639
);
657640
// Register new_tag as a child of parent_tag
658641
self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
659642

643+
// We need to know the biggest SIFA for `update_last_accessed_after_retag` below.
644+
let mut max_sifa = default_strongest_idempotent;
660645
for (Range { start, end }, &perm) in
661-
initial_perms.iter(Size::from_bytes(0), initial_perms.size())
646+
inside_perms.iter(Size::from_bytes(0), inside_perms.size())
662647
{
663-
assert!(perm.is_initial());
648+
assert!(perm.permission.is_initial());
649+
max_sifa = cmp::max(max_sifa, perm.idempotent_foreign_access);
664650
for (_perms_range, perms) in self
665651
.rperms
666652
.iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start))
667653
{
668-
assert!(
669-
default_strongest_idempotent
670-
>= perm.permission.strongest_idempotent_foreign_access(protected)
671-
);
672654
perms.insert(idx, perm);
673655
}
674656
}
675657

676-
// Inserting the new perms might have broken the SIFA invariant (see `foreign_access_skipping.rs`).
677-
// We now weaken the recorded SIFA for our parents, until the invariant is restored.
678-
// We could weaken them all to `LocalAccess`, but it is more efficient to compute the SIFA
679-
// for the new permission statically, and use that.
680-
// See the comment in `tb_reborrow` for why it is correct to use the SIFA of `default_uninit_perm`.
681-
self.update_last_accessed_after_retag(parent_idx, default_strongest_idempotent);
658+
// Inserting the new perms might have broken the SIFA invariant (see
659+
// `foreign_access_skipping.rs`). We now weaken the recorded SIFA for our parents, until the
660+
// invariant is restored. We could weaken them all to `LocalAccess`, but it is more
661+
// efficient to compute the SIFA for the new permission statically, and use that. For this
662+
// we need the *maximum* SIFA (`Write` needs more fixup than `None`).
663+
self.update_last_accessed_after_retag(parent_idx, max_sifa);
682664

683665
interp_ok(())
684666
}
@@ -755,9 +737,9 @@ impl<'tcx> Tree {
755737
== Some(&ProtectorKind::StrongProtector)
756738
// Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).
757739
// Related to https://github.com/rust-lang/rust/issues/55005.
758-
&& !perm.permission().is_cell()
740+
&& !perm.permission.is_cell()
759741
// Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579.
760-
&& perm.is_accessed()
742+
&& perm.accessed
761743
{
762744
Err(TransitionError::ProtectedDealloc)
763745
} else {
@@ -790,7 +772,7 @@ impl<'tcx> Tree {
790772
/// - the access will be applied only to accessed locations of the allocation,
791773
/// - it will not be visible to children,
792774
/// - it will be recorded as a `FnExit` diagnostic access
793-
/// - and it will be a read except if the location is `Active`, i.e. has been written to,
775+
/// - and it will be a read except if the location is `Unique`, i.e. has been written to,
794776
/// in which case it will be a write.
795777
///
796778
/// `LocationState::perform_access` will take care of raising transition

src/borrow_tracker/tree_borrows/tree/tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ fn tree_compacting_is_sound() {
106106
as_foreign_or_child(rel),
107107
kind,
108108
parent.permission(),
109-
as_lazy_or_accessed(child.is_accessed()),
109+
as_lazy_or_accessed(child.accessed()),
110110
child.permission(),
111111
as_protected(child_protected),
112112
np.permission(),
@@ -122,7 +122,7 @@ fn tree_compacting_is_sound() {
122122
as_foreign_or_child(rel),
123123
kind,
124124
parent.permission(),
125-
as_lazy_or_accessed(child.is_accessed()),
125+
as_lazy_or_accessed(child.accessed()),
126126
child.permission(),
127127
as_protected(child_protected),
128128
nc.permission()
@@ -375,7 +375,7 @@ mod spurious_read {
375375

376376
impl LocStateProt {
377377
fn is_initial(&self) -> bool {
378-
self.state.is_initial()
378+
self.state.permission().is_initial()
379379
}
380380

381381
fn perform_access(&self, kind: AccessKind, rel: AccessRelatedness) -> Result<Self, ()> {
@@ -420,7 +420,7 @@ mod spurious_read {
420420
/// `(LocStateProt, LocStateProt)` where the two states are not guaranteed
421421
/// to be updated at the same time.
422422
/// Some `LocStateProtPair` may be unreachable through normal means
423-
/// such as `x: Active, y: Active` in the case of mutually foreign pointers.
423+
/// such as `x: Unique, y: Unique` in the case of mutually foreign pointers.
424424
struct LocStateProtPair {
425425
xy_rel: RelPosXY,
426426
x: LocStateProt,
@@ -709,7 +709,7 @@ mod spurious_read {
709709
let mut err = 0;
710710
for pat in Pattern::exhaustive() {
711711
let Ok(initial_source) = pat.initial_state() else {
712-
// Failed to retag `x` in the source (e.g. `y` was protected Active)
712+
// Failed to retag `x` in the source (e.g. `y` was protected Unique)
713713
continue;
714714
};
715715
// `x` must stay protected, but the function protecting `y` might return here

tests/fail/async-shared-mutable.tree.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ LL | | Poll::<()>::Pending
1616
LL | | })
1717
LL | | .await
1818
| |______________^
19-
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [OFFSET]
19+
help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [OFFSET]
2020
--> tests/fail/async-shared-mutable.rs:LL:CC
2121
|
2222
LL | *x = 1;

tests/fail/both_borrows/box_noalias_violation.tree.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | *y
77
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
88
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
99
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
10-
= help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
10+
= help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
1111
= help: protected tags must never be Disabled
1212
help: the accessed tag <TAG> was created here
1313
--> 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
1919
|
2020
LL | unsafe fn test(mut x: Box<i32>, y: *const i32) -> i32 {
2121
| ^^^^^
22-
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
22+
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
2323
--> tests/fail/both_borrows/box_noalias_violation.rs:LL:CC
2424
|
2525
LL | *x = 5;

tests/fail/both_borrows/illegal_write6.tree.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | unsafe { *y = 2 };
77
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
88
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
99
= help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
10-
= help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
10+
= help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
1111
= help: protected tags must never be Disabled
1212
help: the accessed tag <TAG> was created here
1313
--> 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
1919
|
2020
LL | fn foo(a: &mut u32, y: *mut u32) -> u32 {
2121
| ^
22-
help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
22+
help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
2323
--> tests/fail/both_borrows/illegal_write6.rs:LL:CC
2424
|
2525
LL | *a = 1;

0 commit comments

Comments
 (0)