Auto merge of #66646 - RalfJung:unwind_to_block, r=oli-obk
refactor goto_block and also add unwind_to_block r? @oli-obk
This commit is contained in:
commit
a7d791b450
|
@ -323,8 +323,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
args: &[OpTy<'tcx>],
|
args: &[OpTy<'tcx>],
|
||||||
dest: Option<PlaceTy<'tcx>>,
|
ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
|
||||||
ret: Option<mir::BasicBlock>,
|
|
||||||
_unwind: Option<mir::BasicBlock> // unwinding is not supported in consts
|
_unwind: Option<mir::BasicBlock> // unwinding is not supported in consts
|
||||||
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
|
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
|
||||||
debug!("eval_fn_call: {:?}", instance);
|
debug!("eval_fn_call: {:?}", instance);
|
||||||
|
@ -337,8 +336,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
// Some functions we support even if they are non-const -- but avoid testing
|
// Some functions we support even if they are non-const -- but avoid testing
|
||||||
// that for const fn! We certainly do *not* want to actually call the fn
|
// that for const fn! We certainly do *not* want to actually call the fn
|
||||||
// though, so be sure we return here.
|
// though, so be sure we return here.
|
||||||
return if ecx.hook_panic_fn(instance, args, dest)? {
|
return if ecx.hook_panic_fn(instance, args, ret)? {
|
||||||
ecx.goto_block(ret)?; // fully evaluated and done
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
throw_unsup_format!("calling non-const function `{}`", instance)
|
throw_unsup_format!("calling non-const function `{}`", instance)
|
||||||
|
@ -364,8 +362,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
fn_val: !,
|
fn_val: !,
|
||||||
_args: &[OpTy<'tcx>],
|
_args: &[OpTy<'tcx>],
|
||||||
_dest: Option<PlaceTy<'tcx>>,
|
_ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
|
||||||
_ret: Option<mir::BasicBlock>,
|
_unwind: Option<mir::BasicBlock>
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
match fn_val {}
|
match fn_val {}
|
||||||
}
|
}
|
||||||
|
@ -375,11 +373,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
span: Span,
|
span: Span,
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
args: &[OpTy<'tcx>],
|
args: &[OpTy<'tcx>],
|
||||||
dest: Option<PlaceTy<'tcx>>,
|
ret: Option<(PlaceTy<'tcx>, mir::BasicBlock)>,
|
||||||
_ret: Option<mir::BasicBlock>,
|
|
||||||
_unwind: Option<mir::BasicBlock>
|
_unwind: Option<mir::BasicBlock>
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
if ecx.emulate_intrinsic(span, instance, args, dest)? {
|
if ecx.emulate_intrinsic(span, instance, args, ret)? {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// An intrinsic that we do not support
|
// An intrinsic that we do not support
|
||||||
|
|
|
@ -555,6 +555,37 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Jump to the given block.
|
||||||
|
#[inline]
|
||||||
|
pub fn go_to_block(&mut self, target: mir::BasicBlock) {
|
||||||
|
let frame = self.frame_mut();
|
||||||
|
frame.block = Some(target);
|
||||||
|
frame.stmt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// *Return* to the given `target` basic block.
|
||||||
|
/// Do *not* use for unwinding! Use `unwind_to_block` instead.
|
||||||
|
///
|
||||||
|
/// If `target` is `None`, that indicates the function cannot return, so we raise UB.
|
||||||
|
pub fn return_to_block(&mut self, target: Option<mir::BasicBlock>) -> InterpResult<'tcx> {
|
||||||
|
if let Some(target) = target {
|
||||||
|
Ok(self.go_to_block(target))
|
||||||
|
} else {
|
||||||
|
throw_ub!(Unreachable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// *Unwind* to the given `target` basic block.
|
||||||
|
/// Do *not* use for returning! Use `return_to_block` instead.
|
||||||
|
///
|
||||||
|
/// If `target` is `None`, that indicates the function does not need cleanup during
|
||||||
|
/// unwinding, and we will just keep propagating that upwards.
|
||||||
|
pub fn unwind_to_block(&mut self, target: Option<mir::BasicBlock>) {
|
||||||
|
let frame = self.frame_mut();
|
||||||
|
frame.block = target;
|
||||||
|
frame.stmt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Pops the current frame from the stack, deallocating the
|
/// Pops the current frame from the stack, deallocating the
|
||||||
/// memory for allocated locals.
|
/// memory for allocated locals.
|
||||||
///
|
///
|
||||||
|
@ -630,10 +661,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
if cur_unwinding {
|
if cur_unwinding {
|
||||||
// Follow the unwind edge.
|
// Follow the unwind edge.
|
||||||
let unwind = next_block.expect("Encounted StackPopCleanup::None when unwinding!");
|
let unwind = next_block.expect("Encounted StackPopCleanup::None when unwinding!");
|
||||||
let next_frame = self.frame_mut();
|
self.unwind_to_block(unwind);
|
||||||
// If `unwind` is `None`, we'll leave that function immediately again.
|
|
||||||
next_frame.block = unwind;
|
|
||||||
next_frame.stmt = 0;
|
|
||||||
} else {
|
} else {
|
||||||
// Follow the normal return edge.
|
// Follow the normal return edge.
|
||||||
// Validate the return value. Do this after deallocating so that we catch dangling
|
// Validate the return value. Do this after deallocating so that we catch dangling
|
||||||
|
@ -660,7 +688,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
|
|
||||||
// Jump to new block -- *after* validation so that the spans make more sense.
|
// Jump to new block -- *after* validation so that the spans make more sense.
|
||||||
if let Some(ret) = next_block {
|
if let Some(ret) = next_block {
|
||||||
self.goto_block(ret)?;
|
self.return_to_block(ret)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,10 @@ use rustc::ty::layout::{LayoutOf, Primitive, Size};
|
||||||
use rustc::ty::subst::SubstsRef;
|
use rustc::ty::subst::SubstsRef;
|
||||||
use rustc::hir::def_id::DefId;
|
use rustc::hir::def_id::DefId;
|
||||||
use rustc::ty::TyCtxt;
|
use rustc::ty::TyCtxt;
|
||||||
use rustc::mir::BinOp;
|
use rustc::mir::{
|
||||||
use rustc::mir::interpret::{InterpResult, Scalar, GlobalId, ConstValue};
|
self, BinOp,
|
||||||
|
interpret::{InterpResult, Scalar, GlobalId, ConstValue}
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Machine, PlaceTy, OpTy, InterpCx, ImmTy,
|
Machine, PlaceTy, OpTy, InterpCx, ImmTy,
|
||||||
|
@ -91,17 +93,21 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
span: Span,
|
span: Span,
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
args: &[OpTy<'tcx, M::PointerTag>],
|
args: &[OpTy<'tcx, M::PointerTag>],
|
||||||
dest: Option<PlaceTy<'tcx, M::PointerTag>>,
|
ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
|
||||||
) -> InterpResult<'tcx, bool> {
|
) -> InterpResult<'tcx, bool> {
|
||||||
let substs = instance.substs;
|
let substs = instance.substs;
|
||||||
|
|
||||||
// We currently do not handle any diverging intrinsics.
|
|
||||||
let dest = match dest {
|
|
||||||
Some(dest) => dest,
|
|
||||||
None => return Ok(false)
|
|
||||||
};
|
|
||||||
let intrinsic_name = &*self.tcx.item_name(instance.def_id()).as_str();
|
let intrinsic_name = &*self.tcx.item_name(instance.def_id()).as_str();
|
||||||
|
|
||||||
|
// We currently do not handle any intrinsics that are *allowed* to diverge,
|
||||||
|
// but `transmute` could lack a return place in case of UB.
|
||||||
|
let (dest, ret) = match ret {
|
||||||
|
Some(p) => p,
|
||||||
|
None => match intrinsic_name {
|
||||||
|
"transmute" => throw_ub!(Unreachable),
|
||||||
|
_ => return Ok(false),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match intrinsic_name {
|
match intrinsic_name {
|
||||||
"caller_location" => {
|
"caller_location" => {
|
||||||
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
|
||||||
|
@ -268,34 +274,39 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
// exception from the exception.)
|
// exception from the exception.)
|
||||||
// This is the dual to the special exception for offset-by-0
|
// This is the dual to the special exception for offset-by-0
|
||||||
// in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`).
|
// in the inbounds pointer offset operation (see the Miri code, `src/operator.rs`).
|
||||||
if a.is_bits() && b.is_bits() {
|
//
|
||||||
|
// Control flow is weird because we cannot early-return (to reach the
|
||||||
|
// `go_to_block` at the end).
|
||||||
|
let done = if a.is_bits() && b.is_bits() {
|
||||||
let a = a.to_machine_usize(self)?;
|
let a = a.to_machine_usize(self)?;
|
||||||
let b = b.to_machine_usize(self)?;
|
let b = b.to_machine_usize(self)?;
|
||||||
if a == b && a != 0 {
|
if a == b && a != 0 {
|
||||||
self.write_scalar(Scalar::from_int(0, isize_layout.size), dest)?;
|
self.write_scalar(Scalar::from_int(0, isize_layout.size), dest)?;
|
||||||
return Ok(true);
|
true
|
||||||
}
|
} else { false }
|
||||||
}
|
} else { false };
|
||||||
|
|
||||||
// General case: we need two pointers.
|
if !done {
|
||||||
let a = self.force_ptr(a)?;
|
// General case: we need two pointers.
|
||||||
let b = self.force_ptr(b)?;
|
let a = self.force_ptr(a)?;
|
||||||
if a.alloc_id != b.alloc_id {
|
let b = self.force_ptr(b)?;
|
||||||
throw_ub_format!(
|
if a.alloc_id != b.alloc_id {
|
||||||
"ptr_offset_from cannot compute offset of pointers into different \
|
throw_ub_format!(
|
||||||
allocations.",
|
"ptr_offset_from cannot compute offset of pointers into different \
|
||||||
);
|
allocations.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let usize_layout = self.layout_of(self.tcx.types.usize)?;
|
||||||
|
let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout);
|
||||||
|
let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout);
|
||||||
|
let (val, _overflowed, _ty) = self.overflowing_binary_op(
|
||||||
|
BinOp::Sub, a_offset, b_offset,
|
||||||
|
)?;
|
||||||
|
let pointee_layout = self.layout_of(substs.type_at(0))?;
|
||||||
|
let val = ImmTy::from_scalar(val, isize_layout);
|
||||||
|
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
|
||||||
|
self.exact_div(val, size, dest)?;
|
||||||
}
|
}
|
||||||
let usize_layout = self.layout_of(self.tcx.types.usize)?;
|
|
||||||
let a_offset = ImmTy::from_uint(a.offset.bytes(), usize_layout);
|
|
||||||
let b_offset = ImmTy::from_uint(b.offset.bytes(), usize_layout);
|
|
||||||
let (val, _overflowed, _ty) = self.overflowing_binary_op(
|
|
||||||
BinOp::Sub, a_offset, b_offset,
|
|
||||||
)?;
|
|
||||||
let pointee_layout = self.layout_of(substs.type_at(0))?;
|
|
||||||
let val = ImmTy::from_scalar(val, isize_layout);
|
|
||||||
let size = ImmTy::from_int(pointee_layout.size.bytes(), isize_layout);
|
|
||||||
self.exact_div(val, size, dest)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"transmute" => {
|
"transmute" => {
|
||||||
|
@ -350,6 +361,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
_ => return Ok(false),
|
_ => return Ok(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.dump_place(*dest);
|
||||||
|
self.go_to_block(ret);
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +373,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
&mut self,
|
&mut self,
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
args: &[OpTy<'tcx, M::PointerTag>],
|
args: &[OpTy<'tcx, M::PointerTag>],
|
||||||
_dest: Option<PlaceTy<'tcx, M::PointerTag>>,
|
_ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
|
||||||
) -> InterpResult<'tcx, bool> {
|
) -> InterpResult<'tcx, bool> {
|
||||||
let def_id = instance.def_id();
|
let def_id = instance.def_id();
|
||||||
if Some(def_id) == self.tcx.lang_items().panic_fn() {
|
if Some(def_id) == self.tcx.lang_items().panic_fn() {
|
||||||
|
|
|
@ -141,7 +141,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
||||||
/// Returns either the mir to use for the call, or `None` if execution should
|
/// Returns either the mir to use for the call, or `None` if execution should
|
||||||
/// just proceed (which usually means this hook did all the work that the
|
/// just proceed (which usually means this hook did all the work that the
|
||||||
/// called function should usually have done). In the latter case, it is
|
/// called function should usually have done). In the latter case, it is
|
||||||
/// this hook's responsibility to call `goto_block(ret)` to advance the instruction pointer!
|
/// this hook's responsibility to advance the instruction pointer!
|
||||||
/// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
|
/// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
|
||||||
/// nor just jump to `ret`, but instead push their own stack frame.)
|
/// nor just jump to `ret`, but instead push their own stack frame.)
|
||||||
/// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
|
/// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
|
||||||
|
@ -150,30 +150,28 @@ pub trait Machine<'mir, 'tcx>: Sized {
|
||||||
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
args: &[OpTy<'tcx, Self::PointerTag>],
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
||||||
dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
|
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
|
||||||
ret: Option<mir::BasicBlock>,
|
unwind: Option<mir::BasicBlock>,
|
||||||
unwind: Option<mir::BasicBlock>
|
|
||||||
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
|
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
|
||||||
|
|
||||||
/// Execute `fn_val`. it is the hook's responsibility to advance the instruction
|
/// Execute `fn_val`. It is the hook's responsibility to advance the instruction
|
||||||
/// pointer as appropriate.
|
/// pointer as appropriate.
|
||||||
fn call_extra_fn(
|
fn call_extra_fn(
|
||||||
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
fn_val: Self::ExtraFnVal,
|
fn_val: Self::ExtraFnVal,
|
||||||
args: &[OpTy<'tcx, Self::PointerTag>],
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
||||||
dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
|
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
|
||||||
ret: Option<mir::BasicBlock>,
|
unwind: Option<mir::BasicBlock>,
|
||||||
) -> InterpResult<'tcx>;
|
) -> InterpResult<'tcx>;
|
||||||
|
|
||||||
/// Directly process an intrinsic without pushing a stack frame.
|
/// Directly process an intrinsic without pushing a stack frame. It is the hook's
|
||||||
/// If this returns successfully, the engine will take care of jumping to the next block.
|
/// responsibility to advance the instruction pointer as appropriate.
|
||||||
fn call_intrinsic(
|
fn call_intrinsic(
|
||||||
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
span: Span,
|
span: Span,
|
||||||
instance: ty::Instance<'tcx>,
|
instance: ty::Instance<'tcx>,
|
||||||
args: &[OpTy<'tcx, Self::PointerTag>],
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
||||||
dest: Option<PlaceTy<'tcx, Self::PointerTag>>,
|
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
|
||||||
ret: Option<mir::BasicBlock>,
|
|
||||||
unwind: Option<mir::BasicBlock>,
|
unwind: Option<mir::BasicBlock>,
|
||||||
) -> InterpResult<'tcx>;
|
) -> InterpResult<'tcx>;
|
||||||
|
|
||||||
|
|
|
@ -12,17 +12,6 @@ use super::{
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
#[inline]
|
|
||||||
pub fn goto_block(&mut self, target: Option<mir::BasicBlock>) -> InterpResult<'tcx> {
|
|
||||||
if let Some(target) = target {
|
|
||||||
self.frame_mut().block = Some(target);
|
|
||||||
self.frame_mut().stmt = 0;
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
throw_ub!(Unreachable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn eval_terminator(
|
pub(super) fn eval_terminator(
|
||||||
&mut self,
|
&mut self,
|
||||||
terminator: &mir::Terminator<'tcx>,
|
terminator: &mir::Terminator<'tcx>,
|
||||||
|
@ -34,7 +23,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
self.pop_stack_frame(/* unwinding */ false)?
|
self.pop_stack_frame(/* unwinding */ false)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Goto { target } => self.goto_block(Some(target))?,
|
Goto { target } => self.go_to_block(target),
|
||||||
|
|
||||||
SwitchInt {
|
SwitchInt {
|
||||||
ref discr,
|
ref discr,
|
||||||
|
@ -60,7 +49,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.goto_block(Some(target_block))?;
|
self.go_to_block(target_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
Call {
|
Call {
|
||||||
|
@ -70,11 +59,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
ref cleanup,
|
ref cleanup,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let (dest, ret) = match *destination {
|
|
||||||
Some((ref lv, target)) => (Some(self.eval_place(lv)?), Some(target)),
|
|
||||||
None => (None, None),
|
|
||||||
};
|
|
||||||
|
|
||||||
let func = self.eval_operand(func, None)?;
|
let func = self.eval_operand(func, None)?;
|
||||||
let (fn_val, abi) = match func.layout.ty.kind {
|
let (fn_val, abi) = match func.layout.ty.kind {
|
||||||
ty::FnPtr(sig) => {
|
ty::FnPtr(sig) => {
|
||||||
|
@ -92,12 +76,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let args = self.eval_operands(args)?;
|
let args = self.eval_operands(args)?;
|
||||||
|
let ret = match destination {
|
||||||
|
Some((dest, ret)) => Some((self.eval_place(dest)?, *ret)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
self.eval_fn_call(
|
self.eval_fn_call(
|
||||||
fn_val,
|
fn_val,
|
||||||
terminator.source_info.span,
|
terminator.source_info.span,
|
||||||
abi,
|
abi,
|
||||||
&args[..],
|
&args[..],
|
||||||
dest,
|
|
||||||
ret,
|
ret,
|
||||||
*cleanup
|
*cleanup
|
||||||
)?;
|
)?;
|
||||||
|
@ -133,7 +120,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
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.goto_block(Some(target))?;
|
self.go_to_block(target);
|
||||||
} else {
|
} else {
|
||||||
// Compute error message
|
// Compute error message
|
||||||
use rustc::mir::interpret::PanicInfo::*;
|
use rustc::mir::interpret::PanicInfo::*;
|
||||||
|
@ -249,8 +236,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
span: Span,
|
span: Span,
|
||||||
caller_abi: Abi,
|
caller_abi: Abi,
|
||||||
args: &[OpTy<'tcx, M::PointerTag>],
|
args: &[OpTy<'tcx, M::PointerTag>],
|
||||||
dest: Option<PlaceTy<'tcx, M::PointerTag>>,
|
ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
|
||||||
ret: Option<mir::BasicBlock>,
|
|
||||||
unwind: Option<mir::BasicBlock>
|
unwind: Option<mir::BasicBlock>
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
trace!("eval_fn_call: {:#?}", fn_val);
|
trace!("eval_fn_call: {:#?}", fn_val);
|
||||||
|
@ -258,32 +244,38 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
let instance = match fn_val {
|
let instance = match fn_val {
|
||||||
FnVal::Instance(instance) => instance,
|
FnVal::Instance(instance) => instance,
|
||||||
FnVal::Other(extra) => {
|
FnVal::Other(extra) => {
|
||||||
return M::call_extra_fn(self, extra, args, dest, ret);
|
return M::call_extra_fn(self, extra, args, ret, unwind);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ABI check
|
||||||
|
{
|
||||||
|
let callee_abi = {
|
||||||
|
let instance_ty = instance.ty(*self.tcx);
|
||||||
|
match instance_ty.kind {
|
||||||
|
ty::FnDef(..) =>
|
||||||
|
instance_ty.fn_sig(*self.tcx).abi(),
|
||||||
|
ty::Closure(..) => Abi::RustCall,
|
||||||
|
ty::Generator(..) => Abi::Rust,
|
||||||
|
_ => bug!("unexpected callee ty: {:?}", instance_ty),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let normalize_abi = |abi| match abi {
|
||||||
|
Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic =>
|
||||||
|
// These are all the same ABI, really.
|
||||||
|
Abi::Rust,
|
||||||
|
abi =>
|
||||||
|
abi,
|
||||||
|
};
|
||||||
|
if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
|
||||||
|
throw_unsup!(FunctionAbiMismatch(caller_abi, callee_abi))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match instance.def {
|
match instance.def {
|
||||||
ty::InstanceDef::Intrinsic(..) => {
|
ty::InstanceDef::Intrinsic(..) => {
|
||||||
assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic);
|
assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic);
|
||||||
|
return M::call_intrinsic(self, span, instance, args, ret, unwind);
|
||||||
let old_stack = self.cur_frame();
|
|
||||||
let old_bb = self.frame().block;
|
|
||||||
M::call_intrinsic(self, span, instance, args, dest, ret, unwind)?;
|
|
||||||
// No stack frame gets pushed, the main loop will just act as if the
|
|
||||||
// call completed.
|
|
||||||
if ret.is_some() {
|
|
||||||
self.goto_block(ret)?;
|
|
||||||
} else {
|
|
||||||
// If this intrinsic call doesn't have a ret block,
|
|
||||||
// then the intrinsic implementation should have
|
|
||||||
// changed the stack frame (otherwise, we'll end
|
|
||||||
// up trying to execute this intrinsic call again)
|
|
||||||
debug_assert!(self.cur_frame() != old_stack || self.frame().block != old_bb);
|
|
||||||
}
|
|
||||||
if let Some(dest) = dest {
|
|
||||||
self.dump_place(*dest)
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
ty::InstanceDef::VtableShim(..) |
|
ty::InstanceDef::VtableShim(..) |
|
||||||
ty::InstanceDef::ReifyShim(..) |
|
ty::InstanceDef::ReifyShim(..) |
|
||||||
|
@ -292,32 +284,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
ty::InstanceDef::DropGlue(..) |
|
ty::InstanceDef::DropGlue(..) |
|
||||||
ty::InstanceDef::CloneShim(..) |
|
ty::InstanceDef::CloneShim(..) |
|
||||||
ty::InstanceDef::Item(_) => {
|
ty::InstanceDef::Item(_) => {
|
||||||
// ABI check
|
|
||||||
{
|
|
||||||
let callee_abi = {
|
|
||||||
let instance_ty = instance.ty(*self.tcx);
|
|
||||||
match instance_ty.kind {
|
|
||||||
ty::FnDef(..) =>
|
|
||||||
instance_ty.fn_sig(*self.tcx).abi(),
|
|
||||||
ty::Closure(..) => Abi::RustCall,
|
|
||||||
ty::Generator(..) => Abi::Rust,
|
|
||||||
_ => bug!("unexpected callee ty: {:?}", instance_ty),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let normalize_abi = |abi| match abi {
|
|
||||||
Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic =>
|
|
||||||
// These are all the same ABI, really.
|
|
||||||
Abi::Rust,
|
|
||||||
abi =>
|
|
||||||
abi,
|
|
||||||
};
|
|
||||||
if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
|
|
||||||
throw_unsup!(FunctionAbiMismatch(caller_abi, callee_abi))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need MIR for this fn
|
// We need MIR for this fn
|
||||||
let body = match M::find_fn(self, instance, args, dest, ret, unwind)? {
|
let body = match M::find_fn(self, instance, args, ret, unwind)? {
|
||||||
Some(body) => body,
|
Some(body) => body,
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
@ -326,8 +294,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
instance,
|
instance,
|
||||||
span,
|
span,
|
||||||
body,
|
body,
|
||||||
dest,
|
ret.map(|p| p.0),
|
||||||
StackPopCleanup::Goto { ret, unwind }
|
StackPopCleanup::Goto { ret: ret.map(|p| p.1), unwind }
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// We want to pop this frame again in case there was an error, to put
|
// We want to pop this frame again in case there was an error, to put
|
||||||
|
@ -410,7 +378,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
throw_unsup!(FunctionArgCountMismatch)
|
throw_unsup!(FunctionArgCountMismatch)
|
||||||
}
|
}
|
||||||
// Don't forget to check the return type!
|
// Don't forget to check the return type!
|
||||||
if let Some(caller_ret) = dest {
|
if let Some((caller_ret, _)) = ret {
|
||||||
let callee_ret = self.eval_place(
|
let callee_ret = self.eval_place(
|
||||||
&mir::Place::return_place()
|
&mir::Place::return_place()
|
||||||
)?;
|
)?;
|
||||||
|
@ -476,7 +444,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
});
|
});
|
||||||
trace!("Patched self operand to {:#?}", args[0]);
|
trace!("Patched self operand to {:#?}", args[0]);
|
||||||
// recurse with concrete function
|
// recurse with concrete function
|
||||||
self.eval_fn_call(drop_fn, span, caller_abi, &args, dest, ret, unwind)
|
self.eval_fn_call(drop_fn, span, caller_abi, &args, ret, unwind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -516,8 +484,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||||
span,
|
span,
|
||||||
Abi::Rust,
|
Abi::Rust,
|
||||||
&[arg.into()],
|
&[arg.into()],
|
||||||
Some(dest.into()),
|
Some((dest.into(), target)),
|
||||||
Some(target),
|
|
||||||
unwind
|
unwind
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,8 +144,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
||||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
_instance: ty::Instance<'tcx>,
|
_instance: ty::Instance<'tcx>,
|
||||||
_args: &[OpTy<'tcx>],
|
_args: &[OpTy<'tcx>],
|
||||||
_dest: Option<PlaceTy<'tcx>>,
|
_ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
|
||||||
_ret: Option<BasicBlock>,
|
|
||||||
_unwind: Option<BasicBlock>,
|
_unwind: Option<BasicBlock>,
|
||||||
) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
|
) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -155,8 +154,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
||||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
fn_val: !,
|
fn_val: !,
|
||||||
_args: &[OpTy<'tcx>],
|
_args: &[OpTy<'tcx>],
|
||||||
_dest: Option<PlaceTy<'tcx>>,
|
_ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
|
||||||
_ret: Option<BasicBlock>,
|
_unwind: Option<BasicBlock>
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
match fn_val {}
|
match fn_val {}
|
||||||
}
|
}
|
||||||
|
@ -166,8 +165,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine {
|
||||||
_span: Span,
|
_span: Span,
|
||||||
_instance: ty::Instance<'tcx>,
|
_instance: ty::Instance<'tcx>,
|
||||||
_args: &[OpTy<'tcx>],
|
_args: &[OpTy<'tcx>],
|
||||||
_dest: Option<PlaceTy<'tcx>>,
|
_ret: Option<(PlaceTy<'tcx>, BasicBlock)>,
|
||||||
_ret: Option<BasicBlock>,
|
|
||||||
_unwind: Option<BasicBlock>
|
_unwind: Option<BasicBlock>
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
throw_unsup_format!("calling intrinsics isn't supported in ConstProp");
|
throw_unsup_format!("calling intrinsics isn't supported in ConstProp");
|
||||||
|
|
Loading…
Reference in New Issue