@@ -404,166 +404,158 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
404404
405405 // Push the "raw" frame -- this leaves locals uninitialized.
406406 self . push_stack_frame_raw ( instance, body, destination, cont) ?;
407+ let preamble_span = self . frame ( ) . loc . unwrap_right ( ) ; // the span used for preamble errors
408+
409+ trace ! (
410+ "caller ABI: {:#?}, args: {:#?}" ,
411+ caller_fn_abi,
412+ args. iter( )
413+ . map( |arg| (
414+ arg. layout( ) . ty,
415+ match arg {
416+ FnArg :: Copy ( op) => format!( "copy({op:?})" ) ,
417+ FnArg :: InPlace ( mplace) => format!( "in-place({mplace:?})" ) ,
418+ }
419+ ) )
420+ . collect:: <Vec <_>>( )
421+ ) ;
422+ trace ! (
423+ "spread_arg: {:?}, locals: {:#?}" ,
424+ body. spread_arg,
425+ body. args_iter( )
426+ . map( |local| ( local, self . layout_of_local( self . frame( ) , local, None ) . unwrap( ) . ty, ) )
427+ . collect:: <Vec <_>>( )
428+ ) ;
407429
408- // If an error is raised here, pop the frame again to get an accurate backtrace.
409- // To this end, we wrap it all in a `try` block.
410- let res: InterpResult < ' tcx > = try {
411- trace ! (
412- "caller ABI: {:#?}, args: {:#?}" ,
413- caller_fn_abi,
414- args. iter( )
415- . map( |arg| (
416- arg. layout( ) . ty,
417- match arg {
418- FnArg :: Copy ( op) => format!( "copy({op:?})" ) ,
419- FnArg :: InPlace ( mplace) => format!( "in-place({mplace:?})" ) ,
420- }
421- ) )
422- . collect:: <Vec <_>>( )
423- ) ;
424- trace ! (
425- "spread_arg: {:?}, locals: {:#?}" ,
426- body. spread_arg,
427- body. args_iter( )
428- . map( |local| (
429- local,
430- self . layout_of_local( self . frame( ) , local, None ) . unwrap( ) . ty,
431- ) )
432- . collect:: <Vec <_>>( )
433- ) ;
434-
435- // In principle, we have two iterators: Where the arguments come from, and where
436- // they go to.
437-
438- // The "where they come from" part is easy, we expect the caller to do any special handling
439- // that might be required here (e.g. for untupling).
440- // If `with_caller_location` is set we pretend there is an extra argument (that
441- // we will not pass; our `caller_location` intrinsic implementation walks the stack instead).
442- assert_eq ! (
443- args. len( ) + if with_caller_location { 1 } else { 0 } ,
444- caller_fn_abi. args. len( ) ,
445- "mismatch between caller ABI and caller arguments" ,
446- ) ;
447- let mut caller_args = args
448- . iter ( )
449- . zip ( caller_fn_abi. args . iter ( ) )
450- . filter ( |arg_and_abi| !arg_and_abi. 1 . is_ignore ( ) ) ;
451-
452- // Now we have to spread them out across the callee's locals,
453- // taking into account the `spread_arg`. If we could write
454- // this is a single iterator (that handles `spread_arg`), then
455- // `pass_argument` would be the loop body. It takes care to
456- // not advance `caller_iter` for ignored arguments.
457- let mut callee_args_abis = callee_fn_abi. args . iter ( ) . enumerate ( ) ;
458- // Determine whether there is a special VaList argument. This is always the
459- // last argument, and since arguments start at index 1 that's `arg_count`.
460- let va_list_arg =
461- callee_fn_abi. c_variadic . then ( || mir:: Local :: from_usize ( body. arg_count ) ) ;
462- for local in body. args_iter ( ) {
463- // Construct the destination place for this argument. At this point all
464- // locals are still dead, so we cannot construct a `PlaceTy`.
465- let dest = mir:: Place :: from ( local) ;
466- // `layout_of_local` does more than just the instantiation we need to get the
467- // type, but the result gets cached so this avoids calling the instantiation
468- // query *again* the next time this local is accessed.
469- let ty = self . layout_of_local ( self . frame ( ) , local, None ) ?. ty ;
470- if Some ( local) == va_list_arg {
471- // This is the last callee-side argument of a variadic function.
472- // This argument is a VaList holding the remaining caller-side arguments.
473- self . storage_live ( local) ?;
474-
475- let place = self . eval_place ( dest) ?;
476- let mplace = self . force_allocation ( & place) ?;
477-
478- // Consume the remaining arguments by putting them into the variable argument
479- // list.
480- let varargs = self . allocate_varargs (
481- & mut caller_args,
482- // "Ignored" arguments aren't actually passed, so the callee should also
483- // ignore them. (`pass_argument` does this for regular arguments.)
484- ( & mut callee_args_abis) . filter ( |( _, abi) | !abi. is_ignore ( ) ) ,
485- ) ?;
486- // When the frame is dropped, these variable arguments are deallocated.
487- self . frame_mut ( ) . va_list = varargs. clone ( ) ;
488- let key = self . va_list_ptr ( varargs. into ( ) ) ;
489-
490- // Zero the VaList, so it is fully initialized.
491- self . write_bytes_ptr (
492- mplace. ptr ( ) ,
493- ( 0 ..mplace. layout . size . bytes ( ) ) . map ( |_| 0u8 ) ,
494- ) ?;
430+ // In principle, we have two iterators: Where the arguments come from, and where
431+ // they go to.
495432
496- // Store the "key" pointer in the right field.
497- let key_mplace = self . va_list_key_field ( & mplace) ?;
498- self . write_pointer ( key, & key_mplace) ?;
499- } else if Some ( local) == body. spread_arg {
500- // Make the local live once, then fill in the value field by field.
501- self . storage_live ( local) ?;
502- // Must be a tuple
503- let ty:: Tuple ( fields) = ty. kind ( ) else {
504- span_bug ! ( self . cur_span( ) , "non-tuple type for `spread_arg`: {ty}" )
505- } ;
506- for ( i, field_ty) in fields. iter ( ) . enumerate ( ) {
507- let dest = dest. project_deeper (
508- & [ mir:: ProjectionElem :: Field ( FieldIdx :: from_usize ( i) , field_ty) ] ,
509- * self . tcx ,
510- ) ;
511- let ( idx, callee_abi) = callee_args_abis. next ( ) . unwrap ( ) ;
512- self . pass_argument (
513- & mut caller_args,
514- callee_abi,
515- idx,
516- & dest,
517- field_ty,
518- /* already_live */ true ,
519- ) ?;
520- }
521- } else {
522- // Normal argument. Cannot mark it as live yet, it might be unsized!
433+ // The "where they come from" part is easy, we expect the caller to do any special handling
434+ // that might be required here (e.g. for untupling).
435+ // If `with_caller_location` is set we pretend there is an extra argument (that
436+ // we will not pass; our `caller_location` intrinsic implementation walks the stack instead).
437+ assert_eq ! (
438+ args. len( ) + if with_caller_location { 1 } else { 0 } ,
439+ caller_fn_abi. args. len( ) ,
440+ "mismatch between caller ABI and caller arguments" ,
441+ ) ;
442+ let mut caller_args = args
443+ . iter ( )
444+ . zip ( caller_fn_abi. args . iter ( ) )
445+ . filter ( |arg_and_abi| !arg_and_abi. 1 . is_ignore ( ) ) ;
446+
447+ // Now we have to spread them out across the callee's locals,
448+ // taking into account the `spread_arg`. If we could write
449+ // this is a single iterator (that handles `spread_arg`), then
450+ // `pass_argument` would be the loop body. It takes care to
451+ // not advance `caller_iter` for ignored arguments.
452+ let mut callee_args_abis = callee_fn_abi. args . iter ( ) . enumerate ( ) ;
453+ // Determine whether there is a special VaList argument. This is always the
454+ // last argument, and since arguments start at index 1 that's `arg_count`.
455+ let va_list_arg = callee_fn_abi. c_variadic . then ( || mir:: Local :: from_usize ( body. arg_count ) ) ;
456+ for local in body. args_iter ( ) {
457+ // Update the span that we show in case of an error to point to this argument.
458+ self . frame_mut ( ) . loc = Right ( body. local_decls [ local] . source_info . span ) ;
459+ // Construct the destination place for this argument. At this point all
460+ // locals are still dead, so we cannot construct a `PlaceTy`.
461+ let dest = mir:: Place :: from ( local) ;
462+ // `layout_of_local` does more than just the instantiation we need to get the
463+ // type, but the result gets cached so this avoids calling the instantiation
464+ // query *again* the next time this local is accessed.
465+ let ty = self . layout_of_local ( self . frame ( ) , local, None ) ?. ty ;
466+ if Some ( local) == va_list_arg {
467+ // This is the last callee-side argument of a variadic function.
468+ // This argument is a VaList holding the remaining caller-side arguments.
469+ self . storage_live ( local) ?;
470+
471+ let place = self . eval_place ( dest) ?;
472+ let mplace = self . force_allocation ( & place) ?;
473+
474+ // Consume the remaining arguments by putting them into the variable argument
475+ // list.
476+ let varargs = self . allocate_varargs (
477+ & mut caller_args,
478+ // "Ignored" arguments aren't actually passed, so the callee should also
479+ // ignore them. (`pass_argument` does this for regular arguments.)
480+ ( & mut callee_args_abis) . filter ( |( _, abi) | !abi. is_ignore ( ) ) ,
481+ ) ?;
482+ // When the frame is dropped, these variable arguments are deallocated.
483+ self . frame_mut ( ) . va_list = varargs. clone ( ) ;
484+ let key = self . va_list_ptr ( varargs. into ( ) ) ;
485+
486+ // Zero the VaList, so it is fully initialized.
487+ self . write_bytes_ptr ( mplace. ptr ( ) , ( 0 ..mplace. layout . size . bytes ( ) ) . map ( |_| 0u8 ) ) ?;
488+
489+ // Store the "key" pointer in the right field.
490+ let key_mplace = self . va_list_key_field ( & mplace) ?;
491+ self . write_pointer ( key, & key_mplace) ?;
492+ } else if Some ( local) == body. spread_arg {
493+ // Make the local live once, then fill in the value field by field.
494+ self . storage_live ( local) ?;
495+ // Must be a tuple
496+ let ty:: Tuple ( fields) = ty. kind ( ) else {
497+ span_bug ! ( self . cur_span( ) , "non-tuple type for `spread_arg`: {ty}" )
498+ } ;
499+ for ( i, field_ty) in fields. iter ( ) . enumerate ( ) {
500+ let dest = dest. project_deeper (
501+ & [ mir:: ProjectionElem :: Field ( FieldIdx :: from_usize ( i) , field_ty) ] ,
502+ * self . tcx ,
503+ ) ;
523504 let ( idx, callee_abi) = callee_args_abis. next ( ) . unwrap ( ) ;
524505 self . pass_argument (
525506 & mut caller_args,
526507 callee_abi,
527508 idx,
528509 & dest,
529- ty ,
530- /* already_live */ false ,
510+ field_ty ,
511+ /* already_live */ true ,
531512 ) ?;
532513 }
514+ } else {
515+ // Normal argument. Cannot mark it as live yet, it might be unsized!
516+ let ( idx, callee_abi) = callee_args_abis. next ( ) . unwrap ( ) ;
517+ self . pass_argument (
518+ & mut caller_args,
519+ callee_abi,
520+ idx,
521+ & dest,
522+ ty,
523+ /* already_live */ false ,
524+ ) ?;
533525 }
534- // If the callee needs a caller location, pretend we consume one more argument from the ABI.
535- if instance. def . requires_caller_location ( * self . tcx ) {
536- callee_args_abis. next ( ) . unwrap ( ) ;
537- }
538- // Now we should have no more caller args or callee arg ABIs.
539- assert ! (
540- callee_args_abis. next( ) . is_none( ) ,
541- "mismatch between callee ABI and callee body arguments"
542- ) ;
543- if caller_args. next ( ) . is_some ( ) {
544- throw_ub_format ! ( "calling a function with more arguments than it expected" ) ;
545- }
546- // Don't forget to check the return type!
547- if !self . check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) ? {
548- throw_ub ! ( AbiMismatchReturn {
549- caller_ty: caller_fn_abi. ret. layout. ty,
550- callee_ty: callee_fn_abi. ret. layout. ty
551- } ) ;
552- }
526+ }
553527
554- // Protect return place for in-place return value passing.
555- // We only need to protect anything if this is actually an in-memory place.
556- if let Some ( mplace) = destination_mplace {
557- M :: protect_in_place_function_argument ( self , & mplace) ?;
558- }
528+ // Don't forget to check the return type!
529+ self . frame_mut ( ) . loc = Right ( body. local_decls [ mir:: RETURN_PLACE ] . source_info . span ) ;
530+ if !self . check_argument_compat ( & caller_fn_abi. ret , & callee_fn_abi. ret ) ? {
531+ throw_ub ! ( AbiMismatchReturn {
532+ caller_ty: caller_fn_abi. ret. layout. ty,
533+ callee_ty: callee_fn_abi. ret. layout. ty
534+ } ) ;
535+ }
536+ // Protect return place for in-place return value passing.
537+ // We only need to protect anything if this is actually an in-memory place.
538+ if let Some ( mplace) = destination_mplace {
539+ M :: protect_in_place_function_argument ( self , & mplace) ?;
540+ }
559541
560- // Don't forget to mark "initially live" locals as live.
561- self . storage_live_for_always_live_locals ( ) ?;
562- } ;
563- res. inspect_err_kind ( |_| {
564- // Don't show the incomplete stack frame in the error stacktrace.
565- self . stack_mut ( ) . pop ( ) ;
566- } )
542+ // For the final checks, use same span as preamble since it is unclear what else to do.
543+ self . frame_mut ( ) . loc = Right ( preamble_span) ;
544+ // If the callee needs a caller location, pretend we consume one more argument from the ABI.
545+ if instance. def . requires_caller_location ( * self . tcx ) {
546+ callee_args_abis. next ( ) . unwrap ( ) ;
547+ }
548+ // Now we should have no more caller args or callee arg ABIs.
549+ assert ! (
550+ callee_args_abis. next( ) . is_none( ) ,
551+ "mismatch between callee ABI and callee body arguments"
552+ ) ;
553+ if caller_args. next ( ) . is_some ( ) {
554+ throw_ub_format ! ( "calling a function with more arguments than it expected" ) ;
555+ }
556+
557+ // Done!
558+ self . push_stack_frame_done ( )
567559 }
568560
569561 /// Initiate a call to this function -- pushing the stack frame and initializing the arguments.
0 commit comments