@@ -23,8 +23,8 @@ use crate::fluent_generated as fluent;
2323use crate :: interpret:: {
2424 self , AllocId , AllocInit , AllocRange , ConstAllocation , CtfeProvenance , FnArg , Frame ,
2525 GlobalAlloc , ImmTy , InterpCx , InterpResult , OpTy , PlaceTy , Pointer , RangeSet , Scalar ,
26- compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub ,
27- throw_ub_custom, throw_unsup, throw_unsup_format,
26+ compile_time_machine, err_inval, err_unsup_format , interp_ok, throw_exhaust, throw_inval,
27+ throw_ub , throw_ub_custom, throw_unsup, throw_unsup_format,
2828} ;
2929
3030/// When hitting this many interpreted terminators we emit a deny by default lint
@@ -586,6 +586,51 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
586586 }
587587 }
588588
589+ sym:: va_arg => {
590+ let ptr_size = ecx. tcx . data_layout . pointer_size ( ) ;
591+
592+ // The only argument is a `&mut VaList`.
593+ let ap_ref = ecx. read_pointer ( & args[ 0 ] ) ?;
594+
595+ // The first bytes of the `VaList` value store a pointer. The `AllocId` of this
596+ // pointer is a key into a global map of variable argument lists. The offset is
597+ // used as the index of the argument to read.
598+ let va_list_ptr = {
599+ let alloc = ecx
600+ . get_ptr_alloc ( ap_ref, ptr_size) ?
601+ . expect ( "va_list storage should not be a ZST" ) ;
602+ let scalar = alloc. read_pointer ( Size :: ZERO ) ?; // or: alloc.read_scalar(Size::ZERO, ptr_layout)?
603+ scalar. to_pointer ( ecx) ?
604+ } ;
605+
606+ let ( prov, offset) = va_list_ptr. into_raw_parts ( ) ;
607+ let alloc_id = prov. unwrap ( ) . alloc_id ( ) ;
608+ let index = offset. bytes_usize ( ) ;
609+
610+ // Update the offset in this `VaList` value so that a subsequent call to `va_arg`
611+ // reads the next argument.
612+ let new_va_list_ptr = va_list_ptr. wrapping_offset ( Size :: from_bytes ( 1 ) , ecx) ;
613+ let addr = Scalar :: from_maybe_pointer ( new_va_list_ptr, ecx) ;
614+ let mut alloc = ecx
615+ . get_ptr_alloc_mut ( ap_ref, ptr_size) ?
616+ . expect ( "va_list storage should not be a ZST" ) ;
617+ alloc. write_ptr_sized ( Size :: ZERO , addr) ?;
618+
619+ let arguments = ecx. memory . va_list_map . get ( & alloc_id) . ok_or_else ( || {
620+ err_unsup_format ! ( "va_arg on unknown va_list allocation {:?}" , alloc_id)
621+ } ) ?;
622+
623+ let src_mplace = arguments
624+ . get ( index)
625+ . ok_or_else ( || err_unsup_format ! ( "va_arg out of bounds (index={index})" ) ) ?
626+ . clone ( ) ;
627+
628+ // NOTE: In C some type conversions are allowed (e.g. casting between signed and
629+ // unsigned integers). For now we require c-variadic arguments to be read with the
630+ // exact type they were passed as.
631+ ecx. copy_op ( & src_mplace, dest) ?;
632+ }
633+
589634 _ => {
590635 // We haven't handled the intrinsic, let's see if we can use a fallback body.
591636 if ecx. tcx . intrinsic ( instance. def_id ( ) ) . unwrap ( ) . must_be_overridden {
0 commit comments