Skip to content

Commit 610ea1d

Browse files
Rollup merge of #153398 - folkertdev:const-c-variadic-trailing-zst, r=RalfJung
fix ICE in `const_c_variadic` when passing ZSTs fixes #153351 r? RalfJung There was a mismatch between the caller and callee ABI where the caller does not pass ZST arguments, but the callee does expect them. Because ZSTs don't implement `VaArgSafe` the program must already be invalid if this comes up.
2 parents 8120cc4 + 225b7e0 commit 610ea1d

4 files changed

Lines changed: 50 additions & 6 deletions

File tree

compiler/rustc_const_eval/src/interpret/call.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_hir::find_attr;
1212
use rustc_middle::ty::layout::{IntegerExt, TyAndLayout};
1313
use rustc_middle::ty::{self, AdtDef, Instance, Ty, VariantDef};
1414
use rustc_middle::{bug, mir, span_bug};
15-
use rustc_target::callconv::{ArgAbi, FnAbi, PassMode};
15+
use rustc_target::callconv::{ArgAbi, FnAbi};
1616
use tracing::field::Empty;
1717
use tracing::{info, instrument, trace};
1818

@@ -284,7 +284,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
284284
'tcx: 'y,
285285
{
286286
assert_eq!(callee_ty, callee_abi.layout.ty);
287-
if callee_abi.mode == PassMode::Ignore {
287+
if callee_abi.is_ignore() {
288288
// This one is skipped. Still must be made live though!
289289
if !already_live {
290290
self.storage_live(callee_arg.as_local().unwrap())?;
@@ -450,7 +450,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
450450
let mut caller_args = args
451451
.iter()
452452
.zip(caller_fn_abi.args.iter())
453-
.filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore));
453+
.filter(|arg_and_abi| !arg_and_abi.1.is_ignore());
454454

455455
// Now we have to spread them out across the callee's locals,
456456
// taking into account the `spread_arg`. If we could write
@@ -480,7 +480,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
480480

481481
// Consume the remaining arguments by putting them into the variable argument
482482
// list.
483-
let varargs = self.allocate_varargs(&mut caller_args, &mut callee_args_abis)?;
483+
let varargs = self.allocate_varargs(
484+
&mut caller_args,
485+
// "Ignored" arguments aren't actually passed, so the callee should also
486+
// ignore them. (`pass_argument` does this for regular arguments.)
487+
(&mut callee_args_abis).filter(|(_, abi)| !abi.is_ignore()),
488+
)?;
484489
// When the frame is dropped, these variable arguments are deallocated.
485490
self.frame_mut().va_list = varargs.clone();
486491
let key = self.va_list_ptr(varargs.into());

compiler/rustc_const_eval/src/interpret/stack.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -631,8 +631,8 @@ impl<'a, 'tcx: 'a, M: Machine<'tcx>> InterpCx<'tcx, M> {
631631
/// of variadic arguments. Return a list of the places that hold those arguments.
632632
pub(crate) fn allocate_varargs<I, J>(
633633
&mut self,
634-
caller_args: &mut I,
635-
callee_abis: &mut J,
634+
caller_args: I,
635+
mut callee_abis: J,
636636
) -> InterpResult<'tcx, Vec<MPlaceTy<'tcx, M::Provenance>>>
637637
where
638638
I: Iterator<Item = (&'a FnArg<'tcx, M::Provenance>, &'a ArgAbi<'tcx, Ty<'tcx>>)>,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ ignore-target: windows # does not ignore ZST arguments
2+
//@ ignore-target: powerpc # does not ignore ZST arguments
3+
//@ ignore-target: s390x # does not ignore ZST arguments
4+
//@ ignore-target: sparc # does not ignore ZST arguments
5+
#![feature(c_variadic)]
6+
7+
// Some platforms ignore ZSTs, meaning that the argument is not passed, even though it is part
8+
// of the callee's ABI. Test that this doesn't trip any asserts.
9+
//
10+
// NOTE: this test only succeeds when the `()` argument uses `Passmode::Ignore`. For some targets,
11+
// notably msvc, such arguments are not ignored, which would cause UB when attempting to read the
12+
// second `i32` argument while the next item in the variable argument list is `()`.
13+
14+
fn main() {
15+
unsafe extern "C" fn variadic(mut ap: ...) {
16+
ap.arg::<i32>();
17+
ap.arg::<i32>();
18+
}
19+
20+
unsafe { variadic(0i32, (), 1i32) }
21+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ build-pass
2+
//@ compile-flags: --emit=obj
3+
#![feature(c_variadic)]
4+
#![feature(const_c_variadic)]
5+
#![feature(const_destruct)]
6+
#![crate_type = "lib"]
7+
8+
// Regression test for when a c-variadic argument is `PassMode::Ignore`. The caller won't pass the
9+
// argument, but the callee ABI does have the argument. Ensure that const-eval is able to handle
10+
// this case without tripping any asserts.
11+
12+
const unsafe extern "C" fn read_n<const N: usize>(_: ...) {}
13+
14+
unsafe fn read_too_many() {
15+
const { read_n::<0>((), 1i32) }
16+
}
17+
18+
fn read_as<T>() -> () {}

0 commit comments

Comments
 (0)