Skip to content
Merged
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_end_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, // diagnostics
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:
/// - 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_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