|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
| 11 | +use std::borrow::Cow; |
| 12 | + |
11 | 13 | use rustc::mir;
|
12 | 14 | use rustc::ty::{self, Ty};
|
13 | 15 | use rustc::ty::layout::LayoutOf;
|
@@ -335,84 +337,76 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
|
335 | 337 | // Figure out how to pass which arguments.
|
336 | 338 | // FIXME: Somehow this is horribly full of special cases here, and codegen has
|
337 | 339 | // none of that. What is going on?
|
338 |
| - trace!("ABI: {:?}", sig.abi); |
339 | 340 | trace!(
|
340 |
| - "args: {:#?}", |
| 341 | + "ABI: {:?}, args: {:#?}", |
| 342 | + sig.abi, |
341 | 343 | args.iter()
|
342 | 344 | .map(|arg| (arg.layout.ty, format!("{:?}", **arg)))
|
343 | 345 | .collect::<Vec<_>>()
|
344 | 346 | );
|
345 | 347 | trace!(
|
346 |
| - "locals: {:#?}", |
| 348 | + "spread_arg: {:?}, locals: {:#?}", |
| 349 | + mir.spread_arg, |
347 | 350 | mir.args_iter()
|
348 | 351 | .map(|local|
|
349 | 352 | (local, self.layout_of_local(self.cur_frame(), local).unwrap().ty)
|
350 | 353 | )
|
351 | 354 | .collect::<Vec<_>>()
|
352 | 355 | );
|
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 |
| - } |
366 | 356 |
|
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 | + }; |
376 | 378 |
|
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()); |
379 | 391 | }
|
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)?; |
411 | 402 | }
|
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)?; |
414 | 406 | }
|
415 | 407 | }
|
| 408 | + // Now we should be done |
| 409 | + assert!(args_iter.next().is_none()); |
416 | 410 | Ok(())
|
417 | 411 | }
|
418 | 412 | // cannot use the shim here, because that will only result in infinite recursion
|
|
0 commit comments