44use std:: borrow:: Cow ;
55
66use either:: { Left , Right } ;
7- use rustc_abi:: { self as abi, ExternAbi , FieldIdx , Integer , VariantIdx } ;
7+ use rustc_abi:: { self as abi, ExternAbi , FieldIdx , HasDataLayout , Integer , Size , VariantIdx } ;
88use rustc_data_structures:: assert_matches;
99use rustc_errors:: msg;
1010use rustc_hir:: def_id:: DefId ;
@@ -17,9 +17,9 @@ use tracing::field::Empty;
1717use tracing:: { info, instrument, trace} ;
1818
1919use super :: {
20- CtfeProvenance , FnVal , ImmTy , InterpCx , InterpResult , MPlaceTy , Machine , OpTy , PlaceTy ,
21- Projectable , Provenance , ReturnAction , ReturnContinuation , Scalar , StackPopInfo , interp_ok ,
22- throw_ub , throw_ub_custom , throw_unsup_format ,
20+ CtfeProvenance , FnVal , ImmTy , InterpCx , InterpResult , MPlaceTy , Machine , MemoryKind , OpTy ,
21+ PlaceTy , Projectable , Provenance , ReturnAction , ReturnContinuation , Scalar , StackPopInfo ,
22+ interp_ok , throw_ub , throw_ub_custom ,
2323} ;
2424use crate :: enter_trace_span;
2525use crate :: interpret:: EnteredTraceSpan ;
@@ -354,12 +354,22 @@ 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 ( ) ) ?;
357+ let ( fixed_count, c_variadic_args) = if caller_fn_abi. c_variadic {
358+ let sig = self . tcx . fn_sig ( instance. def_id ( ) ) . skip_binder ( ) ;
359+ let fixed_count = sig. inputs ( ) . skip_binder ( ) . len ( ) ;
360+ assert ! ( caller_fn_abi. args. len( ) >= fixed_count) ;
361+ let extra_tys: Vec < Ty < ' tcx > > =
362+ caller_fn_abi. args [ fixed_count..] . iter ( ) . map ( |arg_abi| arg_abi. layout . ty ) . collect ( ) ;
360363
361- if callee_fn_abi. c_variadic || caller_fn_abi. c_variadic {
362- throw_unsup_format ! ( "calling a c-variadic function is not supported" ) ;
364+ ( fixed_count, self . tcx . mk_type_list ( & extra_tys) )
365+ } else {
366+ ( caller_fn_abi. args . len ( ) , ty:: List :: empty ( ) )
367+ } ;
368+
369+ let callee_fn_abi = self . fn_abi_of_instance ( instance, c_variadic_args) ?;
370+
371+ if callee_fn_abi. c_variadic ^ caller_fn_abi. c_variadic {
372+ unreachable ! ( "caller and callee disagree on being c-variadic" ) ;
363373 }
364374
365375 if caller_fn_abi. conv != callee_fn_abi. conv {
@@ -443,16 +453,60 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
443453 // this is a single iterator (that handles `spread_arg`), then
444454 // `pass_argument` would be the loop body. It takes care to
445455 // not advance `caller_iter` for ignored arguments.
446- let mut callee_args_abis = callee_fn_abi. args . iter ( ) . enumerate ( ) ;
447- for local in body. args_iter ( ) {
456+ let mut callee_args_abis = if caller_fn_abi. c_variadic {
457+ callee_fn_abi. args [ ..fixed_count] . iter ( ) . enumerate ( )
458+ } else {
459+ callee_fn_abi. args . iter ( ) . enumerate ( )
460+ } ;
461+
462+ let mut it = body. args_iter ( ) . peekable ( ) ;
463+ while let Some ( local) = it. next ( ) {
448464 // Construct the destination place for this argument. At this point all
449465 // locals are still dead, so we cannot construct a `PlaceTy`.
450466 let dest = mir:: Place :: from ( local) ;
451467 // `layout_of_local` does more than just the instantiation we need to get the
452468 // type, but the result gets cached so this avoids calling the instantiation
453469 // query *again* the next time this local is accessed.
454470 let ty = self . layout_of_local ( self . frame ( ) , local, None ) ?. ty ;
455- if Some ( local) == body. spread_arg {
471+ if caller_fn_abi. c_variadic && it. peek ( ) . is_none ( ) {
472+ // The callee's signature has an additional VaList argument, that the caller
473+ // won't actually pass. Here we synthesize a `VaList` value, whose leading bytes
474+ // are a pointer that can be mapped to the corresponding variable argument list.
475+ self . storage_live ( local) ?;
476+
477+ let place = self . eval_place ( dest) ?;
478+ let mplace = self . force_allocation ( & place) ?;
479+
480+ // Consume the remaining arguments and store them in a global allocation.
481+ let mut varargs = Vec :: new ( ) ;
482+ for ( fn_arg, abi) in & mut caller_args {
483+ let op = self . copy_fn_arg ( fn_arg) ;
484+ let mplace = self . allocate ( abi. layout , MemoryKind :: Stack ) ?;
485+ self . copy_op ( & op, & mplace) ?;
486+
487+ varargs. push ( mplace) ;
488+ }
489+
490+ // When the frame is dropped, this ID is used to deallocate the variable arguments list.
491+ self . frame_mut ( ) . va_list = varargs. clone ( ) ;
492+
493+ // This is a new VaList, so start at index 0.
494+ let ptr = self . va_list_ptr ( varargs, 0 ) ;
495+ let addr = Scalar :: from_pointer ( ptr, self ) ;
496+
497+ // Zero the mplace, so it is fully initialized.
498+ self . write_bytes_ptr (
499+ mplace. ptr ( ) ,
500+ ( 0 ..mplace. layout . size . bytes ( ) ) . map ( |_| 0u8 ) ,
501+ ) ?;
502+
503+ // Store the pointer to the global variable arguments list allocation in the
504+ // first bytes of the `VaList` value.
505+ let mut alloc = self
506+ . get_ptr_alloc_mut ( mplace. ptr ( ) , self . data_layout ( ) . pointer_size ( ) ) ?
507+ . expect ( "not a ZST" ) ;
508+ alloc. write_ptr_sized ( Size :: ZERO , addr) ?;
509+ } else if Some ( local) == body. spread_arg {
456510 // Make the local live once, then fill in the value field by field.
457511 self . storage_live ( local) ?;
458512 // Must be a tuple
0 commit comments