diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 0de1fcd7c3a9..b207244349d8 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::layout::{IntegerExt, TyAndLayout}; use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef}; use rustc_middle::{bug, mir, span_bug}; -use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, FnAbi}; use tracing::field::Empty; use tracing::{info, instrument, trace}; @@ -284,7 +284,7 @@ fn pass_argument<'x, 'y>( 'tcx: 'y, { assert_eq!(callee_ty, callee_abi.layout.ty); - if callee_abi.mode == PassMode::Ignore { + if callee_abi.is_ignore() { // This one is skipped. Still must be made live though! if !already_live { self.storage_live(callee_arg.as_local().unwrap())?; @@ -450,7 +450,7 @@ pub fn init_stack_frame( let mut caller_args = args .iter() .zip(caller_fn_abi.args.iter()) - .filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore)); + .filter(|arg_and_abi| !arg_and_abi.1.is_ignore()); // Now we have to spread them out across the callee's locals, // taking into account the `spread_arg`. If we could write @@ -480,7 +480,12 @@ pub fn init_stack_frame( // Consume the remaining arguments by putting them into the variable argument // list. - let varargs = self.allocate_varargs(&mut caller_args, &mut callee_args_abis)?; + let varargs = self.allocate_varargs( + &mut caller_args, + // "Ignored" arguments aren't actually passed, so the callee should also + // ignore them. (`pass_argument` does this for regular arguments.) + (&mut callee_args_abis).filter(|(_, abi)| !abi.is_ignore()), + )?; // When the frame is dropped, these variable arguments are deallocated. self.frame_mut().va_list = varargs.clone(); let key = self.va_list_ptr(varargs.into()); diff --git a/compiler/rustc_const_eval/src/interpret/stack.rs b/compiler/rustc_const_eval/src/interpret/stack.rs index 6da6ed2ec757..b0f34264ad84 100644 --- a/compiler/rustc_const_eval/src/interpret/stack.rs +++ b/compiler/rustc_const_eval/src/interpret/stack.rs @@ -631,8 +631,8 @@ impl<'a, 'tcx: 'a, M: Machine<'tcx>> InterpCx<'tcx, M> { /// of variadic arguments. Return a list of the places that hold those arguments. pub(crate) fn allocate_varargs( &mut self, - caller_args: &mut I, - callee_abis: &mut J, + caller_args: I, + mut callee_abis: J, ) -> InterpResult<'tcx, Vec>> where I: Iterator, &'a ArgAbi<'tcx, Ty<'tcx>>)>, diff --git a/src/tools/miri/tests/pass/c-variadic-ignored-argument.rs b/src/tools/miri/tests/pass/c-variadic-ignored-argument.rs new file mode 100644 index 000000000000..b41e1b0f076a --- /dev/null +++ b/src/tools/miri/tests/pass/c-variadic-ignored-argument.rs @@ -0,0 +1,21 @@ +//@ ignore-target: windows # does not ignore ZST arguments +//@ ignore-target: powerpc # does not ignore ZST arguments +//@ ignore-target: s390x # does not ignore ZST arguments +//@ ignore-target: sparc # does not ignore ZST arguments +#![feature(c_variadic)] + +// Some platforms ignore ZSTs, meaning that the argument is not passed, even though it is part +// of the callee's ABI. Test that this doesn't trip any asserts. +// +// NOTE: this test only succeeds when the `()` argument uses `Passmode::Ignore`. For some targets, +// notably msvc, such arguments are not ignored, which would cause UB when attempting to read the +// second `i32` argument while the next item in the variable argument list is `()`. + +fn main() { + unsafe extern "C" fn variadic(mut ap: ...) { + ap.arg::(); + ap.arg::(); + } + + unsafe { variadic(0i32, (), 1i32) } +} diff --git a/tests/ui/consts/const-eval/c-variadic-ignored-argument.rs b/tests/ui/consts/const-eval/c-variadic-ignored-argument.rs new file mode 100644 index 000000000000..fe700eea186e --- /dev/null +++ b/tests/ui/consts/const-eval/c-variadic-ignored-argument.rs @@ -0,0 +1,18 @@ +//@ build-pass +//@ compile-flags: --emit=obj +#![feature(c_variadic)] +#![feature(const_c_variadic)] +#![feature(const_destruct)] +#![crate_type = "lib"] + +// Regression test for when a c-variadic argument is `PassMode::Ignore`. The caller won't pass the +// argument, but the callee ABI does have the argument. Ensure that const-eval is able to handle +// this case without tripping any asserts. + +const unsafe extern "C" fn read_n(_: ...) {} + +unsafe fn read_too_many() { + const { read_n::<0>((), 1i32) } +} + +fn read_as() -> () {}