about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2018-08-27 13:34:35 +0200
committerRalf Jung <post@ralfj.de>2018-08-29 08:44:37 +0200
commitec056d51889349818138d0c92cebcd2eeb4eb730 (patch)
tree71f034498bf54b237b20ee32ea82f29b476dbb36
parentb8a40aad1b309bdeac2bf2a7d1509aa568f84093 (diff)
downloadrust-ec056d51889349818138d0c92cebcd2eeb4eb730.tar.gz
rust-ec056d51889349818138d0c92cebcd2eeb4eb730.zip
re-do argument passing one more time to finally be sane
-rw-r--r--src/librustc/ich/impls_ty.rs7
-rw-r--r--src/librustc/mir/interpret/error.rs24
-rw-r--r--src/librustc/ty/structural_impls.rs4
-rw-r--r--src/librustc_mir/interpret/terminator.rs343
-rw-r--r--src/librustc_mir/transform/const_prop.rs4
5 files changed, 189 insertions, 193 deletions
diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs
index c598f99a2b5..6250e12f430 100644
--- a/src/librustc/ich/impls_ty.rs
+++ b/src/librustc/ich/impls_ty.rs
@@ -512,6 +512,7 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
         mem::discriminant(&self).hash_stable(hcx, hasher);
 
         match *self {
+            FunctionArgCountMismatch |
             DanglingPointerDeref |
             DoubleFree |
             InvalidMemoryAccess |
@@ -558,7 +559,11 @@ for ::mir::interpret::EvalErrorKind<'gcx, O> {
             },
             ReferencedConstant(ref err) => err.hash_stable(hcx, hasher),
             MachineError(ref err) => err.hash_stable(hcx, hasher),
-            FunctionPointerTyMismatch(a, b) => {
+            FunctionAbiMismatch(a, b) => {
+                a.hash_stable(hcx, hasher);
+                b.hash_stable(hcx, hasher)
+            },
+            FunctionArgMismatch(a, b) => {
                 a.hash_stable(hcx, hasher);
                 b.hash_stable(hcx, hasher)
             },
diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs
index ab38f8fa721..84d55c84ce1 100644
--- a/src/librustc/mir/interpret/error.rs
+++ b/src/librustc/mir/interpret/error.rs
@@ -11,9 +11,10 @@
 use std::{fmt, env};
 
 use mir;
-use ty::{FnSig, Ty, layout};
+use ty::{Ty, layout};
 use ty::layout::{Size, Align};
 use rustc_data_structures::sync::Lrc;
+use rustc_target::spec::abi::Abi;
 
 use super::{
     Pointer, Lock, AccessKind
@@ -182,7 +183,10 @@ pub enum EvalErrorKind<'tcx, O> {
     /// This variant is used by machines to signal their own errors that do not
     /// match an existing variant
     MachineError(String),
-    FunctionPointerTyMismatch(FnSig<'tcx>, FnSig<'tcx>),
+
+    FunctionAbiMismatch(Abi, Abi),
+    FunctionArgMismatch(Ty<'tcx>, Ty<'tcx>),
+    FunctionArgCountMismatch,
     NoMirFor(String),
     UnterminatedCString(Pointer),
     DanglingPointerDeref,
@@ -290,8 +294,8 @@ impl<'tcx, O> EvalErrorKind<'tcx, O> {
         use self::EvalErrorKind::*;
         match *self {
             MachineError(ref inner) => inner,
-            FunctionPointerTyMismatch(..) =>
-                "tried to call a function through a function pointer of a different type",
+            FunctionAbiMismatch(..) | FunctionArgMismatch(..) | FunctionArgCountMismatch =>
+                "tried to call a function through a function pointer of incompatible type",
             InvalidMemoryAccess =>
                 "tried to access memory through an invalid pointer",
             DanglingPointerDeref =>
@@ -459,9 +463,15 @@ impl<'tcx, O: fmt::Debug> fmt::Debug for EvalErrorKind<'tcx, O> {
                 write!(f, "type validation failed: {}", err)
             }
             NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
-            FunctionPointerTyMismatch(sig, got) =>
-                write!(f, "tried to call a function with sig {} through a \
-                       function pointer of type {}", sig, got),
+            FunctionAbiMismatch(caller_abi, callee_abi) =>
+                write!(f, "tried to call a function with ABI {:?} using caller ABI {:?}",
+                    callee_abi, caller_abi),
+            FunctionArgMismatch(caller_ty, callee_ty) =>
+                write!(f, "tried to call a function with argument of type {:?} \
+                           passing data of type {:?}",
+                    callee_ty, caller_ty),
+            FunctionArgCountMismatch =>
+                write!(f, "tried to call a function with incorrect number of arguments"),
             BoundsCheck { ref len, ref index } =>
                 write!(f, "index out of bounds: the len is {:?} but the index is {:?}", len, index),
             ReallocatedWrongMemoryKind(ref old, ref new) =>
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index e3ef74f44db..60b85e8a8eb 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -487,10 +487,12 @@ impl<'a, 'tcx, O: Lift<'tcx>> Lift<'tcx> for interpret::EvalErrorKind<'a, O> {
         use ::mir::interpret::EvalErrorKind::*;
         Some(match *self {
             MachineError(ref err) => MachineError(err.clone()),
-            FunctionPointerTyMismatch(a, b) => FunctionPointerTyMismatch(
+            FunctionAbiMismatch(a, b) => FunctionAbiMismatch(a, b),
+            FunctionArgMismatch(a, b) => FunctionArgMismatch(
                 tcx.lift(&a)?,
                 tcx.lift(&b)?,
             ),
+            FunctionArgCountMismatch => FunctionArgCountMismatch,
             NoMirFor(ref s) => NoMirFor(s.clone()),
             UnterminatedCString(ptr) => UnterminatedCString(ptr),
             DanglingPointerDeref => DanglingPointerDeref,
diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs
index 8fc7c11c6ab..08ee30e5948 100644
--- a/src/librustc_mir/interpret/terminator.rs
+++ b/src/librustc_mir/interpret/terminator.rs
@@ -10,13 +10,12 @@
 
 use std::borrow::Cow;
 
-use rustc::mir;
-use rustc::ty::{self, Ty};
-use rustc::ty::layout::LayoutOf;
+use rustc::{mir, ty};
+use rustc::ty::layout::{self, TyLayout, LayoutOf};
 use syntax::source_map::Span;
 use rustc_target::spec::abi::Abi;
 
-use rustc::mir::interpret::{EvalResult, Scalar, PointerArithmetic};
+use rustc::mir::interpret::{EvalResult, PointerArithmetic, EvalErrorKind, Scalar};
 use super::{
     EvalContext, Machine, Value, OpTy, Place, PlaceTy, ValTy, Operand, StackPopCleanup
 };
@@ -59,7 +58,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                 let mut target_block = targets[targets.len() - 1];
 
                 for (index, &const_int) in values.iter().enumerate() {
-                    // Compare using binary_op
+                    // 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,
@@ -86,37 +85,16 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                 };
 
                 let func = self.eval_operand(func, None)?;
-                let (fn_def, sig) = match func.layout.ty.sty {
+                let (fn_def, abi) = match func.layout.ty.sty {
                     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)?;
-                        let instance_ty = instance.ty(*self.tcx);
-                        match instance_ty.sty {
-                            ty::FnDef(..) => {
-                                let sig = self.tcx.normalize_erasing_late_bound_regions(
-                                    self.param_env,
-                                    &sig,
-                                );
-                                let real_sig = instance_ty.fn_sig(*self.tcx);
-                                let real_sig = self.tcx.normalize_erasing_late_bound_regions(
-                                    self.param_env,
-                                    &real_sig,
-                                );
-                                if !self.check_sig_compat(sig, real_sig)? {
-                                    return err!(FunctionPointerTyMismatch(real_sig, sig));
-                                }
-                                (instance, sig)
-                            }
-                            _ => bug!("unexpected fn ptr to ty: {:?}", instance_ty),
-                        }
+                        (instance, caller_abi)
                     }
                     ty::FnDef(def_id, substs) => {
                         let sig = func.layout.ty.fn_sig(*self.tcx);
-                        let sig = self.tcx.normalize_erasing_late_bound_regions(
-                            self.param_env,
-                            &sig,
-                        );
-                        (self.resolve(def_id, substs)?, sig)
+                        (self.resolve(def_id, substs)?, sig.abi())
                     },
                     _ => {
                         let msg = format!("can't handle callee of type {:?}", func.layout.ty);
@@ -126,11 +104,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                 let args = self.eval_operands(args)?;
                 self.eval_fn_call(
                     fn_def,
+                    terminator.source_info.span,
+                    abi,
                     &args[..],
                     dest,
                     ret,
-                    terminator.source_info.span,
-                    Some(sig),
                 )?;
             }
 
@@ -165,6 +143,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                 if expected == cond_val {
                     self.goto_block(Some(target))?;
                 } else {
+                    // Compute error message
                     use rustc::mir::interpret::EvalErrorKind::*;
                     return match *msg {
                         BoundsCheck { ref len, ref index } => {
@@ -187,11 +166,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                 }
             }
 
-            Yield { .. } => unimplemented!("{:#?}", terminator.kind),
-            GeneratorDrop => unimplemented!(),
-            DropAndReplace { .. } => unimplemented!(),
-            Resume => unimplemented!(),
-            Abort => unimplemented!(),
+            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\
@@ -202,91 +181,67 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
         Ok(())
     }
 
-    /// Decides whether it is okay to call the method with signature `real_sig`
-    /// using signature `sig`.
-    /// FIXME: This should take into account the platform-dependent ABI description.
-    fn check_sig_compat(
-        &mut self,
-        sig: ty::FnSig<'tcx>,
-        real_sig: ty::FnSig<'tcx>,
-    ) -> EvalResult<'tcx, bool> {
-        fn check_ty_compat<'tcx>(ty: Ty<'tcx>, real_ty: Ty<'tcx>) -> bool {
-            if ty == real_ty {
-                return true;
-            } // This is actually a fast pointer comparison
-            return match (&ty.sty, &real_ty.sty) {
-                // Permit changing the pointer type of raw pointers and references as well as
-                // mutability of raw pointers.
-                // FIXME: Should not be allowed when fat pointers are involved.
-                (&ty::RawPtr(_), &ty::RawPtr(_)) => true,
-                (&ty::Ref(_, _, _), &ty::Ref(_, _, _)) => {
-                    ty.is_mutable_pointer() == real_ty.is_mutable_pointer()
-                }
-                // rule out everything else
-                _ => false,
-            };
+    fn check_argument_compat(
+        caller: TyLayout<'tcx>,
+        callee: TyLayout<'tcx>,
+    ) -> bool {
+        if caller.ty == callee.ty {
+            // No question
+            return true;
         }
-
-        if sig.abi == real_sig.abi && sig.variadic == real_sig.variadic &&
-            sig.inputs_and_output.len() == real_sig.inputs_and_output.len() &&
-            sig.inputs_and_output
-                .iter()
-                .zip(real_sig.inputs_and_output)
-                .all(|(ty, real_ty)| check_ty_compat(ty, real_ty))
-        {
-            // Definitely good.
-            return Ok(true);
+        // 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,
+            // Be conservative
+            _ => false
         }
+    }
 
-        if sig.variadic || real_sig.variadic {
-            // We're not touching this
-            return Ok(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() {
+            // Nothing to do.
+            trace!("Skipping callee ZST");
+            return Ok(());
         }
-
-        // We need to allow what comes up when a non-capturing closure is cast to a fn().
-        match (sig.abi, real_sig.abi) {
-            (Abi::Rust, Abi::RustCall) // check the ABIs.  This makes the test here non-symmetric.
-                if check_ty_compat(sig.output(), real_sig.output())
-                    && real_sig.inputs_and_output.len() == 3 => {
-                // First argument of real_sig must be a ZST
-                let fst_ty = real_sig.inputs_and_output[0];
-                if self.layout_of(fst_ty)?.is_zst() {
-                    // Second argument must be a tuple matching the argument list of sig
-                    let snd_ty = real_sig.inputs_and_output[1];
-                    match snd_ty.sty {
-                        ty::Tuple(tys) if sig.inputs().len() == tys.len() =>
-                            if sig.inputs()
-                                .iter()
-                                .zip(tys)
-                                .all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) {
-                                return Ok(true)
-                            },
-                        _ => {}
-                    }
-                }
-            }
-            _ => {}
-        };
-
-        // Nope, this doesn't work.
-        return Ok(false);
+        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");
+        }
+        // Now, check
+        if !Self::check_argument_compat(caller_arg.layout, callee_arg.layout) {
+            return err!(FunctionArgMismatch(caller_arg.layout.ty, callee_arg.layout.ty));
+        }
+        self.copy_op(caller_arg, callee_arg)
     }
 
     /// Call this function -- pushing the stack frame and initializing the arguments.
-    /// `sig` is optional in case of FnPtr/FnDef -- but mandatory for closures!
     fn eval_fn_call(
         &mut self,
         instance: ty::Instance<'tcx>,
+        span: Span,
+        caller_abi: Abi,
         args: &[OpTy<'tcx>],
         dest: Option<PlaceTy<'tcx>>,
         ret: Option<mir::BasicBlock>,
-        span: Span,
-        sig: Option<ty::FnSig<'tcx>>,
     ) -> EvalResult<'tcx> {
         trace!("eval_fn_call: {:#?}", instance);
 
         match instance.def {
             ty::InstanceDef::Intrinsic(..) => {
+                if caller_abi != Abi::RustIntrinsic {
+                    return err!(FunctionAbiMismatch(caller_abi, Abi::RustIntrinsic));
+                }
                 // 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 {
@@ -305,6 +260,26 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
             ty::InstanceDef::DropGlue(..) |
             ty::InstanceDef::CloneShim(..) |
             ty::InstanceDef::Item(_) => {
+                // ABI check
+                {
+                    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));
+                    }
+                }
+
+                // We need MIR for this fn
                 let mir = match M::find_fn(self, instance, args, dest, ret)? {
                     Some(mir) => mir,
                     None => return Ok(()),
@@ -322,89 +297,91 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                     StackPopCleanup::Goto(ret),
                 )?;
 
-                // If we didn't get a signture, ask `fn_sig`
-                let sig = sig.unwrap_or_else(|| {
-                    let fn_sig = instance.ty(*self.tcx).fn_sig(*self.tcx);
-                    self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig)
-                });
-                assert_eq!(sig.inputs().len(), args.len());
-                // We can't test the types, as it is fine if the types are ABI-compatible but
-                // not equal.
-
-                // Figure out how to pass which arguments.
-                // FIXME: Somehow this is horribly full of special cases here, and codegen has
-                // none of that.  What is going on?
-                trace!(
-                    "ABI: {:?}, args: {:#?}",
-                    sig.abi,
-                    args.iter()
-                        .map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
-                        .collect::<Vec<_>>()
-                );
-                trace!(
-                    "spread_arg: {:?}, locals: {:#?}",
-                    mir.spread_arg,
-                    mir.args_iter()
-                        .map(|local|
-                            (local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
-                        )
-                        .collect::<Vec<_>>()
-                );
-
-                // 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 do not have the same type,
-                // so to keep the code paths uniform we accept an allocation
-                // (for RustCall ABI only).
-                let args_effective : Cow<[OpTy<'tcx>]> =
-                    if sig.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))
+                // 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 = (||{
+                    trace!(
+                        "caller ABI: {:?}, args: {:#?}",
+                        caller_abi,
+                        args.iter()
+                            .map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
+                            .collect::<Vec<_>>()
+                    );
+                    trace!(
+                        "spread_arg: {:?}, locals: {:#?}",
+                        mir.spread_arg,
+                        mir.args_iter()
+                            .map(|local|
+                                (local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
                             )
-                            .collect::<EvalResult<Vec<OpTy<'tcx>>>>()?)
-                    } else {
-                        // Plain arg passing
-                        Cow::from(args)
+                            .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 {
+                        Abi::Rust | Abi::RustCall => true,
+                        _ => false
                     };
 
-                // Now we have to spread them out across the callee's locals,
-                // taking into account the `spread_arg`.
-                let mut args_iter = args_effective.iter();
-                let mut local_iter = mir.args_iter();
-                // HACK: ClosureOnceShim calls something that expects a ZST as
-                // first argument, but the callers do not actually pass that ZST.
-                // Codegen doesn't care because ZST arguments do not even exist there.
-                match instance.def {
-                    ty::InstanceDef::ClosureOnceShim { .. } if sig.abi == Abi::Rust => {
-                        let local = local_iter.next().unwrap();
+                    // 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>]> =
+                        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>>>>()?)
+                        } 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);
+
+                    // 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))?;
-                        assert!(dest.layout.is_zst());
-                    }
-                    _ => {}
-                }
-                // Now back to norml argument passing.
-                while let Some(local) = local_iter.next() {
-                    let dest = self.eval_place(&mir::Place::Local(local))?;
-                    if Some(local) == mir.spread_arg {
-                        // Must be a tuple
-                        for i in 0..dest.layout.fields.count() {
-                            let dest = self.place_field(dest, i as u64)?;
-                            self.copy_op(*args_iter.next().unwrap(), dest)?;
+                        if Some(local) == mir.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)?;
+                            }
+                        } else {
+                            // Normal argument
+                            self.pass_argument(skip_zst, &mut caller_iter, dest)?;
                         }
-                    } else {
-                        // Normal argument
-                        self.copy_op(*args_iter.next().unwrap(), 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);
+                    }
+                    Ok(())
+                })();
+                match res {
+                    Err(err) => {
+                        self.stack.pop();
+                        Err(err)
+                    }
+                    Ok(v) => Ok(v)
                 }
-                // Now we should be done
-                assert!(args_iter.next().is_none());
-                Ok(())
             }
             // cannot use the shim here, because that will only result in infinite recursion
             ty::InstanceDef::Virtual(_, idx) => {
@@ -428,7 +405,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
                 args[0].op = Operand::Immediate(Value::Scalar(ptr.ptr.into())); // strip vtable
                 trace!("Patched self operand to {:#?}", args[0]);
                 // recurse with concrete function
-                self.eval_fn_call(instance, &args, dest, ret, span, sig)
+                self.eval_fn_call(instance, span, caller_abi, &args, dest, ret)
             }
         }
     }
@@ -464,11 +441,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
 
         self.eval_fn_call(
             instance,
+            span,
+            Abi::Rust,
             &[arg],
             Some(dest),
             Some(target),
-            span,
-            None,
         )
     }
 }
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 1715930dbb6..8d4e67b96e5 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -153,7 +153,9 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
                     | MachineError(_)
                     // at runtime these transformations might make sense
                     // FIXME: figure out the rules and start linting
-                    | FunctionPointerTyMismatch(..)
+                    | FunctionAbiMismatch(..)
+                    | FunctionArgMismatch(..)
+                    | FunctionArgCountMismatch
                     // fine at runtime, might be a register address or sth
                     | ReadBytesAsPointer
                     // fine at runtime