44use std:: borrow:: Cow ;
55
66use either:: { Left , Right } ;
7- use rustc_abi:: { self as abi, ExternAbi , FieldIdx , HasDataLayout , Integer , Size , VariantIdx } ;
7+ use rustc_abi:: { self as abi, ExternAbi , FieldIdx , Integer , 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 , MemoryKind , OpTy ,
21- PlaceTy , Projectable , Provenance , ReturnAction , ReturnContinuation , Scalar , StackPopInfo ,
22- interp_ok , throw_ub, throw_ub_custom,
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,
2323} ;
2424use crate :: enter_trace_span;
2525use crate :: interpret:: EnteredTraceSpan ;
@@ -354,23 +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- 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 ( ) ;
363-
364- ( fixed_count , self . tcx . mk_type_list ( & extra_tys) )
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)
365365 } else {
366- ( caller_fn_abi . args . len ( ) , ty:: List :: empty ( ) )
366+ ty:: List :: empty ( )
367367 } ;
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" ) ;
373- }
368+ let callee_fn_abi = self . fn_abi_of_instance ( instance, extra_tys) ?;
374369
375370 if caller_fn_abi. conv != callee_fn_abi. conv {
376371 throw_ub_custom ! (
@@ -382,6 +377,19 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
382377 )
383378 }
384379
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+
385393 // Check that all target features required by the callee (i.e., from
386394 // the attribute `#[target_feature(enable = ...)]`) are enabled at
387395 // compile time.
@@ -453,59 +461,43 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
453461 // this is a single iterator (that handles `spread_arg`), then
454462 // `pass_argument` would be the loop body. It takes care to
455463 // not advance `caller_iter` for ignored arguments.
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 ( ) {
464+ 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 ) ) ;
469+ for local in body. args_iter ( ) {
464470 // Construct the destination place for this argument. At this point all
465471 // locals are still dead, so we cannot construct a `PlaceTy`.
466472 let dest = mir:: Place :: from ( local) ;
467473 // `layout_of_local` does more than just the instantiation we need to get the
468474 // type, but the result gets cached so this avoids calling the instantiation
469475 // query *again* the next time this local is accessed.
470476 let ty = self . layout_of_local ( self . frame ( ) , local, None ) ?. ty ;
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.
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.
475480 self . storage_live ( local) ?;
476481
477482 let place = self . eval_place ( dest) ?;
478483 let mplace = self . force_allocation ( & place) ?;
479484
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.
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.
491489 self . frame_mut ( ) . va_list = varargs. clone ( ) ;
490+ let key = self . va_list_ptr ( varargs. into ( ) ) ;
492491
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.
492+ // Zero the VaList, so it is fully initialized.
498493 self . write_bytes_ptr (
499494 mplace. ptr ( ) ,
500495 ( 0 ..mplace. layout . size . bytes ( ) ) . map ( |_| 0u8 ) ,
501496 ) ?;
502497
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) ?;
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) ?;
509501 } else if Some ( local) == body. spread_arg {
510502 // Make the local live once, then fill in the value field by field.
511503 self . storage_live ( local) ?;
@@ -545,7 +537,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
545537 if instance. def . requires_caller_location ( * self . tcx ) {
546538 callee_args_abis. next ( ) . unwrap ( ) ;
547539 }
548- // 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.
549541 assert ! (
550542 callee_args_abis. next( ) . is_none( ) ,
551543 "mismatch between callee ABI and callee body arguments"
0 commit comments