Skip to content

Commit

Permalink
Auto merge of #66874 - RalfJung:miri-assert-panic, r=oli-obk
Browse files Browse the repository at this point in the history
Miri engine: proper support for `Assert` MIR terminators

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

r? @oli-obk
  • Loading branch information
bors committed Dec 2, 2019
2 parents 4af3ee8 + 5e51a15 commit 2da942f
Show file tree
Hide file tree
Showing 13 changed files with 195 additions and 118 deletions.
7 changes: 7 additions & 0 deletions src/librustc/mir/interpret/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
40 changes: 37 additions & 3 deletions src/librustc_mir/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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,
Expand Down
5 changes: 2 additions & 3 deletions src/librustc_mir/interpret/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down Expand Up @@ -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),
}
Expand Down
42 changes: 28 additions & 14 deletions src/librustc_mir/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
8 changes: 1 addition & 7 deletions src/librustc_mir/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?;
}

Expand Down
58 changes: 28 additions & 30 deletions src/librustc_mir/interpret/intrinsics/caller_location.rs
Original file line number Diff line number Diff line change
@@ -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)?;

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)?;
location
}

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,
)
}
}
25 changes: 16 additions & 9 deletions src/librustc_mir/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
21 changes: 15 additions & 6 deletions src/librustc_mir/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 2da942f

Please sign in to comment.