Skip to content

Commit 575e901

Browse files
author
Xinglu Chen
committed
"initialized" to "accessed" rename; refactor of new_child
1 parent f224deb commit 575e901

File tree

6 files changed

+99
-85
lines changed

6 files changed

+99
-85
lines changed

src/borrow_tracker/tree_borrows/diagnostics.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ impl DisplayFmt {
504504
if let Some(perm) = perm {
505505
format!(
506506
"{ac}{st}",
507-
ac = if perm.is_initialized() { self.accessed.yes } else { self.accessed.no },
507+
ac = if perm.is_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: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use rustc_middle::mir::{Mutability, RetagKind};
33
use rustc_middle::ty::layout::HasTypingEnv;
44
use rustc_middle::ty::{self, Ty};
55

6+
use self::tree::LocationState;
67
use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind};
78
use crate::concurrency::data_race::NaReadType;
89
use crate::*;
@@ -16,6 +17,7 @@ mod unimap;
1617
#[cfg(test)]
1718
mod exhaustive;
1819

20+
use self::foreign_access_skipping::IdempotentForeignAccess;
1921
use self::perms::Permission;
2022
pub use self::tree::Tree;
2123

@@ -95,7 +97,7 @@ impl<'tcx> Tree {
9597
/// A tag just lost its protector.
9698
///
9799
/// This emits a special kind of access that is only applied
98-
/// to initialized locations, as a protection against other
100+
/// to accessed locations, as a protection against other
99101
/// tags not having been made aware of the existence of this
100102
/// protector.
101103
pub fn release_protector(
@@ -314,29 +316,31 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
314316
let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
315317

316318
// Store initial permissions and their corresponding range.
317-
let mut perms_map: RangeMap<Permission> = RangeMap::new(
319+
let mut perms_map: RangeMap<LocationState> = RangeMap::new(
318320
ptr_size,
319-
Permission::new_disabled(), // this will be overwritten
321+
LocationState::new_accessed(Permission::new_disabled(), IdempotentForeignAccess::None), // this will be overwritten
320322
);
321323
// Keep track of whether the node has any part that allows for interior mutability.
322324
// FIXME: This misses `PhantomData<UnsafeCell<T>>` which could be considered a marker
323325
// for requesting interior mutability.
324326
let mut has_unsafe_cell = false;
325327

326-
// We should always do a read access to the `freeze` parts of the reference.
327-
// This is important for properly updating the SIFA later:
328-
// * If we do have an `UnsafeCell` (`has_unsafe_cell` becomes true), then
329-
// + some parts inside the initial range of the reference have `nonfreeze_perm`
330-
// + the "dynamic" area outside the initial range of the reference also have `nonfreeze_perm`
331-
// and thus it is sufficient to reset the strongest foreign access of all parents to the
332-
// `nonfreeze_perm` SIFA, because all the parts where we put `freeze_perm` already had a read
333-
// access happen, and thus the SFAs there are already reset. The assert below ensures that this
334-
// read access to the `freeze_perm` happens.
328+
// When adding a new node, the SIFA of its parents needs to be updated, potentially across
329+
// the entire memory range. For the parts that are being accessed below, the access itself
330+
// trivially takes care of that. However, we have to do some more work to also deal with
331+
// the parts that are not being accessed. Specifically what we do is that we
332+
// call `update_last_accessed_after_retag` on the SIFA of the permission set for the non-accessed
333+
// part of memory -- so that part is definitely taken care of. The remaining concern is the part of
334+
// memory that is in the accessed range, but not accessed below. There we have two cases:
335+
// * If we do have an `UnsafeCell` (`has_unsafe_cell` becomes true), then the non-accessed part
336+
// uses `nonfreeze_perm`, so the `nonfreeze_perm` initialized parts are also fine. We enforce
337+
// the `freeze_perm` parts to be accessed, and thus everything is taken care of.
335338
// * If there is no `UnsafeCell`, then `freeze_perm` is used everywhere (both inside and outside the initial range),
336339
// and we update everything to have the `freeze_perm`'s SIFA, so there are no issues. (And this assert below is not
337340
// actually needed in this case).
338341
assert!(new_perm.freeze_access);
339342

343+
let protected = new_perm.protector.is_some();
340344
this.visit_freeze_sensitive(place, ptr_size, |range, frozen| {
341345
has_unsafe_cell = has_unsafe_cell || !frozen;
342346

@@ -348,8 +352,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
348352
};
349353

350354
// Store initial permissions.
351-
for (_perm_range, perm) in perms_map.iter_mut(range.start, range.size) {
352-
*perm = perm_init;
355+
for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) {
356+
let sifa = perm_init.strongest_idempotent_foreign_access(protected);
357+
if access {
358+
*loc = LocationState::new_accessed(perm_init, sifa);
359+
} else {
360+
*loc = LocationState::new_non_accessed(perm_init, sifa);
361+
}
353362
}
354363

355364
// Some reborrows incur a read access to the parent.
@@ -390,7 +399,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
390399
perms_map,
391400
// Allow lazily writing to surrounding data if we found an `UnsafeCell`.
392401
if has_unsafe_cell { new_perm.nonfreeze_perm } else { new_perm.freeze_perm },
393-
new_perm.protector.is_some(),
402+
protected,
394403
span,
395404
)?;
396405
drop(tree_borrows);

src/borrow_tracker/tree_borrows/tree.rs

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@ mod tests;
3333
/// Data for a single *location*.
3434
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3535
pub(super) struct LocationState {
36-
/// A location is initialized when it is child-accessed for the first time (and the initial
36+
/// A location is "accessed" when it is child-accessed for the first time (and the initial
3737
/// retag initializes the location for the range covered by the type), and it then stays
38-
/// initialized forever.
39-
/// For initialized locations, "permission" is the current permission. However, for
40-
/// uninitialized locations, we still need to track the "future initial permission": this will
38+
/// accessed forever.
39+
/// For accessed locations, "permission" is the current permission. However, for
40+
/// non-accessed locations, we still need to track the "future initial permission": this will
4141
/// start out to be `default_initial_perm`, but foreign accesses need to be taken into account.
4242
/// Crucially however, while transitions to `Disabled` would usually be UB if this location is
43-
/// protected, that is *not* the case for uninitialized locations. Instead we just have a latent
43+
/// protected, that is *not* the case for non-accessed locations. Instead we just have a latent
4444
/// "future initial permission" of `Disabled`, causing UB only if an access is ever actually
4545
/// performed.
46-
/// Note that the tree root is also always initialized, as if the allocation was a write access.
47-
initialized: bool,
46+
/// Note that the tree root is also always accessed, as if the allocation was a write access.
47+
accessed: bool,
4848
/// This pointer's current permission / future initial permission.
4949
permission: Permission,
5050
/// See `foreign_access_skipping.rs`.
@@ -59,30 +59,30 @@ impl LocationState {
5959
/// to any foreign access yet.
6060
/// The permission is not allowed to be `Active`.
6161
/// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
62-
pub fn new_uninit(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
62+
pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
6363
assert!(permission.is_initial() || permission.is_disabled());
64-
Self { permission, initialized: false, idempotent_foreign_access: sifa }
64+
Self { permission, accessed: false, idempotent_foreign_access: sifa }
6565
}
6666

6767
/// Constructs a new initial state. It has not yet been subjected
6868
/// to any foreign access. However, it is already marked as having been accessed.
6969
/// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
70-
pub fn new_init(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
71-
Self { permission, initialized: true, idempotent_foreign_access: sifa }
70+
pub fn new_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
71+
Self { permission, accessed: true, idempotent_foreign_access: sifa }
7272
}
7373

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

8080
/// Check if the state can exist as the initial permission of a pointer.
8181
///
82-
/// Do not confuse with `is_initialized`, the two are almost orthogonal
83-
/// as apart from `Active` which is not initial and must be initialized,
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,
8484
/// any other permission can have an arbitrary combination of being
85-
/// initial/initialized.
85+
/// initial/accessed.
8686
/// FIXME: when the corresponding `assert` in `tree_borrows/mod.rs` finally
8787
/// passes and can be uncommented, remove this `#[allow(dead_code)]`.
8888
#[cfg_attr(not(test), allow(dead_code))]
@@ -96,8 +96,8 @@ impl LocationState {
9696

9797
/// Apply the effect of an access to one location, including
9898
/// - applying `Permission::perform_access` to the inner `Permission`,
99-
/// - emitting protector UB if the location is initialized,
100-
/// - updating the initialized status (child accesses produce initialized locations).
99+
/// - emitting protector UB if the location is accessed,
100+
/// - updating the accessed status (child accesses produce accessed locations).
101101
fn perform_access(
102102
&mut self,
103103
access_kind: AccessKind,
@@ -107,23 +107,23 @@ impl LocationState {
107107
let old_perm = self.permission;
108108
let transition = Permission::perform_access(access_kind, rel_pos, old_perm, protected)
109109
.ok_or(TransitionError::ChildAccessForbidden(old_perm))?;
110-
self.initialized |= !rel_pos.is_foreign();
110+
self.accessed |= !rel_pos.is_foreign();
111111
self.permission = transition.applied(old_perm).unwrap();
112-
// Why do only initialized locations cause protector errors?
112+
// Why do only accessed locations cause protector errors?
113113
// Consider two mutable references `x`, `y` into disjoint parts of
114114
// the same allocation. A priori, these may actually both be used to
115115
// access the entire allocation, as long as only reads occur. However,
116116
// a write to `y` needs to somehow record that `x` can no longer be used
117-
// on that location at all. For these uninitialized locations (i.e., locations
117+
// on that location at all. For these non-accessed locations (i.e., locations
118118
// that haven't been accessed with `x` yet), we track the "future initial state":
119119
// it defaults to whatever the initial state of the tag is,
120120
// but the access to `y` moves that "future initial state" of `x` to `Disabled`.
121121
// However, usually a `Reserved -> Disabled` transition would be UB due to the protector!
122122
// So clearly protectors shouldn't fire for such "future initial state" transitions.
123123
//
124124
// See the test `two_mut_protected_same_alloc` in `tests/pass/tree_borrows/tree-borrows.rs`
125-
// for an example of safe code that would be UB if we forgot to check `self.initialized`.
126-
if protected && self.initialized && transition.produces_disabled() {
125+
// for an example of safe code that would be UB if we forgot to check `self.accessed`.
126+
if protected && self.accessed && transition.produces_disabled() {
127127
return Err(TransitionError::ProtectedDisabled(old_perm));
128128
}
129129
Ok(transition)
@@ -158,11 +158,11 @@ impl LocationState {
158158
self.idempotent_foreign_access.can_skip_foreign_access(happening_now);
159159
if self.permission.is_disabled() {
160160
// A foreign access to a `Disabled` tag will have almost no observable effect.
161-
// It's a theorem that `Disabled` node have no protected initialized children,
161+
// It's a theorem that `Disabled` node have no protected accessed children,
162162
// and so this foreign access will never trigger any protector.
163-
// (Intuition: You're either protected initialized, and thus can't become Disabled
164-
// or you're already Disabled protected, but not initialized, and then can't
165-
// become initialized since that requires a child access, which Disabled blocks.)
163+
// (Intuition: You're either protected accessed, and thus can't become Disabled
164+
// or you're already Disabled protected, but not accessed, and then can't
165+
// become accessed since that requires a child access, which Disabled blocks.)
166166
// Further, the children will never be able to read or write again, since they
167167
// have a `Disabled` parent. So this only affects diagnostics, such that the
168168
// blocking write will still be identified directly, just at a different tag.
@@ -218,7 +218,7 @@ impl LocationState {
218218
impl fmt::Display for LocationState {
219219
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220220
write!(f, "{}", self.permission)?;
221-
if !self.initialized {
221+
if !self.accessed {
222222
write!(f, "?")?;
223223
}
224224
Ok(())
@@ -599,12 +599,15 @@ impl Tree {
599599
let rperms = {
600600
let mut perms = UniValMap::default();
601601
// We manually set it to `Active` on all in-bounds positions.
602-
// We also ensure that it is initialized, so that no `Active` but
603-
// not yet initialized nodes exist. Essentially, we pretend there
602+
// We also ensure that it is accessed, so that no `Active` but
603+
// not yet accessed nodes exist. Essentially, we pretend there
604604
// was a write that initialized these to `Active`.
605605
perms.insert(
606606
root_idx,
607-
LocationState::new_init(Permission::new_active(), IdempotentForeignAccess::None),
607+
LocationState::new_accessed(
608+
Permission::new_active(),
609+
IdempotentForeignAccess::None,
610+
),
608611
);
609612
RangeMap::new(size, perms)
610613
};
@@ -619,12 +622,16 @@ impl<'tcx> Tree {
619622
/// that is already considered "initialized" immediately. The ranges in this
620623
/// map are relative to `base_offset`.
621624
/// `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_uninit_perm`.
622629
pub(super) fn new_child(
623630
&mut self,
624631
base_offset: Size,
625632
parent_tag: BorTag,
626633
new_tag: BorTag,
627-
init_perms: RangeMap<Permission>,
634+
init_perms: RangeMap<LocationState>,
628635
default_uninit_perm: Permission,
629636
protected: bool,
630637
span: Span,
@@ -657,13 +664,11 @@ impl<'tcx> Tree {
657664
.rperms
658665
.iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start))
659666
{
660-
perms.insert(
661-
idx,
662-
LocationState::new_init(
663-
perm,
664-
perm.strongest_idempotent_foreign_access(protected),
665-
),
667+
assert!(
668+
default_strongest_idempotent
669+
>= perm.permission.strongest_idempotent_foreign_access(protected)
666670
);
671+
perms.insert(idx, perm);
667672
}
668673
}
669674

@@ -779,14 +784,14 @@ impl<'tcx> Tree {
779784
///
780785
/// If `access_range_and_kind` is `None`, this is interpreted as the special
781786
/// access that is applied on protector release:
782-
/// - the access will be applied only to initialized locations of the allocation,
787+
/// - the access will be applied only to accessed locations of the allocation,
783788
/// - it will not be visible to children,
784789
/// - it will be recorded as a `FnExit` diagnostic access
785790
/// - and it will be a read except if the location is `Active`, i.e. has been written to,
786791
/// in which case it will be a write.
787792
///
788793
/// `LocationState::perform_access` will take care of raising transition
789-
/// errors and updating the `initialized` status of each location,
794+
/// errors and updating the `accessed` status of each location,
790795
/// this traversal adds to that:
791796
/// - inserting into the map locations that do not exist yet,
792797
/// - trimming the traversal,
@@ -879,7 +884,7 @@ impl<'tcx> Tree {
879884
}
880885
} else {
881886
// This is a special access through the entire allocation.
882-
// It actually only affects `initialized` locations, so we need
887+
// It actually only affects `accessed` locations, so we need
883888
// to filter on those before initiating the traversal.
884889
//
885890
// In addition this implicit access should not be visible to children,
@@ -889,10 +894,10 @@ impl<'tcx> Tree {
889894
// why this is important.
890895
for (perms_range, perms) in self.rperms.iter_mut_all() {
891896
let idx = self.tag_mapping.get(&tag).unwrap();
892-
// Only visit initialized permissions
897+
// Only visit accessed permissions
893898
if let Some(p) = perms.get(idx)
894899
&& let Some(access_kind) = p.permission.protector_end_access()
895-
&& p.initialized
900+
&& p.accessed
896901
{
897902
let access_cause = diagnostics::AccessCause::FnExit(access_kind);
898903
TreeVisitor { nodes: &mut self.nodes, tag_mapping: &self.tag_mapping, perms }
@@ -1056,7 +1061,7 @@ impl Tree {
10561061

10571062
impl Node {
10581063
pub fn default_location_state(&self) -> LocationState {
1059-
LocationState::new_uninit(
1064+
LocationState::new_non_accessed(
10601065
self.default_initial_perm,
10611066
self.default_initial_idempotent_foreign_access,
10621067
)

0 commit comments

Comments
 (0)