@@ -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"
0 commit comments