From 5b737dbbf473d75a82ce77ce70066dd57b4e7bd7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 25 Aug 2018 21:22:00 +0200 Subject: [PATCH] get rid of *most* of the fn call hack by honoring mir.spread_arg --- src/librustc_mir/interpret/terminator.rs | 114 +++++++++++------------ src/librustc_mir/interpret/validity.rs | 4 +- 2 files changed, 56 insertions(+), 62 deletions(-) diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index e637633b90b0..87ee94cbe30f 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::borrow::Cow; + use rustc::mir; use rustc::ty::{self, Ty}; use rustc::ty::layout::LayoutOf; @@ -335,84 +337,76 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // 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: {:?}", sig.abi); trace!( - "args: {:#?}", + "ABI: {:?}, args: {:#?}", + sig.abi, args.iter() .map(|arg| (arg.layout.ty, format!("{:?}", **arg))) .collect::>() ); trace!( - "locals: {:#?}", + "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)) + ) + .collect::>>>()?) + } else { + // Plain arg passing + Cow::from(args) + }; + + // 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 => { - // this has an entirely ridicolous calling convention where it uses the - // "Rust" ABI, but arguments come in untupled and are supposed to be tupled - // for the callee! The function's first argument is a ZST, and then - // there comes a tuple for the rest. - let mut arg_locals = mir.args_iter(); - - { // the ZST. nothing to write. - let arg_local = arg_locals.next().unwrap(); - let dest = self.eval_place(&mir::Place::Local(arg_local))?; - assert!(dest.layout.is_zst()); - } - - { // the tuple argument. - let arg_local = arg_locals.next().unwrap(); - let dest = self.eval_place(&mir::Place::Local(arg_local))?; - assert_eq!(dest.layout.fields.count(), args.len()); - for (i, &op) in args.iter().enumerate() { - let dest_field = self.place_field(dest, i as u64)?; - self.copy_op(op, dest_field)?; - } - } - - // that should be it - assert!(arg_locals.next().is_none()); + let local = local_iter.next().unwrap(); + let dest = self.eval_place(&mir::Place::Local(local))?; + assert!(dest.layout.is_zst()); } - _ => { - // overloaded-calls-simple.rs in miri's test suite demomstrates that there is - // no way to predict, from the ABI and instance.def, whether the function - // wants arguments passed with untupling or not. So we just make it - // depend on the number of arguments... - let untuple = - sig.abi == Abi::RustCall && !args.is_empty() && args.len() != mir.arg_count; - let (normal_args, untuple_arg) = if untuple { - let (tup, args) = args.split_last().unwrap(); - trace!("eval_fn_call: Will pass last argument by untupling"); - (args, Some(tup)) - } else { - (&args[..], None) - }; - - // Pass the arguments. - let mut arg_locals = mir.args_iter(); - // First the normal ones. - for &op in normal_args { - let arg_local = arg_locals.next().unwrap(); - let dest = self.eval_place(&mir::Place::Local(arg_local))?; - self.copy_op(op, dest)?; + _ => {} + } + // 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)?; } - // The the ones to untuple. - if let Some(&untuple_arg) = untuple_arg { - for i in 0..untuple_arg.layout.fields.count() { - let arg_local = arg_locals.next().unwrap(); - let dest = self.eval_place(&mir::Place::Local(arg_local))?; - let op = self.operand_field(untuple_arg, i as u64)?; - self.copy_op(op, dest)?; - } - } - // That should be it. - assert!(arg_locals.next().is_none()); + } else { + // Normal argument + self.copy_op(*args_iter.next().unwrap(), dest)?; } } + // 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 diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index 3ca40aa9f425..47d5c88e5f51 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -357,10 +357,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { Ok(ptr) => ptr, Err(_) => return validation_failure!( - "undefined metadata in fat pointer", path + "undefined location or metadata in fat pointer", path ), }; - // check metadata + // check metadata early, for better diagnostics match self.tcx.struct_tail(ptr.layout.ty).sty { ty::Dynamic(..) => { match ptr.extra.unwrap().to_ptr() {