Skip to content

Commit a827507

Browse files
committed
get rid of *most* of the fn call hack by honoring mir.spread_arg
1 parent d3c3e3c commit a827507

File tree

2 files changed

+56
-62
lines changed

2 files changed

+56
-62
lines changed

src/librustc_mir/interpret/terminator.rs

+54-60
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use std::borrow::Cow;
12+
1113
use rustc::mir;
1214
use rustc::ty::{self, Ty};
1315
use rustc::ty::layout::LayoutOf;
@@ -335,84 +337,76 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
335337
// Figure out how to pass which arguments.
336338
// FIXME: Somehow this is horribly full of special cases here, and codegen has
337339
// none of that. What is going on?
338-
trace!("ABI: {:?}", sig.abi);
339340
trace!(
340-
"args: {:#?}",
341+
"ABI: {:?}, args: {:#?}",
342+
sig.abi,
341343
args.iter()
342344
.map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
343345
.collect::<Vec<_>>()
344346
);
345347
trace!(
346-
"locals: {:#?}",
348+
"spread_arg: {:?}, locals: {:#?}",
349+
mir.spread_arg,
347350
mir.args_iter()
348351
.map(|local|
349352
(local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
350353
)
351354
.collect::<Vec<_>>()
352355
);
353-
match instance.def {
354-
ty::InstanceDef::ClosureOnceShim { .. } if sig.abi == Abi::Rust => {
355-
// this has an entirely ridicolous calling convention where it uses the
356-
// "Rust" ABI, but arguments come in untupled and are supposed to be tupled
357-
// for the callee! The function's first argument is a ZST, and then
358-
// there comes a tuple for the rest.
359-
let mut arg_locals = mir.args_iter();
360-
361-
{ // the ZST. nothing to write.
362-
let arg_local = arg_locals.next().unwrap();
363-
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
364-
assert!(dest.layout.is_zst());
365-
}
366356

367-
{ // the tuple argument.
368-
let arg_local = arg_locals.next().unwrap();
369-
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
370-
assert_eq!(dest.layout.fields.count(), args.len());
371-
for (i, &op) in args.iter().enumerate() {
372-
let dest_field = self.place_field(dest, i as u64)?;
373-
self.copy_op(op, dest_field)?;
374-
}
375-
}
357+
// We have two iterators: Where the arguments come from,
358+
// and where they go to.
359+
360+
// For where they come from: If the ABI is RustCall, we untuple the
361+
// last incoming argument. These do not have the same type,
362+
// so to keep the code paths uniform we accept an allocation
363+
// (for RustCall ABI only).
364+
let args_effective : Cow<[OpTy<'tcx>]> =
365+
if sig.abi == Abi::RustCall && !args.is_empty() {
366+
// Untuple
367+
let (&untuple_arg, args) = args.split_last().unwrap();
368+
trace!("eval_fn_call: Will pass last argument by untupling");
369+
Cow::from(args.iter().map(|&a| Ok(a))
370+
.chain((0..untuple_arg.layout.fields.count()).into_iter()
371+
.map(|i| self.operand_field(untuple_arg, i as u64))
372+
)
373+
.collect::<EvalResult<Vec<OpTy<'tcx>>>>()?)
374+
} else {
375+
// Plain arg passing
376+
Cow::from(args)
377+
};
376378

377-
// that should be it
378-
assert!(arg_locals.next().is_none());
379+
// Now we have to spread them out across the callee's locals,
380+
// taking into account the `spread_arg`.
381+
let mut args_iter = args_effective.iter();
382+
let mut local_iter = mir.args_iter();
383+
// HACK: ClosureOnceShim calls something that expects a ZST as
384+
// first argument, but the callers do not actually pass that ZST.
385+
// Codegen doesn't care because ZST arguments do not even exist there.
386+
match instance.def {
387+
ty::InstanceDef::ClosureOnceShim { .. } if sig.abi == Abi::Rust => {
388+
let local = local_iter.next().unwrap();
389+
let dest = self.eval_place(&mir::Place::Local(local))?;
390+
assert!(dest.layout.is_zst());
379391
}
380-
_ => {
381-
// overloaded-calls-simple.rs in miri's test suite demomstrates that there is
382-
// no way to predict, from the ABI and instance.def, whether the function
383-
// wants arguments passed with untupling or not. So we just make it
384-
// depend on the number of arguments...
385-
let untuple =
386-
sig.abi == Abi::RustCall && !args.is_empty() && args.len() != mir.arg_count;
387-
let (normal_args, untuple_arg) = if untuple {
388-
let (tup, args) = args.split_last().unwrap();
389-
trace!("eval_fn_call: Will pass last argument by untupling");
390-
(args, Some(tup))
391-
} else {
392-
(&args[..], None)
393-
};
394-
395-
// Pass the arguments.
396-
let mut arg_locals = mir.args_iter();
397-
// First the normal ones.
398-
for &op in normal_args {
399-
let arg_local = arg_locals.next().unwrap();
400-
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
401-
self.copy_op(op, dest)?;
402-
}
403-
// The the ones to untuple.
404-
if let Some(&untuple_arg) = untuple_arg {
405-
for i in 0..untuple_arg.layout.fields.count() {
406-
let arg_local = arg_locals.next().unwrap();
407-
let dest = self.eval_place(&mir::Place::Local(arg_local))?;
408-
let op = self.operand_field(untuple_arg, i as u64)?;
409-
self.copy_op(op, dest)?;
410-
}
392+
_ => {}
393+
}
394+
// Now back to norml argument passing.
395+
while let Some(local) = local_iter.next() {
396+
let dest = self.eval_place(&mir::Place::Local(local))?;
397+
if Some(local) == mir.spread_arg {
398+
// Must be a tuple
399+
for i in 0..dest.layout.fields.count() {
400+
let dest = self.place_field(dest, i as u64)?;
401+
self.copy_op(*args_iter.next().unwrap(), dest)?;
411402
}
412-
// That should be it.
413-
assert!(arg_locals.next().is_none());
403+
} else {
404+
// Normal argument
405+
self.copy_op(*args_iter.next().unwrap(), dest)?;
414406
}
415407
}
408+
// Now we should be done
409+
assert!(args_iter.next().is_none());
416410
Ok(())
417411
}
418412
// cannot use the shim here, because that will only result in infinite recursion

src/librustc_mir/interpret/validity.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
357357
Ok(ptr) => ptr,
358358
Err(_) =>
359359
return validation_failure!(
360-
"undefined metadata in fat pointer", path
360+
"undefined location or metadata in fat pointer", path
361361
),
362362
};
363-
// check metadata
363+
// check metadata early, for better diagnostics
364364
match self.tcx.struct_tail(ptr.layout.ty).sty {
365365
ty::Dynamic(..) => {
366366
match ptr.extra.unwrap().to_ptr() {

0 commit comments

Comments
 (0)