-// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
use std::borrow::Cow;
+use std::convert::TryFrom;
-use rustc::{mir, ty};
-use rustc::ty::layout::{self, TyLayout, LayoutOf};
-use syntax::source_map::Span;
+use rustc_middle::ty::layout::TyAndLayout;
+use rustc_middle::ty::Instance;
+use rustc_middle::{mir, ty};
+use rustc_target::abi::{self, LayoutOf as _};
use rustc_target::spec::abi::Abi;
-use rustc::mir::interpret::{EvalResult, PointerArithmetic, EvalErrorKind, Scalar};
use super::{
- EvalContext, Machine, Value, OpTy, Place, PlaceTy, Operand, StackPopCleanup
+ FnVal, ImmTy, InterpCx, InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, StackPopCleanup,
};
-impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
- #[inline]
- pub fn goto_block(&mut self, target: Option<mir::BasicBlock>) -> EvalResult<'tcx> {
- if let Some(target) = target {
- self.frame_mut().block = target;
- self.frame_mut().stmt = 0;
- Ok(())
- } else {
- err!(Unreachable)
- }
- }
-
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn eval_terminator(
&mut self,
terminator: &mir::Terminator<'tcx>,
- ) -> EvalResult<'tcx> {
- use rustc::mir::TerminatorKind::*;
+ ) -> InterpResult<'tcx> {
+ use rustc_middle::mir::TerminatorKind::*;
match terminator.kind {
Return => {
- self.dump_place(self.frame().return_place);
- self.pop_stack_frame()?
+ self.frame().return_place.map(|r| self.dump_place(*r));
+ self.pop_stack_frame(/* unwinding */ false)?
}
- Goto { target } => self.goto_block(Some(target))?,
+ Goto { target } => self.go_to_block(target),
- SwitchInt {
- ref discr,
- ref values,
- ref targets,
- ..
- } => {
- let discr = self.read_value(self.eval_operand(discr, None)?)?;
+ SwitchInt { ref discr, ref values, ref targets, .. } => {
+ let discr = self.read_immediate(self.eval_operand(discr, None)?)?;
trace!("SwitchInt({:?})", *discr);
// Branch to the `otherwise` case by default, if no match is found.
+ assert!(!targets.is_empty());
let mut target_block = targets[targets.len() - 1];
for (index, &const_int) in values.iter().enumerate() {
// Compare using binary_op, to also support pointer values
- let const_int = Scalar::from_uint(const_int, discr.layout.size);
- let (res, _) = self.binary_op(mir::BinOp::Eq,
- discr.to_scalar()?, discr.layout,
- const_int, discr.layout,
- )?;
+ let res = self
+ .overflowing_binary_op(
+ mir::BinOp::Eq,
+ discr,
+ ImmTy::from_uint(const_int, discr.layout),
+ )?
+ .0;
if res.to_bool()? {
target_block = targets[index];
break;
}
}
- self.goto_block(Some(target_block))?;
+ self.go_to_block(target_block);
}
- Call {
- ref func,
- ref args,
- ref destination,
- ..
- } => {
- let (dest, ret) = match *destination {
- Some((ref lv, target)) => (Some(self.eval_place(lv)?), Some(target)),
- None => (None, None),
- };
-
+ Call { ref func, ref args, destination, ref cleanup, .. } => {
+ let old_stack = self.frame_idx();
+ let old_bb = self.frame().block;
let func = self.eval_operand(func, None)?;
- let (fn_def, abi) = match func.layout.ty.sty {
+ let (fn_val, abi) = match func.layout.ty.kind {
ty::FnPtr(sig) => {
let caller_abi = sig.abi();
- let fn_ptr = self.read_scalar(func)?.to_ptr()?;
- let instance = self.memory.get_fn(fn_ptr)?;
- (instance, caller_abi)
+ let fn_ptr = self.read_scalar(func)?.not_undef()?;
+ let fn_val = self.memory.get_fn(fn_ptr)?;
+ (fn_val, caller_abi)
}
ty::FnDef(def_id, substs) => {
let sig = func.layout.ty.fn_sig(*self.tcx);
- (self.resolve(def_id, substs)?, sig.abi())
- },
- _ => {
- let msg = format!("can't handle callee of type {:?}", func.layout.ty);
- return err!(Unimplemented(msg));
+ (FnVal::Instance(self.resolve(def_id, substs)?), sig.abi())
}
+ _ => span_bug!(
+ terminator.source_info.span,
+ "invalid callee of type {:?}",
+ func.layout.ty
+ ),
};
let args = self.eval_operands(args)?;
- self.eval_fn_call(
- fn_def,
- terminator.source_info.span,
- abi,
- &args[..],
- dest,
- ret,
- )?;
+ let ret = match destination {
+ Some((dest, ret)) => Some((self.eval_place(dest)?, ret)),
+ None => None,
+ };
+ self.eval_fn_call(fn_val, abi, &args[..], ret, *cleanup)?;
+ // Sanity-check that `eval_fn_call` either pushed a new frame or
+ // did a jump to another block.
+ if self.frame_idx() == old_stack && self.frame().block == old_bb {
+ span_bug!(terminator.source_info.span, "evaluating this call made no progress");
+ }
}
- Drop {
- ref location,
- target,
- ..
- } => {
- // FIXME(CTFE): forbid drop in const eval
+ Drop { location, target, unwind } => {
let place = self.eval_place(location)?;
let ty = place.layout.ty;
trace!("TerminatorKind::drop: {:?}, type {}", location, ty);
- let instance = ::monomorphize::resolve_drop_in_place(*self.tcx, ty);
- self.drop_in_place(
- place,
- instance,
- terminator.source_info.span,
- target,
- )?;
+ let instance = Instance::resolve_drop_in_place(*self.tcx, ty);
+ self.drop_in_place(place, instance, target, unwind)?;
}
- Assert {
- ref cond,
- expected,
- ref msg,
- target,
- ..
- } => {
- let cond_val = self.read_value(self.eval_operand(cond, None)?)?
- .to_scalar()?.to_bool()?;
+ Assert { ref cond, 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.goto_block(Some(target))?;
+ self.go_to_block(target);
} else {
- // Compute error message
- use rustc::mir::interpret::EvalErrorKind::*;
- return match *msg {
- BoundsCheck { ref len, ref index } => {
- let len = self.read_value(self.eval_operand(len, None)?)
- .expect("can't eval len").to_scalar()?
- .to_bits(self.memory().pointer_size())? as u64;
- let index = self.read_value(self.eval_operand(index, None)?)
- .expect("can't eval index").to_scalar()?
- .to_bits(self.memory().pointer_size())? as u64;
- err!(BoundsCheck { len, index })
- }
- Overflow(op) => Err(Overflow(op).into()),
- OverflowNeg => Err(OverflowNeg.into()),
- DivisionByZero => Err(DivisionByZero.into()),
- RemainderByZero => Err(RemainderByZero.into()),
- GeneratorResumedAfterReturn |
- GeneratorResumedAfterPanic => unimplemented!(),
- _ => bug!(),
- };
+ M::assert_panic(self, msg, cleanup)?;
}
}
- Yield { .. } |
- GeneratorDrop |
- DropAndReplace { .. } |
- Resume |
- 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 => return err!(Unreachable),
+ Abort => {
+ M::abort(self)?;
+ }
+
+ // When we encounter Resume, we've finished unwinding
+ // cleanup for the current stack frame. We pop it in order
+ // to continue unwinding the next frame
+ Resume => {
+ trace!("unwinding: resuming from cleanup");
+ // By definition, a Resume terminator means
+ // that we're unwinding
+ self.pop_stack_frame(/* unwinding */ true)?;
+ 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 { .. }
+ | Yield { .. }
+ | GeneratorDrop => span_bug!(
+ terminator.source_info.span,
+ "{:#?} should have been eliminated by MIR pass",
+ terminator.kind
+ ),
}
Ok(())
}
fn check_argument_compat(
- caller: TyLayout<'tcx>,
- callee: TyLayout<'tcx>,
+ rust_abi: bool,
+ caller: TyAndLayout<'tcx>,
+ callee: TyAndLayout<'tcx>,
) -> bool {
if caller.ty == callee.ty {
// No question
return true;
}
+ if !rust_abi {
+ // Don't risk anything
+ return false;
+ }
// Compare layout
match (&caller.abi, &callee.abi) {
- (layout::Abi::Scalar(ref caller), layout::Abi::Scalar(ref callee)) =>
- // Different valid ranges are okay (once we enforce validity,
- // that will take care to make it UB to leave the range, just
- // like for transmute).
- caller.value == callee.value,
+ // Different valid ranges are okay (once we enforce validity,
+ // that will take care to make it UB to leave the range, just
+ // like for transmute).
+ (abi::Abi::Scalar(ref caller), abi::Abi::Scalar(ref callee)) => {
+ caller.value == callee.value
+ }
+ (
+ abi::Abi::ScalarPair(ref caller1, ref caller2),
+ abi::Abi::ScalarPair(ref callee1, ref callee2),
+ ) => caller1.value == callee1.value && caller2.value == callee2.value,
// Be conservative
- _ => false
+ _ => false,
}
}
/// Pass a single argument, checking the types for compatibility.
fn pass_argument(
&mut self,
- skip_zst: bool,
- caller_arg: &mut impl Iterator<Item=OpTy<'tcx>>,
- callee_arg: PlaceTy<'tcx>,
- ) -> EvalResult<'tcx> {
- if skip_zst && callee_arg.layout.is_zst() {
+ rust_abi: bool,
+ caller_arg: &mut impl Iterator<Item = OpTy<'tcx, M::PointerTag>>,
+ callee_arg: PlaceTy<'tcx, M::PointerTag>,
+ ) -> InterpResult<'tcx> {
+ if rust_abi && callee_arg.layout.is_zst() {
// Nothing to do.
trace!("Skipping callee ZST");
return Ok(());
}
- let caller_arg = caller_arg.next()
- .ok_or_else(|| EvalErrorKind::FunctionArgCountMismatch)?;
- if skip_zst {
- debug_assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out");
+ let caller_arg = caller_arg.next().ok_or_else(|| {
+ err_ub_format!("calling a function with fewer arguments than it requires")
+ })?;
+ if rust_abi {
+ assert!(!caller_arg.layout.is_zst(), "ZSTs must have been already filtered out");
}
// Now, check
- if !Self::check_argument_compat(caller_arg.layout, callee_arg.layout) {
- return err!(FunctionArgMismatch(caller_arg.layout.ty, callee_arg.layout.ty));
+ if !Self::check_argument_compat(rust_abi, caller_arg.layout, callee_arg.layout) {
+ throw_ub_format!(
+ "calling a function with argument of type {:?} passing data of type {:?}",
+ callee_arg.layout.ty,
+ caller_arg.layout.ty
+ )
}
- self.copy_op(caller_arg, callee_arg)
+ // We allow some transmutes here
+ self.copy_op_transmute(caller_arg, callee_arg)
}
/// Call this function -- pushing the stack frame and initializing the arguments.
fn eval_fn_call(
&mut self,
- instance: ty::Instance<'tcx>,
- span: Span,
+ fn_val: FnVal<'tcx, M::ExtraFnVal>,
caller_abi: Abi,
- args: &[OpTy<'tcx>],
- dest: Option<PlaceTy<'tcx>>,
- ret: Option<mir::BasicBlock>,
- ) -> EvalResult<'tcx> {
- trace!("eval_fn_call: {:#?}", instance);
+ args: &[OpTy<'tcx, M::PointerTag>],
+ ret: Option<(PlaceTy<'tcx, M::PointerTag>, mir::BasicBlock)>,
+ unwind: Option<mir::BasicBlock>,
+ ) -> InterpResult<'tcx> {
+ trace!("eval_fn_call: {:#?}", fn_val);
+
+ let instance = match fn_val {
+ FnVal::Instance(instance) => instance,
+ FnVal::Other(extra) => {
+ return M::call_extra_fn(self, extra, args, ret, unwind);
+ }
+ };
- match instance.def {
- ty::InstanceDef::Intrinsic(..) => {
- if caller_abi != Abi::RustIntrinsic {
- return err!(FunctionAbiMismatch(caller_abi, Abi::RustIntrinsic));
+ // ABI check
+ {
+ let callee_abi = {
+ let instance_ty = instance.ty_env(*self.tcx, self.param_env);
+ 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),
}
- // The intrinsic itself cannot diverge, so if we got here without a return
- // place... (can happen e.g. for transmute returning `!`)
- let dest = match dest {
- Some(dest) => dest,
- None => return err!(Unreachable)
- };
- M::call_intrinsic(self, instance, args, dest)?;
- // No stack frame gets pushed, the main loop will just act as if the
- // call completed.
- self.goto_block(ret)?;
- self.dump_place(*dest);
- Ok(())
- }
- ty::InstanceDef::ClosureOnceShim { .. } |
- ty::InstanceDef::FnPtrShim(..) |
- ty::InstanceDef::DropGlue(..) |
- ty::InstanceDef::CloneShim(..) |
- ty::InstanceDef::Item(_) => {
- // ABI check
+ };
+ let normalize_abi = |abi| match abi {
+ Abi::Rust | Abi::RustCall | Abi::RustIntrinsic | Abi::PlatformIntrinsic =>
+ // These are all the same ABI, really.
{
- let callee_abi = {
- let instance_ty = instance.ty(*self.tcx);
- match instance_ty.sty {
- ty::FnDef(..) =>
- instance_ty.fn_sig(*self.tcx).abi(),
- ty::Closure(..) => Abi::RustCall,
- ty::Generator(..) => Abi::Rust,
- _ => bug!("unexpected callee ty: {:?}", instance_ty),
- }
- };
- // Rust and RustCall are compatible
- let normalize_abi = |abi| if abi == Abi::RustCall { Abi::Rust } else { abi };
- if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
- return err!(FunctionAbiMismatch(caller_abi, callee_abi));
- }
+ Abi::Rust
}
+ abi => abi,
+ };
+ if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
+ throw_ub_format!(
+ "calling a function with ABI {:?} using caller ABI {:?}",
+ callee_abi,
+ caller_abi
+ )
+ }
+ }
+ match instance.def {
+ ty::InstanceDef::Intrinsic(..) => {
+ assert!(caller_abi == Abi::RustIntrinsic || caller_abi == Abi::PlatformIntrinsic);
+ M::call_intrinsic(self, instance, args, ret, unwind)
+ }
+ ty::InstanceDef::VtableShim(..)
+ | ty::InstanceDef::ReifyShim(..)
+ | ty::InstanceDef::ClosureOnceShim { .. }
+ | ty::InstanceDef::FnPtrShim(..)
+ | ty::InstanceDef::DropGlue(..)
+ | ty::InstanceDef::CloneShim(..)
+ | ty::InstanceDef::Item(_) => {
// We need MIR for this fn
- let mir = match M::find_fn(self, instance, args, dest, ret)? {
- Some(mir) => mir,
+ let body = match M::find_mir_or_eval_fn(self, instance, args, ret, unwind)? {
+ Some(body) => body,
None => return Ok(()),
};
- let return_place = match dest {
- Some(place) => *place,
- None => Place::null(&self),
- };
self.push_stack_frame(
instance,
- span,
- mir,
- return_place,
- StackPopCleanup::Goto(ret),
+ body,
+ ret.map(|p| p.0),
+ StackPopCleanup::Goto { ret: ret.map(|p| p.1), unwind },
)?;
- // We want to pop this frame again in case there was an error, to put
- // the blame in the right location. Until the 2018 edition is used in
- // the compiler, we have to do this with an immediately invoked function.
- let res = (||{
+ // If an error is raised here, pop the frame again to get an accurate backtrace.
+ // To this end, we wrap it all in a `try` block.
+ let res: InterpResult<'tcx> = try {
trace!(
"caller ABI: {:?}, args: {:#?}",
caller_abi,
);
trace!(
"spread_arg: {:?}, locals: {:#?}",
- mir.spread_arg,
- mir.args_iter()
- .map(|local|
- (local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
- )
+ body.spread_arg,
+ body.args_iter()
+ .map(|local| (
+ local,
+ self.layout_of_local(self.frame(), local, None).unwrap().ty
+ ))
.collect::<Vec<_>>()
);
// Figure out how to pass which arguments.
- // We have two iterators: Where the arguments come from,
- // and where they go to.
- let skip_zst = match caller_abi {
+ // The Rust ABI is special: ZST get skipped.
+ let rust_abi = match caller_abi {
Abi::Rust | Abi::RustCall => true,
- _ => false
+ _ => false,
};
+ // We have two iterators: Where the arguments come from,
+ // and where they go to.
// For where they come from: If the ABI is RustCall, we untuple the
// last incoming argument. These two iterators do not have the same type,
// so to keep the code paths uniform we accept an allocation
// (for RustCall ABI only).
- let caller_args : Cow<[OpTy<'tcx>]> =
+ let caller_args: Cow<'_, [OpTy<'tcx, M::PointerTag>]> =
if caller_abi == Abi::RustCall && !args.is_empty() {
// Untuple
let (&untuple_arg, args) = args.split_last().unwrap();
trace!("eval_fn_call: Will pass last argument by untupling");
- Cow::from(args.iter().map(|&a| Ok(a))
- .chain((0..untuple_arg.layout.fields.count()).into_iter()
- .map(|i| self.operand_field(untuple_arg, i as u64))
- )
- .collect::<EvalResult<Vec<OpTy<'tcx>>>>()?)
+ Cow::from(
+ args.iter()
+ .map(|&a| Ok(a))
+ .chain(
+ (0..untuple_arg.layout.fields.count())
+ .map(|i| self.operand_field(untuple_arg, i)),
+ )
+ .collect::<InterpResult<'_, Vec<OpTy<'tcx, M::PointerTag>>>>(
+ )?,
+ )
} else {
// Plain arg passing
Cow::from(args)
};
// Skip ZSTs
- let mut caller_iter = caller_args.iter()
- .filter(|op| !skip_zst || !op.layout.is_zst())
- .map(|op| *op);
+ let mut caller_iter =
+ caller_args.iter().filter(|op| !rust_abi || !op.layout.is_zst()).copied();
// Now we have to spread them out across the callee's locals,
// taking into account the `spread_arg`. If we could write
// this is a single iterator (that handles `spread_arg`), then
// `pass_argument` would be the loop body. It takes care to
// not advance `caller_iter` for ZSTs.
- let mut locals_iter = mir.args_iter();
- while let Some(local) = locals_iter.next() {
- let dest = self.eval_place(&mir::Place::Local(local))?;
- if Some(local) == mir.spread_arg {
+ for local in body.args_iter() {
+ let dest = self.eval_place(mir::Place::from(local))?;
+ if Some(local) == body.spread_arg {
// Must be a tuple
for i in 0..dest.layout.fields.count() {
- let dest = self.place_field(dest, i as u64)?;
- self.pass_argument(skip_zst, &mut caller_iter, dest)?;
+ let dest = self.place_field(dest, i)?;
+ self.pass_argument(rust_abi, &mut caller_iter, dest)?;
}
} else {
// Normal argument
- self.pass_argument(skip_zst, &mut caller_iter, dest)?;
+ self.pass_argument(rust_abi, &mut caller_iter, dest)?;
}
}
// Now we should have no more caller args
if caller_iter.next().is_some() {
- trace!("Caller has too many args over");
- return err!(FunctionArgCountMismatch);
+ throw_ub_format!("calling a function with more arguments than it expected")
+ }
+ // Don't forget to check the return type!
+ if let Some((caller_ret, _)) = ret {
+ let callee_ret = self.eval_place(mir::Place::return_place())?;
+ if !Self::check_argument_compat(
+ rust_abi,
+ caller_ret.layout,
+ callee_ret.layout,
+ ) {
+ throw_ub_format!(
+ "calling a function with return type {:?} passing \
+ return place of type {:?}",
+ callee_ret.layout.ty,
+ caller_ret.layout.ty
+ )
+ }
+ } else {
+ let local = mir::RETURN_PLACE;
+ let callee_layout = self.layout_of_local(self.frame(), local, None)?;
+ if !callee_layout.abi.is_uninhabited() {
+ throw_ub_format!("calling a returning function without a return place")
+ }
}
- Ok(())
- })();
+ };
match res {
Err(err) => {
- self.stack.pop();
+ self.stack_mut().pop();
Err(err)
}
- Ok(v) => Ok(v)
+ Ok(()) => Ok(()),
}
}
// cannot use the shim here, because that will only result in infinite recursion
ty::InstanceDef::Virtual(_, idx) => {
- let ptr_size = self.pointer_size();
- let ptr_align = self.tcx.data_layout.pointer_align;
- let ptr = self.ref_to_mplace(self.read_value(args[0])?)?;
- let vtable = ptr.vtable()?;
- let fn_ptr = self.memory.read_ptr_sized(
- vtable.offset(ptr_size * (idx as u64 + 3), &self)?,
- ptr_align
- )?.to_ptr()?;
- let instance = self.memory.get_fn(fn_ptr)?;
-
- // We have to patch the self argument, in particular get the layout
- // expected by the actual function. Cannot just use "field 0" due to
- // Box<self>.
let mut args = args.to_vec();
- let pointee = args[0].layout.ty.builtin_deref(true).unwrap().ty;
- let fake_fat_ptr_ty = self.tcx.mk_mut_ptr(pointee);
- args[0].layout = self.layout_of(fake_fat_ptr_ty)?.field(&self, 0)?;
- args[0].op = Operand::Immediate(Value::Scalar(ptr.ptr.into())); // strip vtable
+ // We have to implement all "object safe receivers". Currently we
+ // support built-in pointers (&, &mut, Box) as well as unsized-self. We do
+ // not yet support custom self types.
+ // Also see librustc_codegen_llvm/abi.rs and librustc_codegen_llvm/mir/block.rs.
+ let receiver_place = match args[0].layout.ty.builtin_deref(true) {
+ Some(_) => {
+ // Built-in pointer.
+ self.deref_operand(args[0])?
+ }
+ None => {
+ // Unsized self.
+ args[0].assert_mem_place(self)
+ }
+ };
+ // Find and consult vtable
+ let vtable = receiver_place.vtable();
+ let drop_fn = self.get_vtable_slot(vtable, u64::try_from(idx).unwrap())?;
+
+ // `*mut receiver_place.layout.ty` is almost the layout that we
+ // want for args[0]: We have to project to field 0 because we want
+ // a thin pointer.
+ assert!(receiver_place.layout.is_unsized());
+ let receiver_ptr_ty = self.tcx.mk_mut_ptr(receiver_place.layout.ty);
+ let this_receiver_ptr = self.layout_of(receiver_ptr_ty)?.field(self, 0)?;
+ // Adjust receiver argument.
+ args[0] =
+ OpTy::from(ImmTy::from_immediate(receiver_place.ptr.into(), this_receiver_ptr));
trace!("Patched self operand to {:#?}", args[0]);
// recurse with concrete function
- self.eval_fn_call(instance, span, caller_abi, &args, dest, ret)
+ self.eval_fn_call(drop_fn, caller_abi, &args, ret, unwind)
}
}
}
fn drop_in_place(
&mut self,
- place: PlaceTy<'tcx>,
+ place: PlaceTy<'tcx, M::PointerTag>,
instance: ty::Instance<'tcx>,
- span: Span,
target: mir::BasicBlock,
- ) -> EvalResult<'tcx> {
+ unwind: Option<mir::BasicBlock>,
+ ) -> InterpResult<'tcx> {
trace!("drop_in_place: {:?},\n {:?}, {:?}", *place, place.layout.ty, instance);
// We take the address of the object. This may well be unaligned, which is fine
// for us here. However, unaligned accesses will probably make the actual drop
// implementation fail -- a problem shared by rustc.
let place = self.force_allocation(place)?;
- let (instance, place) = match place.layout.ty.sty {
+ let (instance, place) = match place.layout.ty.kind {
ty::Dynamic(..) => {
// Dropping a trait object.
self.unpack_dyn_trait(place)?
_ => (instance, place),
};
- let arg = OpTy {
- op: Operand::Immediate(place.to_ref()),
- layout: self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
- };
+ let arg = ImmTy::from_immediate(
+ place.to_ref(),
+ self.layout_of(self.tcx.mk_mut_ptr(place.layout.ty))?,
+ );
let ty = self.tcx.mk_unit(); // return type is ()
- let dest = PlaceTy::null(&self, self.layout_of(ty)?);
+ let dest = MPlaceTy::dangling(self.layout_of(ty)?, self);
self.eval_fn_call(
- instance,
- span,
+ FnVal::Instance(instance),
Abi::Rust,
- &[arg],
- Some(dest),
- Some(target),
+ &[arg.into()],
+ Some((dest.into(), target)),
+ unwind,
)
}
}