Auto merge of #2936 - Vanille-N:unique, r=RalfJung
Optional semantics for `Unique` Use with `-Zmiri-unique-is-unique`, makes `core::ptr::Unique` get a reborrow with its own permissions.
This commit is contained in:
commit
cec5ec44b1
25 changed files with 524 additions and 39 deletions
|
@ -435,6 +435,9 @@ to Miri failing to detect cases of undefined behavior in a program.
|
||||||
so with this flag.
|
so with this flag.
|
||||||
* `-Zmiri-force-page-size=<num>` overrides the default page size for an architecture, in multiples of 1k.
|
* `-Zmiri-force-page-size=<num>` overrides the default page size for an architecture, in multiples of 1k.
|
||||||
`4` is default for most targets. This value should always be a power of 2 and nonzero.
|
`4` is default for most targets. This value should always be a power of 2 and nonzero.
|
||||||
|
* `-Zmiri-unique-is-unique` performs additional aliasing checks for `core::ptr::Unique` to ensure
|
||||||
|
that it could theoretically be considered `noalias`. This flag is experimental and has
|
||||||
|
an effect only when used with `-Zmiri-tree-borrows`.
|
||||||
|
|
||||||
[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
|
[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
|
||||||
|
|
||||||
|
|
|
@ -343,6 +343,8 @@ fn main() {
|
||||||
miri_config.borrow_tracker = None;
|
miri_config.borrow_tracker = None;
|
||||||
} else if arg == "-Zmiri-tree-borrows" {
|
} else if arg == "-Zmiri-tree-borrows" {
|
||||||
miri_config.borrow_tracker = Some(BorrowTrackerMethod::TreeBorrows);
|
miri_config.borrow_tracker = Some(BorrowTrackerMethod::TreeBorrows);
|
||||||
|
} else if arg == "-Zmiri-unique-is-unique" {
|
||||||
|
miri_config.unique_is_unique = true;
|
||||||
} else if arg == "-Zmiri-disable-data-race-detector" {
|
} else if arg == "-Zmiri-disable-data-race-detector" {
|
||||||
miri_config.data_race_detector = false;
|
miri_config.data_race_detector = false;
|
||||||
miri_config.weak_memory_emulation = false;
|
miri_config.weak_memory_emulation = false;
|
||||||
|
@ -560,6 +562,14 @@ fn main() {
|
||||||
rustc_args.push(arg);
|
rustc_args.push(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// `-Zmiri-unique-is-unique` should only be used with `-Zmiri-tree-borrows`
|
||||||
|
if miri_config.unique_is_unique
|
||||||
|
&& !matches!(miri_config.borrow_tracker, Some(BorrowTrackerMethod::TreeBorrows))
|
||||||
|
{
|
||||||
|
show_error!(
|
||||||
|
"-Zmiri-unique-is-unique only has an effect when -Zmiri-tree-borrows is also used"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
debug!("rustc arguments: {:?}", rustc_args);
|
debug!("rustc arguments: {:?}", rustc_args);
|
||||||
debug!("crate arguments: {:?}", miri_config.args);
|
debug!("crate arguments: {:?}", miri_config.args);
|
||||||
|
|
|
@ -103,6 +103,8 @@ pub struct GlobalStateInner {
|
||||||
pub tracked_call_ids: FxHashSet<CallId>,
|
pub tracked_call_ids: FxHashSet<CallId>,
|
||||||
/// Whether to recurse into datatypes when searching for pointers to retag.
|
/// Whether to recurse into datatypes when searching for pointers to retag.
|
||||||
pub retag_fields: RetagFields,
|
pub retag_fields: RetagFields,
|
||||||
|
/// Whether `core::ptr::Unique` gets special (`Box`-like) handling.
|
||||||
|
pub unique_is_unique: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VisitTags for GlobalStateInner {
|
impl VisitTags for GlobalStateInner {
|
||||||
|
@ -170,6 +172,7 @@ impl GlobalStateInner {
|
||||||
tracked_pointer_tags: FxHashSet<BorTag>,
|
tracked_pointer_tags: FxHashSet<BorTag>,
|
||||||
tracked_call_ids: FxHashSet<CallId>,
|
tracked_call_ids: FxHashSet<CallId>,
|
||||||
retag_fields: RetagFields,
|
retag_fields: RetagFields,
|
||||||
|
unique_is_unique: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
GlobalStateInner {
|
GlobalStateInner {
|
||||||
borrow_tracker_method,
|
borrow_tracker_method,
|
||||||
|
@ -180,6 +183,7 @@ impl GlobalStateInner {
|
||||||
tracked_pointer_tags,
|
tracked_pointer_tags,
|
||||||
tracked_call_ids,
|
tracked_call_ids,
|
||||||
retag_fields,
|
retag_fields,
|
||||||
|
unique_is_unique,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,6 +248,7 @@ impl BorrowTrackerMethod {
|
||||||
config.tracked_pointer_tags.clone(),
|
config.tracked_pointer_tags.clone(),
|
||||||
config.tracked_call_ids.clone(),
|
config.tracked_call_ids.clone(),
|
||||||
config.retag_fields,
|
config.retag_fields,
|
||||||
|
config.unique_is_unique,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use rustc_middle::{
|
||||||
Ty,
|
Ty,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use rustc_span::def_id::DefId;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
|
@ -99,9 +100,9 @@ impl<'tcx> Tree {
|
||||||
/// Policy for a new borrow.
|
/// Policy for a new borrow.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
struct NewPermission {
|
struct NewPermission {
|
||||||
/// Whether this borrow requires a read access on its parent.
|
/// Optionally ignore the actual size to do a zero-size reborrow.
|
||||||
/// `perform_read_access` is `true` for all pointers marked `dereferenceable`.
|
/// If this is set then `dereferenceable` is not enforced.
|
||||||
perform_read_access: bool,
|
zero_size: bool,
|
||||||
/// Which permission should the pointer start with.
|
/// Which permission should the pointer start with.
|
||||||
initial_state: Permission,
|
initial_state: Permission,
|
||||||
/// Whether this pointer is part of the arguments of a function call.
|
/// Whether this pointer is part of the arguments of a function call.
|
||||||
|
@ -128,20 +129,19 @@ impl<'tcx> NewPermission {
|
||||||
// `&`s, which are excluded above.
|
// `&`s, which are excluded above.
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
// This field happens to be redundant since right now we always do a read,
|
|
||||||
// but it could be useful in the future.
|
|
||||||
let perform_read_access = true;
|
|
||||||
|
|
||||||
let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
|
let protector = (kind == RetagKind::FnEntry).then_some(ProtectorKind::StrongProtector);
|
||||||
Some(Self { perform_read_access, initial_state, protector })
|
Some(Self { zero_size: false, initial_state, protector })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Boxes are not handled by `from_ref_ty`, they need special behavior
|
/// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled).
|
||||||
// implemented here.
|
/// These pointers allow deallocation so need a different kind of protector not handled
|
||||||
fn from_box_ty(
|
/// by `from_ref_ty`.
|
||||||
|
fn from_unique_ty(
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
kind: RetagKind,
|
kind: RetagKind,
|
||||||
cx: &crate::MiriInterpCx<'_, 'tcx>,
|
cx: &crate::MiriInterpCx<'_, 'tcx>,
|
||||||
|
zero_size: bool,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let pointee = ty.builtin_deref(true).unwrap().ty;
|
let pointee = ty.builtin_deref(true).unwrap().ty;
|
||||||
pointee.is_unpin(*cx.tcx, cx.param_env()).then_some(()).map(|()| {
|
pointee.is_unpin(*cx.tcx, cx.param_env()).then_some(()).map(|()| {
|
||||||
|
@ -149,7 +149,7 @@ impl<'tcx> NewPermission {
|
||||||
// because it is valid to deallocate it within the function.
|
// because it is valid to deallocate it within the function.
|
||||||
let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.param_env());
|
let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.param_env());
|
||||||
Self {
|
Self {
|
||||||
perform_read_access: true,
|
zero_size,
|
||||||
initial_state: Permission::new_unique_2phase(ty_is_freeze),
|
initial_state: Permission::new_unique_2phase(ty_is_freeze),
|
||||||
protector: (kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
|
protector: (kind == RetagKind::FnEntry).then_some(ProtectorKind::WeakProtector),
|
||||||
}
|
}
|
||||||
|
@ -201,6 +201,7 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
Ok(())
|
Ok(())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
trace!("Reborrow of size {:?}", ptr_size);
|
||||||
let (alloc_id, base_offset, parent_prov) = if ptr_size > Size::ZERO {
|
let (alloc_id, base_offset, parent_prov) = if ptr_size > Size::ZERO {
|
||||||
this.ptr_get_alloc_id(place.ptr)?
|
this.ptr_get_alloc_id(place.ptr)?
|
||||||
} else {
|
} else {
|
||||||
|
@ -276,8 +277,8 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
let range = alloc_range(base_offset, ptr_size);
|
let range = alloc_range(base_offset, ptr_size);
|
||||||
let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
|
let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
|
||||||
|
|
||||||
if new_perm.perform_read_access {
|
// All reborrows incur a (possibly zero-sized) read access to the parent
|
||||||
// Count this reborrow as a read access
|
{
|
||||||
let global = &this.machine.borrow_tracker.as_ref().unwrap();
|
let global = &this.machine.borrow_tracker.as_ref().unwrap();
|
||||||
let span = this.machine.current_span();
|
let span = this.machine.current_span();
|
||||||
tree_borrows.perform_access(
|
tree_borrows.perform_access(
|
||||||
|
@ -308,12 +309,19 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
|
||||||
// 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.
|
||||||
let place = this.ref_to_mplace(val)?;
|
let place = this.ref_to_mplace(val)?;
|
||||||
|
|
||||||
// Get a lower bound of the size of this place.
|
// Determine the size of the reborrow.
|
||||||
// (When `extern type` are involved, use the size of the known prefix.)
|
// For most types this is the entire size of the place, however
|
||||||
let size = this
|
// - when `extern type` is involved we use the size of the known prefix,
|
||||||
.size_and_align_of_mplace(&place)?
|
// - if the pointer is not reborrowed (raw pointer) or if `zero_size` is set
|
||||||
.map(|(size, _)| size)
|
// then we override the size to do a zero-length reborrow.
|
||||||
.unwrap_or(place.layout.size);
|
let reborrow_size = match new_perm {
|
||||||
|
Some(NewPermission { zero_size: false, .. }) =>
|
||||||
|
this.size_and_align_of_mplace(&place)?
|
||||||
|
.map(|(size, _)| size)
|
||||||
|
.unwrap_or(place.layout.size),
|
||||||
|
_ => Size::from_bytes(0),
|
||||||
|
};
|
||||||
|
trace!("Creating new permission: {:?} with size {:?}", new_perm, reborrow_size);
|
||||||
|
|
||||||
// This new tag is not guaranteed to actually be used.
|
// This new tag is not guaranteed to actually be used.
|
||||||
//
|
//
|
||||||
|
@ -324,7 +332,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();
|
||||||
|
|
||||||
// Compute the actual reborrow.
|
// Compute the actual reborrow.
|
||||||
let reborrowed = this.tb_reborrow(&place, size, new_perm, new_tag)?;
|
let reborrowed = this.tb_reborrow(&place, reborrow_size, new_perm, new_tag)?;
|
||||||
|
|
||||||
// Adjust pointer.
|
// Adjust pointer.
|
||||||
let new_place = place.map_provenance(|p| {
|
let new_place = place.map_provenance(|p| {
|
||||||
|
@ -359,10 +367,10 @@ 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 new_perm = if let &ty::Ref(_, pointee, mutability) = val.layout.ty.kind() {
|
let new_perm = match val.layout.ty.kind() {
|
||||||
NewPermission::from_ref_ty(pointee, mutability, kind, this)
|
&ty::Ref(_, pointee, mutability) =>
|
||||||
} else {
|
NewPermission::from_ref_ty(pointee, mutability, kind, this),
|
||||||
None
|
_ => None,
|
||||||
};
|
};
|
||||||
this.tb_retag_reference(val, new_perm)
|
this.tb_retag_reference(val, new_perm)
|
||||||
}
|
}
|
||||||
|
@ -374,8 +382,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
place: &PlaceTy<'tcx, Provenance>,
|
place: &PlaceTy<'tcx, Provenance>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'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 options = this.machine.borrow_tracker.as_mut().unwrap().get_mut();
|
||||||
let mut visitor = RetagVisitor { ecx: this, kind, retag_fields };
|
let retag_fields = options.retag_fields;
|
||||||
|
let unique_did =
|
||||||
|
options.unique_is_unique.then(|| this.tcx.lang_items().ptr_unique()).flatten();
|
||||||
|
let mut visitor = RetagVisitor { ecx: this, kind, retag_fields, unique_did };
|
||||||
return visitor.visit_value(place);
|
return visitor.visit_value(place);
|
||||||
|
|
||||||
// The actual visitor.
|
// The actual visitor.
|
||||||
|
@ -383,6 +394,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
|
ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
|
||||||
kind: RetagKind,
|
kind: RetagKind,
|
||||||
retag_fields: RetagFields,
|
retag_fields: RetagFields,
|
||||||
|
unique_did: Option<DefId>,
|
||||||
}
|
}
|
||||||
impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> {
|
impl<'ecx, 'mir, 'tcx> RetagVisitor<'ecx, 'mir, 'tcx> {
|
||||||
#[inline(always)] // yes this helps in our benchmarks
|
#[inline(always)] // yes this helps in our benchmarks
|
||||||
|
@ -407,8 +419,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
self.ecx
|
self.ecx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Regardless of how `Unique` is handled, Boxes are always reborrowed.
|
||||||
|
/// When `Unique` is also reborrowed, then it behaves exactly like `Box`
|
||||||
|
/// except for the fact that `Box` has a non-zero-sized reborrow.
|
||||||
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
||||||
let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx);
|
let new_perm = NewPermission::from_unique_ty(
|
||||||
|
place.layout.ty,
|
||||||
|
self.kind,
|
||||||
|
self.ecx,
|
||||||
|
/* zero_size */ false,
|
||||||
|
);
|
||||||
self.retag_ptr_inplace(place, new_perm)
|
self.retag_ptr_inplace(place, new_perm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,6 +460,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
// even if field retagging is not enabled. *shrug*)
|
// even if field retagging is not enabled. *shrug*)
|
||||||
self.walk_value(place)?;
|
self.walk_value(place)?;
|
||||||
}
|
}
|
||||||
|
ty::Adt(adt, _) if self.unique_did == Some(adt.did()) => {
|
||||||
|
let place = inner_ptr_of_unique(self.ecx, place)?;
|
||||||
|
let new_perm = NewPermission::from_unique_ty(
|
||||||
|
place.layout.ty,
|
||||||
|
self.kind,
|
||||||
|
self.ecx,
|
||||||
|
/* zero_size */ true,
|
||||||
|
);
|
||||||
|
self.retag_ptr_inplace(&place, new_perm)?;
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Not a reference/pointer/box. Only recurse if configured appropriately.
|
// Not a reference/pointer/box. Only recurse if configured appropriately.
|
||||||
let recurse = match self.retag_fields {
|
let recurse = match self.retag_fields {
|
||||||
|
@ -456,7 +486,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -486,7 +515,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
// FIXME: do we truly want a 2phase borrow here?
|
// FIXME: do we truly want a 2phase borrow here?
|
||||||
let new_perm = Some(NewPermission {
|
let new_perm = Some(NewPermission {
|
||||||
initial_state: Permission::new_unique_2phase(/*freeze*/ false),
|
initial_state: Permission::new_unique_2phase(/*freeze*/ false),
|
||||||
perform_read_access: true,
|
zero_size: false,
|
||||||
protector: Some(ProtectorKind::StrongProtector),
|
protector: Some(ProtectorKind::StrongProtector),
|
||||||
});
|
});
|
||||||
let val = this.tb_retag_reference(&val, new_perm)?;
|
let val = this.tb_retag_reference(&val, new_perm)?;
|
||||||
|
@ -552,3 +581,27 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||||
tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
|
tree_borrows.give_pointer_debug_name(tag, nth_parent, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Takes a place for a `Unique` and turns it into a place with the inner raw pointer.
|
||||||
|
/// I.e. input is what you get from the visitor upon encountering an `adt` that is `Unique`,
|
||||||
|
/// and output can be used by `retag_ptr_inplace`.
|
||||||
|
fn inner_ptr_of_unique<'tcx>(
|
||||||
|
ecx: &mut MiriInterpCx<'_, 'tcx>,
|
||||||
|
place: &PlaceTy<'tcx, Provenance>,
|
||||||
|
) -> InterpResult<'tcx, PlaceTy<'tcx, Provenance>> {
|
||||||
|
// Follows the same layout as `interpret/visitor.rs:walk_value` for `Box` in
|
||||||
|
// `rustc_const_eval`, just with one fewer layer.
|
||||||
|
// Here we have a `Unique(NonNull(*mut), PhantomData)`
|
||||||
|
assert_eq!(place.layout.fields.count(), 2, "Unique must have exactly 2 fields");
|
||||||
|
let (nonnull, phantom) = (ecx.place_field(place, 0)?, ecx.place_field(place, 1)?);
|
||||||
|
assert!(
|
||||||
|
phantom.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_phantom_data()),
|
||||||
|
"2nd field of `Unique` should be `PhantomData` but is `{:?}`",
|
||||||
|
phantom.layout.ty,
|
||||||
|
);
|
||||||
|
// Now down to `NonNull(*mut)`
|
||||||
|
assert_eq!(nonnull.layout.fields.count(), 1, "NonNull must have exactly 1 field");
|
||||||
|
let ptr = ecx.place_field(&nonnull, 0)?;
|
||||||
|
// Finally a plain `*mut`
|
||||||
|
Ok(ptr)
|
||||||
|
}
|
||||||
|
|
|
@ -90,6 +90,10 @@ pub struct MiriConfig {
|
||||||
pub validate: bool,
|
pub validate: bool,
|
||||||
/// Determines if Stacked Borrows or Tree Borrows is enabled.
|
/// Determines if Stacked Borrows or Tree Borrows is enabled.
|
||||||
pub borrow_tracker: Option<BorrowTrackerMethod>,
|
pub borrow_tracker: Option<BorrowTrackerMethod>,
|
||||||
|
/// Whether `core::ptr::Unique` receives special treatment.
|
||||||
|
/// If `true` then `Unique` is reborrowed with its own new tag and permission,
|
||||||
|
/// otherwise `Unique` is just another raw pointer.
|
||||||
|
pub unique_is_unique: bool,
|
||||||
/// Controls alignment checking.
|
/// Controls alignment checking.
|
||||||
pub check_alignment: AlignmentCheck,
|
pub check_alignment: AlignmentCheck,
|
||||||
/// Controls function [ABI](Abi) checking.
|
/// Controls function [ABI](Abi) checking.
|
||||||
|
@ -156,6 +160,7 @@ impl Default for MiriConfig {
|
||||||
env: vec![],
|
env: vec![],
|
||||||
validate: true,
|
validate: true,
|
||||||
borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows),
|
borrow_tracker: Some(BorrowTrackerMethod::StackedBorrows),
|
||||||
|
unique_is_unique: false,
|
||||||
check_alignment: AlignmentCheck::Int,
|
check_alignment: AlignmentCheck::Int,
|
||||||
check_abi: true,
|
check_abi: true,
|
||||||
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
|
isolated_op: IsolatedOp::Reject(RejectOpWith::Abort),
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
error: Undefined Behavior: entering unreachable code
|
||||||
|
--> $DIR/children-can-alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | std::hint::unreachable_unchecked();
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code
|
||||||
|
|
|
||||||
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
|
||||||
|
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
|
||||||
|
= note: BACKTRACE:
|
||||||
|
= note: inside `main` at $DIR/children-can-alias.rs:LL:CC
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
59
src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs
Normal file
59
src/tools/miri/tests/fail/tree_borrows/children-can-alias.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
//@revisions: default uniq
|
||||||
|
//@compile-flags: -Zmiri-tree-borrows
|
||||||
|
//@[uniq]compile-flags: -Zmiri-unique-is-unique
|
||||||
|
|
||||||
|
//! This is NOT intended behavior.
|
||||||
|
//! We should eventually find a solution so that the version with `Unique` passes too,
|
||||||
|
//! otherwise `Unique` is more strict than `&mut`!
|
||||||
|
|
||||||
|
#![feature(ptr_internals)]
|
||||||
|
|
||||||
|
use core::ptr::addr_of_mut;
|
||||||
|
use core::ptr::Unique;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut data = 0u8;
|
||||||
|
let raw = addr_of_mut!(data);
|
||||||
|
unsafe {
|
||||||
|
raw_children_of_refmut_can_alias(&mut *raw);
|
||||||
|
raw_children_of_unique_can_alias(Unique::new_unchecked(raw));
|
||||||
|
|
||||||
|
// Ultimately the intended behavior is that both above tests would
|
||||||
|
// succeed.
|
||||||
|
std::hint::unreachable_unchecked();
|
||||||
|
//~[default]^ ERROR: entering unreachable code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn raw_children_of_refmut_can_alias(x: &mut u8) {
|
||||||
|
let child1 = addr_of_mut!(*x);
|
||||||
|
let child2 = addr_of_mut!(*x);
|
||||||
|
// We create two raw aliases of `x`: they have the exact same
|
||||||
|
// tag and can be used interchangeably.
|
||||||
|
child1.write(1);
|
||||||
|
child2.write(2);
|
||||||
|
child1.write(1);
|
||||||
|
child2.write(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn raw_children_of_unique_can_alias(x: Unique<u8>) {
|
||||||
|
let child1 = x.as_ptr();
|
||||||
|
let child2 = x.as_ptr();
|
||||||
|
// Under `-Zmiri-unique-is-unique`, `Unique` accidentally offers more guarantees
|
||||||
|
// than `&mut`. Not because it responds differently to accesses but because
|
||||||
|
// there is no easy way to obtain a copy with the same tag.
|
||||||
|
//
|
||||||
|
// The closest (non-hack) attempt is two calls to `as_ptr`.
|
||||||
|
// - Without `-Zmiri-unique-is-unique`, independent `as_ptr` calls return pointers
|
||||||
|
// with the same tag that can thus be used interchangeably.
|
||||||
|
// - With the current implementation of `-Zmiri-unique-is-unique`, they return cousin
|
||||||
|
// tags with permissions that do not tolerate aliasing.
|
||||||
|
// Eventually we should make such aliasing allowed in some situations
|
||||||
|
// (e.g. when there is no protector), which will probably involve
|
||||||
|
// introducing a new kind of permission.
|
||||||
|
child1.write(1);
|
||||||
|
child2.write(2);
|
||||||
|
//~[uniq]^ ERROR: /write access through .* is forbidden/
|
||||||
|
child1.write(1);
|
||||||
|
child2.write(2);
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
error: Undefined Behavior: write access through <TAG> is forbidden
|
||||||
|
--> $DIR/children-can-alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | child2.write(2);
|
||||||
|
| ^^^^^^^^^^^^^^^ write access through <TAG> is forbidden
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||||
|
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
|
||||||
|
= help: the conflicting tag <TAG> has state Disabled which forbids this child write access
|
||||||
|
help: the accessed tag <TAG> was created here
|
||||||
|
--> $DIR/children-can-alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let child2 = x.as_ptr();
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
||||||
|
--> $DIR/children-can-alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let child2 = x.as_ptr();
|
||||||
|
| ^
|
||||||
|
help: the conflicting tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x1]
|
||||||
|
--> $DIR/children-can-alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | child1.write(1);
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
= help: this transition corresponds to a loss of read and write permissions
|
||||||
|
= note: BACKTRACE (of the first span):
|
||||||
|
= note: inside `raw_children_of_unique_can_alias` at $DIR/children-can-alias.rs:LL:CC
|
||||||
|
note: inside `main`
|
||||||
|
--> $DIR/children-can-alias.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | raw_children_of_unique_can_alias(Unique::new_unchecked(raw));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
32
src/tools/miri/tests/fail/tree_borrows/unique.default.stderr
Normal file
32
src/tools/miri/tests/fail/tree_borrows/unique.default.stderr
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
error: Undefined Behavior: write access through <TAG> is forbidden
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | *uniq.as_ptr() = 3;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ write access through <TAG> is forbidden
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||||
|
= help: the accessed tag <TAG> has state Frozen which forbids this child write access
|
||||||
|
help: the accessed tag <TAG> was created here, in the initial state Reserved
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let refmut = &mut data;
|
||||||
|
| ^^^^^^^^^
|
||||||
|
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | *uniq.as_ptr() = 1; // activation
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||||
|
help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let _definitely_parent = data; // definitely Frozen by now
|
||||||
|
| ^^^^
|
||||||
|
= help: this transition corresponds to a loss of write permissions
|
||||||
|
= note: BACKTRACE (of the first span):
|
||||||
|
= note: inside `main` at $DIR/unique.rs:LL:CC
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
27
src/tools/miri/tests/fail/tree_borrows/unique.rs
Normal file
27
src/tools/miri/tests/fail/tree_borrows/unique.rs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
//@revisions: default uniq
|
||||||
|
//@compile-flags: -Zmiri-tree-borrows
|
||||||
|
//@[uniq]compile-flags: -Zmiri-unique-is-unique
|
||||||
|
|
||||||
|
// A pattern that detects if `Unique` is treated as exclusive or not:
|
||||||
|
// activate the pointer behind a `Unique` then do a read that is parent
|
||||||
|
// iff `Unique` was specially reborrowed.
|
||||||
|
|
||||||
|
#![feature(ptr_internals)]
|
||||||
|
use core::ptr::Unique;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut data = 0u8;
|
||||||
|
let refmut = &mut data;
|
||||||
|
let rawptr = refmut as *mut u8;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let uniq = Unique::new_unchecked(rawptr);
|
||||||
|
*uniq.as_ptr() = 1; // activation
|
||||||
|
let _maybe_parent = *rawptr; // maybe becomes Frozen
|
||||||
|
*uniq.as_ptr() = 2;
|
||||||
|
//~[uniq]^ ERROR: /write access through .* is forbidden/
|
||||||
|
let _definitely_parent = data; // definitely Frozen by now
|
||||||
|
*uniq.as_ptr() = 3;
|
||||||
|
//~[default]^ ERROR: /write access through .* is forbidden/
|
||||||
|
}
|
||||||
|
}
|
38
src/tools/miri/tests/fail/tree_borrows/unique.uniq.stderr
Normal file
38
src/tools/miri/tests/fail/tree_borrows/unique.uniq.stderr
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
error: Undefined Behavior: write access through <TAG> is forbidden
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | *uniq.as_ptr() = 2;
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ write access through <TAG> is forbidden
|
||||||
|
|
|
||||||
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
|
||||||
|
= help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
|
||||||
|
= help: the conflicting tag <TAG> has state Frozen which forbids this child write access
|
||||||
|
help: the accessed tag <TAG> was created here
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | *uniq.as_ptr() = 2;
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
help: the conflicting tag <TAG> was created here, in the initial state Reserved
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let uniq = Unique::new_unchecked(rawptr);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: the conflicting tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | *uniq.as_ptr() = 1; // activation
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
|
||||||
|
help: the conflicting tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
|
||||||
|
--> $DIR/unique.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | let _maybe_parent = *rawptr; // maybe becomes Frozen
|
||||||
|
| ^^^^^^^
|
||||||
|
= help: this transition corresponds to a loss of write permissions
|
||||||
|
= note: BACKTRACE (of the first span):
|
||||||
|
= note: inside `main` at $DIR/unique.rs:LL:CC
|
||||||
|
|
||||||
|
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
|
@ -17,6 +17,7 @@ fn main() {
|
||||||
unsafe fn alignment_check() {
|
unsafe fn alignment_check() {
|
||||||
let data: &mut [u8] = &mut [0; 1024];
|
let data: &mut [u8] = &mut [0; 1024];
|
||||||
name!(data.as_ptr()=>2, "data");
|
name!(data.as_ptr()=>2, "data");
|
||||||
|
name!(data.as_ptr()=>2, "data");
|
||||||
let alloc_id = alloc_id!(data.as_ptr());
|
let alloc_id = alloc_id!(data.as_ptr());
|
||||||
let x = &mut data[1];
|
let x = &mut data[1];
|
||||||
name!(x as *mut _, "data[1]");
|
name!(x as *mut _, "data[1]");
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
Warning: this tree is indicative only. Some tags may have been hidden.
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
0.. 1.. 2.. 10.. 11..100..101..1000..1001..1024
|
0.. 1.. 2.. 10.. 11..100..101..1000..1001..1024
|
||||||
| Act| Act| Act| Act| Act| Act| Act| Act| Act| └─┬──<TAG=root of the allocation>
|
| Act| Act| Act| Act| Act| Act| Act| Act| Act| └─┬──<TAG=root of the allocation>
|
||||||
| Res| Act| Res| Act| Res| Act| Res| Act| Res| └─┬──<TAG=data>
|
| Res| Act| Res| Act| Res| Act| Res| Act| Res| └─┬──<TAG=data, data>
|
||||||
|----| Act|----|?Dis|----|?Dis| ----| ?Dis| ----| ├────<TAG=data[1]>
|
|----| Act|----|?Dis|----|?Dis| ----| ?Dis| ----| ├────<TAG=data[1]>
|
||||||
|----|----|----| Act|----|?Dis| ----| ?Dis| ----| ├────<TAG=data[10]>
|
|----|----|----| Act|----|?Dis| ----| ?Dis| ----| ├────<TAG=data[10]>
|
||||||
|----|----|----|----|----| Frz| ----| ?Dis| ----| ├────<TAG=data[100]>
|
|----|----|----|----|----| Frz| ----| ?Dis| ----| ├────<TAG=data[100]>
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
//@revisions: default uniq
|
||||||
//@compile-flags: -Zmiri-tree-borrows
|
//@compile-flags: -Zmiri-tree-borrows
|
||||||
|
//@[uniq]compile-flags: -Zmiri-unique-is-unique
|
||||||
#![feature(allocator_api)]
|
#![feature(allocator_api)]
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
21
src/tools/miri/tests/pass/tree_borrows/unique.default.stderr
Normal file
21
src/tools/miri/tests/pass/tree_borrows/unique.default.stderr
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 1
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
| Res| └─┬──<TAG=base>
|
||||||
|
| Res| └────<TAG=raw, uniq, uniq>
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 1
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
| Act| └─┬──<TAG=base>
|
||||||
|
| Act| └────<TAG=raw, uniq, uniq>
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 1
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
| Act| └─┬──<TAG=base>
|
||||||
|
| Act| └────<TAG=raw, uniq, uniq>
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
66
src/tools/miri/tests/pass/tree_borrows/unique.rs
Normal file
66
src/tools/miri/tests/pass/tree_borrows/unique.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
//@revisions: default uniq
|
||||||
|
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
|
||||||
|
//@[uniq]compile-flags: -Zmiri-unique-is-unique
|
||||||
|
|
||||||
|
#![feature(ptr_internals)]
|
||||||
|
|
||||||
|
#[path = "../../utils/mod.rs"]
|
||||||
|
mod utils;
|
||||||
|
use utils::macros::*;
|
||||||
|
|
||||||
|
use core::ptr::Unique;
|
||||||
|
|
||||||
|
// Check general handling of Unique
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe {
|
||||||
|
let base = &mut 5u8;
|
||||||
|
let alloc_id = alloc_id!(base);
|
||||||
|
name!(base);
|
||||||
|
|
||||||
|
let raw = &mut *base as *mut u8;
|
||||||
|
name!(raw);
|
||||||
|
|
||||||
|
// We create a `Unique` and expect it to have a fresh tag
|
||||||
|
// and uninitialized permissions.
|
||||||
|
let uniq = Unique::new_unchecked(raw);
|
||||||
|
|
||||||
|
// With `-Zmiri-unique-is-unique`, `Unique::as_ptr` (which is called by
|
||||||
|
// `Vec::as_ptr`) generates pointers with a fresh tag, so to name the actual
|
||||||
|
// `base` pointer we care about we have to walk up the tree a bit.
|
||||||
|
//
|
||||||
|
// We care about naming this specific parent tag because it is the one
|
||||||
|
// that stays `Active` during the entire execution, unlike the leaves
|
||||||
|
// that will be invalidated the next time `as_ptr` is called.
|
||||||
|
//
|
||||||
|
// (We name it twice so that we have an indicator in the output of
|
||||||
|
// whether we got the distance correct:
|
||||||
|
// If the output shows
|
||||||
|
//
|
||||||
|
// |- <XYZ: uniq>
|
||||||
|
// '- <XYZ: uniq>
|
||||||
|
//
|
||||||
|
// then `nth_parent` is not big enough.
|
||||||
|
// The correct value for `nth_parent` should be the minimum
|
||||||
|
// integer for which the output shows
|
||||||
|
//
|
||||||
|
// '- <XYZ: uniq, uniq>
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// Ultimately we want pointers obtained through independent
|
||||||
|
// calls of `as_ptr` to be able to alias, which will probably involve
|
||||||
|
// a new permission that allows aliasing when there is no protector.
|
||||||
|
let nth_parent = if cfg!(uniq) { 2 } else { 0 };
|
||||||
|
name!(uniq.as_ptr()=>nth_parent, "uniq");
|
||||||
|
name!(uniq.as_ptr()=>nth_parent, "uniq");
|
||||||
|
print_state!(alloc_id);
|
||||||
|
|
||||||
|
// We can activate the Unique and use it mutably.
|
||||||
|
*uniq.as_ptr() = 42;
|
||||||
|
print_state!(alloc_id);
|
||||||
|
|
||||||
|
// Write through the raw parent disables the Unique
|
||||||
|
*raw = 42;
|
||||||
|
print_state!(alloc_id);
|
||||||
|
}
|
||||||
|
}
|
24
src/tools/miri/tests/pass/tree_borrows/unique.uniq.stderr
Normal file
24
src/tools/miri/tests/pass/tree_borrows/unique.uniq.stderr
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 1
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
| Res| └─┬──<TAG=base>
|
||||||
|
| Res| └─┬──<TAG=raw>
|
||||||
|
|----| └────<TAG=uniq, uniq>
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 1
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
| Act| └─┬──<TAG=base>
|
||||||
|
| Act| └─┬──<TAG=raw>
|
||||||
|
| Act| └────<TAG=uniq, uniq>
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 1
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
| Act| └─┬──<TAG=base>
|
||||||
|
| Act| └─┬──<TAG=raw>
|
||||||
|
| Dis| └────<TAG=uniq, uniq>
|
||||||
|
──────────────────────────────────────────────────────────────────────
|
|
@ -0,0 +1,6 @@
|
||||||
|
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 2
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
| Res| └────<TAG=base.as_ptr(), base.as_ptr(), raw_parts.0, reconstructed.as_ptr(), reconstructed.as_ptr()>
|
||||||
|
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
|
68
src/tools/miri/tests/pass/tree_borrows/vec_unique.rs
Normal file
68
src/tools/miri/tests/pass/tree_borrows/vec_unique.rs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
//@revisions: default uniq
|
||||||
|
//@compile-flags: -Zmiri-tree-borrows -Zmiri-tag-gc=0
|
||||||
|
//@[uniq]compile-flags: -Zmiri-unique-is-unique
|
||||||
|
|
||||||
|
#![feature(vec_into_raw_parts)]
|
||||||
|
|
||||||
|
#[path = "../../utils/mod.rs"]
|
||||||
|
mod utils;
|
||||||
|
use utils::macros::*;
|
||||||
|
|
||||||
|
// Check general handling of `Unique`:
|
||||||
|
// there is no *explicit* `Unique` being used here, but there is one
|
||||||
|
// hidden a few layers inside `Vec` that should be reflected in the tree structure.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
unsafe {
|
||||||
|
let base = vec![0u8, 1];
|
||||||
|
let alloc_id = alloc_id!(base.as_ptr());
|
||||||
|
|
||||||
|
// With `-Zmiri-unique-is-unique`, `Unique::as_ptr` (which is called by
|
||||||
|
// `Vec::as_ptr`) generates pointers with a fresh tag, so to name the actual
|
||||||
|
// `base` pointer we care about we have to walk up the tree a bit.
|
||||||
|
//
|
||||||
|
// We care about naming this specific parent tag because it is the one
|
||||||
|
// that stays `Active` during the entire execution, unlike the leaves
|
||||||
|
// that will be invalidated the next time `as_ptr` is called.
|
||||||
|
//
|
||||||
|
// (We name it twice so that we have an indicator in the output of
|
||||||
|
// whether we got the distance correct:
|
||||||
|
// If the output shows
|
||||||
|
//
|
||||||
|
// |- <XYZ: uniq>
|
||||||
|
// '- <XYZ: uniq>
|
||||||
|
//
|
||||||
|
// then `nth_parent` is not big enough.
|
||||||
|
// The correct value for `nth_parent` should be the minimum
|
||||||
|
// integer for which the output shows
|
||||||
|
//
|
||||||
|
// '- <XYZ: uniq, uniq>
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// Ultimately we want pointers obtained through independent
|
||||||
|
// calls of `as_ptr` to be able to alias, which will probably involve
|
||||||
|
// a new permission that allows aliasing when there is no protector.
|
||||||
|
let nth_parent = if cfg!(uniq) { 2 } else { 0 };
|
||||||
|
name!(base.as_ptr()=>nth_parent);
|
||||||
|
name!(base.as_ptr()=>nth_parent);
|
||||||
|
|
||||||
|
// Destruct the `Vec`
|
||||||
|
let (ptr, len, cap) = base.into_raw_parts();
|
||||||
|
|
||||||
|
// Expect this to be again the same pointer as the one obtained from `as_ptr`.
|
||||||
|
// Under `-Zmiri-unique-is-unique`, this will be a strict child.
|
||||||
|
name!(ptr, "raw_parts.0");
|
||||||
|
|
||||||
|
// This is where the presence of `Unique` has implications,
|
||||||
|
// because there will be a reborrow here iff the exclusivity of `Unique`
|
||||||
|
// is enforced.
|
||||||
|
let reconstructed = Vec::from_raw_parts(ptr, len, cap);
|
||||||
|
|
||||||
|
// The `as_ptr` here (twice for the same reason as above) return either
|
||||||
|
// the same pointer once more (default) or a strict child (uniq).
|
||||||
|
name!(reconstructed.as_ptr()=>nth_parent);
|
||||||
|
name!(reconstructed.as_ptr()=>nth_parent);
|
||||||
|
|
||||||
|
print_state!(alloc_id, false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
──────────────────────────────────────────────────────────────────────────
|
||||||
|
Warning: this tree is indicative only. Some tags may have been hidden.
|
||||||
|
0.. 2
|
||||||
|
| Act| └─┬──<TAG=root of the allocation>
|
||||||
|
|----| └─┬──<TAG=base.as_ptr(), base.as_ptr()>
|
||||||
|
|----| └─┬──<TAG=raw_parts.0>
|
||||||
|
|----| └────<TAG=reconstructed.as_ptr(), reconstructed.as_ptr()>
|
||||||
|
──────────────────────────────────────────────────────────────────────────
|
|
@ -1,6 +1,7 @@
|
||||||
//@revisions: stack tree
|
//@revisions: stack tree tree_uniq
|
||||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
|
||||||
//@compile-flags: -Zmiri-strict-provenance
|
//@compile-flags: -Zmiri-strict-provenance
|
||||||
|
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||||
|
//@[tree_uniq]compile-flags: -Zmiri-tree-borrows -Zmiri-unique-is-unique
|
||||||
#![feature(iter_advance_by, iter_next_chunk)]
|
#![feature(iter_advance_by, iter_next_chunk)]
|
||||||
|
|
||||||
// Gather all references from a mutable iterator and make sure Miri notices if
|
// Gather all references from a mutable iterator and make sure Miri notices if
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
//@revisions: stack tree
|
//@revisions: stack tree tree_uniq
|
||||||
//@[tree]compile-flags: -Zmiri-tree-borrows
|
|
||||||
//@compile-flags: -Zmiri-strict-provenance
|
//@compile-flags: -Zmiri-strict-provenance
|
||||||
|
//@[tree]compile-flags: -Zmiri-tree-borrows
|
||||||
|
//@[tree_uniq]compile-flags: -Zmiri-tree-borrows -Zmiri-unique-is-unique
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator<Item = &'a mut T>) {
|
fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator<Item = &'a mut T>) {
|
||||||
|
|
2
src/tools/miri/tests/pass/vecdeque.tree_uniq.stdout
Normal file
2
src/tools/miri/tests/pass/vecdeque.tree_uniq.stdout
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[2, 2] Iter([2, 2], [])
|
||||||
|
Iter([], [])
|
|
@ -47,12 +47,12 @@ macro_rules! name {
|
||||||
($ptr:expr) => {
|
($ptr:expr) => {
|
||||||
crate::utils::macros::name!($ptr => 0, stringify!($ptr));
|
crate::utils::macros::name!($ptr => 0, stringify!($ptr));
|
||||||
};
|
};
|
||||||
($ptr:expr => $nb:expr) => {
|
($ptr:expr => $nth_parent:expr) => {
|
||||||
crate::utils::macros::name!($ptr => $nb, stringify!($ptr));
|
crate::utils::macros::name!($ptr => $nth_parent, stringify!($ptr));
|
||||||
};
|
};
|
||||||
($ptr:expr => $nb:expr, $name:expr) => {
|
($ptr:expr => $nth_parent:expr, $name:expr) => {
|
||||||
let name = $name.as_bytes();
|
let name = $name.as_bytes();
|
||||||
crate::utils::miri_extern::miri_pointer_name($ptr as *const u8 as *const (), $nb, name);
|
crate::utils::miri_extern::miri_pointer_name($ptr as *const u8 as *const (), $nth_parent, name);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ extern "Rust" {
|
||||||
///
|
///
|
||||||
/// This is only useful as an input to `miri_print_borrow_stacks`, and it is a separate call because
|
/// This is only useful as an input to `miri_print_borrow_stacks`, and it is a separate call because
|
||||||
/// getting a pointer to an allocation at runtime can change the borrow stacks in the allocation.
|
/// getting a pointer to an allocation at runtime can change the borrow stacks in the allocation.
|
||||||
/// This function should be considered unstable. It exists only to support `miri_print_borrow_stacks` and so
|
/// This function should be considered unstable. It exists only to support `miri_print_borrow_state` and so
|
||||||
/// inherits all of its instability.
|
/// inherits all of its instability.
|
||||||
pub fn miri_get_alloc_id(ptr: *const ()) -> u64;
|
pub fn miri_get_alloc_id(ptr: *const ()) -> u64;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue