diff --git a/src/librustc/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs
index a73fe2b8a1ab3..1bd02dfeacaad 100644
--- a/src/librustc/ich/impls_mir.rs
+++ b/src/librustc/ich/impls_mir.rs
@@ -257,6 +257,9 @@ for mir::StatementKind<'gcx> {
mir::StatementKind::EndRegion(ref region_scope) => {
region_scope.hash_stable(hcx, hasher);
}
+ mir::StatementKind::EscapeToRaw(ref place) => {
+ place.hash_stable(hcx, hasher);
+ }
mir::StatementKind::Retag { fn_entry, ref place } => {
fn_entry.hash_stable(hcx, hasher);
place.hash_stable(hcx, hasher);
diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs
index 36bc2edcf584e..0817c2e8bf273 100644
--- a/src/librustc/mir/mod.rs
+++ b/src/librustc/mir/mod.rs
@@ -1766,6 +1766,13 @@ pub enum StatementKind<'tcx> {
place: Place<'tcx>,
},
+ /// Escape the given reference to a raw pointer, so that it can be accessed
+ /// without precise provenance tracking. These statements are currently only interpreted
+ /// by miri and only generated when "-Z mir-emit-retag" is passed.
+ /// See
+ /// for more details.
+ EscapeToRaw(Operand<'tcx>),
+
/// Mark one terminating point of a region scope (i.e. static region).
/// (The starting point(s) arise implicitly from borrows.)
EndRegion(region::Scope),
@@ -1827,6 +1834,7 @@ impl<'tcx> Debug for Statement<'tcx> {
EndRegion(ref ce) => write!(fmt, "EndRegion({})", ty::ReScope(*ce)),
Retag { fn_entry, ref place } =>
write!(fmt, "Retag({}{:?})", if fn_entry { "[fn entry] " } else { "" }, place),
+ EscapeToRaw(ref place) => write!(fmt, "EscapeToRaw({:?})", place),
StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place),
StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place),
SetDiscriminant {
@@ -2968,6 +2976,7 @@ EnumTypeFoldableImpl! {
(StatementKind::StorageDead)(a),
(StatementKind::InlineAsm) { asm, outputs, inputs },
(StatementKind::Retag) { fn_entry, place },
+ (StatementKind::EscapeToRaw)(place),
(StatementKind::EndRegion)(a),
(StatementKind::AscribeUserType)(a, v, b),
(StatementKind::Nop),
diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs
index c130e047e47d1..2a994ee0509c2 100644
--- a/src/librustc/mir/visit.rs
+++ b/src/librustc/mir/visit.rs
@@ -385,6 +385,9 @@ macro_rules! make_mir_visitor {
location
);
}
+ StatementKind::EscapeToRaw(ref $($mutability)* op) => {
+ self.visit_operand(op, location);
+ }
StatementKind::StorageLive(ref $($mutability)* local) => {
self.visit_local(
local,
@@ -1022,7 +1025,7 @@ pub enum MutatingUseContext<'tcx> {
/// f(&mut x.y);
///
Projection,
- /// Retagging (updating the "Stacked Borrows" tag)
+ /// Retagging, a "Stacked Borrows" shadow state operation
Retag,
}
diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs
index c8c8e02bf05f7..8bda2c98594e5 100644
--- a/src/librustc_codegen_llvm/mir/statement.rs
+++ b/src/librustc_codegen_llvm/mir/statement.rs
@@ -105,8 +105,9 @@ impl FunctionCx<'a, 'll, 'tcx> {
bx
}
mir::StatementKind::FakeRead(..) |
- mir::StatementKind::EndRegion(_) |
+ mir::StatementKind::EndRegion(..) |
mir::StatementKind::Retag { .. } |
+ mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => bx,
}
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index d4f00ab3bb91a..533b777d67d73 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -601,6 +601,7 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
StatementKind::Nop
| StatementKind::AscribeUserType(..)
| StatementKind::Retag { .. }
+ | StatementKind::EscapeToRaw { .. }
| StatementKind::StorageLive(..) => {
// `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
// to borrow check.
diff --git a/src/librustc_mir/borrow_check/nll/invalidation.rs b/src/librustc_mir/borrow_check/nll/invalidation.rs
index cfe03c2d1c71e..576509c0fddda 100644
--- a/src/librustc_mir/borrow_check/nll/invalidation.rs
+++ b/src/librustc_mir/borrow_check/nll/invalidation.rs
@@ -137,6 +137,7 @@ impl<'cx, 'tcx, 'gcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx, 'gcx> {
StatementKind::Nop |
StatementKind::AscribeUserType(..) |
StatementKind::Retag { .. } |
+ StatementKind::EscapeToRaw { .. } |
StatementKind::StorageLive(..) => {
// `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
// to borrow check.
diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
index 734ddbc3ab9a7..3c2301ba4a7d4 100644
--- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs
@@ -1290,11 +1290,12 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}
StatementKind::FakeRead(..)
- | StatementKind::StorageLive(_)
- | StatementKind::StorageDead(_)
+ | StatementKind::StorageLive(..)
+ | StatementKind::StorageDead(..)
| StatementKind::InlineAsm { .. }
| StatementKind::EndRegion(_)
| StatementKind::Retag { .. }
+ | StatementKind::EscapeToRaw { .. }
| StatementKind::Nop => {}
}
}
diff --git a/src/librustc_mir/build/expr/as_place.rs b/src/librustc_mir/build/expr/as_place.rs
index 77746e5538d65..cb3c88876a3a8 100644
--- a/src/librustc_mir/build/expr/as_place.rs
+++ b/src/librustc_mir/build/expr/as_place.rs
@@ -86,6 +86,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// region_scope=None so place indexes live forever. They are scalars so they
// do not need storage annotations, and they are often copied between
// places.
+ // Making this a *fresh* temporary also means we do not have to worry about
+ // the index changing later: Nothing will ever change this temporary.
+ // The "retagging" transformation (for Stacked Borrows) relies on this.
let idx = unpack!(block = this.as_temp(block, None, index, Mutability::Mut));
// bounds check:
diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs
index 011887090eefe..cbcc6709c0199 100644
--- a/src/librustc_mir/const_eval.rs
+++ b/src/librustc_mir/const_eval.rs
@@ -351,7 +351,6 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
type MemoryMap = FxHashMap, Allocation)>;
const STATIC_KIND: Option = None; // no copying of statics allowed
- const ENABLE_PTR_TRACKING_HOOKS: bool = false; // we don't have no provenance
#[inline(always)]
fn enforce_validity(_ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index 69d2a89b5f237..811da9e1acca7 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -339,6 +339,7 @@ impl<'a, 'gcx, 'tcx> BitDenotation for Borrows<'a, 'gcx, 'tcx> {
mir::StatementKind::SetDiscriminant { .. } |
mir::StatementKind::StorageLive(..) |
mir::StatementKind::Retag { .. } |
+ mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => {}
diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs
index 874e862de238b..e6e165ef3de4a 100644
--- a/src/librustc_mir/dataflow/move_paths/builder.rs
+++ b/src/librustc_mir/dataflow/move_paths/builder.rs
@@ -301,8 +301,9 @@ impl<'b, 'a, 'gcx, 'tcx> Gatherer<'b, 'a, 'gcx, 'tcx> {
span_bug!(stmt.source_info.span,
"SetDiscriminant should not exist during borrowck");
}
- StatementKind::EndRegion(_) |
+ StatementKind::EndRegion(..) |
StatementKind::Retag { .. } |
+ StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {}
}
diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs
index 06748d60e4583..118539fc58ebf 100644
--- a/src/librustc_mir/interpret/cast.rs
+++ b/src/librustc_mir/interpret/cast.rs
@@ -44,28 +44,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
}
Misc => {
- let src_layout = src.layout;
let src = self.read_immediate(src)?;
- let src = if M::ENABLE_PTR_TRACKING_HOOKS && src_layout.ty.is_region_ptr() {
- // The only `Misc` casts on references are those creating raw pointers.
- assert!(dest.layout.ty.is_unsafe_ptr());
- // For the purpose of the "ptr tag hooks", treat this as creating
- // a new, raw reference.
- let place = self.ref_to_mplace(src)?;
- self.create_ref(place, None)?
- } else {
- *src
- };
-
- if self.type_is_fat_ptr(src_layout.ty) {
- match (src, self.type_is_fat_ptr(dest.layout.ty)) {
+ if self.type_is_fat_ptr(src.layout.ty) {
+ match (*src, self.type_is_fat_ptr(dest.layout.ty)) {
// pointers to extern types
(Immediate::Scalar(_),_) |
// slices and trait objects to other slices/trait objects
(Immediate::ScalarPair(..), true) => {
// No change to immediate
- self.write_immediate(src, dest)?;
+ self.write_immediate(*src, dest)?;
}
// slices and trait objects to thin pointers (dropping the metadata)
(Immediate::ScalarPair(data, _), false) => {
@@ -73,11 +61,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
}
}
} else {
- match src_layout.variants {
+ match src.layout.variants {
layout::Variants::Single { index } => {
- if let Some(def) = src_layout.ty.ty_adt_def() {
+ if let Some(def) = src.layout.ty.ty_adt_def() {
// Cast from a univariant enum
- assert!(src_layout.is_zst());
+ assert!(src.layout.is_zst());
let discr_val = def
.discriminant_for_variant(*self.tcx, index)
.val;
@@ -90,8 +78,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
layout::Variants::NicheFilling { .. } => {},
}
- let src = src.to_scalar()?;
- let dest_val = self.cast_scalar(src, src_layout, dest.layout)?;
+ let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
self.write_scalar(dest_val, dest)?;
}
}
diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs
index cb2a750f4e3b6..7c704e9791140 100644
--- a/src/librustc_mir/interpret/intrinsics.rs
+++ b/src/librustc_mir/interpret/intrinsics.rs
@@ -183,8 +183,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
} else if Some(def_id) == self.tcx.lang_items().panic_fn() {
assert!(args.len() == 1);
// &(&'static str, &'static str, u32, u32)
- let ptr = self.read_immediate(args[0])?;
- let place = self.ref_to_mplace(ptr)?;
+ let place = self.deref_operand(args[0])?;
let (msg, file, line, col) = (
self.mplace_field(place, 0)?,
self.mplace_field(place, 1)?,
@@ -192,9 +191,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
self.mplace_field(place, 3)?,
);
- let msg_place = self.ref_to_mplace(self.read_immediate(msg.into())?)?;
+ let msg_place = self.deref_operand(msg.into())?;
let msg = Symbol::intern(self.read_str(msg_place)?);
- let file_place = self.ref_to_mplace(self.read_immediate(file.into())?)?;
+ let file_place = self.deref_operand(file.into())?;
let file = Symbol::intern(self.read_str(file_place)?);
let line = self.read_scalar(line.into())?.to_u32()?;
let col = self.read_scalar(col.into())?.to_u32()?;
@@ -203,17 +202,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
assert!(args.len() == 2);
// &'static str, &(&'static str, u32, u32)
let msg = args[0];
- let ptr = self.read_immediate(args[1])?;
- let place = self.ref_to_mplace(ptr)?;
+ let place = self.deref_operand(args[1])?;
let (file, line, col) = (
self.mplace_field(place, 0)?,
self.mplace_field(place, 1)?,
self.mplace_field(place, 2)?,
);
- let msg_place = self.ref_to_mplace(self.read_immediate(msg.into())?)?;
+ let msg_place = self.deref_operand(msg.into())?;
let msg = Symbol::intern(self.read_str(msg_place)?);
- let file_place = self.ref_to_mplace(self.read_immediate(file.into())?)?;
+ let file_place = self.deref_operand(file.into())?;
let file = Symbol::intern(self.read_str(file_place)?);
let line = self.read_scalar(line.into())?.to_u32()?;
let col = self.read_scalar(col.into())?.to_u32()?;
diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs
index 27cf28ef41e8a..55da12a68e385 100644
--- a/src/librustc_mir/interpret/machine.rs
+++ b/src/librustc_mir/interpret/machine.rs
@@ -95,11 +95,6 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
/// that is added to the memory so that the work is not done twice.
const STATIC_KIND: Option;
- /// As an optimization, you can prevent the pointer tracking hooks from ever being
- /// called. You should only do this if you do not care about provenance tracking.
- /// This controls the `tag_reference` and `tag_dereference` hooks.
- const ENABLE_PTR_TRACKING_HOOKS: bool;
-
/// Whether to enforce the validity invariant
fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool;
@@ -211,18 +206,6 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
kind: MemoryKind,
) -> EvalResult<'tcx, Pointer>;
- /// Executed when evaluating the `&` operator: Creating a new reference.
- /// This has the chance to adjust the tag. It should not change anything else!
- /// `mutability` can be `None` in case a raw ptr is being created.
- #[inline]
- fn tag_reference(
- _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
- place: MPlaceTy<'tcx, Self::PointerTag>,
- _mutability: Option,
- ) -> EvalResult<'tcx, Scalar> {
- Ok(place.ptr)
- }
-
/// Executed when evaluating the `*` operator: Following a reference.
/// This has the chance to adjust the tag. It should not change anything else!
/// `mutability` can be `None` in case a raw ptr is being dereferenced.
@@ -235,7 +218,7 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
Ok(place.ptr)
}
- /// Execute a validation operation
+ /// Execute a retagging operation
#[inline]
fn retag(
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
@@ -244,4 +227,13 @@ pub trait Machine<'a, 'mir, 'tcx>: Sized {
) -> EvalResult<'tcx> {
Ok(())
}
+
+ /// Execute an escape-to-raw operation
+ #[inline]
+ fn escape_to_raw(
+ _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
+ _ptr: OpTy<'tcx, Self::PointerTag>,
+ ) -> EvalResult<'tcx> {
+ Ok(())
+ }
}
diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs
index 83a2d14b7ca4c..a97bf95e7345d 100644
--- a/src/librustc_mir/interpret/operand.rs
+++ b/src/librustc_mir/interpret/operand.rs
@@ -555,17 +555,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
})
}
- // Take an operand, representing a pointer, and dereference it to a place -- that
- // will always be a MemPlace.
- pub(super) fn deref_operand(
- &self,
- src: OpTy<'tcx, M::PointerTag>,
- ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
- let val = self.read_immediate(src)?;
- trace!("deref to {} on {:?}", val.layout.ty, *val);
- Ok(self.ref_to_mplace(val)?)
- }
-
pub fn operand_projection(
&self,
base: OpTy<'tcx, M::PointerTag>,
diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs
index 19430c85cf73c..510b5826e489b 100644
--- a/src/librustc_mir/interpret/place.rs
+++ b/src/librustc_mir/interpret/place.rs
@@ -151,6 +151,16 @@ impl MemPlace {
// it now must be aligned.
self.to_scalar_ptr_align().0.to_ptr()
}
+
+ /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
+ /// This is the inverse of `ref_to_mplace`.
+ #[inline(always)]
+ pub fn to_ref(self) -> Immediate {
+ match self.meta {
+ None => Immediate::Scalar(self.ptr.into()),
+ Some(meta) => Immediate::ScalarPair(self.ptr.into(), meta.into()),
+ }
+ }
}
impl<'tcx, Tag> MPlaceTy<'tcx, Tag> {
@@ -266,7 +276,9 @@ where
M::MemoryMap: AllocMap, Allocation)>,
{
/// Take a value, which represents a (thin or fat) reference, and make it a place.
- /// Alignment is just based on the type. This is the inverse of `create_ref`.
+ /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
+ /// This does NOT call the "deref" machine hook, so it does NOT count as a
+ /// deref as far as Stacked Borrows is concerned. Use `deref_operand` for that!
pub fn ref_to_mplace(
&self,
val: ImmTy<'tcx, M::PointerTag>,
@@ -274,42 +286,35 @@ where
let pointee_type = val.layout.ty.builtin_deref(true).unwrap().ty;
let layout = self.layout_of(pointee_type)?;
- let align = layout.align;
- let meta = val.to_meta()?;
- let ptr = val.to_scalar_ptr()?;
- let mplace = MemPlace { ptr, align, meta };
- let mut mplace = MPlaceTy { mplace, layout };
- // Pointer tag tracking might want to adjust the tag.
- if M::ENABLE_PTR_TRACKING_HOOKS {
- let mutbl = match val.layout.ty.sty {
- // `builtin_deref` considers boxes immutable, that's useless for our purposes
- ty::Ref(_, _, mutbl) => Some(mutbl),
- ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
- ty::RawPtr(_) => None,
- _ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
- };
- mplace.mplace.ptr = M::tag_dereference(self, mplace, mutbl)?;
- }
- // Done
- Ok(mplace)
+ let mplace = MemPlace {
+ ptr: val.to_scalar_ptr()?,
+ align: layout.align,
+ meta: val.to_meta()?,
+ };
+ Ok(MPlaceTy { mplace, layout })
}
- /// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space.
- /// This is the inverse of `ref_to_mplace`.
- /// `mutbl` indicates whether we are create a shared or mutable ref, or a raw pointer (`None`).
- pub fn create_ref(
- &mut self,
- mut place: MPlaceTy<'tcx, M::PointerTag>,
- mutbl: Option,
- ) -> EvalResult<'tcx, Immediate> {
- // Pointer tag tracking might want to adjust the tag
- if M::ENABLE_PTR_TRACKING_HOOKS {
- place.mplace.ptr = M::tag_reference(self, place, mutbl)?
- }
- Ok(match place.meta {
- None => Immediate::Scalar(place.ptr.into()),
- Some(meta) => Immediate::ScalarPair(place.ptr.into(), meta.into()),
- })
+ // Take an operand, representing a pointer, and dereference it to a place -- that
+ // will always be a MemPlace. Lives in `place.rs` because it creates a place.
+ // This calls the "deref" machine hook, and counts as a deref as far as
+ // Stacked Borrows is concerned.
+ pub fn deref_operand(
+ &self,
+ src: OpTy<'tcx, M::PointerTag>,
+ ) -> EvalResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
+ let val = self.read_immediate(src)?;
+ trace!("deref to {} on {:?}", val.layout.ty, *val);
+ let mut place = self.ref_to_mplace(val)?;
+ // Pointer tag tracking might want to adjust the tag.
+ let mutbl = match val.layout.ty.sty {
+ // `builtin_deref` considers boxes immutable, that's useless for our purposes
+ ty::Ref(_, _, mutbl) => Some(mutbl),
+ ty::Adt(def, _) if def.is_box() => Some(hir::MutMutable),
+ ty::RawPtr(_) => None,
+ _ => bug!("Unexpected pointer type {}", val.layout.ty.sty),
+ };
+ place.mplace.ptr = M::tag_dereference(self, place, mutbl)?;
+ Ok(place)
}
/// Offset a pointer to project to a field. Unlike place_field, this is always
diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs
index db055204c0a19..ac13e5982dae3 100644
--- a/src/librustc_mir/interpret/step.rs
+++ b/src/librustc_mir/interpret/step.rs
@@ -12,7 +12,7 @@
//!
//! The main entry point is the `step` method.
-use rustc::{hir, mir};
+use rustc::mir;
use rustc::ty::layout::LayoutOf;
use rustc::mir::interpret::{EvalResult, Scalar, PointerArithmetic};
@@ -118,12 +118,17 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
// interpreter is solely intended for borrowck'ed code.
FakeRead(..) => {}
- // Retagging.
+ // Stacked Borrows.
Retag { fn_entry, ref place } => {
let dest = self.eval_place(place)?;
M::retag(self, fn_entry, dest)?;
}
+ EscapeToRaw(ref op) => {
+ let op = self.eval_operand(op, None)?;
+ M::escape_to_raw(self, op)?;
+ }
+ // Statements we do not track.
EndRegion(..) => {}
AscribeUserType(..) => {}
@@ -247,19 +252,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
)?;
}
- Ref(_, borrow_kind, ref place) => {
+ Ref(_, _, ref place) => {
let src = self.eval_place(place)?;
let val = self.force_allocation(src)?;
- let mutbl = match borrow_kind {
- mir::BorrowKind::Mut { .. } |
- mir::BorrowKind::Unique =>
- hir::MutMutable,
- mir::BorrowKind::Shared |
- mir::BorrowKind::Shallow =>
- hir::MutImmutable,
- };
- let val = self.create_ref(val, Some(mutbl))?;
- self.write_immediate(val, dest)?;
+ self.write_immediate(val.to_ref(), dest)?;
}
NullaryOp(mir::NullOp::Box, _) => {
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index b5df625b3028b..6070b31d3e7a3 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -402,7 +402,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
ty::InstanceDef::Virtual(_, idx) => {
let ptr_size = self.pointer_size();
let ptr_align = self.tcx.data_layout.pointer_align;
- let ptr = self.ref_to_mplace(self.read_immediate(args[0])?)?;
+ let ptr = self.deref_operand(args[0])?;
let vtable = ptr.vtable()?;
let fn_ptr = self.memory.read_ptr_sized(
vtable.offset(ptr_size * (idx as u64 + 3), self)?,
@@ -447,10 +447,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
};
let arg = OpTy {
- op: Operand::Immediate(self.create_ref(
- place,
- None // this is a "raw reference"
- )?),
+ op: Operand::Immediate(place.to_ref()),
layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
};
diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs
index 8c8b3e2ca77c4..97bce651c05a1 100644
--- a/src/librustc_mir/interpret/validity.rs
+++ b/src/librustc_mir/interpret/validity.rs
@@ -322,13 +322,10 @@ impl<'rt, 'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>>
}
}
}
- // Turn ptr into place.
- // `ref_to_mplace` also calls the machine hook for (re)activating the tag,
- // which in turn will (in full miri) check if the pointer is dereferencable.
- let place = self.ecx.ref_to_mplace(value)?;
// Recursive checking
if let Some(ref mut ref_tracking) = self.ref_tracking {
assert!(self.const_mode, "We should only do recursie checking in const mode");
+ let place = self.ecx.ref_to_mplace(value)?;
if size != Size::ZERO {
// Non-ZST also have to be dereferencable
let ptr = try_validation!(place.ptr.to_ptr(),
diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs
index 76a8501fb177a..2d548dd8cf10a 100644
--- a/src/librustc_mir/shim.rs
+++ b/src/librustc_mir/shim.rs
@@ -222,6 +222,15 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
);
if let Some(..) = ty {
+ // The first argument (index 0), but add 1 for the return value.
+ let dropee_ptr = Place::Local(Local::new(1+0));
+ if tcx.sess.opts.debugging_opts.mir_emit_retag {
+ // We use raw ptr operations, better prepare the alias tracking for that
+ mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
+ source_info,
+ kind: StatementKind::EscapeToRaw(Operand::Copy(dropee_ptr.clone())),
+ })
+ }
let patch = {
let param_env = tcx.param_env(def_id).with_reveal_all();
let mut elaborator = DropShimElaborator {
@@ -230,7 +239,7 @@ fn build_drop_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
tcx,
param_env
};
- let dropee = Place::Local(Local::new(1+0)).deref();
+ let dropee = dropee_ptr.deref();
let resume_block = elaborator.patch.resume_block();
elaborate_drops::elaborate_drop(
&mut elaborator,
diff --git a/src/librustc_mir/transform/add_retag.rs b/src/librustc_mir/transform/add_retag.rs
index a50011cf5a15e..be7e34e2dcb3c 100644
--- a/src/librustc_mir/transform/add_retag.rs
+++ b/src/librustc_mir/transform/add_retag.rs
@@ -20,42 +20,46 @@ use transform::{MirPass, MirSource};
pub struct AddRetag;
-/// Determines whether this place is local: If it is part of a local variable.
-/// We do not consider writes to pointers local, only writes that immediately assign
-/// to a local variable.
-/// One important property here is that evaluating the place immediately after
-/// the assignment must produce the same place as what was used during the assignment.
-fn is_local<'tcx>(
+/// Determines whether this place is "stable": Whether, if we evaluate it again
+/// after the assignment, we can be sure to obtain the same place value.
+/// (Concurrent accesses by other threads are no problem as these are anyway non-atomic
+/// copies. Data races are UB.)
+fn is_stable<'tcx>(
place: &Place<'tcx>,
) -> bool {
use rustc::mir::Place::*;
match *place {
- Local { .. } => true,
- Promoted(_) |
- Static(_) => false,
+ // Locals and statics have stable addresses, for sure
+ Local { .. } |
+ Promoted { .. } |
+ Static { .. } =>
+ true,
+ // Recurse for projections
Projection(ref proj) => {
match proj.elem {
- ProjectionElem::Deref |
- ProjectionElem::Index(_) =>
- // Which place these point to depends on external circumstances
- // (a local storing the array index, the current value of
- // the projection base), so we stop tracking here.
+ // Which place this evaluates to can change with any memory write,
+ // so cannot assume this to be stable.
+ ProjectionElem::Deref =>
false,
+ // Array indices are intersting, but MIR building generates a *fresh*
+ // temporary for every array access, so the index cannot be changed as
+ // a side-effect.
+ ProjectionElem::Index { .. } |
+ // The rest is completely boring, they just offset by a constant.
ProjectionElem::Field { .. } |
ProjectionElem::ConstantIndex { .. } |
ProjectionElem::Subslice { .. } |
ProjectionElem::Downcast { .. } =>
- // These just offset by a constant, entirely independent of everything else.
- is_local(&proj.base),
+ is_stable(&proj.base),
}
}
}
}
-/// Determine whether this type has a reference in it, recursing below compound types but
+/// Determine whether this type may have a reference in it, recursing below compound types but
/// not below references.
-fn has_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
+fn may_have_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> bool {
match ty.sty {
// Primitive types that are not references
ty::Bool | ty::Char |
@@ -68,12 +72,12 @@ fn has_reference<'a, 'gcx, 'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> b
ty::Adt(..) if ty.is_box() => true,
// Compound types
ty::Array(ty, ..) | ty::Slice(ty) =>
- has_reference(ty, tcx),
+ may_have_reference(ty, tcx),
ty::Tuple(tys) =>
- tys.iter().any(|ty| has_reference(ty, tcx)),
+ tys.iter().any(|ty| may_have_reference(ty, tcx)),
ty::Adt(adt, substs) =>
adt.variants.iter().any(|v| v.fields.iter().any(|f|
- has_reference(f.ty(tcx, substs), tcx)
+ may_have_reference(f.ty(tcx, substs), tcx)
)),
// Conservative fallback
_ => true,
@@ -92,7 +96,9 @@ impl MirPass for AddRetag {
let (span, arg_count) = (mir.span, mir.arg_count);
let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();
let needs_retag = |place: &Place<'tcx>| {
- is_local(place) && has_reference(place.ty(&*local_decls, tcx).to_ty(tcx), tcx)
+ // FIXME: Instead of giving up for unstable places, we should introduce
+ // a temporary and retag on that.
+ is_stable(place) && may_have_reference(place.ty(&*local_decls, tcx).to_ty(tcx), tcx)
};
// PART 1
@@ -118,23 +124,29 @@ impl MirPass for AddRetag {
}
// PART 2
- // Retag return values of functions.
+ // Retag return values of functions. Also escape-to-raw the argument of `drop`.
// We collect the return destinations because we cannot mutate while iterating.
let mut returns: Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
for block_data in basic_blocks.iter_mut() {
- match block_data.terminator {
- Some(Terminator { kind: TerminatorKind::Call { ref destination, .. },
- source_info }) => {
+ match block_data.terminator().kind {
+ TerminatorKind::Call { ref destination, .. } => {
// Remember the return destination for later
if let Some(ref destination) = destination {
if needs_retag(&destination.0) {
- returns.push((source_info, destination.0.clone(), destination.1));
+ returns.push((
+ block_data.terminator().source_info,
+ destination.0.clone(),
+ destination.1,
+ ));
}
}
}
+ TerminatorKind::Drop { .. } |
+ TerminatorKind::DropAndReplace { .. } => {
+ // `Drop` is also a call, but it doesn't return anything so we are good.
+ }
_ => {
// Not a block ending in a Call -> ignore.
- // `Drop` is also a call, but it doesn't return anything so we are good.
}
}
}
@@ -153,21 +165,43 @@ impl MirPass for AddRetag {
// iterate backwards using indices.
for i in (0..block_data.statements.len()).rev() {
match block_data.statements[i].kind {
- // Assignments can make values obtained elsewhere "local".
- // We could try to be smart here and e.g. only retag if the assignment
- // loaded from memory, but that seems risky: We might miss a subtle corner
- // case.
- StatementKind::Assign(ref place, box Rvalue::Use(..))
- if needs_retag(place) => {
+ // If we are casting *from* a reference, we may have to escape-to-raw.
+ StatementKind::Assign(_, box Rvalue::Cast(
+ CastKind::Misc,
+ ref src,
+ dest_ty,
+ )) => {
+ let src_ty = src.ty(&*local_decls, tcx);
+ if src_ty.is_region_ptr() {
+ // The only `Misc` casts on references are those creating raw pointers.
+ assert!(dest_ty.is_unsafe_ptr());
+ // Insert escape-to-raw before the cast. We are not concerned
+ // with stability here: Our EscapeToRaw will not change the value
+ // that the cast will then use.
+ // `src` might be a "move", but we rely on this not actually moving
+ // but just doing a memcpy. It is crucial that we do EscapeToRaw
+ // on the src because we need it with its original type.
+ let source_info = block_data.statements[i].source_info;
+ block_data.statements.insert(i, Statement {
+ source_info,
+ kind: StatementKind::EscapeToRaw(src.clone()),
+ });
+ }
+ }
+ // Assignments of reference or ptr type are the ones where we may have
+ // to update tags. This includes `x = &[mut] ...` and hence
+ // we also retag after taking a reference!
+ StatementKind::Assign(ref place, _) if needs_retag(place) => {
// Insert a retag after the assignment.
let source_info = block_data.statements[i].source_info;
- block_data.statements.insert(i+1,Statement {
+ block_data.statements.insert(i+1, Statement {
source_info,
kind: StatementKind::Retag { fn_entry: false, place: place.clone() },
});
}
+ // Do nothing for the rest
_ => {},
- }
+ };
}
}
}
diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs
index c28bb0ca35704..4ebeebca2273b 100644
--- a/src/librustc_mir/transform/check_unsafety.rs
+++ b/src/librustc_mir/transform/check_unsafety.rs
@@ -114,6 +114,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
StatementKind::StorageDead(..) |
StatementKind::EndRegion(..) |
StatementKind::Retag { .. } |
+ StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {
// safe (at least as emitted during MIR construction)
diff --git a/src/librustc_mir/transform/generator.rs b/src/librustc_mir/transform/generator.rs
index 5889fabee9d6e..a292887b17e47 100644
--- a/src/librustc_mir/transform/generator.rs
+++ b/src/librustc_mir/transform/generator.rs
@@ -684,6 +684,13 @@ fn create_generator_drop_shim<'a, 'tcx>(
is_block_tail: None,
is_user_variable: None,
};
+ if tcx.sess.opts.debugging_opts.mir_emit_retag {
+ // Alias tracking must know we changed the type
+ mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
+ source_info,
+ kind: StatementKind::EscapeToRaw(Operand::Copy(Place::Local(self_arg()))),
+ })
+ }
no_landing_pads(tcx, &mut mir);
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index ca9c4eb9b8bb9..5fd8332129664 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -1168,6 +1168,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
StatementKind::InlineAsm {..} |
StatementKind::EndRegion(_) |
StatementKind::Retag { .. } |
+ StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {}
}
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index 1e19348595057..ed13063cfdf17 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -242,6 +242,7 @@ fn check_statement(
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag { .. }
+ | StatementKind::EscapeToRaw { .. }
| StatementKind::EndRegion(_)
| StatementKind::AscribeUserType(..)
| StatementKind::Nop => Ok(()),
diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs
index c1c127fa8d648..445ffbbcf3407 100644
--- a/src/librustc_mir/transform/remove_noop_landing_pads.rs
+++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs
@@ -65,10 +65,11 @@ impl RemoveNoopLandingPads {
// turn a landing pad to a non-nop
}
- StatementKind::Assign(_, _) |
+ StatementKind::Assign { .. } |
StatementKind::SetDiscriminant { .. } |
StatementKind::InlineAsm { .. } |
- StatementKind::Retag { .. } => {
+ StatementKind::Retag { .. } |
+ StatementKind::EscapeToRaw { .. } => {
return false;
}
}
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index a5a19f04b7e8e..8f026c706fdf7 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -163,6 +163,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::EndRegion(_) |
mir::StatementKind::Retag { .. } |
+ mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => continue,
mir::StatementKind::SetDiscriminant{ .. } =>
diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs
index ecfe7d13782de..68840ed4a4804 100644
--- a/src/librustc_passes/mir_stats.rs
+++ b/src/librustc_passes/mir_stats.rs
@@ -85,6 +85,7 @@ impl<'a, 'tcx> mir_visit::Visitor<'tcx> for StatCollector<'a, 'tcx> {
StatementKind::FakeRead(..) => "StatementKind::FakeRead",
StatementKind::EndRegion(..) => "StatementKind::EndRegion",
StatementKind::Retag { .. } => "StatementKind::Retag",
+ StatementKind::EscapeToRaw { .. } => "StatementKind::EscapeToRaw",
StatementKind::SetDiscriminant { .. } => "StatementKind::SetDiscriminant",
StatementKind::StorageLive(..) => "StatementKind::StorageLive",
StatementKind::StorageDead(..) => "StatementKind::StorageDead",
diff --git a/src/test/mir-opt/array-index-is-temporary.rs b/src/test/mir-opt/array-index-is-temporary.rs
new file mode 100644
index 0000000000000..856e1063f6001
--- /dev/null
+++ b/src/test/mir-opt/array-index-is-temporary.rs
@@ -0,0 +1,43 @@
+// Retagging (from Stacked Borrows) relies on the array index being a fresh
+// temporary, so that side-effects cannot change it.
+// Test that this is indeed the case.
+
+unsafe fn foo(z: *mut usize) -> u32 {
+ *z = 2;
+ 99
+}
+
+fn main() {
+ let mut x = [42, 43, 44];
+ let mut y = 1;
+ let z: *mut usize = &mut y;
+ x[y] = unsafe { foo(z) };
+}
+
+// END RUST SOURCE
+// START rustc.main.EraseRegions.after.mir
+// bb0: {
+// ...
+// _6 = &mut _2;
+// _5 = &mut (*_6);
+// _4 = move _5 as *mut usize (Misc);
+// _3 = move _4;
+// ...
+// _8 = _3;
+// _7 = const foo(move _8) -> bb1;
+// }
+//
+// bb1: {
+// ...
+// _9 = _2;
+// _10 = Len(_1);
+// _11 = Lt(_9, _10);
+// assert(move _11, "index out of bounds: the len is move _10 but the index is _9") -> bb2;
+// }
+//
+// bb2: {
+// _1[_9] = move _7;
+// ...
+// return;
+// }
+// END rustc.main.EraseRegions.after.mir
diff --git a/src/test/mir-opt/inline-retag.rs b/src/test/mir-opt/inline-retag.rs
index 4b3280ee56151..1e5e1ad5ed1c5 100644
--- a/src/test/mir-opt/inline-retag.rs
+++ b/src/test/mir-opt/inline-retag.rs
@@ -32,6 +32,8 @@ fn bar() -> bool {
// bb0: {
// ...
// Retag(_3);
+// ...
+// Retag(_3);
// Retag(_6);
// StorageLive(_9);
// _9 = (*_3);
diff --git a/src/test/mir-opt/retag.rs b/src/test/mir-opt/retag.rs
index 9c013008ab272..7da55c0868cd2 100644
--- a/src/test/mir-opt/retag.rs
+++ b/src/test/mir-opt/retag.rs
@@ -26,7 +26,9 @@ fn main() {
{
let v = Test(0).foo(&mut x); // just making sure we do not panic when there is a tuple struct ctor
let w = { v }; // assignment
- let _w = w; // reborrow
+ let w = w; // reborrow
+ // escape-to-raw (mut)
+ let _w = w as *mut _;
}
// Also test closures
@@ -35,6 +37,9 @@ fn main() {
// need to call `foo_shr` or it doesn't even get generated
Test(0).foo_shr(&0);
+
+ // escape-to-raw (shr)
+ let _w = _w as *const _;
}
// END RUST SOURCE
@@ -44,6 +49,7 @@ fn main() {
// Retag([fn entry] _2);
// ...
// _0 = &mut (*_3);
+// Retag(_0);
// ...
// return;
// }
@@ -73,23 +79,36 @@ fn main() {
// _9 = move _3;
// Retag(_9);
// _8 = &mut (*_9);
+// Retag(_8);
// StorageDead(_9);
// StorageLive(_10);
// _10 = move _8;
// Retag(_10);
// ...
-// _13 = move _14(move _15) -> bb2;
+// _14 = &mut (*_10);
+// Retag(_14);
+// EscapeToRaw(move _14);
+// _13 = move _14 as *mut i32 (Misc);
+// ...
+// _17 = move _18(move _19) -> bb2;
// }
//
// bb2: {
-// Retag(_13);
+// Retag(_17);
+// ...
+// _21 = const Test::foo_shr(move _22, move _24) -> bb3;
+// }
+//
+// bb3: {
// ...
+// return;
// }
+//
// ...
// }
// END rustc.main.EraseRegions.after.mir
// START rustc.main-{{closure}}.EraseRegions.after.mir
-// fn main::{{closure}}(_1: &[closure@NodeId(117)], _2: &i32) -> &i32 {
+// fn main::{{closure}}(_1: &[closure@NodeId(124)], _2: &i32) -> &i32 {
// ...
// bb0: {
// Retag([fn entry] _1);