diff --git a/src/librustc/ich/impls_ty.rs b/src/librustc/ich/impls_ty.rs index c598f99a2b54..6250e12f4304 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 ab38f8fa7213..84d55c84ce15 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 e3ef74f44db7..60b85e8a8eb9 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 8fc7c11c6ab8..08ee30e5948a 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( + fn check_argument_compat( + caller: TyLayout<'tcx>, + callee: TyLayout<'tcx>, + ) -> bool { + if caller.ty == callee.ty { + // No question + return 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 + } + } + + /// Pass a single argument, checking the types for compatibility. + fn pass_argument( &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, - }; + skip_zst: bool, + caller_arg: &mut impl Iterator>, + callee_arg: PlaceTy<'tcx>, + ) -> EvalResult<'tcx> { + if skip_zst && callee_arg.layout.is_zst() { + // Nothing to do. + trace!("Skipping callee ZST"); + return Ok(()); } - - 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); + 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"); } - - if sig.variadic || real_sig.variadic { - // We're not touching this - return Ok(false); + // Now, check + if !Self::check_argument_compat(caller_arg.layout, callee_arg.layout) { + return err!(FunctionArgMismatch(caller_arg.layout.ty, callee_arg.layout.ty)); } - - // 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); + 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>, ret: Option, - span: Span, - sig: Option>, ) -> 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::>() - ); - trace!( - "spread_arg: {:?}, locals: {:#?}", - mir.spread_arg, - mir.args_iter() - .map(|local| - (local, self.layout_of_local(self.cur_frame(), local).unwrap().ty) - ) - .collect::>() - ); - - // 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::>() + ); + trace!( + "spread_arg: {:?}, locals: {:#?}", + mir.spread_arg, + mir.args_iter() + .map(|local| + (local, self.layout_of_local(self.cur_frame(), local).unwrap().ty) ) - .collect::>>>()?) - } else { - // Plain arg passing - Cow::from(args) + .collect::>() + ); + + // 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::>>>()?) + } 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 1715930dbb61..8d4e67b96e5b 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