diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 6133cc3548b5f..538918a890d52 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -665,46 +665,42 @@ impl<'a> AstValidator<'a> { /// - Non-const /// - Either foreign, or free and `unsafe extern "C"` semantically fn check_c_variadic_type(&self, fk: FnKind<'a>) { - let variadic_spans: Vec<_> = fk - .decl() - .inputs - .iter() - .filter(|arg| matches!(arg.ty.kind, TyKind::CVarArgs)) - .map(|arg| arg.span) - .collect(); + // `...` is already rejected when it is not the final parameter. + let variadic_param = match fk.decl().inputs.last() { + Some(param) if matches!(param.ty.kind, TyKind::CVarArgs) => param, + _ => return, + }; - if variadic_spans.is_empty() { - return; - } + let FnKind::Fn(fn_ctxt, _, Fn { sig, .. }) = fk else { + // Unreachable because the parser already rejects `...` in closures. + unreachable!("C variable argument list cannot be used in closures") + }; - if let Some(header) = fk.header() - && let Const::Yes(const_span) = header.constness - { - let mut spans = variadic_spans.clone(); - spans.push(const_span); + // C-variadics are not yet implemented in const evaluation. + if let Const::Yes(const_span) = sig.header.constness { self.dcx().emit_err(errors::ConstAndCVariadic { - spans, + spans: vec![const_span, variadic_param.span], const_span, - variadic_spans: variadic_spans.clone(), + variadic_span: variadic_param.span, }); } - match (fk.ctxt(), fk.header()) { - (Some(FnCtxt::Foreign), _) => return, - (Some(FnCtxt::Free), Some(header)) => match header.ext { + match fn_ctxt { + FnCtxt::Foreign => return, + FnCtxt::Free => match sig.header.ext { Extern::Explicit(StrLit { symbol_unescaped: sym::C, .. }, _) | Extern::Explicit(StrLit { symbol_unescaped: sym::C_dash_unwind, .. }, _) | Extern::Implicit(_) - if matches!(header.safety, Safety::Unsafe(_)) => + if matches!(sig.header.safety, Safety::Unsafe(_)) => { return; } _ => {} }, - _ => {} + FnCtxt::Assoc(_) => {} }; - self.dcx().emit_err(errors::BadCVariadic { span: variadic_spans }); + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } fn check_item_named(&self, ident: Ident, kind: &str) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ae8f056cb4e65..476ed27a10e3c 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -322,7 +322,7 @@ pub(crate) struct ExternItemAscii { #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { #[primary_span] - pub span: Vec, + pub span: Span, } #[derive(Diagnostic)] @@ -656,7 +656,7 @@ pub(crate) struct ConstAndCVariadic { #[label(ast_passes_const)] pub const_span: Span, #[label(ast_passes_variadic)] - pub variadic_spans: Vec, + pub variadic_span: Span, } #[derive(Diagnostic)] diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index 675dfd335c42e..b58016b5a8165 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -47,10 +47,10 @@ LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/issue-86053-1.rs:11:12 + --> $DIR/issue-86053-1.rs:11:36 | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { - | ^^^ ^^^ + | ^^^ error[E0412]: cannot find type `F` in this scope --> $DIR/issue-86053-1.rs:11:48 diff --git a/tests/ui/c-variadic/no-closure.rs b/tests/ui/c-variadic/no-closure.rs new file mode 100644 index 0000000000000..c0b77786e8b4a --- /dev/null +++ b/tests/ui/c-variadic/no-closure.rs @@ -0,0 +1,17 @@ +#![feature(c_variadic)] +#![crate_type = "lib"] + +// Check that `...` in closures is rejected. + +const F: extern "C" fn(...) = |_: ...| {}; +//~^ ERROR C-variadic type `...` may not be nested inside another type + +fn foo() { + let f = |...| {}; + //~^ ERROR: `..` patterns are not allowed here + //~| ERROR: unexpected `...` + + let f = |_: ...| {}; + //~^ ERROR C-variadic type `...` may not be nested inside another type + f(1i64) +} diff --git a/tests/ui/c-variadic/no-closure.stderr b/tests/ui/c-variadic/no-closure.stderr new file mode 100644 index 0000000000000..77bd106bb9519 --- /dev/null +++ b/tests/ui/c-variadic/no-closure.stderr @@ -0,0 +1,35 @@ +error[E0743]: C-variadic type `...` may not be nested inside another type + --> $DIR/no-closure.rs:6:35 + | +LL | const F: extern "C" fn(...) = |_: ...| {}; + | ^^^ + +error: unexpected `...` + --> $DIR/no-closure.rs:10:14 + | +LL | let f = |...| {}; + | ^^^ not a valid pattern + | +help: for a rest pattern, use `..` instead of `...` + | +LL - let f = |...| {}; +LL + let f = |..| {}; + | + +error[E0743]: C-variadic type `...` may not be nested inside another type + --> $DIR/no-closure.rs:14:17 + | +LL | let f = |_: ...| {}; + | ^^^ + +error: `..` patterns are not allowed here + --> $DIR/no-closure.rs:10:14 + | +LL | let f = |...| {}; + | ^^^ + | + = note: only allowed in tuple, tuple struct, and slice patterns + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0743`. diff --git a/tests/ui/c-variadic/valid.rs b/tests/ui/c-variadic/valid.rs new file mode 100644 index 0000000000000..5a0b32026dc7a --- /dev/null +++ b/tests/ui/c-variadic/valid.rs @@ -0,0 +1,29 @@ +//@ run-pass +#![feature(c_variadic)] + +// In rust (and C23 and above) `...` can be the only argument. +unsafe extern "C" fn only_dot_dot_dot(mut ap: ...) -> i32 { + unsafe { ap.arg() } +} + +unsafe extern "C-unwind" fn abi_c_unwind(mut ap: ...) -> i32 { + unsafe { ap.arg() } +} + +#[allow(improper_ctypes_definitions)] +unsafe extern "C" fn mix_int_float(mut ap: ...) -> (i64, f64, *const i32, f64) { + (ap.arg(), ap.arg(), ap.arg(), ap.arg()) +} + +fn main() { + unsafe { + assert_eq!(only_dot_dot_dot(32), 32); + assert_eq!(abi_c_unwind(32), 32); + + // Passing more arguments than expected is allowed. + assert_eq!(only_dot_dot_dot(32, 1i64, core::ptr::null::(), 3.14f64), 32); + + let ptr = &14i32 as *const i32; + assert_eq!(mix_int_float(12i64, 13.0f64, ptr, 15.0f64), (12, 13.0, ptr, 15.0)); + } +} diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index e7a0248cffab9..243924e6c53ee 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -16,8 +16,7 @@ extern "C" fn f2_2(...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f2_3(..., x: isize) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention -//~| ERROR `...` must be the last argument of a C-variadic function +//~^ ERROR `...` must be the last argument of a C-variadic function extern "C" fn f3_1(x: isize, ...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention @@ -26,8 +25,7 @@ extern "C" fn f3_2(...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention extern "C" fn f3_3(..., x: isize) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention -//~| ERROR `...` must be the last argument of a C-variadic function +//~^ ERROR `...` must be the last argument of a C-variadic function const unsafe extern "C" fn f4_1(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic @@ -77,9 +75,7 @@ trait T { fn t_f4(...); //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention fn t_f5(..., x: isize) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - //~| ERROR `...` must be the last argument of a C-variadic function + //~^ ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - //~| ERROR `...` must be the last argument of a C-variadic function + //~^ ERROR `...` must be the last argument of a C-variadic function } diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 5379045967aa0..5c55cc38b5682 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -29,118 +29,103 @@ LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 - | -LL | extern "C" fn f2_3(..., x: isize) {} - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:22:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:21:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:25:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:24:20 | LL | extern "C" fn f3_2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20 - | -LL | extern "C" fn f3_3(..., x: isize) {} - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:28:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:32:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:30:1 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:34:1 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:39:26 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:41:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:39:1 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} - | ^^^^^ ^^^ ^^^ C-variadic because of this - | | | - | | C-variadic because of this - | `const` because of this + | ^^^^^ `const` because of this ^^^ C-variadic because of this error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:41:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:39:41 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} - | ^^^ ^^^ + | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:47:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 | LL | fn e_f2(..., x: isize); | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:58:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:56:28 | LL | fn i_f3(..., x: isize, ...) {} - | ^^^ ^^^ + | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:61:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f4(..., x: isize, ...) {} - | ^^^ ^^^ + | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:5 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:5 | LL | const fn i_f5(x: isize, ...) {} | ^^^^^ ^^^ C-variadic because of this @@ -148,61 +133,49 @@ LL | const fn i_f5(x: isize, ...) {} | `const` because of this error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:69:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 | LL | fn t_f3(...) {} | ^^^ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 - | -LL | fn t_f5(..., x: isize) {} - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 - | -LL | fn t_f6(..., x: isize); - | ^^^ - -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 | LL | fn t_f6(..., x: isize); | ^^^ error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:32:43 + --> $DIR/variadic-ffi-semantic-restrictions.rs:30:43 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^ - value is dropped here @@ -210,7 +183,7 @@ LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:36:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ - value is dropped here @@ -218,13 +191,13 @@ LL | const extern "C" fn f4_2(x: isize, ...) {} | the destructor for this type cannot be evaluated in constant functions error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:64:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions -error: aborting due to 36 previous errors +error: aborting due to 32 previous errors For more information about this error, try `rustc --explain E0493`.