Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ impl<'tcx> Tree {
let span = machine.current_user_relevant_span();
self.perform_access(
prov,
Some((range, access_kind, diagnostics::AccessCause::Explicit(access_kind))),
range,
access_kind,
diagnostics::AccessCause::Explicit(access_kind),
global,
alloc_id,
span,
Expand Down Expand Up @@ -93,8 +95,7 @@ impl<'tcx> Tree {
alloc_id: AllocId, // diagnostics
) -> InterpResult<'tcx> {
let span = machine.current_user_relevant_span();
// `None` makes it the magic on-protector-end operation
self.perform_access(ProvenanceExtra::Concrete(tag), None, global, alloc_id, span)?;
self.perform_protector_release_access(tag, global, alloc_id, span)?;

self.update_exposure_for_protector_release(tag);

Expand Down Expand Up @@ -343,7 +344,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {

tree_borrows.perform_access(
parent_prov,
Some((range_in_alloc, AccessKind::Read, diagnostics::AccessCause::Reborrow)),
range_in_alloc,
AccessKind::Read,
diagnostics::AccessCause::Reborrow,
this.machine.borrow_tracker.as_ref().unwrap(),
alloc_id,
this.machine.current_user_relevant_span(),
Expand Down
118 changes: 64 additions & 54 deletions src/borrow_tracker/tree_borrows/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,9 @@ impl<'tcx> Tree {
) -> InterpResult<'tcx> {
self.perform_access(
prov,
Some((access_range, AccessKind::Write, diagnostics::AccessCause::Dealloc)),
access_range,
AccessKind::Write,
diagnostics::AccessCause::Dealloc,
global,
alloc_id,
span,
Expand Down Expand Up @@ -903,14 +905,6 @@ impl<'tcx> Tree {
/// to each location of the first component of `access_range_and_kind`,
/// on every tag of the allocation.
///
/// If `access_range_and_kind` is `None`, this is interpreted as the special
/// access that is applied on protector release:
/// - 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 `Unique`, i.e. has been written to,
/// in which case it will be a write.
///
/// `LocationState::perform_access` will take care of raising transition
/// errors and updating the `accessed` status of each location,
/// this traversal adds to that:
Expand All @@ -920,7 +914,9 @@ impl<'tcx> Tree {
pub fn perform_access(
&mut self,
prov: ProvenanceExtra,
access_range_and_kind: Option<(AllocRange, AccessKind, diagnostics::AccessCause)>,
access_range: AllocRange,
access_kind: AccessKind,
access_cause: AccessCause,
Copy link
Member

Choose a reason for hiding this comment

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

access_cause is also just used for diagnostics I assume?

global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
Expand All @@ -934,61 +930,75 @@ impl<'tcx> Tree {
ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),
ProvenanceExtra::Wildcard => None,
};
if let Some((access_range, access_kind, access_cause)) = access_range_and_kind {
// Default branch: this is a "normal" access through a known range.
// We iterate over affected locations and traverse the tree for each of them.
for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
// We iterate over affected locations and traverse the tree for each of them.
for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
loc.perform_access(
self.roots.iter().copied(),
&mut self.nodes,
source_idx,
loc_range,
Some(access_range),
access_kind,
access_cause,
global,
alloc_id,
span,
ChildrenVisitMode::VisitChildrenOfAccessed,
)?;
}
interp_ok(())
}
/// this is the special access that is applied on protector release:
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 the special access that is applied on protector release:
/// This is the special access that is applied on protector release:

/// - 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 `Unique`, i.e. has been written to,
/// in which case it will be a write.
/// - otherwise identical to `Tree::perform_access`
pub fn perform_protector_release_access(
Copy link
Member

Choose a reason for hiding this comment

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

I think we more typically call this "protector end access", so

Suggested change
pub fn perform_protector_release_access(
pub fn perform_protector_end_access(

&mut self,
tag: BorTag,
global: &GlobalState,
alloc_id: AllocId, // diagnostics
span: Span, // diagnostics
) -> InterpResult<'tcx> {
#[cfg(feature = "expensive-consistency-checks")]
if self.roots.len() > 1 {
self.verify_wildcard_consistency(global);
}

let source_idx = self.tag_mapping.get(&tag).unwrap();

// This is a special access through the entire allocation.
// It actually only affects `accessed` locations, so we need
// to filter on those before initiating the traversal.
//
// In addition this implicit access should not be visible to children,
// thus the use of `traverse_nonchildren`.
// See the test case `returned_mut_is_usable` from
// `tests/pass/tree_borrows/tree-borrows.rs` for an example of
// why this is important.
for (loc_range, loc) in self.locations.iter_mut_all() {
// Only visit accessed permissions
if let Some(p) = loc.perms.get(source_idx)
&& let Some(access_kind) = p.permission.protector_end_access()
&& p.accessed
{
let access_cause = diagnostics::AccessCause::FnExit(access_kind);
loc.perform_access(
self.roots.iter().copied(),
&mut self.nodes,
source_idx,
Some(source_idx),
loc_range,
Some(access_range),
None,
access_kind,
access_cause,
global,
alloc_id,
span,
ChildrenVisitMode::VisitChildrenOfAccessed,
ChildrenVisitMode::SkipChildrenOfAccessed,
)?;
}
} else {
// This is a special access through the entire allocation.
// It actually only affects `accessed` locations, so we need
// to filter on those before initiating the traversal.
//
// In addition this implicit access should not be visible to children,
// thus the use of `traverse_nonchildren`.
// See the test case `returned_mut_is_usable` from
// `tests/pass/tree_borrows/tree-borrows.rs` for an example of
// why this is important.

// Wildcard references are never protected. So this can never be
// called with a wildcard reference.
let source_idx = source_idx.unwrap();

for (loc_range, loc) in self.locations.iter_mut_all() {
// Only visit accessed permissions
if let Some(p) = loc.perms.get(source_idx)
&& let Some(access_kind) = p.permission.protector_end_access()
&& p.accessed
{
let access_cause = diagnostics::AccessCause::FnExit(access_kind);
loc.perform_access(
self.roots.iter().copied(),
&mut self.nodes,
Some(source_idx),
loc_range,
None,
access_kind,
access_cause,
global,
alloc_id,
span,
ChildrenVisitMode::SkipChildrenOfAccessed,
)?;
}
}
}
interp_ok(())
}
Expand Down