Skip to content

Conversation

@royAmmerschuber
Copy link
Contributor

initial support for wildcard writes in tree borrows.

@rustbot
Copy link
Collaborator

rustbot commented Oct 13, 2025

Thank you for contributing to Miri!
Please remember to not force-push to the PR branch except when you need to rebase due to a conflict or when the reviewer asks you for it.

@rustbot rustbot added the S-waiting-on-review Status: Waiting for a review to complete label Oct 13, 2025
Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a first round of comments. I didn't get to the wildcard.rs file yet. Also you did some really deep surgery in this code, much deeper than I expected, so we may have to discuss this in person because this is code I didn't write and therefore don't remember all that much about how it works.

Some general points that I didn't comment on everywhere, but that should be fixed everywhere:

  • Please write complete, correctly capitalized and punctuated sentences.
  • Please leave empty lines between functions and between types (except when you define a group of types that should be seen as a single unit).

View changes since this review

// The body of this loop needs `borrow_tracker` immutably
// so we can't move this code inside the following `end_call`.

// TODO support protected wildcard pointers
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unclear what "support" means.

machine: &MiriMachine<'tcx>,
) -> InterpResult<'tcx> {
// TODO: for now we bail out on wildcard pointers. Eventually we should
// handle them as much as we can.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment seems outdated now?

let orig_tag = match parent_prov {
ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle wildcard pointers
ProvenanceExtra::Concrete(tag) => tag,
let (orig_tag, provenance) = match parent_prov {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So orig_tag is exactly the same as parent_prov except you converted it from ProvenanceExtra to Option...? What's the point of that?

Also, provenance is way to imprecise as a name. There's so many provenances floating around here, this isn't useful. I'd suggest new_prov.

pub fn dealloc(
&mut self,
tag: BorTag,
tag: Option<BorTag>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's odd that dealloc changes its signature like this but perform_access does not.

Comment on lines 784 to 807
for (perms_range, Location { perms, .. }) in
self.rperms.iter_mut(access_range.start, access_range.size)
{
let tag = if let Some(tag) = tag {
tag
} else {
// the order in which we check if any nodes are invalidated doesnt matter,
// so we use the root as a default tag
self.nodes.get(self.root).unwrap().tag
};
TreeVisitor {
nodes: &mut self.nodes,
tag_mapping: &self.tag_mapping,
perms,
wildcard_accesses: None,
}
.traverse_this_parents_children_other(
tag,
// visit all children, skipping none
|_| ContinueTraversal::Recurse,
|args: NodeAppArgs<'_>| -> Result<(), TransitionError> {
let NodeAppArgs { node, perm, .. } = args;
let perm = perm.get().copied().unwrap_or_else(|| node.default_location_state());
if global.borrow().protected_tags.get(&node.tag)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like you entirely changed the old logic here... what is happening?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes are mostly whitespace.
Only the let tag = ... on line 787 and wildcard_accesses: None, on line 798 are actual changes.

Comment on lines 1044 to 1046
if node.is_exposed {
return None;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this make sense? This is definitely non-trivial, therefore needs a comment.

/// methods for wildcard borrows
impl<'tcx> Tree {
/// analogous to `perform_access`, but we do not know from which exposed reference the access happens.
pub fn perform_wildcard_access(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be in wildcard.rs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in tree.rs mostly because it accesses private functions of LocationState (perform_access, skip_if_known_noop, record_new_access)

Comment on lines 1333 to 1336
/// We do not know the accessed pointer, but we know that it is a child of this pointer
WildcardChildAccess,
/// We do not know the accessed pointer, but we know that it is foreign to this pointer
WildcardForeignAccess,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it make sense to have these as new variants in this type, rather than having a separate type for the wildcard traversal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This way I can reuse the code of perms.rs and LocationState::perform_access directly, which both take a AccessRelatedness as an argument. Also, no code seems to rely on the concrete value of AccessRelatedness. Only if its foreign or child access.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So could this enum be simplified to just Local vs Foreign?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be part of the traversal adjustment preparation PR.

// Keep original provenance.
return interp_ok(place.ptr().provenance);
}
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a ptr_try_get_alloc_id above (which github doesn't let me add a comment to -- thanks github) which is subtle and I think not entirely correct. For a wildcard pointer, this will resolve the pointer to an AllocId if it can. If ptr_size is 0, however, that might not be the only legal AllocId!

If the size is 0 on a wildcard pointer I think we have to bail early, there's not much we can do.

/// with possible lazy initialization.
///
/// NOTE: same guarantees on entry initialisation as for `perms`
pub wildcard_accesses: UniValMap<WildcardAccessTracking>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a more extensive comment for why it is a separate map.

/// childrens max_child_access/max_foreign_access
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct WildcardAccessTracking {
/// how many of this nodes direct children have `max_child_access==Write`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// how many of this nodes direct children have `max_child_access==Write`
/// how many of this node's direct children have `max_child_access==Write`

/// were relative to the pointer the access happened from
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WildcardAccessRelatedness {
/// the access definitively happened through a child pointer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"pointer" is confusing here, do you mean "node"?

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum WildcardAccessRelatedness {
/// the access definitively happened through a child pointer
ChildAccess,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you use "child" to mean the reflexive transitive closure, please say "local".

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please factor the changes to the visitor that give the callbacks access to the entire tree into a separate commit/PR. We also don't need two layers of visitors, just adjust the existing NodeAppArgs to have the extra state.

AccessKind::Write => self.write_access_relatedness(exposed_as),
}
}
/// from where relative to this pointer a read access could happen
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"this pointer"/"this node" is confusing here since you don't actually identify a node here.
What you mean is "a node with this wildcard info" or so?

/// this function calculates the siblings `max_child_access`, both of the other fields need to be passed as arguments
///
/// * `other_factors`: we only ever change one of these values. The max value of the other fields we dont change should be passed through the `other_factors` parameter
/// * `old_access_type`,`access_type`: we change the parameter not covered by `other_factors` from `old_access_type`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"we change" seems to mean "the caller changed/changes" or so? It sounds like this function is doing the changing.

/// pushes children onto the stack, if their `max_foreign_access` field needs to be updated
///
/// the `max_foreign_access` fields is set based on the max of the parents `max_foreign_access`,
/// `exposed_as` and its siblings `max_child_access`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 of these aren't fields though...

perms: &UniValMap<LocationState>,
wildcard_accesses: &mut UniValMap<WildcardAccessTracking>,
) {
/// pushes children onto the stack, if their `max_foreign_access` field needs to be updated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a much more extensive comment:

  • on some node (say which!), one of these "fields" (properties? they aren't all fields) changed, from old_access_type to new_access type I think?
  • the other two stay the same, and are stored in other_factors
  • but then there's some more complicated stuff for siblings?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if there's three modes to call this function (depending on what changed), every caller needs to document which mode it is using and why the special requirements for that mode are upheld.

But somehow the function doesn't even know what the mode is so... is there some more uniform way to do this? Can the function be called with just the one info on how the foreign access mode that can come in via the parent edge changed?

access_type: WildcardAccessLevel,
old_access_type: WildcardAccessLevel,
access: WildcardAccessTracking,
mut children: impl Iterator<Item = UniIndex>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess all these have a common parent? Is it all children of that parent?
Is that parent the node that the main comment refers to, whose properties changed?

/* other factors */ src_access.max_foreign_access,
access_type,
old_access_type,
src_access.clone(),
Copy link
Member

@RalfJung RalfJung Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just pass a reference instead of cloning.

EDIT: Or maybe not, turns out that is not always the parent node info...

other_factors: WildcardAccessLevel,
access_type: WildcardAccessLevel,
old_access_type: WildcardAccessLevel,
access: WildcardAccessTracking,
Copy link
Member

@RalfJung RalfJung Oct 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should say what this is. Also maybe call it something involving parent in its name?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, just replace the entire thing by the read and write counts.

// Read -> Write
// Write -> Read
// Write -> None
access.child_writes
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it okay to ignore child_reads here, given that the read permissions can also change?

other_factors: WildcardAccessLevel,
access_type: WildcardAccessLevel,
old_access_type: WildcardAccessLevel,
access: WildcardAccessTracking,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, just replace the entire thing by the read and write counts.

Comment on lines 127 to 128
old_access_type: WildcardAccessLevel,
access_type: WildcardAccessLevel,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe call these old_exposed_as and new_exposed_as?

// dont change (for parents child_permissions and for the other children foreign permissions)
{
// we need to keep track of how the previous permissions changed
let mut prev_old_access = old_access_type;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

child_old_access? Also what exactly is the invariant on this field? It's instantiated based on an exposed_as but gets updated with max_child?

{
// we need to keep track of how the previous permissions changed
let mut prev_old_access = old_access_type;
let mut prev = id;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

child (the one we come from in our upwards traversal)?

Comment on lines 1333 to 1336
/// We do not know the accessed pointer, but we know that it is a child of this pointer
WildcardChildAccess,
/// We do not know the accessed pointer, but we know that it is foreign to this pointer
WildcardForeignAccess,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also be part of the traversal adjustment preparation PR.

// is defined by this child. So we only need to update this one child
stack.push((
children
.find(|id| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should give a unique node, right?

I'd say make this filter, call next once to get the node, then call it again to test that it is unique.

) {
// find root node
let mut root = id;
while let Some(parent) = nodes.get(root).and_then(|n| n.parent) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
while let Some(parent) = nodes.get(root).and_then(|n| n.parent) {
while let Some(parent) = nodes.get(root).unwrap().parent {

.map(|child| {
let node = nodes.get(child).unwrap();
let perm = perms.get(child).map(LocationState::permission);
let access = wildcard_accesses.get(child).unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This uses the data we are checking (wildcard_accesses) to check said data... doesn't that risk that we miss some inconsistencies?

For instance, what if we have a tree where no node is exposed, but there's a node somewhere that says "one of my children is exposed" and there's exactly one child that says "my parent is exposed"? Would we catch that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should probably rename max_other_children to max_access_siblings to make this code clearer.

|
A
| \
B  C

So in the example A has child_reads=1 and B has max_foreign_access=Read (everything else 0)?

This will fail, because B and C both have max_child_access()==None, therefore A should be child_reads=0.

It would also fail because for B the parents max_foreign_access, exposed_as and its siblings (C) max_child_access() are all None, meaning B's max_foreign_access should also be None

The invariants verify_consistency checks should be described in the WildcardAccessTracking struct definition.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will fail, because B and C both have max_child_access()==None, therefore A should be child_reads=0.

It would also fail because for B the parents max_foreign_access, exposed_as and its siblings (C) max_child_access() are all None, meaning B's max_foreign_access should also be None

Sounds good then, thanks!

@RalfJung
Copy link
Member

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: Waiting for the PR author to address review comments and removed S-waiting-on-review Status: Waiting for a review to complete labels Oct 27, 2025
@rustbot
Copy link
Collaborator

rustbot commented Oct 27, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

// What kind of access caused this error (read, write, reborrow, deallocation)
pub access_cause: AccessCause,
/// Which tag the access that caused this error was made through, i.e.
/// Which tag if any the access that caused this error was made through, i.e.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be something like (if any) or , if any,.

@royAmmerschuber
Copy link
Contributor Author

#4654 contains the updates to the TreeVisitor and AccessRelatedness

@royAmmerschuber
Copy link
Contributor Author

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Waiting for a review to complete and removed S-waiting-on-author Status: Waiting for the PR author to address review comments labels Oct 29, 2025
Comment on lines +16 to +26
/// Represensts the maximum access level that is possible.
///
/// Note that we derive Ord and PartialOrd, so the order in which variants are listed below matters:
/// None < Read < Write. Do not change that order.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub enum WildcardAccessLevel {
#[default]
None,
Read,
Write,
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There exists an identical enum in foreign_access_skipping.rs called IdempotentForeignAccess.
I'm unsure if they should be combined into one single AccessLevel type.
Or maybe just rename WildcardAccessLevel to MaxAccessLevel as it isn't really specific to wildcard accesses.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These enums serve different purposes, I think it's fine to have them be separate.

Write,
}

/// Were relative to the node the access happened from.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Were relative to the node the access happened from.
/// Where the access happened relative to the current node.

/// that is foreign to this node.
///
/// This is calculated as the `max()` of parents `max_foreign_access`,
/// `exposed_as` and its siblings `max_local_access()`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// `exposed_as` and its siblings `max_local_access()`.
/// `exposed_as` and the siblings' `max_local_access()`.

/// The maximum access level that could happen from an exposed node
/// that is foreign to this node.
///
/// This is calculated as the `max()` of parents `max_foreign_access`,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// This is calculated as the `max()` of parents `max_foreign_access`,
/// This is calculated as the `max()` of parent's `max_foreign_access`,

Comment on lines 78 to 79
/// The maximum access level that could happen from an exposed
/// node that is a local to this node.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The maximum access level that could happen from an exposed
/// node that is a local to this node.
/// The maximum access level that could happen from an exposed
/// node that is local to this node.

Comment on lines 430 to 431
// If the node is already protected, then it gets already visited
// through the protector. So we can assume `protected=false`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean to get "visited through the protector"?

// This happens because the protected_tags map still contains all protected_tags, they only get removed after
// `release_protector` got called on all the relevant tags.
//
// This is also why we dont call `WildcardState::verify_external_consistency` here.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there some other place we call it instead? Like after the protected_tags map got updated?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we do call if after the map got updated. Will add this to the comment

impl Tree {
/// Checks that the wildcard tracking datastructure is internally consistent and has
/// the correct `exposed_as` values.
pub fn verify_wildcard_consistency(&self, global: &GlobalState) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we sometimes call this, and sometimes only run one of the two checks?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a convenience function, that checks all ranges of the allocation.
If we are checking consistency inside a tree traversal after updating some node, we only really need to check the consistency of the current range, as the other ranges remain the same, so this function would do unnecessary work (and have problems with the borrow checker).

As for only sometimes calling one of the functions:
The verify_internal_consistency function gets always run at the end of update_exposure as the invariants it checks always have to be upheld, for update_exposure to continue to work.

The verify_external_consistency functions invariants only actually needs to be upheld during perform_wildcard_access. Specifically it is not upheld while releasing protectors, which is why we only do the internal check (as part of update_exposure), while the external check gets only run after the tags got removed from the protected map.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep it simple. This is a debug check. There shouldn't be any complicated reasoning involved in figuring out when to invoke which sub-check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say make this a single check for the entire invariant, and call it at the top of perform_wildcard_access (which is the key place where we need the invariant).

@RalfJung
Copy link
Member

RalfJung commented Nov 2, 2025

@rustbot author

@rustbot rustbot added the S-waiting-on-author Status: Waiting for the PR author to address review comments label Nov 2, 2025

/// Weather the datastructure has been allocated.
/// Does not mean the map contains any values.
pub fn is_initialized(&self) -> bool {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exposes internal state in an odd way -- can't we have an is_empty instead?

@rustbot

This comment has been minimized.

@royAmmerschuber royAmmerschuber force-pushed the pull-request/wildcard-provenance branch from d52c0fc to 43a9898 Compare November 2, 2025 13:44
@rustbot

This comment has been minimized.

basic tests for wildcard provenance

wildcard tracking data structure & expose_tag implementation

basic wildcard accesses

fix compilation errors & first working testcase

comments & protector test

update wildcard tracking when neccessary & fix ui tests

remove either state

move exposed_as to node & use own AccessLevel enum

deallocation through wildcards

wildcard reborrowing

Location struct & comments

test for correctly activating through wildcards & fix updating idempotent foreign access

compelete verify function

use check_nondet helper in a few more places
ptr_size=0 support
remove unneccessary nativelib check
use provenance extra instead of option
unify perform_access
This makes strict consistency requirement between wildcard tracking
datastructure and the rest of the tree looser, giving us more
flexibility in how we update it.
@royAmmerschuber royAmmerschuber force-pushed the pull-request/wildcard-provenance branch from 4feed04 to 9d8bf0d Compare November 2, 2025 17:22
@rustbot
Copy link
Collaborator

rustbot commented Nov 2, 2025

This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

pub fn is_initialized(&self) -> bool {
self.data.capacity() != 0
pub fn is_empty(&self) -> bool {
self.data.len() == 0 || self.data.iter().all(|v| v.is_none())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason to check the len here, the all will do the right thing.

Copy link
Member

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inspiration for more tests:

  • tests/pass/stacked_borrows/int-to-ptr.rs
  • tests/fail/stacked_borrows/exposed_only_ro.rs
  • tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs
  • tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs
  • tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs

View changes since this review

let parent_access =
max(parent_state.exposed_as, parent_state.max_foreign_access);

let sibling_reads =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New logic looks good, but needs a comment.

let sibling_reads =
parent_state.child_reads - if new_child_access >= Read { 1 } else { 0 };
let sibling_writes =
parent_state.child_writes - if new_child_access == Write { 1 } else { 0 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
parent_state.child_writes - if new_child_access == Write { 1 } else { 0 };
parent_state.child_writes - if new_child_access >= Write { 1 } else { 0 };

to make it more symmetric

let node = self.nodes.get(id).unwrap();
let protected_tags = &global.borrow().protected_tags;
let protected = protected_tags.contains_key(&tag);
// TODO: Only initialize neccessary ranges.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment worth keeping? Doesn't seem super useful to me. Better comment on what is happening.

Comment on lines 425 to 428
let mut stack = vec![self.root];
let mut has_exposed = false;
while let Some(id) = stack.pop() {
let node = self.nodes.get(id).unwrap();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need a tree traversal, just walking the list in any order should suffice.

/// Visit all tags through which a wilcard access could happen.
/// Used for GC.
pub(super) fn gc_visit_wildcards(&self, visit: &mut VisitWith<'_>) {
for (_, loc) in self.locations.iter_all() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to make this location-sensitive... just visit all exposed nodes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get the filename here either, we re not doing the proper thing after all...?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just put all the "should be UB but we don't catch it" into one file.

// └────────────┘

// Since ref3 is protected, we know that every write from outside it will be UB.
// This means we know that the access is through ref3, disabling ref2.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// This means we know that the access is through ref3, disabling ref2.
// This means we could know that the access is through ref3, disabling ref2.
// However, we currently don't exploit this.

}

// We currently ignore, if a wildcard pointer has a protector
#[allow(unused_variables)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please avoid that, very easy to accidentally disable a test that way

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's wild2? ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: Waiting for the PR author to address review comments

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants