@@ -19,7 +19,7 @@ use tracing::{info, instrument, trace};
1919use super :: {
2020 CtfeProvenance , FnVal , ImmTy , InterpCx , InterpResult , MPlaceTy , Machine , OpTy , PlaceTy ,
2121 Projectable , Provenance , ReturnAction , ReturnContinuation , Scalar , StackPopInfo , interp_ok,
22- throw_ub, throw_ub_custom, throw_unsup_format ,
22+ throw_ub, throw_ub_custom,
2323} ;
2424use crate :: enter_trace_span;
2525use crate :: interpret:: EnteredTraceSpan ;
@@ -354,13 +354,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
354354 ) -> InterpResult < ' tcx > {
355355 let _trace = enter_trace_span ! ( M , step:: init_stack_frame, %instance, tracing_separate_thread = Empty ) ;
356356
357- // Compute callee information.
358- // FIXME: for variadic support, do we have to somehow determine callee's extra_args?
359- let callee_fn_abi = self . fn_abi_of_instance ( instance, ty:: List :: empty ( ) ) ?;
360-
361- if callee_fn_abi. c_variadic || caller_fn_abi. c_variadic {
362- throw_unsup_format ! ( "calling a c-variadic function is not supported" ) ;
363- }
357+ // The first order of business is to figure out the callee signature.
358+ // However, that requires the list of variadic arguments.
359+ // We use the *caller* information to determine where to split the list of arguments,
360+ // and then later check that the callee indeed has the same number of fixed arguments.
361+ let extra_tys = if caller_fn_abi. c_variadic {
362+ let fixed_count = usize:: try_from ( caller_fn_abi. fixed_count ) . unwrap ( ) ;
363+ let extra_tys = args[ fixed_count..] . iter ( ) . map ( |arg| arg. layout ( ) . ty ) ;
364+ self . tcx . mk_type_list_from_iter ( extra_tys)
365+ } else {
366+ ty:: List :: empty ( )
367+ } ;
368+ let callee_fn_abi = self . fn_abi_of_instance ( instance, extra_tys) ?;
364369
365370 if caller_fn_abi. conv != callee_fn_abi. conv {
366371 throw_ub_custom ! (
@@ -372,6 +377,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
372377 )
373378 }
374379
380+ if caller_fn_abi. c_variadic != callee_fn_abi. c_variadic {
381+ throw_ub ! ( CVariadicMismatch {
382+ caller_is_c_variadic: caller_fn_abi. c_variadic,
383+ callee_is_c_variadic: callee_fn_abi. c_variadic,
384+ } ) ;
385+ }
386+ if caller_fn_abi. c_variadic && caller_fn_abi. fixed_count != callee_fn_abi. fixed_count {
387+ throw_ub ! ( CVariadicFixedCountMismatch {
388+ caller: caller_fn_abi. fixed_count,
389+ callee: callee_fn_abi. fixed_count,
390+ } ) ;
391+ }
392+
375393 // Check that all target features required by the callee (i.e., from
376394 // the attribute `#[target_feature(enable = ...)]`) are enabled at
377395 // compile time.
@@ -444,6 +462,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
444462 // `pass_argument` would be the loop body. It takes care to
445463 // not advance `caller_iter` for ignored arguments.
446464 let mut callee_args_abis = callee_fn_abi. args . iter ( ) . enumerate ( ) ;
465+ // Determine whether there is a special VaList argument. This is always the
466+ // last argument, and since arguments start at index 1 that's `arg_count`.
467+ let va_list_arg =
468+ callee_fn_abi. c_variadic . then ( || mir:: Local :: from_usize ( body. arg_count ) ) ;
447469 for local in body. args_iter ( ) {
448470 // Construct the destination place for this argument. At this point all
449471 // locals are still dead, so we cannot construct a `PlaceTy`.
@@ -452,7 +474,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
452474 // type, but the result gets cached so this avoids calling the instantiation
453475 // query *again* the next time this local is accessed.
454476 let ty = self . layout_of_local ( self . frame ( ) , local, None ) ?. ty ;
455- if Some ( local) == body. spread_arg {
477+ if Some ( local) == va_list_arg {
478+ // This is the last callee-side argument of a variadic function.
479+ // This argument is a VaList holding the remaining caller-side arguments.
480+ self . storage_live ( local) ?;
481+
482+ let place = self . eval_place ( dest) ?;
483+ let mplace = self . force_allocation ( & place) ?;
484+
485+ // Consume the remaining arguments by putting them into the variable argument
486+ // list.
487+ let varargs = self . allocate_varargs ( & mut caller_args, & mut callee_args_abis) ?;
488+ // When the frame is dropped, these variable arguments are deallocated.
489+ self . frame_mut ( ) . va_list = varargs. clone ( ) ;
490+ let key = self . va_list_ptr ( varargs. into ( ) ) ;
491+
492+ // Zero the VaList, so it is fully initialized.
493+ self . write_bytes_ptr (
494+ mplace. ptr ( ) ,
495+ ( 0 ..mplace. layout . size . bytes ( ) ) . map ( |_| 0u8 ) ,
496+ ) ?;
497+
498+ // Store the "key" pointer in the right field.
499+ let key_mplace = self . va_list_key_field ( & mplace) ?;
500+ self . write_pointer ( key, & key_mplace) ?;
501+ } else if Some ( local) == body. spread_arg {
456502 // Make the local live once, then fill in the value field by field.
457503 self . storage_live ( local) ?;
458504 // Must be a tuple
@@ -491,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
491537 if instance. def . requires_caller_location ( * self . tcx ) {
492538 callee_args_abis. next ( ) . unwrap ( ) ;
493539 }
494- // Now we should have no more caller args or callee arg ABIs
540+ // Now we should have no more caller args or callee arg ABIs.
495541 assert ! (
496542 callee_args_abis. next( ) . is_none( ) ,
497543 "mismatch between callee ABI and callee body arguments"
0 commit comments