Skip to content

Commit 2da942f

Browse files
committed
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 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
2 parents 4af3ee8 + 5e51a15 commit 2da942f

File tree

13 files changed

+195
-118
lines changed

13 files changed

+195
-118
lines changed

src/librustc/mir/interpret/value.rs

+7
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,13 @@ impl<Tag> From<Scalar<Tag>> for ScalarMaybeUndef<Tag> {
471471
}
472472
}
473473

474+
impl<Tag> From<Pointer<Tag>> for ScalarMaybeUndef<Tag> {
475+
#[inline(always)]
476+
fn from(s: Pointer<Tag>) -> Self {
477+
ScalarMaybeUndef::Scalar(s.into())
478+
}
479+
}
480+
474481
impl<Tag: fmt::Debug, Id: fmt::Debug> fmt::Debug for ScalarMaybeUndef<Tag, Id> {
475482
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
476483
match self {

src/librustc_mir/const_eval.rs

+37-3
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use syntax::{source_map::{Span, DUMMY_SP}, symbol::Symbol};
2323
use crate::interpret::{self,
2424
PlaceTy, MPlaceTy, OpTy, ImmTy, Immediate, Scalar, Pointer,
2525
RawConst, ConstValue, Machine,
26-
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup,
26+
InterpResult, InterpErrorInfo, GlobalId, InterpCx, StackPopCleanup, AssertMessage,
2727
Allocation, AllocId, MemoryKind, Memory,
2828
snapshot, RefTracking, intern_const_alloc_recursive,
2929
};
@@ -395,6 +395,40 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
395395
)
396396
}
397397

398+
fn assert_panic(
399+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
400+
_span: Span,
401+
msg: &AssertMessage<'tcx>,
402+
_unwind: Option<mir::BasicBlock>,
403+
) -> InterpResult<'tcx> {
404+
use rustc::mir::interpret::PanicInfo::*;
405+
Err(match msg {
406+
BoundsCheck { ref len, ref index } => {
407+
let len = ecx
408+
.read_immediate(ecx.eval_operand(len, None)?)
409+
.expect("can't eval len")
410+
.to_scalar()?
411+
.to_machine_usize(&*ecx)?;
412+
let index = ecx
413+
.read_immediate(ecx.eval_operand(index, None)?)
414+
.expect("can't eval index")
415+
.to_scalar()?
416+
.to_machine_usize(&*ecx)?;
417+
err_panic!(BoundsCheck { len, index })
418+
}
419+
Overflow(op) => err_panic!(Overflow(*op)),
420+
OverflowNeg => err_panic!(OverflowNeg),
421+
DivisionByZero => err_panic!(DivisionByZero),
422+
RemainderByZero => err_panic!(RemainderByZero),
423+
ResumedAfterReturn(generator_kind)
424+
=> err_panic!(ResumedAfterReturn(*generator_kind)),
425+
ResumedAfterPanic(generator_kind)
426+
=> err_panic!(ResumedAfterPanic(*generator_kind)),
427+
Panic { .. } => bug!("`Panic` variant cannot occur in MIR"),
428+
}
429+
.into())
430+
}
431+
398432
fn ptr_to_int(
399433
_mem: &Memory<'mir, 'tcx, Self>,
400434
_ptr: Pointer,
@@ -423,7 +457,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
423457
}
424458

425459
#[inline(always)]
426-
fn tag_allocation<'b>(
460+
fn init_allocation_extra<'b>(
427461
_memory_extra: &(),
428462
_id: AllocId,
429463
alloc: Cow<'b, Allocation>,
@@ -518,7 +552,7 @@ pub fn const_caller_location<'tcx>(
518552
tcx.type_of(tcx.require_lang_item(PanicLocationLangItem, None))
519553
.subst(tcx, tcx.mk_substs([tcx.lifetimes.re_static.into()].iter())),
520554
);
521-
let loc_place = ecx.alloc_caller_location(file, line, col).unwrap();
555+
let loc_place = ecx.alloc_caller_location(file, line, col);
522556
intern_const_alloc_recursive(&mut ecx, None, loc_place).unwrap();
523557
let loc_const = ty::Const {
524558
ty: loc_ty,

src/librustc_mir/interpret/cast.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
5555
).ok_or_else(|| err_inval!(TooGeneric))?;
5656

5757
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
58-
self.write_scalar(Scalar::Ptr(fn_ptr.into()), dest)?;
58+
self.write_scalar(fn_ptr, dest)?;
5959
}
6060
_ => bug!("reify fn pointer on {:?}", src.layout.ty),
6161
}
@@ -88,8 +88,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
8888
ty::ClosureKind::FnOnce,
8989
);
9090
let fn_ptr = self.memory.create_fn_alloc(FnVal::Instance(instance));
91-
let val = Immediate::Scalar(Scalar::Ptr(fn_ptr.into()).into());
92-
self.write_immediate(val, dest)?;
91+
self.write_scalar(fn_ptr, dest)?;
9392
}
9493
_ => bug!("closure fn pointer on {:?}", src.layout.ty),
9594
}

src/librustc_mir/interpret/eval_context.rs

+28-14
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,20 @@ impl<'tcx, Tag: Copy + 'static> LocalState<'tcx, Tag> {
164164
}
165165
}
166166

167+
impl<'mir, 'tcx, Tag, Extra> Frame<'mir, 'tcx, Tag, Extra> {
168+
/// Return the `SourceInfo` of the current instruction.
169+
pub fn current_source_info(&self) -> Option<mir::SourceInfo> {
170+
self.block.map(|block| {
171+
let block = &self.body.basic_blocks()[block];
172+
if self.stmt < block.statements.len() {
173+
block.statements[self.stmt].source_info
174+
} else {
175+
block.terminator().source_info
176+
}
177+
})
178+
}
179+
}
180+
167181
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for InterpCx<'mir, 'tcx, M> {
168182
#[inline]
169183
fn data_layout(&self) -> &layout::TargetDataLayout {
@@ -236,6 +250,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
236250
self.memory.force_bits(scalar, size)
237251
}
238252

253+
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
254+
/// the *canonical* machine pointer to the allocation. Must never be used
255+
/// for any other pointers!
256+
///
257+
/// This represents a *direct* access to that memory, as opposed to access
258+
/// through a pointer that was created by the program.
239259
#[inline(always)]
240260
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
241261
self.memory.tag_static_base_pointer(ptr)
@@ -828,34 +848,28 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
828848
pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo<'tcx>> {
829849
let mut last_span = None;
830850
let mut frames = Vec::new();
831-
for &Frame { instance, span, body, block, stmt, .. } in self.stack().iter().rev() {
851+
for frame in self.stack().iter().rev() {
832852
// make sure we don't emit frames that are duplicates of the previous
833-
if explicit_span == Some(span) {
834-
last_span = Some(span);
853+
if explicit_span == Some(frame.span) {
854+
last_span = Some(frame.span);
835855
continue;
836856
}
837857
if let Some(last) = last_span {
838-
if last == span {
858+
if last == frame.span {
839859
continue;
840860
}
841861
} else {
842-
last_span = Some(span);
862+
last_span = Some(frame.span);
843863
}
844864

845-
let lint_root = block.and_then(|block| {
846-
let block = &body.basic_blocks()[block];
847-
let source_info = if stmt < block.statements.len() {
848-
block.statements[stmt].source_info
849-
} else {
850-
block.terminator().source_info
851-
};
852-
match &body.source_scopes[source_info.scope].local_data {
865+
let lint_root = frame.current_source_info().and_then(|source_info| {
866+
match &frame.body.source_scopes[source_info.scope].local_data {
853867
mir::ClearCrossCrate::Set(data) => Some(data.lint_root),
854868
mir::ClearCrossCrate::Clear => None,
855869
}
856870
});
857871

858-
frames.push(FrameInfo { call_site: span, instance, lint_root });
872+
frames.push(FrameInfo { call_site: frame.span, instance: frame.instance, lint_root });
859873
}
860874
trace!("generate stacktrace: {:#?}, {:?}", frames, explicit_span);
861875
frames

src/librustc_mir/interpret/intrinsics.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
110110

111111
match intrinsic_name {
112112
"caller_location" => {
113-
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
114-
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
115-
let location = self.alloc_caller_location(
116-
Symbol::intern(&caller.file.name.to_string()),
117-
caller.line as u32,
118-
caller.col_display as u32 + 1,
119-
)?;
113+
let location = self.alloc_caller_location_for_span(span);
120114
self.write_scalar(location.ptr, dest)?;
121115
}
122116

Original file line numberDiff line numberDiff line change
@@ -1,50 +1,48 @@
11
use rustc::middle::lang_items::PanicLocationLangItem;
2-
use rustc::mir::interpret::{Pointer, PointerArithmetic, Scalar};
32
use rustc::ty::subst::Subst;
4-
use rustc_target::abi::{LayoutOf, Size};
5-
use syntax_pos::Symbol;
3+
use rustc_target::abi::LayoutOf;
4+
use syntax_pos::{Symbol, Span};
65

7-
use crate::interpret::{MemoryKind, MPlaceTy, intrinsics::{InterpCx, InterpResult, Machine}};
6+
use crate::interpret::{Scalar, MemoryKind, MPlaceTy, intrinsics::{InterpCx, Machine}};
87

98
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10-
pub fn alloc_caller_location(
9+
crate fn alloc_caller_location(
1110
&mut self,
1211
filename: Symbol,
1312
line: u32,
1413
col: u32,
15-
) -> InterpResult<'tcx, MPlaceTy<'tcx, M::PointerTag>> {
14+
) -> MPlaceTy<'tcx, M::PointerTag> {
15+
let file = self.allocate_str(&filename.as_str(), MemoryKind::CallerLocation);
1616
let line = Scalar::from_u32(line);
1717
let col = Scalar::from_u32(col);
1818

19-
let ptr_size = self.pointer_size();
20-
let u32_size = Size::from_bits(32);
21-
19+
// Allocate memory for `CallerLocation` struct.
2220
let loc_ty = self.tcx.type_of(self.tcx.require_lang_item(PanicLocationLangItem, None))
2321
.subst(*self.tcx, self.tcx.mk_substs([self.tcx.lifetimes.re_static.into()].iter()));
24-
let loc_layout = self.layout_of(loc_ty)?;
25-
26-
let file_alloc = self.tcx.allocate_bytes(filename.as_str().as_bytes());
27-
let file_ptr = Pointer::new(file_alloc, Size::ZERO);
28-
let file = Scalar::Ptr(self.tag_static_base_pointer(file_ptr));
29-
let file_len = Scalar::from_uint(filename.as_str().len() as u128, ptr_size);
30-
22+
let loc_layout = self.layout_of(loc_ty).unwrap();
3123
let location = self.allocate(loc_layout, MemoryKind::CallerLocation);
3224

33-
let file_out = self.mplace_field(location, 0)?;
34-
let file_ptr_out = self.force_ptr(self.mplace_field(file_out, 0)?.ptr)?;
35-
let file_len_out = self.force_ptr(self.mplace_field(file_out, 1)?.ptr)?;
36-
let line_out = self.force_ptr(self.mplace_field(location, 1)?.ptr)?;
37-
let col_out = self.force_ptr(self.mplace_field(location, 2)?.ptr)?;
25+
// Initialize fields.
26+
self.write_immediate(file.to_ref(), self.mplace_field(location, 0).unwrap().into())
27+
.expect("writing to memory we just allocated cannot fail");
28+
self.write_scalar(line, self.mplace_field(location, 1).unwrap().into())
29+
.expect("writing to memory we just allocated cannot fail");
30+
self.write_scalar(col, self.mplace_field(location, 2).unwrap().into())
31+
.expect("writing to memory we just allocated cannot fail");
3832

39-
let layout = &self.tcx.data_layout;
40-
// We just allocated this, so we can skip the bounds checks.
41-
let alloc = self.memory.get_raw_mut(file_ptr_out.alloc_id)?;
42-
43-
alloc.write_scalar(layout, file_ptr_out, file.into(), ptr_size)?;
44-
alloc.write_scalar(layout, file_len_out, file_len.into(), ptr_size)?;
45-
alloc.write_scalar(layout, line_out, line.into(), u32_size)?;
46-
alloc.write_scalar(layout, col_out, col.into(), u32_size)?;
33+
location
34+
}
4735

48-
Ok(location)
36+
pub fn alloc_caller_location_for_span(
37+
&mut self,
38+
span: Span,
39+
) -> MPlaceTy<'tcx, M::PointerTag> {
40+
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
41+
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
42+
self.alloc_caller_location(
43+
Symbol::intern(&caller.file.name.to_string()),
44+
caller.line as u32,
45+
caller.col_display as u32 + 1,
46+
)
4947
}
5048
}

src/librustc_mir/interpret/machine.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc::ty::{self, Ty, TyCtxt};
1111
use syntax_pos::Span;
1212

1313
use super::{
14-
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
14+
Allocation, AllocId, InterpResult, Scalar, AllocationExtra, AssertMessage,
1515
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
1616
Frame, Operand,
1717
};
@@ -175,6 +175,14 @@ pub trait Machine<'mir, 'tcx>: Sized {
175175
unwind: Option<mir::BasicBlock>,
176176
) -> InterpResult<'tcx>;
177177

178+
/// Called to evaluate `Assert` MIR terminators that trigger a panic.
179+
fn assert_panic(
180+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
181+
span: Span,
182+
msg: &AssertMessage<'tcx>,
183+
unwind: Option<mir::BasicBlock>,
184+
) -> InterpResult<'tcx>;
185+
178186
/// Called for read access to a foreign static item.
179187
///
180188
/// 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 {
233241
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
234242
/// owned allocation to the map even when the map is shared.)
235243
///
236-
/// For static allocations, the tag returned must be the same as the one returned by
237-
/// `tag_static_base_pointer`.
238-
fn tag_allocation<'b>(
244+
/// Also return the "base" tag to use for this allocation: the one that is used for direct
245+
/// accesses to this allocation. If `kind == STATIC_KIND`, this tag must be consistent
246+
/// with `tag_static_base_pointer`.
247+
fn init_allocation_extra<'b>(
239248
memory_extra: &Self::MemoryExtra,
240249
id: AllocId,
241250
alloc: Cow<'b, Allocation>,
242251
kind: Option<MemoryKind<Self::MemoryKinds>>,
243252
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
244253

245-
/// Return the "base" tag for the given static allocation: the one that is used for direct
246-
/// accesses to this static/const/fn allocation.
247-
///
248-
/// Be aware that requesting the `Allocation` for that `id` will lead to cycles
249-
/// for cyclic statics!
254+
/// Return the "base" tag for the given *static* allocation: the one that is used for direct
255+
/// accesses to this static/const/fn allocation. If `id` is not a static allocation,
256+
/// this will return an unusable tag (i.e., accesses will be UB)!
250257
fn tag_static_base_pointer(
251258
memory_extra: &Self::MemoryExtra,
252259
id: AllocId,

src/librustc_mir/interpret/memory.rs

+15-6
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
143143
}
144144
}
145145

146+
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
147+
/// the *canonical* machine pointer to the allocation. Must never be used
148+
/// for any other pointers!
149+
///
150+
/// This represents a *direct* access to that memory, as opposed to access
151+
/// through a pointer that was created by the program.
146152
#[inline]
147153
pub fn tag_static_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
148154
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> {
191197
kind: MemoryKind<M::MemoryKinds>,
192198
) -> Pointer<M::PointerTag> {
193199
let id = self.tcx.alloc_map.lock().reserve();
194-
let (alloc, tag) = M::tag_allocation(&self.extra, id, Cow::Owned(alloc), Some(kind));
200+
debug_assert_ne!(Some(kind), M::STATIC_KIND.map(MemoryKind::Machine),
201+
"dynamically allocating static memory");
202+
let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
195203
self.alloc_map.insert(id, (kind, alloc.into_owned()));
196204
Pointer::from(id).with_tag(tag)
197205
}
@@ -350,7 +358,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
350358
sptr
351359
} else {
352360
// A "real" access, we must get a pointer.
353-
Scalar::Ptr(self.force_ptr(sptr)?)
361+
Scalar::from(self.force_ptr(sptr)?)
354362
};
355363
Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
356364
Ok(bits) => {
@@ -473,14 +481,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
473481
}
474482
}
475483
};
476-
// We got tcx memory. Let the machine figure out whether and how to
477-
// turn that into memory with the right pointer tag.
478-
Ok(M::tag_allocation(
484+
// We got tcx memory. Let the machine initialize its "extra" stuff.
485+
let (alloc, tag) = M::init_allocation_extra(
479486
memory_extra,
480487
id, // always use the ID we got as input, not the "hidden" one.
481488
alloc,
482489
M::STATIC_KIND.map(MemoryKind::Machine),
483-
).0)
490+
);
491+
debug_assert_eq!(tag, M::tag_static_base_pointer(memory_extra, id));
492+
Ok(alloc)
484493
}
485494

486495
/// Gives raw access to the `Allocation`, without bounds or alignment checks.

0 commit comments

Comments
 (0)