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:
commit
2da942f328
13 changed files with 195 additions and 118 deletions
|
@ -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> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
|
|
|
@ -23,7 +23,7 @@ use syntax::{source_map::{Span, DUMMY_SP}, symbol::Symbol};
|
|||
use crate::interpret::{self,
|
||||
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
|
||||
RawConst, ConstValue, Machine,
|
||||
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
|
||||
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup, AssertMessage,
|
||||
Allocation, AllocId, MemoryKind, Memory,
|
||||
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(
|
||||
_mem: &Memory<'mir, 'tcx, Self>,
|
||||
_ptr: Pointer,
|
||||
|
@ -423,7 +457,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tag_allocation<'b>(
|
||||
fn init_allocation_extra<'b>(
|
||||
_memory_extra: &(),
|
||||
_id: AllocId,
|
||||
alloc: Cow<'b, Allocation>,
|
||||
|
@ -518,7 +552,7 @@ pub fn const_caller_location<'tcx>(
|
|||
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
|
||||
.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();
|
||||
let loc_const = ty::Const {
|
||||
ty: loc_ty,
|
||||
|
|
|
@ -55,7 +55,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
).ok_or_else(|| err_inval!(TooGeneric))?;
|
||||
|
||||
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),
|
||||
}
|
||||
|
@ -88,8 +88,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
ty::ClosureKind::FnOnce,
|
||||
);
|
||||
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
|
||||
let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
|
||||
self.write_immediate(val, dest)?;
|
||||
self.write_scalar(fn_ptr, dest)?;
|
||||
}
|
||||
_ => bug!("closure fn pointer on {:?}", src.layout.ty),
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
#[inline]
|
||||
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)
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
|
||||
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>> {
|
||||
let mut last_span = None;
|
||||
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
|
||||
if explicit_span == Some(span) {
|
||||
last_span = Some(span);
|
||||
if explicit_span == Some(frame.span) {
|
||||
last_span = Some(frame.span);
|
||||
continue;
|
||||
}
|
||||
if let Some(last) = last_span {
|
||||
if last == span {
|
||||
if last == frame.span {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
last_span = Some(span);
|
||||
last_span = Some(frame.span);
|
||||
}
|
||||
|
||||
let lint_root = block.and_then(|block| {
|
||||
let block = &body.basic_blocks()[block];
|
||||
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 {
|
||||
let lint_root = frame.current_source_info().and_then(|source_info| {
|
||||
match &frame.body.source_scopes[source_info.scope].local_data {
|
||||
mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
|
||||
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);
|
||||
frames
|
||||
|
|
|
@ -110,13 +110,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
|
||||
match intrinsic_name {
|
||||
"caller_location" => {
|
||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(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,
|
||||
)?;
|
||||
let location = self.alloc_caller_location_for_span(span);
|
||||
self.write_scalar(location.ptr, dest)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +1,48 @@
|
|||
use rustc::middle::lang_items::PanicLocationLangItem;
|
||||
use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar};
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc_target::abi::{LayoutOf, Size};
|
||||
use syntax_pos::Symbol;
|
||||
use rustc_target::abi::LayoutOf;
|
||||
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> {
|
||||
pub fn alloc_caller_location(
|
||||
crate fn alloc_caller_location(
|
||||
&mut self,
|
||||
filename: Symbol,
|
||||
line: 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 col = Scalar::from_u32(col);
|
||||
|
||||
let ptr_size = self.pointer_size();
|
||||
let u32_size = Size::from_bits(32);
|
||||
|
||||
// Allocate memory for `CallerLocation` struct.
|
||||
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()));
|
||||
let loc_layout = self.layout_of(loc_ty)?;
|
||||
|
||||
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 loc_layout = self.layout_of(loc_ty).unwrap();
|
||||
let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
|
||||
|
||||
let file_out = self.mplace_field(location, 0)?;
|
||||
let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?;
|
||||
let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?;
|
||||
let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?;
|
||||
let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?;
|
||||
// Initialize fields.
|
||||
self.write_immediate(file.to_ref(), self.mplace_field(location, 0).unwrap().into())
|
||||
.expect("writing to memory we just allocated cannot fail");
|
||||
self.write_scalar(line, self.mplace_field(location, 1).unwrap().into())
|
||||
.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;
|
||||
// We just allocated this, so we can skip the bounds checks.
|
||||
let alloc = self.memory.get_raw_mut(file_ptr_out.alloc_id)?;
|
||||
location
|
||||
}
|
||||
|
||||
alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?;
|
||||
alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?;
|
||||
alloc.write_scalar(layout, line_out, line.into(), u32_size)?;
|
||||
alloc.write_scalar(layout, col_out, col.into(), u32_size)?;
|
||||
|
||||
Ok(location)
|
||||
pub fn alloc_caller_location_for_span(
|
||||
&mut self,
|
||||
span: Span,
|
||||
) -> MPlaceTy<'tcx, M::PointerTag> {
|
||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc::ty::{self, Ty, TyCtxt};
|
|||
use syntax_pos::Span;
|
||||
|
||||
use super::{
|
||||
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
|
||||
Allocation, AllocId, InterpResult, Scalar, AllocationExtra, AssertMessage,
|
||||
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
|
||||
Frame, Operand,
|
||||
};
|
||||
|
@ -175,6 +175,14 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
|||
unwind: Option<mir::BasicBlock>,
|
||||
) -> 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.
|
||||
///
|
||||
/// 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
|
||||
/// 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
|
||||
/// `tag_static_base_pointer`.
|
||||
fn tag_allocation<'b>(
|
||||
/// Also return the "base" tag to use for this allocation: the one that is used for direct
|
||||
/// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
|
||||
/// with `tag_static_base_pointer`.
|
||||
fn init_allocation_extra<'b>(
|
||||
memory_extra: &Self::MemoryExtra,
|
||||
id: AllocId,
|
||||
alloc: Cow<'b, Allocation>,
|
||||
kind: Option<MemoryKind<Self::MemoryKinds>>,
|
||||
) -> (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
|
||||
/// accesses to this static/const/fn allocation.
|
||||
///
|
||||
/// Be aware that requesting the `Allocation` for that `id` will lead to cycles
|
||||
/// for cyclic statics!
|
||||
/// Return the "base" tag for the given *static* allocation: the one that is used for direct
|
||||
/// 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)!
|
||||
fn tag_static_base_pointer(
|
||||
memory_extra: &Self::MemoryExtra,
|
||||
id: AllocId,
|
||||
|
|
|
@ -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]
|
||||
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))
|
||||
|
@ -191,7 +197,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
|||
kind: MemoryKind<M::MemoryKinds>,
|
||||
) -> Pointer<M::PointerTag> {
|
||||
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()));
|
||||
Pointer::from(id).with_tag(tag)
|
||||
}
|
||||
|
@ -350,7 +358,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
|
|||
sptr
|
||||
} else {
|
||||
// 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(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
|
||||
// turn that into memory with the right pointer tag.
|
||||
Ok(M::tag_allocation(
|
||||
// We got tcx memory. Let the machine initialize its "extra" stuff.
|
||||
let (alloc, tag) = M::init_allocation_extra(
|
||||
memory_extra,
|
||||
id, // always use the ID we got as input, not the "hidden" one.
|
||||
alloc,
|
||||
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.
|
||||
|
|
|
@ -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
|
||||
/// 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
|
||||
/// defined on `Immediate`, and do not have to work with a `Place`.
|
||||
#[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> {
|
||||
pub fn new_slice(
|
||||
val: Scalar<Tag>,
|
||||
|
@ -60,14 +67,14 @@ impl<'tcx, Tag> Immediate<Tag> {
|
|||
}
|
||||
|
||||
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]
|
||||
pub fn to_scalar_or_undef(self) -> ScalarMaybeUndef<Tag> {
|
||||
match self {
|
||||
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())
|
||||
}
|
||||
|
||||
// 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(
|
||||
&self,
|
||||
mplace: MPlaceTy<'tcx, M::PointerTag>,
|
||||
|
|
|
@ -125,7 +125,7 @@ impl<Tag> MemPlace<Tag> {
|
|||
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`.
|
||||
#[inline(always)]
|
||||
pub fn to_ref(self) -> Immediate<Tag> {
|
||||
|
@ -278,7 +278,7 @@ where
|
|||
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<Tag, M::AllocExtra>)>,
|
||||
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()`.
|
||||
///
|
||||
/// 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
|
||||
#[inline(always)]
|
||||
pub fn write_scalar(
|
||||
&mut self,
|
||||
val: impl Into<ScalarMaybeUndef<M::PointerTag>>,
|
||||
|
@ -1041,6 +1042,24 @@ where
|
|||
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(
|
||||
&mut self,
|
||||
variant_index: VariantIdx,
|
||||
|
|
|
@ -7,8 +7,8 @@ use syntax::source_map::Span;
|
|||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
use super::{
|
||||
GlobalId, InterpResult, PointerArithmetic,
|
||||
InterpCx, Machine, OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
|
||||
GlobalId, InterpResult, InterpCx, Machine,
|
||||
OpTy, ImmTy, PlaceTy, MPlaceTy, StackPopCleanup, FnVal,
|
||||
};
|
||||
|
||||
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,
|
||||
ref msg,
|
||||
target,
|
||||
..
|
||||
cleanup,
|
||||
} => {
|
||||
let cond_val = self.read_immediate(self.eval_operand(cond, None)?)?
|
||||
.to_scalar()?.to_bool()?;
|
||||
if expected == cond_val {
|
||||
self.go_to_block(target);
|
||||
} else {
|
||||
// Compute error message
|
||||
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());
|
||||
M::assert_panic(self, terminator.source_info.span, msg, cleanup)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -164,15 +138,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
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 { .. } |
|
||||
GeneratorDrop |
|
||||
DropAndReplace { .. } |
|
||||
Abort => unimplemented!("{:#?}", 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),
|
||||
Abort =>
|
||||
throw_unsup_format!("Unsupported terminator kind: {:#?}", terminator.kind),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -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
|
||||
// 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)?;
|
||||
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)?;
|
||||
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.
|
||||
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), tcx)?;
|
||||
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())?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,6 +156,15 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
|||
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(
|
||||
_mem: &Memory<'mir, 'tcx, Self>,
|
||||
_ptr: Pointer,
|
||||
|
@ -182,7 +191,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn tag_allocation<'b>(
|
||||
fn init_allocation_extra<'b>(
|
||||
_memory_extra: &(),
|
||||
_id: AllocId,
|
||||
alloc: Cow<'b, Allocation>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue