Stacked Borrows: factor the logic determining the new permissions on retag into a separate function
This commit is contained in:
parent
9397ea1368
commit
34c58e897f
3 changed files with 187 additions and 167 deletions
|
@ -459,10 +459,10 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
|
||||||
Operation::Dealloc(_) => format!(" due to deallocation"),
|
Operation::Dealloc(_) => format!(" due to deallocation"),
|
||||||
Operation::Access(AccessOp { kind, tag, .. }) =>
|
Operation::Access(AccessOp { kind, tag, .. }) =>
|
||||||
format!(" due to {kind:?} access for {tag:?}"),
|
format!(" due to {kind:?} access for {tag:?}"),
|
||||||
Operation::Retag(RetagOp { orig_tag, permission, .. }) => {
|
Operation::Retag(RetagOp { orig_tag, permission, new_tag, .. }) => {
|
||||||
let permission = permission
|
let permission = permission
|
||||||
.expect("start_grant should set the current permission before popping a tag");
|
.expect("start_grant should set the current permission before popping a tag");
|
||||||
format!(" due to {permission:?} retag from {orig_tag:?}")
|
format!(" due to {permission:?} retag from {orig_tag:?} (that retag created {new_tag:?})")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
//! Implements "Stacked Borrows". See <https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md>
|
//! Implements "Stacked Borrows". See <https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md>
|
||||||
//! for further information.
|
//! for further information.
|
||||||
|
|
||||||
|
pub mod diagnostics;
|
||||||
mod item;
|
mod item;
|
||||||
mod stack;
|
mod stack;
|
||||||
pub mod diagnostics;
|
|
||||||
|
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::Write;
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_middle::mir::{Mutability, RetagKind};
|
use rustc_middle::mir::{Mutability, RetagKind};
|
||||||
|
@ -23,9 +23,9 @@ use crate::borrow_tracker::{
|
||||||
};
|
};
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
use diagnostics::RetagCause;
|
||||||
pub use item::{Item, Permission};
|
pub use item::{Item, Permission};
|
||||||
pub use stack::Stack;
|
pub use stack::Stack;
|
||||||
use diagnostics::RetagCause;
|
|
||||||
|
|
||||||
pub type AllocState = Stacks;
|
pub type AllocState = Stacks;
|
||||||
|
|
||||||
|
@ -42,30 +42,104 @@ pub struct Stacks {
|
||||||
modified_since_last_gc: bool,
|
modified_since_last_gc: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Indicates which kind of reference is being created.
|
/// Indicates which permissions to grant to the retagged pointer.
|
||||||
/// Used by high-level `reborrow` to compute which permissions to grant to the
|
#[derive(Clone, Debug)]
|
||||||
/// new pointer.
|
enum NewPermission {
|
||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
Uniform {
|
||||||
enum RefKind {
|
perm: Permission,
|
||||||
/// `Box`.
|
access: Option<AccessKind>,
|
||||||
Box,
|
protector: Option<ProtectorKind>,
|
||||||
/// `&mut`.
|
},
|
||||||
Unique { two_phase: bool },
|
FreezeSensitive {
|
||||||
/// `&` with or without interior mutability.
|
freeze_perm: Permission,
|
||||||
Shared,
|
freeze_access: Option<AccessKind>,
|
||||||
/// `*mut`/`*const` (raw pointers).
|
freeze_protector: Option<ProtectorKind>,
|
||||||
Raw { mutable: bool },
|
nonfreeze_perm: Permission,
|
||||||
|
nonfreeze_access: Option<AccessKind>,
|
||||||
|
// nonfreeze_protector must always be None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for RefKind {
|
impl NewPermission {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
/// A key function: determine the permissions to grant at a retag for the given kind of
|
||||||
|
/// reference/pointer.
|
||||||
|
fn from_ref_ty<'tcx>(
|
||||||
|
ty: ty::Ty<'tcx>,
|
||||||
|
kind: RetagKind,
|
||||||
|
cx: &crate::MiriInterpCx<'_, 'tcx>,
|
||||||
|
) -> Self {
|
||||||
|
let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
|
||||||
|
match ty.kind() {
|
||||||
|
ty::Ref(_, pointee, Mutability::Mut) => {
|
||||||
|
if kind == RetagKind::TwoPhase {
|
||||||
|
// We mostly just give up on 2phase-borrows, and treat these exactly like raw pointers.
|
||||||
|
assert!(protector.is_none()); // RetagKind can't be both FnEntry and TwoPhase.
|
||||||
|
NewPermission::Uniform {
|
||||||
|
perm: Permission::SharedReadWrite,
|
||||||
|
access: None,
|
||||||
|
protector: None,
|
||||||
|
}
|
||||||
|
} else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
|
||||||
|
// A regular full mutable reference.
|
||||||
|
NewPermission::Uniform {
|
||||||
|
perm: Permission::Unique,
|
||||||
|
access: Some(AccessKind::Write),
|
||||||
|
protector,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NewPermission::Uniform {
|
||||||
|
perm: Permission::SharedReadWrite,
|
||||||
|
// FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
|
||||||
|
// should do fake accesses here. But then we run into
|
||||||
|
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
|
||||||
|
// we don't do that.
|
||||||
|
access: None,
|
||||||
|
protector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Mut, .. }) => {
|
||||||
|
assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
|
||||||
|
// Mutable raw pointer. No access, not protected.
|
||||||
|
NewPermission::Uniform {
|
||||||
|
perm: Permission::SharedReadWrite,
|
||||||
|
access: None,
|
||||||
|
protector: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::Ref(_, _pointee, Mutability::Not) => {
|
||||||
|
NewPermission::FreezeSensitive {
|
||||||
|
freeze_perm: Permission::SharedReadOnly,
|
||||||
|
freeze_access: Some(AccessKind::Read),
|
||||||
|
freeze_protector: protector,
|
||||||
|
nonfreeze_perm: Permission::SharedReadWrite,
|
||||||
|
// Inside UnsafeCell, this does *not* count as an access, as there
|
||||||
|
// might actually be mutable references further up the stack that
|
||||||
|
// we have to keep alive.
|
||||||
|
nonfreeze_access: None,
|
||||||
|
// We do not protect inside UnsafeCell.
|
||||||
|
// This fixes https://github.com/rust-lang/rust/issues/55005.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::RawPtr(ty::TypeAndMut { mutbl: Mutability::Not, .. }) => {
|
||||||
|
assert!(protector.is_none()); // RetagKind can't be both FnEntry and Raw.
|
||||||
|
// `*const T`, when freshly created, are read-only in the frozen part.
|
||||||
|
NewPermission::FreezeSensitive {
|
||||||
|
freeze_perm: Permission::SharedReadOnly,
|
||||||
|
freeze_access: Some(AccessKind::Read),
|
||||||
|
freeze_protector: None,
|
||||||
|
nonfreeze_perm: Permission::SharedReadWrite,
|
||||||
|
nonfreeze_access: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn protector(&self) -> Option<ProtectorKind> {
|
||||||
match self {
|
match self {
|
||||||
RefKind::Box => write!(f, "Box"),
|
NewPermission::Uniform { protector, .. } => *protector,
|
||||||
RefKind::Unique { two_phase: false } => write!(f, "unique reference"),
|
NewPermission::FreezeSensitive { freeze_protector, .. } => *freeze_protector,
|
||||||
RefKind::Unique { two_phase: true } => write!(f, "unique reference (two-phase)"),
|
|
||||||
RefKind::Shared => write!(f, "shared reference"),
|
|
||||||
RefKind::Raw { mutable: true } => write!(f, "raw (mutable) pointer"),
|
|
||||||
RefKind::Raw { mutable: false } => write!(f, "raw (constant) pointer"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -520,10 +594,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
&mut self,
|
&mut self,
|
||||||
place: &MPlaceTy<'tcx, Provenance>,
|
place: &MPlaceTy<'tcx, Provenance>,
|
||||||
size: Size,
|
size: Size,
|
||||||
kind: RefKind,
|
new_perm: NewPermission,
|
||||||
retag_cause: RetagCause, // What caused this retag, for diagnostics only
|
|
||||||
new_tag: BorTag,
|
new_tag: BorTag,
|
||||||
protect: Option<ProtectorKind>,
|
retag_cause: RetagCause, // What caused this retag, for diagnostics only
|
||||||
) -> InterpResult<'tcx, Option<AllocId>> {
|
) -> InterpResult<'tcx, Option<AllocId>> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
|
|
||||||
|
@ -534,20 +607,16 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
|
let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
|
||||||
let ty = place.layout.ty;
|
let ty = place.layout.ty;
|
||||||
if global.tracked_pointer_tags.contains(&new_tag) {
|
if global.tracked_pointer_tags.contains(&new_tag) {
|
||||||
let mut kind_str = format!("{kind}");
|
let mut kind_str = String::new();
|
||||||
match kind {
|
match new_perm {
|
||||||
RefKind::Unique { two_phase: false }
|
NewPermission::Uniform { perm, .. } =>
|
||||||
if !ty.is_unpin(*this.tcx, this.param_env()) =>
|
write!(kind_str, "{perm:?} permission").unwrap(),
|
||||||
{
|
NewPermission::FreezeSensitive { freeze_perm, .. } if ty.is_freeze(*this.tcx, this.param_env()) =>
|
||||||
write!(kind_str, " (!Unpin pointee type {ty})").unwrap()
|
write!(kind_str, "{freeze_perm:?} permission").unwrap(),
|
||||||
},
|
NewPermission::FreezeSensitive { freeze_perm, nonfreeze_perm, .. } =>
|
||||||
RefKind::Shared
|
write!(kind_str, "{freeze_perm:?}/{nonfreeze_perm:?} permission for frozen/non-frozen parts").unwrap(),
|
||||||
if !ty.is_freeze(*this.tcx, this.param_env()) =>
|
}
|
||||||
{
|
write!(kind_str, " (pointee type {ty})").unwrap();
|
||||||
write!(kind_str, " (!Freeze pointee type {ty})").unwrap()
|
|
||||||
},
|
|
||||||
_ => write!(kind_str, " (pointee type {ty})").unwrap(),
|
|
||||||
};
|
|
||||||
this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
|
this.emit_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
|
||||||
new_tag.inner(),
|
new_tag.inner(),
|
||||||
Some(kind_str),
|
Some(kind_str),
|
||||||
|
@ -581,7 +650,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
);
|
);
|
||||||
let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
|
let mut dcx = dcx.build(&mut stacked_borrows.history, base_offset);
|
||||||
dcx.log_creation();
|
dcx.log_creation();
|
||||||
if protect.is_some() {
|
if new_perm.protector().is_some() {
|
||||||
dcx.log_protector();
|
dcx.log_protector();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -594,8 +663,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
|
|
||||||
if size == Size::ZERO {
|
if size == Size::ZERO {
|
||||||
trace!(
|
trace!(
|
||||||
"reborrow of size 0: {} reference {:?} derived from {:?} (pointee {})",
|
"reborrow of size 0: reference {:?} derived from {:?} (pointee {})",
|
||||||
kind,
|
|
||||||
new_tag,
|
new_tag,
|
||||||
place.ptr,
|
place.ptr,
|
||||||
place.layout.ty,
|
place.layout.ty,
|
||||||
|
@ -632,8 +700,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"reborrow: {} reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
|
"reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
|
||||||
kind,
|
|
||||||
new_tag,
|
new_tag,
|
||||||
orig_tag,
|
orig_tag,
|
||||||
place.layout.ty,
|
place.layout.ty,
|
||||||
|
@ -641,7 +708,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
size.bytes()
|
size.bytes()
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(protect) = protect {
|
if let Some(protect) = new_perm.protector() {
|
||||||
// See comment in `Stack::item_invalidated` for why we store the tag twice.
|
// See comment in `Stack::item_invalidated` for why we store the tag twice.
|
||||||
this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
|
this.frame_mut().extra.borrow_tracker.as_mut().unwrap().protected_tags.push(new_tag);
|
||||||
this.machine
|
this.machine
|
||||||
|
@ -653,30 +720,45 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
.insert(new_tag, protect);
|
.insert(new_tag, protect);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the stacks.
|
// Update the stacks, according to the new permission information we are given.
|
||||||
// Make sure that raw pointers and mutable shared references are reborrowed "weak":
|
match new_perm {
|
||||||
// There could be existing unique pointers reborrowed from them that should remain valid!
|
NewPermission::Uniform { perm, access, protector } => {
|
||||||
let (perm, access) = match kind {
|
assert!(perm != Permission::SharedReadOnly);
|
||||||
RefKind::Unique { two_phase } => {
|
// Here we can avoid `borrow()` calls because we have mutable references.
|
||||||
// Permission is Unique only if the type is `Unpin` and this is not twophase
|
// Note that this asserts that the allocation is mutable -- but since we are creating a
|
||||||
if !two_phase && place.layout.ty.is_unpin(*this.tcx, this.param_env()) {
|
// mutable pointer, that seems reasonable.
|
||||||
(Permission::Unique, Some(AccessKind::Write))
|
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
|
||||||
} else {
|
let stacked_borrows = alloc_extra.borrow_tracker_sb_mut().get_mut();
|
||||||
// FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
|
let item = Item::new(new_tag, perm, protector.is_some());
|
||||||
// should do fake accesses here. But then we run into
|
let range = alloc_range(base_offset, size);
|
||||||
// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
|
let global = machine.borrow_tracker.as_ref().unwrap().borrow();
|
||||||
// we don't do that.
|
let dcx = DiagnosticCxBuilder::retag(
|
||||||
(Permission::SharedReadWrite, None)
|
machine,
|
||||||
|
retag_cause,
|
||||||
|
new_tag,
|
||||||
|
orig_tag,
|
||||||
|
alloc_range(base_offset, size),
|
||||||
|
);
|
||||||
|
stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
|
||||||
|
stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
|
||||||
|
})?;
|
||||||
|
drop(global);
|
||||||
|
if let Some(access) = access {
|
||||||
|
assert_eq!(access, AccessKind::Write);
|
||||||
|
// Make sure the data race model also knows about this.
|
||||||
|
if let Some(data_race) = alloc_extra.data_race.as_mut() {
|
||||||
|
data_race.write(alloc_id, range, machine)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RefKind::Box => (Permission::Unique, Some(AccessKind::Write)),
|
|
||||||
RefKind::Raw { mutable: true } => {
|
|
||||||
// Creating a raw ptr does not count as an access
|
|
||||||
(Permission::SharedReadWrite, None)
|
|
||||||
}
|
}
|
||||||
RefKind::Shared | RefKind::Raw { mutable: false } => {
|
NewPermission::FreezeSensitive {
|
||||||
// Shared references and *const are a whole different kind of game, the
|
freeze_perm,
|
||||||
// permission is not uniform across the entire range!
|
freeze_access,
|
||||||
|
freeze_protector,
|
||||||
|
nonfreeze_perm,
|
||||||
|
nonfreeze_access,
|
||||||
|
} => {
|
||||||
|
// The permission is not uniform across the entire range!
|
||||||
// We need a frozen-sensitive reborrow.
|
// We need a frozen-sensitive reborrow.
|
||||||
// We have to use shared references to alloc/memory_extra here since
|
// We have to use shared references to alloc/memory_extra here since
|
||||||
// `visit_freeze_sensitive` needs to access the global state.
|
// `visit_freeze_sensitive` needs to access the global state.
|
||||||
|
@ -686,22 +768,12 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
// Adjust range.
|
// Adjust range.
|
||||||
range.start += base_offset;
|
range.start += base_offset;
|
||||||
// We are only ever `SharedReadOnly` inside the frozen bits.
|
// We are only ever `SharedReadOnly` inside the frozen bits.
|
||||||
let (perm, access) = if frozen {
|
let (perm, access, protector) = if frozen {
|
||||||
(Permission::SharedReadOnly, Some(AccessKind::Read))
|
(freeze_perm, freeze_access, freeze_protector)
|
||||||
} else {
|
} else {
|
||||||
// Inside UnsafeCell, this does *not* count as an access, as there
|
(nonfreeze_perm, nonfreeze_access, None)
|
||||||
// might actually be mutable references further up the stack that
|
|
||||||
// we have to keep alive.
|
|
||||||
(Permission::SharedReadWrite, None)
|
|
||||||
};
|
};
|
||||||
let protected = if frozen {
|
let item = Item::new(new_tag, perm, protector.is_some());
|
||||||
protect.is_some()
|
|
||||||
} else {
|
|
||||||
// We do not protect inside UnsafeCell.
|
|
||||||
// This fixes https://github.com/rust-lang/rust/issues/55005.
|
|
||||||
false
|
|
||||||
};
|
|
||||||
let item = Item::new(new_tag, perm, protected);
|
|
||||||
let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
|
let global = this.machine.borrow_tracker.as_ref().unwrap().borrow();
|
||||||
let dcx = DiagnosticCxBuilder::retag(
|
let dcx = DiagnosticCxBuilder::retag(
|
||||||
&this.machine,
|
&this.machine,
|
||||||
|
@ -723,34 +795,6 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
return Ok(Some(alloc_id));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Here we can avoid `borrow()` calls because we have mutable references.
|
|
||||||
// Note that this asserts that the allocation is mutable -- but since we are creating a
|
|
||||||
// mutable pointer, that seems reasonable.
|
|
||||||
let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc_id)?;
|
|
||||||
let stacked_borrows = alloc_extra.borrow_tracker_sb_mut().get_mut();
|
|
||||||
let item = Item::new(new_tag, perm, protect.is_some());
|
|
||||||
let range = alloc_range(base_offset, size);
|
|
||||||
let global = machine.borrow_tracker.as_ref().unwrap().borrow();
|
|
||||||
let dcx = DiagnosticCxBuilder::retag(
|
|
||||||
machine,
|
|
||||||
retag_cause,
|
|
||||||
new_tag,
|
|
||||||
orig_tag,
|
|
||||||
alloc_range(base_offset, size),
|
|
||||||
);
|
|
||||||
stacked_borrows.for_each(range, dcx, |stack, dcx, exposed_tags| {
|
|
||||||
stack.grant(orig_tag, item, access, &global, dcx, exposed_tags)
|
|
||||||
})?;
|
|
||||||
drop(global);
|
|
||||||
if let Some(access) = access {
|
|
||||||
assert_eq!(access, AccessKind::Write);
|
|
||||||
// Make sure the data race model also knows about this.
|
|
||||||
if let Some(data_race) = alloc_extra.data_race.as_mut() {
|
|
||||||
data_race.write(alloc_id, range, machine)?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,9 +806,8 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
fn sb_retag_reference(
|
fn sb_retag_reference(
|
||||||
&mut self,
|
&mut self,
|
||||||
val: &ImmTy<'tcx, Provenance>,
|
val: &ImmTy<'tcx, Provenance>,
|
||||||
kind: RefKind,
|
new_perm: NewPermission,
|
||||||
retag_cause: RetagCause, // What caused this retag, for diagnostics only
|
cause: RetagCause, // What caused this retag, for diagnostics only
|
||||||
protect: Option<ProtectorKind>,
|
|
||||||
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
|
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
// We want a place for where the ptr *points to*, so we get one.
|
// We want a place for where the ptr *points to*, so we get one.
|
||||||
|
@ -782,7 +825,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
|
let new_tag = this.machine.borrow_tracker.as_mut().unwrap().get_mut().new_ptr();
|
||||||
|
|
||||||
// Reborrow.
|
// Reborrow.
|
||||||
let alloc_id = this.sb_reborrow(&place, size, kind, retag_cause, new_tag, protect)?;
|
let alloc_id = this.sb_reborrow(&place, size, new_perm, new_tag, cause)?;
|
||||||
|
|
||||||
// Adjust pointer.
|
// Adjust pointer.
|
||||||
let new_place = place.map_provenance(|p| {
|
let new_place = place.map_provenance(|p| {
|
||||||
|
@ -815,25 +858,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
val: &ImmTy<'tcx, Provenance>,
|
val: &ImmTy<'tcx, Provenance>,
|
||||||
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
|
) -> InterpResult<'tcx, ImmTy<'tcx, Provenance>> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let ref_kind = match val.layout.ty.kind() {
|
let new_perm = NewPermission::from_ref_ty(val.layout.ty, kind, this);
|
||||||
ty::Ref(_, _, mutbl) => {
|
|
||||||
match mutbl {
|
|
||||||
Mutability::Mut =>
|
|
||||||
RefKind::Unique { two_phase: kind == RetagKind::TwoPhase },
|
|
||||||
Mutability::Not => RefKind::Shared,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ty::RawPtr(tym) => {
|
|
||||||
RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
let retag_cause = match kind {
|
let retag_cause = match kind {
|
||||||
RetagKind::TwoPhase { .. } => RetagCause::TwoPhase,
|
RetagKind::TwoPhase { .. } => RetagCause::TwoPhase,
|
||||||
RetagKind::FnEntry => unreachable!(),
|
RetagKind::FnEntry => unreachable!(),
|
||||||
RetagKind::Raw | RetagKind::Default => RetagCause::Normal,
|
RetagKind::Raw | RetagKind::Default => RetagCause::Normal,
|
||||||
};
|
};
|
||||||
this.sb_retag_reference(&val, ref_kind, retag_cause, None)
|
this.sb_retag_reference(&val, new_perm, retag_cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sb_retag_place_contents(
|
fn sb_retag_place_contents(
|
||||||
|
@ -844,7 +875,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
let this = self.eval_context_mut();
|
let this = self.eval_context_mut();
|
||||||
let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
|
let retag_fields = this.machine.borrow_tracker.as_mut().unwrap().get_mut().retag_fields;
|
||||||
let retag_cause = match kind {
|
let retag_cause = match kind {
|
||||||
RetagKind::Raw | RetagKind::TwoPhase { .. } => unreachable!(),
|
RetagKind::Raw | RetagKind::TwoPhase { .. } => unreachable!(), // these can only happen in `retag_ptr_value`
|
||||||
RetagKind::FnEntry => RetagCause::FnEntry,
|
RetagKind::FnEntry => RetagCause::FnEntry,
|
||||||
RetagKind::Default => RetagCause::Normal,
|
RetagKind::Default => RetagCause::Normal,
|
||||||
};
|
};
|
||||||
|
@ -863,12 +894,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
fn retag_ptr_inplace(
|
fn retag_ptr_inplace(
|
||||||
&mut self,
|
&mut self,
|
||||||
place: &PlaceTy<'tcx, Provenance>,
|
place: &PlaceTy<'tcx, Provenance>,
|
||||||
ref_kind: RefKind,
|
new_perm: NewPermission,
|
||||||
retag_cause: RetagCause,
|
retag_cause: RetagCause,
|
||||||
protector: Option<ProtectorKind>,
|
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
|
let val = self.ecx.read_immediate(&self.ecx.place_to_op(place)?)?;
|
||||||
let val = self.ecx.sb_retag_reference(&val, ref_kind, retag_cause, protector)?;
|
let val = self.ecx.sb_retag_reference(&val, new_perm, retag_cause)?;
|
||||||
self.ecx.write_immediate(*val, place)?;
|
self.ecx.write_immediate(*val, place)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -885,13 +915,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
|
|
||||||
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
||||||
// Boxes get a weak protectors, since they may be deallocated.
|
// Boxes get a weak protectors, since they may be deallocated.
|
||||||
self.retag_ptr_inplace(
|
let new_perm = NewPermission::Uniform {
|
||||||
place,
|
perm: Permission::Unique,
|
||||||
RefKind::Box,
|
access: Some(AccessKind::Write),
|
||||||
self.retag_cause,
|
protector: (self.kind == RetagKind::FnEntry)
|
||||||
/*protector*/
|
.then_some(ProtectorKind::WeakProtector),
|
||||||
(self.kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
|
};
|
||||||
)
|
self.retag_ptr_inplace(place, new_perm, self.retag_cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
fn visit_value(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
||||||
|
@ -905,20 +935,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
|
|
||||||
// Check the type of this value to see what to do with it (retag, or recurse).
|
// Check the type of this value to see what to do with it (retag, or recurse).
|
||||||
match place.layout.ty.kind() {
|
match place.layout.ty.kind() {
|
||||||
ty::Ref(_, _, mutbl) => {
|
ty::Ref(..) => {
|
||||||
let ref_kind = match mutbl {
|
let new_perm =
|
||||||
Mutability::Mut =>
|
NewPermission::from_ref_ty(place.layout.ty, self.kind, self.ecx);
|
||||||
RefKind::Unique { two_phase: false },
|
self.retag_ptr_inplace(place, new_perm, self.retag_cause)?;
|
||||||
Mutability::Not => RefKind::Shared,
|
|
||||||
};
|
|
||||||
self.retag_ptr_inplace(
|
|
||||||
place,
|
|
||||||
ref_kind,
|
|
||||||
self.retag_cause,
|
|
||||||
/*protector*/
|
|
||||||
(self.kind == RetagKind::FnEntry)
|
|
||||||
.then_some(ProtectorKind::StrongProtector),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
ty::RawPtr(..) => {
|
ty::RawPtr(..) => {
|
||||||
// We do *not* want to recurse into raw pointers -- wide raw pointers have
|
// We do *not* want to recurse into raw pointers -- wide raw pointers have
|
||||||
|
@ -972,12 +992,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
|
let ptr_layout = this.layout_of(this.tcx.mk_mut_ptr(return_place.layout.ty))?;
|
||||||
let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
|
let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
|
||||||
// Reborrow it. With protection! That is part of the point.
|
// Reborrow it. With protection! That is part of the point.
|
||||||
let val = this.sb_retag_reference(
|
let new_perm = NewPermission::Uniform {
|
||||||
&val,
|
perm: Permission::Unique,
|
||||||
RefKind::Unique { two_phase: false },
|
access: Some(AccessKind::Write),
|
||||||
RetagCause::FnReturn,
|
protector: Some(ProtectorKind::StrongProtector),
|
||||||
/*protector*/ Some(ProtectorKind::StrongProtector),
|
};
|
||||||
)?;
|
let val = this.sb_retag_reference(&val, new_perm, RetagCause::FnReturn)?;
|
||||||
// And use reborrowed pointer for return place.
|
// And use reborrowed pointer for return place.
|
||||||
let return_place = this.ref_to_mplace(&val)?;
|
let return_place = this.ref_to_mplace(&val)?;
|
||||||
this.frame_mut().return_place = return_place.into();
|
this.frame_mut().return_place = return_place.into();
|
||||||
|
|
|
@ -63,9 +63,9 @@ impl MachineStopType for TerminationInfo {}
|
||||||
|
|
||||||
/// Miri specific diagnostics
|
/// Miri specific diagnostics
|
||||||
pub enum NonHaltingDiagnostic {
|
pub enum NonHaltingDiagnostic {
|
||||||
/// (new_tag, new_kind, (alloc_id, base_offset, orig_tag))
|
/// (new_tag, new_perm, (alloc_id, base_offset, orig_tag))
|
||||||
///
|
///
|
||||||
/// new_kind is `None` for base tags.
|
/// new_perm is `None` for base tags.
|
||||||
CreatedPointerTag(NonZeroU64, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
|
CreatedPointerTag(NonZeroU64, Option<String>, Option<(AllocId, AllocRange, ProvenanceExtra)>),
|
||||||
/// This `Item` was popped from the borrow stack. The string explains the reason.
|
/// This `Item` was popped from the borrow stack. The string explains the reason.
|
||||||
PoppedPointerTag(Item, String),
|
PoppedPointerTag(Item, String),
|
||||||
|
@ -393,10 +393,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
|
||||||
|
|
||||||
let msg = match &e {
|
let msg = match &e {
|
||||||
CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
|
CreatedPointerTag(tag, None, _) => format!("created base tag {tag:?}"),
|
||||||
CreatedPointerTag(tag, Some(kind), None) => format!("created {tag:?} for {kind}"),
|
CreatedPointerTag(tag, Some(perm), None) => format!("created {tag:?} with {perm} derived from unknown tag"),
|
||||||
CreatedPointerTag(tag, Some(kind), Some((alloc_id, range, orig_tag))) =>
|
CreatedPointerTag(tag, Some(perm), Some((alloc_id, range, orig_tag))) =>
|
||||||
format!(
|
format!(
|
||||||
"created tag {tag:?} for {kind} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
|
"created tag {tag:?} with {perm} at {alloc_id:?}{range:?} derived from {orig_tag:?}"
|
||||||
),
|
),
|
||||||
PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
|
PoppedPointerTag(item, cause) => format!("popped tracked tag for item {item:?}{cause}"),
|
||||||
CreatedCallId(id) => format!("function call with id {id}"),
|
CreatedCallId(id) => format!("function call with id {id}"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue