SetDiscriminant { place, variant_index },
StorageLive(place),
StorageDead(place),
- EscapeToRaw(place),
- Retag { fn_entry, two_phase, place },
+ Retag(retag_kind, place),
AscribeUserType(place, variance, c_ty),
Nop,
InlineAsm { asm, outputs, inputs },
});
+impl_stable_hash_for!(enum mir::RetagKind { FnEntry, TwoPhase, Raw, Default });
impl_stable_hash_for!(enum mir::FakeReadCause { ForMatchGuard, ForMatchedPlace, ForLet });
impl<'a, 'gcx> HashStable<StableHashingContext<'a>> for mir::Place<'gcx> {
/// by miri and only generated when "-Z mir-emit-retag" is passed.
/// See <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/>
/// for more details.
- Retag {
- /// `fn_entry` indicates whether this is the initial retag that happens in the
- /// function prolog.
- fn_entry: bool,
- /// `two_phase` indicates whether this is just the reservation action of
- /// a two-phase borrow.
- two_phase: bool,
- /// The place to retag
- 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 <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/>
- /// for more details.
- EscapeToRaw(Operand<'tcx>),
+ Retag(RetagKind, Place<'tcx>),
/// Encodes a user's type ascription. These need to be preserved
/// intact so that NLL can respect them. For example:
Nop,
}
+/// `RetagKind` describes what kind of retag is to be performed.
+#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug, PartialEq, Eq)]
+pub enum RetagKind {
+ /// The initial retag when entering a function
+ FnEntry,
+ /// Retag preparing for a two-phase borrow
+ TwoPhase,
+ /// Retagging raw pointers
+ Raw,
+ /// A "normal" retag
+ Default,
+}
+
/// The `FakeReadCause` describes the type of pattern why a `FakeRead` statement exists.
#[derive(Copy, Clone, RustcEncodable, RustcDecodable, Debug)]
pub enum FakeReadCause {
match self.kind {
Assign(ref place, ref rv) => write!(fmt, "{:?} = {:?}", place, rv),
FakeRead(ref cause, ref place) => write!(fmt, "FakeRead({:?}, {:?})", cause, place),
- Retag { fn_entry, two_phase, ref place } =>
- write!(fmt, "Retag({}{}{:?})",
- if fn_entry { "[fn entry] " } else { "" },
- if two_phase { "[2phase] " } else { "" },
+ Retag(ref kind, ref place) =>
+ write!(fmt, "Retag({}{:?})",
+ match kind {
+ RetagKind::FnEntry => "[fn entry] ",
+ RetagKind::TwoPhase => "[2phase] ",
+ RetagKind::Raw => "[raw] ",
+ RetagKind::Default => "",
+ },
place,
),
- EscapeToRaw(ref place) => write!(fmt, "EscapeToRaw({:?})", place),
StorageLive(ref place) => write!(fmt, "StorageLive({:?})", place),
StorageDead(ref place) => write!(fmt, "StorageDead({:?})", place),
SetDiscriminant {
SourceInfo,
UpvarDecl,
FakeReadCause,
+ RetagKind,
SourceScope,
SourceScopeData,
SourceScopeLocalData,
(StatementKind::StorageLive)(a),
(StatementKind::StorageDead)(a),
(StatementKind::InlineAsm) { asm, outputs, inputs },
- (StatementKind::Retag) { fn_entry, two_phase, place },
- (StatementKind::EscapeToRaw)(place),
+ (StatementKind::Retag)(kind, place),
(StatementKind::AscribeUserType)(a, v, b),
(StatementKind::Nop),
}
}
fn visit_retag(&mut self,
- fn_entry: & $($mutability)* bool,
- two_phase: & $($mutability)* bool,
+ kind: & $($mutability)* RetagKind,
place: & $($mutability)* Place<'tcx>,
location: Location) {
- self.super_retag(fn_entry, two_phase, place, location);
+ self.super_retag(kind, place, location);
}
fn visit_place(&mut self,
location
);
}
- StatementKind::EscapeToRaw(ref $($mutability)* op) => {
- self.visit_operand(op, location);
- }
StatementKind::StorageLive(ref $($mutability)* local) => {
self.visit_local(
local,
self.visit_operand(input, location);
}
}
- StatementKind::Retag { ref $($mutability)* fn_entry,
- ref $($mutability)* two_phase,
- ref $($mutability)* place } => {
- self.visit_retag(fn_entry, two_phase, place, location);
+ StatementKind::Retag ( ref $($mutability)* kind,
+ ref $($mutability)* place ) => {
+ self.visit_retag(kind, place, location);
}
StatementKind::AscribeUserType(
ref $($mutability)* place,
}
fn super_retag(&mut self,
- _fn_entry: & $($mutability)* bool,
- _two_phase: & $($mutability)* bool,
+ _kind: & $($mutability)* RetagKind,
place: & $($mutability)* Place<'tcx>,
location: Location) {
self.visit_place(
}
mir::StatementKind::FakeRead(..) |
mir::StatementKind::Retag { .. } |
- mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => bx,
}
StatementKind::Nop
| StatementKind::AscribeUserType(..)
| StatementKind::Retag { .. }
- | StatementKind::EscapeToRaw { .. }
| StatementKind::StorageLive(..) => {
// `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
// to borrow check.
StatementKind::Nop |
StatementKind::AscribeUserType(..) |
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::StorageLive(..) => {
// `Nop`, `AscribeUserType`, `Retag`, and `StorageLive` are irrelevant
// to borrow check.
| StatementKind::StorageDead(..)
| StatementKind::InlineAsm { .. }
| StatementKind::Retag { .. }
- | StatementKind::EscapeToRaw { .. }
| StatementKind::Nop => {}
}
}
mir::StatementKind::SetDiscriminant { .. } |
mir::StatementKind::StorageLive(..) |
mir::StatementKind::Retag { .. } |
- mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => {}
"SetDiscriminant should not exist during borrowck");
}
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {}
}
}
Misc => {
- let src_layout = src.layout;
let src = self.read_immediate(src)?;
- // There are no casts to references
- assert!(!dest.layout.ty.is_region_ptr());
- // Hence we make all casts erase the tag
- let src = src.erase_tag().with_default_tag();
-
- 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) => {
}
}
} 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;
layout::Variants::NicheFilling { .. } => {},
}
- let dest_val = self.cast_scalar(src.to_scalar()?, src_layout, dest.layout)?;
+ let dest_val = self.cast_scalar(src.to_scalar()?, src.layout, dest.layout)?;
self.write_scalar(dest_val, dest)?;
}
}
#[inline]
fn retag(
_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
- _fn_entry: bool,
- _two_phase: bool,
+ _kind: mir::RetagKind,
_place: PlaceTy<'tcx, Self::PointerTag>,
) -> 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(())
- }
-
/// Called immediately before a new stack frame got pushed
fn stack_push(
ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
FakeRead(..) => {}
// Stacked Borrows.
- Retag { fn_entry, two_phase, ref place } => {
+ Retag(kind, ref place) => {
let dest = self.eval_place(place)?;
- M::retag(self, fn_entry, two_phase, dest)?;
- }
- EscapeToRaw(ref op) => {
- let op = self.eval_operand(op, None)?;
- M::escape_to_raw(self, op)?;
+ M::retag(self, kind, dest)?;
}
// Statements we do not track.
// 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 {
- // Function arguments should be retagged
+ // Function arguments should be retagged, and we make this one raw.
mir.basic_blocks_mut()[START_BLOCK].statements.insert(0, Statement {
source_info,
- kind: StatementKind::Retag {
- fn_entry: true,
- two_phase: false,
- place: dropee_ptr.clone(),
- },
+ kind: StatementKind::Retag(RetagKind::Raw, dropee_ptr.clone()),
});
- // We use raw ptr operations, better prepare the alias tracking for that
- mir.basic_blocks_mut()[START_BLOCK].statements.insert(1, Statement {
- source_info,
- kind: StatementKind::EscapeToRaw(Operand::Copy(dropee_ptr.clone())),
- })
}
let patch = {
let param_env = tcx.param_env(def_id).with_reveal_all();
basic_blocks[START_BLOCK].statements.splice(0..0,
places.into_iter().map(|place| Statement {
source_info,
- kind: StatementKind::Retag { fn_entry: true, two_phase: false, place },
+ kind: StatementKind::Retag(RetagKind::FnEntry, place),
})
);
}
for (source_info, dest_place, dest_block) in returns {
basic_blocks[dest_block].statements.insert(0, Statement {
source_info,
- kind: StatementKind::Retag { fn_entry: false, two_phase: false, place: dest_place },
+ kind: StatementKind::Retag(RetagKind::Default, dest_place),
});
}
// We want to insert statements as we iterate. To this end, we
// iterate backwards using indices.
for i in (0..block_data.statements.len()).rev() {
- match block_data.statements[i].kind {
- // If we are casting *from* a reference, we may have to escape-to-raw.
- StatementKind::Assign(_, box Rvalue::Cast(
+ let (retag_kind, place) = match block_data.statements[i].kind {
+ // If we are casting *from* a reference, we may have to retag-as-raw.
+ StatementKind::Assign(ref place, box Rvalue::Cast(
CastKind::Misc,
ref src,
dest_ty,
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()),
- });
+ (RetagKind::Raw, place)
+ } else {
+ // Some other cast, no retag
+ continue
}
}
// 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, box ref rvalue) if needs_retag(place) => {
- let two_phase = match rvalue {
- Rvalue::Ref(_, borrow_kind, _) =>
- borrow_kind.allows_two_phase_borrow(),
- _ => false
+ let kind = match rvalue {
+ Rvalue::Ref(_, borrow_kind, _)
+ if borrow_kind.allows_two_phase_borrow()
+ =>
+ RetagKind::TwoPhase,
+ _ =>
+ RetagKind::Default,
};
- // Insert a retag after the assignment.
- let source_info = block_data.statements[i].source_info;
- block_data.statements.insert(i+1, Statement {
- source_info,
- kind: StatementKind::Retag {
- fn_entry: false,
- two_phase,
- place: place.clone(),
- },
- });
+ (kind, place)
}
// Do nothing for the rest
- _ => {},
+ _ => continue,
};
+ // Insert a retag after the statement.
+ let source_info = block_data.statements[i].source_info;
+ block_data.statements.insert(i+1, Statement {
+ source_info,
+ kind: StatementKind::Retag(retag_kind, place.clone()),
+ });
}
}
}
StatementKind::StorageLive(..) |
StatementKind::StorageDead(..) |
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {
// safe (at least as emitted during MIR construction)
// 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()))),
+ kind: StatementKind::Retag(RetagKind::Raw, Place::Local(self_arg())),
})
}
fn visit_retag(
&mut self,
- fn_entry: &mut bool,
- two_phase: &mut bool,
+ kind: &mut RetagKind,
place: &mut Place<'tcx>,
loc: Location,
) {
- self.super_retag(fn_entry, two_phase, place, loc);
+ self.super_retag(kind, place, loc);
// We have to patch all inlined retags to be aware that they are no longer
// happening on function entry.
- *fn_entry = false;
+ if *kind == RetagKind::FnEntry {
+ *kind = RetagKind::Default;
+ }
}
fn visit_terminator_kind(&mut self, block: BasicBlock,
StatementKind::StorageDead(_) |
StatementKind::InlineAsm {..} |
StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } |
StatementKind::AscribeUserType(..) |
StatementKind::Nop => {}
}
| StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Retag { .. }
- | StatementKind::EscapeToRaw { .. }
| StatementKind::AscribeUserType(..)
| StatementKind::Nop => Ok(()),
}
StatementKind::Assign { .. } |
StatementKind::SetDiscriminant { .. } |
StatementKind::InlineAsm { .. } |
- StatementKind::Retag { .. } |
- StatementKind::EscapeToRaw { .. } => {
+ StatementKind::Retag { .. } => {
return false;
}
}
mir::StatementKind::StorageDead(_) |
mir::StatementKind::InlineAsm { .. } |
mir::StatementKind::Retag { .. } |
- mir::StatementKind::EscapeToRaw { .. } |
mir::StatementKind::AscribeUserType(..) |
mir::StatementKind::Nop => continue,
mir::StatementKind::SetDiscriminant{ .. } =>
// ...
// _14 = &mut (*_10);
// Retag(_14);
-// EscapeToRaw(move _14);
// _13 = move _14 as *mut i32 (Misc);
+// Retag([raw] _13);
// ...
// _17 = move _18(move _19) -> bb2;
// }