1
Fork 0

Auto merge of #66874 - RalfJung:miri-assert-panic, r=oli-obk

Miri engine: proper support for `Assert` MIR terminators

This puts down the basis for https://github.com/rust-lang/miri/issues/1070, and I also did some clean-up. The Miri side of this is at https://github.com/rust-lang/miri/pull/1084.

r? @oli-obk
This commit is contained in:
bors 2019-12-02 11:43:50 +00:00
commit 2da942f328
13 changed files with 195 additions and 118 deletions

View file

@ -471,6 +471,13 @@ impl<Tag> From<Scalar<Tag>> for ScalarMaybeUndef<Tag> {
} }
} }
impl<Tag> From<Pointer<Tag>> for ScalarMaybeUndef<Tag> {
#[inline(always)]
fn from(s: Pointer<Tag>) -> Self {
ScalarMaybeUndef::Scalar(s.into())
}
}
impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> { impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {

View file

@ -23,7 +23,7 @@ use syntax::{source_map::{Span, DUMMY_SP}, symbol::Symbol};
use crate::interpret::{self, use crate::interpret::{self,
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer, PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
RawConst, ConstValue, Machine, RawConst, ConstValue, Machine,
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup, InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup, AssertMessage,
Allocation, AllocId, MemoryKind, Memory, Allocation, AllocId, MemoryKind, Memory,
snapshot, RefTracking, intern_const_alloc_recursive, snapshot, RefTracking, intern_const_alloc_recursive,
}; };
@ -395,6 +395,40 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
) )
} }
fn assert_panic(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
_span: Span,
msg: &AssertMessage<'tcx>,
_unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
use rustc::mir::interpret::PanicInfo::*;
Err(match msg {
BoundsCheck { ref len, ref index } => {
let len = ecx
.read_immediate(ecx.eval_operand(len, None)?)
.expect("can't eval len")
.to_scalar()?
.to_machine_usize(&*ecx)?;
let index = ecx
.read_immediate(ecx.eval_operand(index, None)?)
.expect("can't eval index")
.to_scalar()?
.to_machine_usize(&*ecx)?;
err_panic!(BoundsCheck { len, index })
}
Overflow(op) => err_panic!(Overflow(*op)),
OverflowNeg => err_panic!(OverflowNeg),
DivisionByZero => err_panic!(DivisionByZero),
RemainderByZero => err_panic!(RemainderByZero),
ResumedAfterReturn(generator_kind)
=> err_panic!(ResumedAfterReturn(*generator_kind)),
ResumedAfterPanic(generator_kind)
=> err_panic!(ResumedAfterPanic(*generator_kind)),
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
}
.into())
}
fn ptr_to_int( fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>, _mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer, _ptr: Pointer,
@ -423,7 +457,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
} }
#[inline(always)] #[inline(always)]
fn tag_allocation<'b>( fn init_allocation_extra<'b>(
_memory_extra: &(), _memory_extra: &(),
_id: AllocId, _id: AllocId,
alloc: Cow<'b, Allocation>, alloc: Cow<'b, Allocation>,
@ -518,7 +552,7 @@ pub fn const_caller_location<'tcx>(
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None)) tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())), .subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
); );
let loc_place = ecx.alloc_caller_location(file, line, col).unwrap(); let loc_place = ecx.alloc_caller_location(file, line, col);
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap(); intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
let loc_const = ty::Const { let loc_const = ty::Const {
ty: loc_ty, ty: loc_ty,

View file

@ -55,7 +55,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
).ok_or_else(|| err_inval!(TooGeneric))?; ).ok_or_else(|| err_inval!(TooGeneric))?;
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?; self.write_scalar(fn_ptr, dest)?;
} }
_ => bug!("reify fn pointer on {:?}", src.layout.ty), _ => bug!("reify fn pointer on {:?}", src.layout.ty),
} }
@ -88,8 +88,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
ty::ClosureKind::FnOnce, ty::ClosureKind::FnOnce,
); );
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance)); let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into()); self.write_scalar(fn_ptr, dest)?;
self.write_immediate(val, dest)?;
} }
_ => bug!("closure fn pointer on {:?}", src.layout.ty), _ => bug!("closure fn pointer on {:?}", src.layout.ty),
} }

View file

@ -164,6 +164,20 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
} }
} }
impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> {
/// Return the `SourceInfo` of the current instruction.
pub fn current_source_info(&self) -> Option<mir::SourceInfo> {
self.block.map(|block| {
let block = &self.body.basic_blocks()[block];
if self.stmt < block.statements.len() {
block.statements[self.stmt].source_info
} else {
block.terminator().source_info
}
})
}
}
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> { impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
#[inline] #[inline]
fn data_layout(&self) -> &layout::TargetDataLayout { fn data_layout(&self) -> &layout::TargetDataLayout {
@ -236,6 +250,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.memory.force_bits(scalar, size) self.memory.force_bits(scalar, size)
} }
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
/// the *canonical* machine pointer to the allocation. Must never be used
/// for any other pointers!
///
/// This represents a *direct* access to that memory, as opposed to access
/// through a pointer that was created by the program.
#[inline(always)] #[inline(always)]
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> { pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
self.memory.tag_static_base_pointer(ptr) self.memory.tag_static_base_pointer(ptr)
@ -828,34 +848,28 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<'tcx>> { pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<'tcx>> {
let mut last_span = None; let mut last_span = None;
let mut frames = Vec::new(); let mut frames = Vec::new();
for &Frame { instance, span, body, block, stmt, .. } in self.stack().iter().rev() { for frame in self.stack().iter().rev() {
// make sure we don't emit frames that are duplicates of the previous // make sure we don't emit frames that are duplicates of the previous
if explicit_span == Some(span) { if explicit_span == Some(frame.span) {
last_span = Some(span); last_span = Some(frame.span);
continue; continue;
} }
if let Some(last) = last_span { if let Some(last) = last_span {
if last == span { if last == frame.span {
continue; continue;
} }
} else { } else {
last_span = Some(span); last_span = Some(frame.span);
} }
let lint_root = block.and_then(|block| { let lint_root = frame.current_source_info().and_then(|source_info| {
let block = &body.basic_blocks()[block]; match &frame.body.source_scopes[source_info.scope].local_data {
let source_info = if stmt < block.statements.len() {
block.statements[stmt].source_info
} else {
block.terminator().source_info
};
match &body.source_scopes[source_info.scope].local_data {
mir::ClearCrossCrate::Set(data) => Some(data.lint_root), mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
mir::ClearCrossCrate::Clear => None, mir::ClearCrossCrate::Clear => None,
} }
}); });
frames.push(FrameInfo { call_site: span, instance, lint_root }); frames.push(FrameInfo { call_site: frame.span, instance: frame.instance, lint_root });
} }
trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span); trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
frames frames

View file

@ -110,13 +110,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
match intrinsic_name { match intrinsic_name {
"caller_location" => { "caller_location" => {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span); let location = self.alloc_caller_location_for_span(span);
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
let location = self.alloc_caller_location(
Symbol::intern(&caller.file.name.to_string()),
caller.line as u32,
caller.col_display as u32 + 1,
)?;
self.write_scalar(location.ptr, dest)?; self.write_scalar(location.ptr, dest)?;
} }

View file

@ -1,50 +1,48 @@
use rustc::middle::lang_items::PanicLocationLangItem; use rustc::middle::lang_items::PanicLocationLangItem;
use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar};
use rustc::ty::subst::Subst; use rustc::ty::subst::Subst;
use rustc_target::abi::{LayoutOf, Size}; use rustc_target::abi::LayoutOf;
use syntax_pos::Symbol; use syntax_pos::{Symbol, Span};
use crate::interpret::{MemoryKind, MPlaceTy, intrinsics::{InterpCx, InterpResult, Machine}}; use crate::interpret::{Scalar, MemoryKind, MPlaceTy, intrinsics::{InterpCx, Machine}};
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub fn alloc_caller_location( crate fn alloc_caller_location(
&mut self, &mut self,
filename: Symbol, filename: Symbol,
line: u32, line: u32,
col: u32, col: u32,
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> { ) -> MPlaceTy<'tcx, M::PointerTag> {
let file = self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation);
let line = Scalar::from_u32(line); let line = Scalar::from_u32(line);
let col = Scalar::from_u32(col); let col = Scalar::from_u32(col);
let ptr_size = self.pointer_size(); // Allocate memory for `CallerLocation` struct.
let u32_size = Size::from_bits(32);
let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None)) let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None))
.subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter())); .subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter()));
let loc_layout = self.layout_of(loc_ty)?; let loc_layout = self.layout_of(loc_ty).unwrap();
let file_alloc = self.tcx.allocate_bytes(filename.as_str().as_bytes());
let file_ptr = Pointer::new(file_alloc, Size::ZERO);
let file = Scalar::Ptr(self.tag_static_base_pointer(file_ptr));
let file_len = Scalar::from_uint(filename.as_str().len() as u128, ptr_size);
let location = self.allocate(loc_layout, MemoryKind::CallerLocation); let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
let file_out = self.mplace_field(location, 0)?; // Initialize fields.
let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?; self.write_immediate(file.to_ref(), self.mplace_field(location, 0).unwrap().into())
let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?; .expect("writing to memory we just allocated cannot fail");
let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?; self.write_scalar(line, self.mplace_field(location, 1).unwrap().into())
let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?; .expect("writing to memory we just allocated cannot fail");
self.write_scalar(col, self.mplace_field(location, 2).unwrap().into())
.expect("writing to memory we just allocated cannot fail");
let layout = &self.tcx.data_layout; location
// We just allocated this, so we can skip the bounds checks. }
let alloc = self.memory.get_raw_mut(file_ptr_out.alloc_id)?;
alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?; pub fn alloc_caller_location_for_span(
alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?; &mut self,
alloc.write_scalar(layout, line_out, line.into(), u32_size)?; span: Span,
alloc.write_scalar(layout, col_out, col.into(), u32_size)?; ) -> MPlaceTy<'tcx, M::PointerTag> {
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
Ok(location) let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
self.alloc_caller_location(
Symbol::intern(&caller.file.name.to_string()),
caller.line as u32,
caller.col_display as u32 + 1,
)
} }
} }

View file

@ -11,7 +11,7 @@ use rustc::ty::{self, Ty, TyCtxt};
use syntax_pos::Span; use syntax_pos::Span;
use super::{ use super::{
Allocation, AllocId, InterpResult, Scalar, AllocationExtra, Allocation, AllocId, InterpResult, Scalar, AllocationExtra, AssertMessage,
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory, InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
Frame, Operand, Frame, Operand,
}; };
@ -175,6 +175,14 @@ pub trait Machine<'mir, 'tcx>: Sized {
unwind: Option<mir::BasicBlock>, unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx>; ) -> InterpResult<'tcx>;
/// Called to evaluate `Assert` MIR terminators that trigger a panic.
fn assert_panic(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
span: Span,
msg: &AssertMessage<'tcx>,
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx>;
/// Called for read access to a foreign static item. /// Called for read access to a foreign static item.
/// ///
/// This will only be called once per static and machine; the result is cached in /// This will only be called once per static and machine; the result is cached in
@ -233,20 +241,19 @@ pub trait Machine<'mir, 'tcx>: Sized {
/// cache the result. (This relies on `AllocMap::get_or` being able to add the /// cache the result. (This relies on `AllocMap::get_or` being able to add the
/// owned allocation to the map even when the map is shared.) /// owned allocation to the map even when the map is shared.)
/// ///
/// For static allocations, the tag returned must be the same as the one returned by /// Also return the "base" tag to use for this allocation: the one that is used for direct
/// `tag_static_base_pointer`. /// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
fn tag_allocation<'b>( /// with `tag_static_base_pointer`.
fn init_allocation_extra<'b>(
memory_extra: &Self::MemoryExtra, memory_extra: &Self::MemoryExtra,
id: AllocId, id: AllocId,
alloc: Cow<'b, Allocation>, alloc: Cow<'b, Allocation>,
kind: Option<MemoryKind<Self::MemoryKinds>>, kind: Option<MemoryKind<Self::MemoryKinds>>,
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag); ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
/// Return the "base" tag for the given static allocation: the one that is used for direct /// Return the "base" tag for the given *static* allocation: the one that is used for direct
/// accesses to this static/const/fn allocation. /// accesses to this static/const/fn allocation. If `id` is not a static allocation,
/// /// this will return an unusable tag (i.e., accesses will be UB)!
/// Be aware that requesting the `Allocation` for that `id` will lead to cycles
/// for cyclic statics!
fn tag_static_base_pointer( fn tag_static_base_pointer(
memory_extra: &Self::MemoryExtra, memory_extra: &Self::MemoryExtra,
id: AllocId, id: AllocId,

View file

@ -143,6 +143,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
} }
} }
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
/// the *canonical* machine pointer to the allocation. Must never be used
/// for any other pointers!
///
/// This represents a *direct* access to that memory, as opposed to access
/// through a pointer that was created by the program.
#[inline] #[inline]
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> { pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
ptr.with_tag(M::tag_static_base_pointer(&self.extra, ptr.alloc_id)) ptr.with_tag(M::tag_static_base_pointer(&self.extra, ptr.alloc_id))
@ -191,7 +197,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
kind: MemoryKind<M::MemoryKinds>, kind: MemoryKind<M::MemoryKinds>,
) -> Pointer<M::PointerTag> { ) -> Pointer<M::PointerTag> {
let id = self.tcx.alloc_map.lock().reserve(); let id = self.tcx.alloc_map.lock().reserve();
let (alloc, tag) = M::tag_allocation(&self.extra, id, Cow::Owned(alloc), Some(kind)); debug_assert_ne!(Some(kind), M::STATIC_KIND.map(MemoryKind::Machine),
"dynamically allocating static memory");
let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
self.alloc_map.insert(id, (kind, alloc.into_owned())); self.alloc_map.insert(id, (kind, alloc.into_owned()));
Pointer::from(id).with_tag(tag) Pointer::from(id).with_tag(tag)
} }
@ -350,7 +358,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
sptr sptr
} else { } else {
// A "real" access, we must get a pointer. // A "real" access, we must get a pointer.
Scalar::Ptr(self.force_ptr(sptr)?) Scalar::from(self.force_ptr(sptr)?)
}; };
Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) { Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
Ok(bits) => { Ok(bits) => {
@ -473,14 +481,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
} }
} }
}; };
// We got tcx memory. Let the machine figure out whether and how to // We got tcx memory. Let the machine initialize its "extra" stuff.
// turn that into memory with the right pointer tag. let (alloc, tag) = M::init_allocation_extra(
Ok(M::tag_allocation(
memory_extra, memory_extra,
id, // always use the ID we got as input, not the "hidden" one. id, // always use the ID we got as input, not the "hidden" one.
alloc, alloc,
M::STATIC_KIND.map(MemoryKind::Machine), M::STATIC_KIND.map(MemoryKind::Machine),
).0) );
debug_assert_eq!(tag, M::tag_static_base_pointer(memory_extra, id));
Ok(alloc)
} }
/// Gives raw access to the `Allocation`, without bounds or alignment checks. /// Gives raw access to the `Allocation`, without bounds or alignment checks.

View file

@ -24,7 +24,7 @@ use rustc_macros::HashStable;
/// ///
/// For optimization of a few very common cases, there is also a representation for a pair of /// For optimization of a few very common cases, there is also a representation for a pair of
/// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary /// primitive values (`ScalarPair`). It allows Miri to avoid making allocations for checked binary
/// operations and fat pointers. This idea was taken from rustc's codegen. /// operations and wide pointers. This idea was taken from rustc's codegen.
/// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely /// In particular, thanks to `ScalarPair`, arithmetic operations and casts can be entirely
/// defined on `Immediate`, and do not have to work with a `Place`. /// defined on `Immediate`, and do not have to work with a `Place`.
#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, Hash)]
@ -47,6 +47,13 @@ impl<Tag> From<Scalar<Tag>> for Immediate<Tag> {
} }
} }
impl<Tag> From<Pointer<Tag>> for Immediate<Tag> {
#[inline(always)]
fn from(val: Pointer<Tag>) -> Self {
Immediate::Scalar(Scalar::from(val).into())
}
}
impl<'tcx, Tag> Immediate<Tag> { impl<'tcx, Tag> Immediate<Tag> {
pub fn new_slice( pub fn new_slice(
val: Scalar<Tag>, val: Scalar<Tag>,
@ -60,14 +67,14 @@ impl<'tcx, Tag> Immediate<Tag> {
} }
pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>) -> Self { pub fn new_dyn_trait(val: Scalar<Tag>, vtable: Pointer<Tag>) -> Self {
Immediate::ScalarPair(val.into(), Scalar::Ptr(vtable).into()) Immediate::ScalarPair(val.into(), vtable.into())
} }
#[inline] #[inline]
pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef<Tag> { pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef<Tag> {
match self { match self {
Immediate::Scalar(val) => val, Immediate::Scalar(val) => val,
Immediate::ScalarPair(..) => bug!("Got a fat pointer where a scalar was expected"), Immediate::ScalarPair(..) => bug!("Got a wide pointer where a scalar was expected"),
} }
} }
@ -324,7 +331,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Ok(self.read_immediate(op)?.to_scalar_or_undef()) Ok(self.read_immediate(op)?.to_scalar_or_undef())
} }
// Turn the MPlace into a string (must already be dereferenced!) // Turn the wide MPlace into a string (must already be dereferenced!)
pub fn read_str( pub fn read_str(
&self, &self,
mplace: MPlaceTy<'tcx, M::PointerTag>, mplace: MPlaceTy<'tcx, M::PointerTag>,

View file

@ -125,7 +125,7 @@ impl<Tag> MemPlace<Tag> {
Self::from_scalar_ptr(ptr.into(), align) Self::from_scalar_ptr(ptr.into(), align)
} }
/// Turn a mplace into a (thin or fat) pointer, as a reference, pointing to the same space. /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space.
/// This is the inverse of `ref_to_mplace`. /// This is the inverse of `ref_to_mplace`.
#[inline(always)] #[inline(always)]
pub fn to_ref(self) -> Immediate<Tag> { pub fn to_ref(self) -> Immediate<Tag> {
@ -278,7 +278,7 @@ where
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>, M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
M::AllocExtra: AllocationExtra<Tag>, M::AllocExtra: AllocationExtra<Tag>,
{ {
/// Take a value, which represents a (thin or fat) reference, and make it a place. /// Take a value, which represents a (thin or wide) reference, and make it a place.
/// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
/// ///
/// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not /// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
@ -694,6 +694,7 @@ where
} }
/// Write a scalar to a place /// Write a scalar to a place
#[inline(always)]
pub fn write_scalar( pub fn write_scalar(
&mut self, &mut self,
val: impl Into<ScalarMaybeUndef<M::PointerTag>>, val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
@ -1041,6 +1042,24 @@ where
MPlaceTy::from_aligned_ptr(ptr, layout) MPlaceTy::from_aligned_ptr(ptr, layout)
} }
/// Returns a wide MPlace.
pub fn allocate_str(
&mut self,
str: &str,
kind: MemoryKind<M::MemoryKinds>,
) -> MPlaceTy<'tcx, M::PointerTag> {
let ptr = self.memory.allocate_static_bytes(str.as_bytes(), kind);
let meta = Scalar::from_uint(str.len() as u128, self.pointer_size());
let mplace = MemPlace {
ptr: ptr.into(),
align: Align::from_bytes(1).unwrap(),
meta: Some(meta),
};
let layout = self.layout_of(self.tcx.mk_static_str()).unwrap();
MPlaceTy { mplace, layout }
}
pub fn write_discriminant_index( pub fn write_discriminant_index(
&mut self, &mut self,
variant_index: VariantIdx, variant_index: VariantIdx,

View file

@ -7,8 +7,8 @@ use syntax::source_map::Span;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use super::{ use super::{
GlobalId, InterpResult, PointerArithmetic, GlobalId, InterpResult, InterpCx, Machine,
InterpCx, Machine, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
}; };
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
@ -115,40 +115,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
expected, expected,
ref msg, ref msg,
target, target,
.. cleanup,
} => { } => {
let cond_val = self.read_immediate(self.eval_operand(cond, None)?)? let cond_val = self.read_immediate(self.eval_operand(cond, None)?)?
.to_scalar()?.to_bool()?; .to_scalar()?.to_bool()?;
if expected == cond_val { if expected == cond_val {
self.go_to_block(target); self.go_to_block(target);
} else { } else {
// Compute error message M::assert_panic(self, terminator.source_info.span, msg, cleanup)?;
use rustc::mir::interpret::PanicInfo::*;
return Err(match msg {
BoundsCheck { ref len, ref index } => {
let len = self
.read_immediate(self.eval_operand(len, None)?)
.expect("can't eval len")
.to_scalar()?
.to_bits(self.memory.pointer_size())? as u64;
let index = self
.read_immediate(self.eval_operand(index, None)?)
.expect("can't eval index")
.to_scalar()?
.to_bits(self.memory.pointer_size())? as u64;
err_panic!(BoundsCheck { len, index })
}
Overflow(op) => err_panic!(Overflow(*op)),
OverflowNeg => err_panic!(OverflowNeg),
DivisionByZero => err_panic!(DivisionByZero),
RemainderByZero => err_panic!(RemainderByZero),
ResumedAfterReturn(generator_kind)
=> err_panic!(ResumedAfterReturn(*generator_kind)),
ResumedAfterPanic(generator_kind)
=> err_panic!(ResumedAfterPanic(*generator_kind)),
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
}
.into());
} }
} }
@ -164,15 +138,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
return Ok(()) return Ok(())
}, },
// It is UB to ever encounter this.
Unreachable => throw_ub!(Unreachable),
// These should never occur for MIR we actually run.
DropAndReplace { .. } |
FalseEdges { .. } |
FalseUnwind { .. } =>
bug!("{:#?} should have been eliminated by MIR pass", terminator.kind),
// These are not (yet) supported. It is unclear if they even can occur in
// MIR that we actually run.
Yield { .. } | Yield { .. } |
GeneratorDrop | GeneratorDrop |
DropAndReplace { .. } | Abort =>
Abort => unimplemented!("{:#?}", terminator.kind), throw_unsup_format!("Unsupported terminator kind: {:#?}", terminator.kind),
FalseEdges { .. } => bug!("should have been eliminated by\
`simplify_branches` mir pass"),
FalseUnwind { .. } => bug!("should have been eliminated by\
`simplify_branches` mir pass"),
Unreachable => throw_ub!(Unreachable),
} }
Ok(()) Ok(())

