Skip to content

Commit 3c69b50

Browse files
committed
simplify callee signature computation and make ABI handling more consistent
1 parent 05768c3 commit 3c69b50

2 files changed

Lines changed: 29 additions & 40 deletions

File tree

compiler/rustc_const_eval/src/interpret/call.rs

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -353,23 +353,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
353353
) -> InterpResult<'tcx> {
354354
let _trace = enter_trace_span!(M, step::init_stack_frame, %instance, tracing_separate_thread = Empty);
355355

356-
// A mismatch between caller and callee c-variadicness or fixed_count will error below.
357-
let (fixed_count, callee_c_variadic_args) = if caller_fn_abi.c_variadic {
358-
let callee_fn_sig = self.tcx.fn_sig(instance.def_id()).skip_binder();
359-
let fixed_count = callee_fn_sig.inputs().skip_binder().len();
360-
361-
// NOTE: use the caller's fixed count here to slice into the caller's arguments. Using
362-
// the callee's fixed_count could go out of bounds for malformed input programs.
363-
let extra_tys = caller_fn_abi.args[caller_fn_abi.fixed_count as usize..]
364-
.iter()
365-
.map(|arg_abi| arg_abi.layout.ty);
366-
367-
(fixed_count, self.tcx.mk_type_list_from_iter(extra_tys))
356+
// The first order of business is to figure out the callee signature.
357+
// However, that requires the list of variadic arguments.
358+
// We use the *caller* information to determine where to split the list of arguments,
359+
// and then later check that the callee indeed has the same number of fixed arguments.
360+
let extra_tys = if caller_fn_abi.c_variadic {
361+
let fixed_count = usize::try_from(caller_fn_abi.fixed_count).unwrap();
362+
let extra_tys = args[fixed_count..].iter().map(|arg| arg.layout().ty);
363+
self.tcx.mk_type_list_from_iter(extra_tys)
368364
} else {
369-
(caller_fn_abi.fixed_count.try_into().unwrap(), ty::List::empty())
365+
ty::List::empty()
370366
};
371-
372-
let callee_fn_abi = self.fn_abi_of_instance(instance, callee_c_variadic_args)?;
367+
let callee_fn_abi = self.fn_abi_of_instance(instance, extra_tys)?;
373368

374369
if caller_fn_abi.conv != callee_fn_abi.conv {
375370
throw_ub_custom!(
@@ -387,7 +382,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
387382
callee_is_c_variadic: callee_fn_abi.c_variadic,
388383
});
389384
}
390-
391385
if caller_fn_abi.c_variadic && caller_fn_abi.fixed_count != callee_fn_abi.fixed_count {
392386
throw_ub!(CVariadicFixedCountMismatch {
393387
caller: caller_fn_abi.fixed_count,
@@ -466,32 +460,20 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
466460
// this is a single iterator (that handles `spread_arg`), then
467461
// `pass_argument` would be the loop body. It takes care to
468462
// not advance `caller_iter` for ignored arguments.
469-
let mut callee_args_abis = if caller_fn_abi.c_variadic {
470-
// Only the fixed arguments are passed normally. All remaining arguments are
471-
// put into the va_list. Crucially, there can't be a caller location among those
472-
// remaining arguments, so the caller location handling below does not interfere
473-
// with the variadic argument handling: C-variadic functions cannot be
474-
// `extern "Rust"` and `#[track_caller]` can only be applied to `extern "Rust"`.
475-
assert!(!instance.def.requires_caller_location(*self.tcx));
476-
callee_fn_abi.args[..fixed_count].iter().enumerate()
477-
} else {
478-
// NOTE: this iterator includes the extra caller location argument that is passed
479-
// when `#[track_caller]` is used. The `fixed_count` does not account for this
480-
// argument. This attribute is only allowed on `extern "Rust"` functions, so the
481-
// c-variadic case does not need to handle the extra argument.
482-
callee_fn_abi.args.iter().enumerate()
483-
};
484-
485-
let mut it = body.args_iter().peekable();
486-
while let Some(local) = it.next() {
463+
let mut callee_args_abis = callee_fn_abi.args.iter().enumerate();
464+
// Determine whether there is a special VaList argument. This is always the
465+
// last argument, and since arguments start at index 1 that's `arg_count`.
466+
let va_list_arg =
467+
callee_fn_abi.c_variadic.then(|| mir::Local::from_usize(body.arg_count));
468+
for local in body.args_iter() {
487469
// Construct the destination place for this argument. At this point all
488470
// locals are still dead, so we cannot construct a `PlaceTy`.
489471
let dest = mir::Place::from(local);
490472
// `layout_of_local` does more than just the instantiation we need to get the
491473
// type, but the result gets cached so this avoids calling the instantiation
492474
// query *again* the next time this local is accessed.
493475
let ty = self.layout_of_local(self.frame(), local, None)?.ty;
494-
if caller_fn_abi.c_variadic && it.peek().is_none() {
476+
if Some(local) == va_list_arg {
495477
// This is the last callee-side argument of a variadic function.
496478
// This argument is a VaList holding the remaining caller-side arguments.
497479
self.storage_live(local)?;
@@ -501,7 +483,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
501483

502484
// Consume the remaining arguments by putting them into the variable argument
503485
// list.
504-
let varargs = self.allocate_varargs(&mut caller_args)?;
486+
let varargs = self.allocate_varargs(&mut caller_args, &mut callee_args_abis)?;
505487
// When the frame is dropped, these variable arguments are deallocated.
506488
self.frame_mut().va_list = varargs.clone();
507489
let key = self.va_list_ptr(varargs.into());
@@ -554,7 +536,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
554536
if instance.def.requires_caller_location(*self.tcx) {
555537
callee_args_abis.next().unwrap();
556538
}
557-
// Now we should have no more caller args or callee arg ABIs
539+
// Now we should have no more caller args or callee arg ABIs.
558540
assert!(
559541
callee_args_abis.next().is_none(),
560542
"mismatch between callee ABI and callee body arguments"

compiler/rustc_const_eval/src/interpret/stack.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,23 +638,30 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
638638
impl<'a, 'tcx: 'a, M: Machine<'tcx>> InterpCx<'tcx, M> {
639639
/// Consume the arguments provided by the iterator and store them as a list
640640
/// of variadic arguments. Return a list of the places that hold those arguments.
641-
pub(crate) fn allocate_varargs<I>(
641+
pub(crate) fn allocate_varargs<I, J>(
642642
&mut self,
643643
caller_args: &mut I,
644+
callee_abis: &mut J,
644645
) -> InterpResult<'tcx, Vec<MPlaceTy<'tcx, M::Provenance>>>
645646
where
646647
I: Iterator<Item = (&'a FnArg<'tcx, M::Provenance>, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
648+
J: Iterator<Item = (usize, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
647649
{
648650
// Consume the remaining arguments and store them in fresh allocations.
649651
let mut varargs = Vec::new();
650-
for (fn_arg, abi) in caller_args {
652+
for (fn_arg, caller_abi) in caller_args {
653+
// The callee ABI is entirely computed based on which arguments the caller has
654+
// provided so it should not be possible to get a mismatch here.
655+
let (_idx, callee_abi) = callee_abis.next().unwrap();
656+
assert!(self.check_argument_compat(caller_abi, callee_abi)?);
651657
// FIXME: do we have to worry about in-place argument passing?
652658
let op = self.copy_fn_arg(fn_arg);
653-
let mplace = self.allocate(abi.layout, MemoryKind::Stack)?;
659+
let mplace = self.allocate(op.layout, MemoryKind::Stack)?;
654660
self.copy_op(&op, &mplace)?;
655661

656662
varargs.push(mplace);
657663
}
664+
assert!(callee_abis.next().is_none());
658665

659666
interp_ok(varargs)
660667
}

0 commit comments

Comments
 (0)