View file

@ -67,7 +67,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// allocation is correctly aligned as we created it above. Also we're only offsetting by // allocation is correctly aligned as we created it above. Also we're only offsetting by
// multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`. // multiples of `ptr_align`, which means that it will stay aligned to `ptr_align`.
let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?; let vtable_alloc = self.memory.get_raw_mut(vtable.alloc_id)?;
vtable_alloc.write_ptr_sized(tcx, vtable, Scalar::Ptr(drop).into())?; vtable_alloc.write_ptr_sized(tcx, vtable, drop.into())?;
let size_ptr = vtable.offset(ptr_size, tcx)?; let size_ptr = vtable.offset(ptr_size, tcx)?;
vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?; vtable_alloc.write_ptr_sized(tcx, size_ptr, Scalar::from_uint(size, ptr_size).into())?;
@ -87,7 +87,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// We cannot use `vtable_allic` as we are creating fn ptrs in this loop. // We cannot use `vtable_allic` as we are creating fn ptrs in this loop.
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?; let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
self.memory.get_raw_mut(vtable.alloc_id)? self.memory.get_raw_mut(vtable.alloc_id)?
.write_ptr_sized(tcx, method_ptr, Scalar::Ptr(fn_ptr).into())?; .write_ptr_sized(tcx, method_ptr, fn_ptr.into())?;
} }
} }

View file

@ -156,6 +156,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp")); throw_unsup!(ConstPropUnsupported("calling intrinsics isn't supported in ConstProp"));
} }
fn assert_panic(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_span: Span,
_msg: &rustc::mir::interpret::AssertMessage<'tcx>,
_unwind: Option<rustc::mir::BasicBlock>,
) -> InterpResult<'tcx> {
bug!("panics terminators are not evaluated in ConstProp");
}
fn ptr_to_int( fn ptr_to_int(
_mem: &Memory<'mir, 'tcx, Self>, _mem: &Memory<'mir, 'tcx, Self>,
_ptr: Pointer, _ptr: Pointer,
@ -182,7 +191,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
} }
#[inline(always)] #[inline(always)]
fn tag_allocation<'b>( fn init_allocation_extra<'b>(
_memory_extra: &(), _memory_extra: &(),
_id: AllocId, _id: AllocId,
alloc: Cow<'b, Allocation>, alloc: Cow<'b, Allocation>,