From 1656f6c668e0603fb31dac6b2b95f1cfb1be2402 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 15:00:19 +0200 Subject: [PATCH 1/8] disallow c-variadic coroutines --- compiler/rustc_ast_passes/messages.ftl | 4 ++++ .../rustc_ast_passes/src/ast_validation.rs | 9 +++++++++ compiler/rustc_ast_passes/src/errors.rs | 12 ++++++++++++ tests/ui/c-variadic/not-async.rs | 7 +++++++ tests/ui/c-variadic/not-async.stderr | 19 +++++++++++++++++++ .../note-and-explain-ReVar-124973.rs | 3 ++- .../note-and-explain-ReVar-124973.stderr | 8 +++++++- 7 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/ui/c-variadic/not-async.rs create mode 100644 tests/ui/c-variadic/not-async.stderr diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index e5f1fcdc4b40d..68ac2a05a4883 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -84,6 +84,10 @@ ast_passes_const_without_body = ast_passes_constraint_on_negative_bound = associated type constraints not allowed on negative bounds +ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic + .const = `{$coroutine_kind}` because of this + .variadic = C-variadic because of this + ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses .label = not supported .suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 538918a890d52..672c1e9441884 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -685,6 +685,15 @@ impl<'a> AstValidator<'a> { }); } + if let Some(coroutine_kind) = sig.header.coroutine_kind { + self.dcx().emit_err(errors::CoroutineAndCVariadic { + spans: vec![coroutine_kind.span(), variadic_param.span], + coroutine_kind: coroutine_kind.as_str(), + coroutine_span: coroutine_kind.span(), + variadic_span: variadic_param.span, + }); + } + match fn_ctxt { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 476ed27a10e3c..f8806ef21c16d 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -659,6 +659,18 @@ pub(crate) struct ConstAndCVariadic { pub variadic_span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_coroutine_and_c_variadic)] +pub(crate) struct CoroutineAndCVariadic { + #[primary_span] + pub spans: Vec, + pub coroutine_kind: &'static str, + #[label(ast_passes_const)] + pub coroutine_span: Span, + #[label(ast_passes_variadic)] + pub variadic_span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_pattern_in_foreign, code = E0130)] // FIXME: deduplicate with rustc_lint (`BuiltinLintDiag::PatternsInFnsWithoutBody`) diff --git a/tests/ui/c-variadic/not-async.rs b/tests/ui/c-variadic/not-async.rs new file mode 100644 index 0000000000000..45a7e1f8972bf --- /dev/null +++ b/tests/ui/c-variadic/not-async.rs @@ -0,0 +1,7 @@ +//@ edition: 2021 +#![feature(c_variadic)] +#![crate_type = "lib"] + +async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} +//~^ ERROR functions cannot be both `async` and C-variadic +//~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds diff --git a/tests/ui/c-variadic/not-async.stderr b/tests/ui/c-variadic/not-async.stderr new file mode 100644 index 0000000000000..b8caf0d8bd852 --- /dev/null +++ b/tests/ui/c-variadic/not-async.stderr @@ -0,0 +1,19 @@ +error: functions cannot be both `async` and C-variadic + --> $DIR/not-async.rs:5:1 + | +LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + +error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds + --> $DIR/not-async.rs:5:59 + | +LL | async unsafe extern "C" fn cannot_be_async(x: isize, ...) {} + | --------------------------------------------------------- ^^ + | | + | opaque type defined here + | + = note: hidden type `{async fn body of cannot_be_async()}` captures lifetime `'_` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.rs b/tests/ui/inference/note-and-explain-ReVar-124973.rs index f1e2464563699..aa4b909fa76c8 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.rs +++ b/tests/ui/inference/note-and-explain-ReVar-124973.rs @@ -3,6 +3,7 @@ #![feature(c_variadic)] async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} -//~^ ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds +//~^ ERROR functions cannot be both `async` and C-variadic +//~| ERROR hidden type for `impl Future` captures lifetime that does not appear in bounds fn main() {} diff --git a/tests/ui/inference/note-and-explain-ReVar-124973.stderr b/tests/ui/inference/note-and-explain-ReVar-124973.stderr index 574f6508e4c78..964fbc4a4fb7a 100644 --- a/tests/ui/inference/note-and-explain-ReVar-124973.stderr +++ b/tests/ui/inference/note-and-explain-ReVar-124973.stderr @@ -1,3 +1,9 @@ +error: functions cannot be both `async` and C-variadic + --> $DIR/note-and-explain-ReVar-124973.rs:5:1 + | +LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} + | ^^^^^ `async` because of this ^^^ C-variadic because of this + error[E0700]: hidden type for `impl Future` captures lifetime that does not appear in bounds --> $DIR/note-and-explain-ReVar-124973.rs:5:73 | @@ -8,6 +14,6 @@ LL | async unsafe extern "C" fn multiple_named_lifetimes<'a, 'b>(_: u8, ...) {} | = note: hidden type `{async fn body of multiple_named_lifetimes<'a, 'b>()}` captures lifetime `'_` -error: aborting due to 1 previous error +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0700`. From a093372e5e560b1a768971c2b87b306755f2c17d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 15:13:02 +0200 Subject: [PATCH 2/8] disallow c-variadic associated functions (for now) there is no reason this should not work, really, we're just cutting some scope for now --- compiler/rustc_ast_passes/messages.ftl | 2 ++ .../rustc_ast_passes/src/ast_validation.rs | 14 +++++++++----- compiler/rustc_ast_passes/src/errors.rs | 7 +++++++ .../feature-gates/feature-gate-c_variadic.rs | 8 ++++---- .../feature-gate-c_variadic.stderr | 12 ++++++------ .../variadic-ffi-semantic-restrictions.rs | 18 +++++++++--------- .../variadic-ffi-semantic-restrictions.stderr | 18 +++++++++--------- 7 files changed, 46 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 68ac2a05a4883..6eb4e4bc45272 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -66,6 +66,8 @@ ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect +ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list + ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic .const = `const` because of this .variadic = C-variadic because of this diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 672c1e9441884..c5c23c47e74e2 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -704,12 +704,16 @@ impl<'a> AstValidator<'a> { { return; } - _ => {} + _ => { + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + } }, - FnCtxt::Assoc(_) => {} - }; - - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + FnCtxt::Assoc(_) => { + // For now, C variable argument lists are unsupported in associated functions. + let err = errors::CVariadicAssociatedFunction { span: variadic_param.span }; + self.dcx().emit_err(err); + } + } } 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 f8806ef21c16d..a085e09fe9483 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -318,6 +318,13 @@ pub(crate) struct ExternItemAscii { pub block: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_associated_function)] +pub(crate) struct CVariadicAssociatedFunction { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.rs b/tests/ui/feature-gates/feature-gate-c_variadic.rs index 45c688420931d..88d91dbd081fb 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.rs +++ b/tests/ui/feature-gates/feature-gate-c_variadic.rs @@ -1,10 +1,10 @@ -#![crate_type="lib"] +#![crate_type = "lib"] -pub unsafe extern "C" fn test(_: i32, ap: ...) { } +pub unsafe extern "C" fn test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable trait Trait { - unsafe extern "C" fn trait_test(_: i32, ap: ...) { } + unsafe extern "C" fn trait_test(_: i32, ap: ...) {} //~^ ERROR C-variadic functions are unstable - //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~| ERROR associated functions cannot have a C variable argument list } diff --git a/tests/ui/feature-gates/feature-gate-c_variadic.stderr b/tests/ui/feature-gates/feature-gate-c_variadic.stderr index e30a2f1ede367..808aa20948d03 100644 --- a/tests/ui/feature-gates/feature-gate-c_variadic.stderr +++ b/tests/ui/feature-gates/feature-gate-c_variadic.stderr @@ -1,14 +1,14 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: associated functions cannot have a C variable argument list --> $DIR/feature-gate-c_variadic.rs:7:45 | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} | ^^^^^^^ error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:3:1 | -LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub unsafe extern "C" fn test(_: i32, ap: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #44930 for more information = help: add `#![feature(c_variadic)]` to the crate attributes to enable @@ -17,8 +17,8 @@ LL | pub unsafe extern "C" fn test(_: i32, ap: ...) { } error[E0658]: C-variadic functions are unstable --> $DIR/feature-gate-c_variadic.rs:7:5 | -LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) { } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | unsafe extern "C" fn trait_test(_: i32, ap: ...) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #44930 for more information = help: add `#![feature(c_variadic)]` to the crate attributes to enable diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index 243924e6c53ee..e76bdc21fa043 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -50,30 +50,30 @@ struct X; impl X { 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 + //~^ ERROR associated functions cannot have a C variable argument list fn i_f2(...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list 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 + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR `...` must be the last argument of a C-variadic function 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 + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR `...` must be the last argument of a C-variadic function 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 + //~^ ERROR associated functions cannot have a C variable argument list //~| ERROR functions cannot be both `const` and C-variadic //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time } trait T { 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 + //~^ ERROR associated functions cannot have a C variable argument list 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 + //~^ ERROR associated functions cannot have a C variable argument list fn t_f3(...) {} - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f4(...); - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR associated functions cannot have a C variable argument list fn t_f5(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function fn t_f6(..., x: isize); diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 5c55cc38b5682..1317f63a88adc 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -88,13 +88,13 @@ error: `...` must be the last argument of a C-variadic function 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 +error: associated functions cannot have a C variable argument list --> $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 +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 | LL | fn i_f2(...) {} @@ -106,7 +106,7 @@ error: `...` must be the last argument of a C-variadic function 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 +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:56:28 | LL | fn i_f3(..., x: isize, ...) {} @@ -118,7 +118,7 @@ error: `...` must be the last argument of a C-variadic function 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 +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f4(..., x: isize, ...) {} @@ -132,31 +132,31 @@ 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 +error: associated functions cannot have a C variable argument list --> $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 +error: associated functions cannot have a C variable argument list --> $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 +error: associated functions cannot have a C variable argument list --> $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 +error: associated functions cannot have a C variable argument list --> $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 +error: associated functions cannot have a C variable argument list --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 | LL | fn t_f4(...); From 7075000ae72c3ead855dc660a37c6bf263b7b452 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 17:19:01 +0200 Subject: [PATCH 3/8] clarify control flow of `check_c_variadic_type` --- .../rustc_ast_passes/src/ast_validation.rs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index c5c23c47e74e2..79947b514b10b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -697,14 +697,23 @@ impl<'a> AstValidator<'a> { 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!(sig.header.safety, Safety::Unsafe(_)) => - { - return; + Extern::Implicit(_) => { + if matches!(sig.header.safety, Safety::Unsafe(_)) { + return; + } + + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + } + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + if matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + if matches!(sig.header.safety, Safety::Unsafe(_)) { + return; + } + } + + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } - _ => { + Extern::None => { self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } }, From 2b9fce8c8cf4373ee9da938ad5eeb97e85ca2739 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 6 Sep 2025 17:23:34 +0200 Subject: [PATCH 4/8] c-variadic: reject non-extern functions --- compiler/rustc_ast_passes/messages.ftl | 2 ++ .../rustc_ast_passes/src/ast_validation.rs | 3 ++- compiler/rustc_ast_passes/src/errors.rs | 7 +++++++ tests/ui/c-variadic/issue-86053-1.rs | 2 +- tests/ui/c-variadic/issue-86053-1.stderr | 2 +- .../issue-83499-input-output-iteration-ice.rs | 5 ++--- ...ue-83499-input-output-iteration-ice.stderr | 20 +++++++------------ .../variadic-ffi-semantic-restrictions.rs | 4 ++-- .../variadic-ffi-semantic-restrictions.stderr | 4 ++-- 9 files changed, 26 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 6eb4e4bc45272..958797a608947 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -68,6 +68,8 @@ ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list +ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions + ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic .const = `const` because of this .variadic = C-variadic because of this diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 79947b514b10b..bbba235a6d16b 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -714,7 +714,8 @@ impl<'a> AstValidator<'a> { self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } Extern::None => { - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + let err = errors::CVariadicNoExtern { span: variadic_param.span }; + self.dcx().emit_err(err); } }, FnCtxt::Assoc(_) => { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index a085e09fe9483..6b57e5ef37613 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -325,6 +325,13 @@ pub(crate) struct CVariadicAssociatedFunction { pub span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_no_extern)] +pub(crate) struct CVariadicNoExtern { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { diff --git a/tests/ui/c-variadic/issue-86053-1.rs b/tests/ui/c-variadic/issue-86053-1.rs index 537d0263adf78..58dfee55cf885 100644 --- a/tests/ui/c-variadic/issue-86053-1.rs +++ b/tests/ui/c-variadic/issue-86053-1.rs @@ -13,6 +13,6 @@ fn ordering4 < 'a , 'b > ( a : , self , self , self , //~| ERROR unexpected `self` parameter in function //~| ERROR unexpected `self` parameter in function //~| ERROR `...` must be the last argument of a C-variadic function - //~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~| ERROR `...` is not supported for non-extern functions //~| ERROR cannot find type `F` in this scope } diff --git a/tests/ui/c-variadic/issue-86053-1.stderr b/tests/ui/c-variadic/issue-86053-1.stderr index b58016b5a8165..eb08370b4f800 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -46,7 +46,7 @@ error: `...` must be the last argument of a C-variadic function 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 +error: `...` is not supported for non-extern functions --> $DIR/issue-86053-1.rs:11:36 | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs index 9277994d9b30f..dc0d14bf9d6b6 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.rs +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.rs @@ -4,7 +4,6 @@ fn main() {} -fn foo(_: Bar, ...) -> impl {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention -//~| ERROR cannot find type `Bar` in this scope +unsafe extern "C" fn foo(_: Bar, ...) -> impl {} +//~^ ERROR cannot find type `Bar` in this scope //~| ERROR at least one trait must be specified diff --git a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr index 4a1aa49eb6e6f..31a393e7367a3 100644 --- a/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr +++ b/tests/ui/mir/issue-83499-input-output-iteration-ice.stderr @@ -1,21 +1,15 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:16 - | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^ - error: at least one trait must be specified - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:24 + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:42 | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^^ +LL | unsafe extern "C" fn foo(_: Bar, ...) -> impl {} + | ^^^^ error[E0412]: cannot find type `Bar` in this scope - --> $DIR/issue-83499-input-output-iteration-ice.rs:7:11 + --> $DIR/issue-83499-input-output-iteration-ice.rs:7:29 | -LL | fn foo(_: Bar, ...) -> impl {} - | ^^^ not found in this scope +LL | unsafe extern "C" fn foo(_: Bar, ...) -> impl {} + | ^^^ not found in this scope -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index e76bdc21fa043..fe5421ab2c0ae 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -4,10 +4,10 @@ fn main() {} fn f1_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR `...` is not supported for non-extern functions fn f1_2(...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR `...` is not supported for non-extern functions extern "C" fn f2_1(x: isize, ...) {} //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 1317f63a88adc..0abe152640530 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -1,10 +1,10 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:6:19 | LL | fn f1_1(x: isize, ...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} From 2912efa879912b56f4676eaa3024382b040144f5 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 8 Sep 2025 19:07:35 +0200 Subject: [PATCH 5/8] c-variadic: update cmse-nonsecure example --- .../cmse-nonsecure-entry/generics.rs | 10 +++----- .../cmse-nonsecure-entry/generics.stderr | 24 +++++++++---------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index ad4fca8252d24..4ebc2bbf76129 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -53,9 +53,7 @@ extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait { x } -extern "cmse-nonsecure-entry" fn static_trait_object( - x: &'static dyn Trait, -) -> &'static dyn Trait { +extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait { //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } @@ -63,14 +61,12 @@ extern "cmse-nonsecure-entry" fn static_trait_object( #[repr(transparent)] struct WrapperTransparent<'a>(&'a dyn Trait); -extern "cmse-nonsecure-entry" fn wrapped_trait_object( - x: WrapperTransparent, -) -> WrapperTransparent { +extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> WrapperTransparent { //~^ ERROR return value of `"cmse-nonsecure-entry"` function too large to pass via registers [E0798] x } -extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { +unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention //~| ERROR requires `va_list` lang_item } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 7aeb6969feb9f..55921e764ba21 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,8 +1,8 @@ error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - --> $DIR/generics.rs:73:53 + --> $DIR/generics.rs:69:60 | -LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ^^^^^^ error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:30:1 @@ -50,28 +50,28 @@ LL | extern "cmse-nonsecure-entry" fn trait_object(x: &dyn Trait) -> &dyn Trait = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:58:6 + --> $DIR/generics.rs:56:80 | -LL | ) -> &'static dyn Trait { - | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers +LL | extern "cmse-nonsecure-entry" fn static_trait_object(x: &'static dyn Trait) -> &'static dyn Trait { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error[E0798]: return value of `"cmse-nonsecure-entry"` function too large to pass via registers - --> $DIR/generics.rs:68:6 + --> $DIR/generics.rs:64:81 | -LL | ) -> WrapperTransparent { - | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers +LL | extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> WrapperTransparent { + | ^^^^^^^^^^^^^^^^^^ this type doesn't fit in the available registers | = note: functions with the `"cmse-nonsecure-entry"` ABI must pass their result via the available return registers = note: the result must either be a (transparently wrapped) i64, u64 or f64, or be at most 4 bytes in size error: requires `va_list` lang_item - --> $DIR/generics.rs:73:53 + --> $DIR/generics.rs:69:60 | -LL | extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ +LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { + | ^^^^^^ error: aborting due to 9 previous errors From 5de9bc73e76f60ee27c37f20d0a9530e5a957df7 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 7 Jul 2025 11:52:14 +0200 Subject: [PATCH 6/8] Refactor how to get the span of a function header --- compiler/rustc_ast/src/ast.rs | 67 +++++++++---------- .../rustc_ast_passes/src/ast_validation.rs | 2 +- 2 files changed, 32 insertions(+), 37 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 802a6fa324984..2733a2603d6d8 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2284,6 +2284,33 @@ pub struct FnSig { pub span: Span, } +impl FnSig { + /// Return a span encompassing the header, or where to insert it if empty. + pub fn header_span(&self) -> Span { + match self.header.ext { + Extern::Implicit(span) | Extern::Explicit(_, span) => { + return self.span.with_hi(span.hi()); + } + Extern::None => {} + } + + match self.header.safety { + Safety::Unsafe(span) | Safety::Safe(span) => return self.span.with_hi(span.hi()), + Safety::Default => {} + }; + + if let Some(coroutine_kind) = self.header.coroutine_kind { + return self.span.with_hi(coroutine_kind.span().hi()); + } + + if let Const::Yes(span) = self.header.constness { + return self.span.with_hi(span.hi()); + } + + self.span.shrink_to_lo() + } +} + /// A constraint on an associated item. /// /// ### Examples @@ -3534,12 +3561,12 @@ impl Extern { /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`). #[derive(Clone, Copy, Encodable, Decodable, Debug, Walkable)] pub struct FnHeader { - /// Whether this is `unsafe`, or has a default safety. - pub safety: Safety, - /// Whether this is `async`, `gen`, or nothing. - pub coroutine_kind: Option, /// The `const` keyword, if any pub constness: Const, + /// Whether this is `async`, `gen`, or nothing. + pub coroutine_kind: Option, + /// Whether this is `unsafe`, or has a default safety. + pub safety: Safety, /// The `extern` keyword and corresponding ABI string, if any. pub ext: Extern, } @@ -3553,38 +3580,6 @@ impl FnHeader { || matches!(constness, Const::Yes(_)) || !matches!(ext, Extern::None) } - - /// Return a span encompassing the header, or none if all options are default. - pub fn span(&self) -> Option { - fn append(a: &mut Option, b: Span) { - *a = match a { - None => Some(b), - Some(x) => Some(x.to(b)), - } - } - - let mut full_span = None; - - match self.safety { - Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span), - Safety::Default => {} - }; - - if let Some(coroutine_kind) = self.coroutine_kind { - append(&mut full_span, coroutine_kind.span()); - } - - if let Const::Yes(span) = self.constness { - append(&mut full_span, span); - } - - match self.ext { - Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span), - Extern::None => {} - } - - full_span - } } impl Default for FnHeader { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index bbba235a6d16b..f425acda3583e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -492,7 +492,7 @@ impl<'a> AstValidator<'a> { } if !spans.is_empty() { - let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo()); + let header_span = sig.header_span(); let suggestion_span = header_span.shrink_to_hi().to(sig.decl.output.span()); let padding = if header_span.is_empty() { "" } else { " " }; From 0c96200f268df10d4f3ac102f3161c67e5e67221 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 8 Sep 2025 18:45:41 +0200 Subject: [PATCH 7/8] c-variadic: reject non-unsafe functions --- compiler/rustc_ast/src/ast.rs | 23 ++++++++++ compiler/rustc_ast_passes/messages.ftl | 4 ++ .../rustc_ast_passes/src/ast_validation.rs | 23 ++++++---- compiler/rustc_ast_passes/src/errors.rs | 15 +++++++ .../variadic-ffi-semantic-restrictions.rs | 12 +++--- .../variadic-ffi-semantic-restrictions.stderr | 42 ++++++++++++++++--- 6 files changed, 98 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2733a2603d6d8..915bae9e5f5aa 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2309,6 +2309,22 @@ impl FnSig { self.span.shrink_to_lo() } + + /// The span of the header's safety, or where to insert it if empty. + pub fn safety_span(&self) -> Span { + match self.header.safety { + Safety::Unsafe(span) | Safety::Safe(span) => span, + Safety::Default => { + // Insert after the `coroutine_kind` if available. + if let Some(extern_span) = self.header.ext.span() { + return extern_span.shrink_to_lo(); + } + + // Insert right at the front of the signature. + self.header_span().shrink_to_hi() + } + } + } } /// A constraint on an associated item. @@ -3553,6 +3569,13 @@ impl Extern { None => Extern::Implicit(span), } } + + pub fn span(self) -> Option { + match self { + Extern::None => None, + Extern::Implicit(span) | Extern::Explicit(_, span) => Some(span), + } + } } /// A function header. diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 958797a608947..1aa38a1beda3a 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -68,6 +68,10 @@ ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list +ast_passes_c_variadic_must_be_unsafe = + functions with a C variable argument list must be unsafe + .suggestion = add the `unsafe` keyword to this definition + ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index f425acda3583e..7b32778ddf042 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -698,20 +698,25 @@ impl<'a> AstValidator<'a> { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { Extern::Implicit(_) => { - if matches!(sig.header.safety, Safety::Unsafe(_)) { - return; + // Implicitly defaults to C. + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); } - - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - if matches!(sig.header.safety, Safety::Unsafe(_)) { - return; - } + if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { + self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); } - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } } Extern::None => { let err = errors::CVariadicNoExtern { span: variadic_param.span }; diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 6b57e5ef37613..946a22d2e759e 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -332,6 +332,21 @@ pub(crate) struct CVariadicNoExtern { pub span: Span, } +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_must_be_unsafe)] +pub(crate) struct CVariadicMustBeUnsafe { + #[primary_span] + pub span: Span, + + #[suggestion( + ast_passes_suggestion, + applicability = "maybe-incorrect", + code = "unsafe ", + style = "verbose" + )] + pub unsafe_span: Span, +} + #[derive(Diagnostic)] #[diag(ast_passes_bad_c_variadic)] pub(crate) struct BadCVariadic { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index fe5421ab2c0ae..ff491322efc50 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -10,19 +10,19 @@ fn f1_2(...) {} //~^ ERROR `...` is not supported for non-extern functions extern "C" fn f2_1(x: isize, ...) {} -//~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~^ ERROR functions with a C variable argument list must be unsafe 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 +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f2_3(..., x: isize) {} //~^ 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 +//~^ ERROR functions with a C variable argument list must be unsafe 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 +//~^ ERROR functions with a C variable argument list must be unsafe extern "C" fn f3_3(..., x: isize) {} //~^ ERROR `...` must be the last argument of a C-variadic function @@ -33,12 +33,12 @@ const unsafe extern "C" fn f4_1(x: isize, ...) {} const extern "C" fn f4_2(x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~| ERROR functions with a C variable argument list must be unsafe //~| ERROR destructor of `VaListImpl<'_>` cannot be evaluated at compile-time const extern "C" fn f4_3(..., x: isize, ...) {} //~^ ERROR functions cannot be both `const` and C-variadic -//~| ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +//~| ERROR functions with a C variable argument list must be unsafe //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 0abe152640530..98f1d79f8f4e3 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -10,17 +10,27 @@ error: `...` is not supported for non-extern functions LL | fn f1_2(...) {} | ^^^ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f2_1(x: isize, ...) {} + | ++++++ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 | LL | extern "C" fn f2_2(...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f2_2(...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 @@ -28,17 +38,27 @@ error: `...` must be the last argument of a C-variadic function 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 +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:21:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe 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 +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:24:20 | LL | extern "C" fn f3_2(...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | unsafe extern "C" fn f3_2(...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 @@ -58,11 +78,16 @@ error: functions cannot be both `const` and C-variadic 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 +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | const unsafe extern "C" fn f4_2(x: isize, ...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:39:26 @@ -76,11 +101,16 @@ error: functions cannot be both `const` and C-variadic LL | const extern "C" fn f4_3(..., 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 +error: functions with a C variable argument list must be unsafe --> $DIR/variadic-ffi-semantic-restrictions.rs:39:41 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ + | +help: add the `unsafe` keyword to this definition + | +LL | const unsafe extern "C" fn f4_3(..., x: isize, ...) {} + | ++++++ error: `...` must be the last argument of a C-variadic function --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 From 9196844f0d86242ee3d6a446fe681b6b2d2d674c Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 8 Sep 2025 19:27:07 +0200 Subject: [PATCH 8/8] c-variadic: reject functions with unsupported extern ABI --- compiler/rustc_ast/src/ast.rs | 5 ++ compiler/rustc_ast_passes/messages.ftl | 7 +- .../rustc_ast_passes/src/ast_validation.rs | 7 +- compiler/rustc_ast_passes/src/errors.rs | 9 ++- tests/ui/c-variadic/issue-86053-1.stderr | 2 + .../cmse-nonsecure-entry/generics.rs | 2 +- .../cmse-nonsecure-entry/generics.stderr | 8 +- .../variadic-ffi-semantic-restrictions.rs | 3 + .../variadic-ffi-semantic-restrictions.stderr | 76 +++++++++++-------- 9 files changed, 79 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 915bae9e5f5aa..3e8fddd9954e2 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2325,6 +2325,11 @@ impl FnSig { } } } + + /// The span of the header's extern, or where to insert it if empty. + pub fn extern_span(&self) -> Span { + self.header.ext.span().unwrap_or(self.safety_span().shrink_to_hi()) + } } /// A constraint on an associated item. diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 1aa38a1beda3a..8dcf3e3aa3882 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -57,8 +57,6 @@ ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetim .label = {ast_passes_auto_super_lifetime} .suggestion = remove the super traits or lifetime bounds -ast_passes_bad_c_variadic = defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention - ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block .cannot_have = cannot have a body .invalid = the invalid body @@ -68,11 +66,16 @@ ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect ast_passes_c_variadic_associated_function = associated functions cannot have a C variable argument list +ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions + .label = `extern "{$abi}"` because of this + .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + ast_passes_c_variadic_must_be_unsafe = functions with a C variable argument list must be unsafe .suggestion = add the `unsafe` keyword to this definition ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions + .help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic .const = `const` because of this diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 7b32778ddf042..a6ef89b553db4 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -698,7 +698,6 @@ impl<'a> AstValidator<'a> { FnCtxt::Foreign => return, FnCtxt::Free => match sig.header.ext { Extern::Implicit(_) => { - // Implicitly defaults to C. if !matches!(sig.header.safety, Safety::Unsafe(_)) { self.dcx().emit_err(errors::CVariadicMustBeUnsafe { span: variadic_param.span, @@ -708,7 +707,11 @@ impl<'a> AstValidator<'a> { } Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::BadCVariadic { span: variadic_param.span }); + self.dcx().emit_err(errors::CVariadicBadExtern { + span: variadic_param.span, + abi: symbol_unescaped, + extern_span: sig.extern_span(), + }); } if !matches!(sig.header.safety, Safety::Unsafe(_)) { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 946a22d2e759e..ae805042c549f 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -327,6 +327,7 @@ pub(crate) struct CVariadicAssociatedFunction { #[derive(Diagnostic)] #[diag(ast_passes_c_variadic_no_extern)] +#[help] pub(crate) struct CVariadicNoExtern { #[primary_span] pub span: Span, @@ -348,10 +349,14 @@ pub(crate) struct CVariadicMustBeUnsafe { } #[derive(Diagnostic)] -#[diag(ast_passes_bad_c_variadic)] -pub(crate) struct BadCVariadic { +#[diag(ast_passes_c_variadic_bad_extern)] +#[help] +pub(crate) struct CVariadicBadExtern { #[primary_span] pub span: Span, + pub abi: Symbol, + #[label] + pub extern_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 eb08370b4f800..adbc04d3a65a4 100644 --- a/tests/ui/c-variadic/issue-86053-1.stderr +++ b/tests/ui/c-variadic/issue-86053-1.stderr @@ -51,6 +51,8 @@ error: `...` is not supported for non-extern functions | LL | self , ... , self , self , ... ) where F : FnOnce ( & 'a & 'b usize ) { | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0412]: cannot find type `F` in this scope --> $DIR/issue-86053-1.rs:11:48 diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs index 4ebc2bbf76129..eb6f4a323655b 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.rs @@ -67,6 +67,6 @@ extern "cmse-nonsecure-entry" fn wrapped_trait_object(x: WrapperTransparent) -> } unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - //~^ ERROR defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention + //~^ ERROR `...` is not supported for `extern "cmse-nonsecure-entry"` functions //~| ERROR requires `va_list` lang_item } diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr index 55921e764ba21..8937def9428cf 100644 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr +++ b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/generics.stderr @@ -1,8 +1,12 @@ -error: defining functions with C-variadic arguments is only allowed for free functions with the "C" or "C-unwind" calling convention +error: `...` is not supported for `extern "cmse-nonsecure-entry"` functions --> $DIR/generics.rs:69:60 | LL | unsafe extern "cmse-nonsecure-entry" fn c_variadic(_: u32, _: ...) { - | ^^^^^^ + | ----------------------------- ^^^^^^ + | | + | `extern "cmse-nonsecure-entry"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error[E0798]: functions with the `"cmse-nonsecure-entry"` ABI cannot contain generics in their type --> $DIR/generics.rs:30:1 diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs index ff491322efc50..4db056f15a51f 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.rs +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.rs @@ -9,6 +9,9 @@ fn f1_1(x: isize, ...) {} fn f1_2(...) {} //~^ ERROR `...` is not supported for non-extern functions +unsafe extern "Rust" fn f1_3(...) {} +//~^ ERROR `...` is not supported for `extern "Rust"` functions + extern "C" fn f2_1(x: isize, ...) {} //~^ ERROR functions with a C variable argument list must be unsafe diff --git a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr index 98f1d79f8f4e3..0cd78318de6a8 100644 --- a/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr +++ b/tests/ui/parser/variadic-ffi-semantic-restrictions.stderr @@ -3,16 +3,30 @@ error: `...` is not supported for non-extern functions | LL | fn f1_1(x: isize, ...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list error: `...` is not supported for non-extern functions --> $DIR/variadic-ffi-semantic-restrictions.rs:9:9 | LL | fn f1_2(...) {} | ^^^ + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list -error: functions with a C variable argument list must be unsafe +error: `...` is not supported for `extern "Rust"` functions --> $DIR/variadic-ffi-semantic-restrictions.rs:12:30 | +LL | unsafe extern "Rust" fn f1_3(...) {} + | ------------- ^^^ + | | + | `extern "Rust"` because of this + | + = help: only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list + +error: functions with a C variable argument list must be unsafe + --> $DIR/variadic-ffi-semantic-restrictions.rs:15:30 + | LL | extern "C" fn f2_1(x: isize, ...) {} | ^^^ | @@ -22,7 +36,7 @@ LL | unsafe extern "C" fn f2_1(x: isize, ...) {} | ++++++ error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:15:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 | LL | extern "C" fn f2_2(...) {} | ^^^ @@ -33,13 +47,13 @@ LL | unsafe extern "C" fn f2_2(...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:18:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:21:20 | LL | extern "C" fn f2_3(..., x: isize) {} | ^^^ error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:21:30 + --> $DIR/variadic-ffi-semantic-restrictions.rs:24:30 | LL | extern "C" fn f3_1(x: isize, ...) {} | ^^^ @@ -50,7 +64,7 @@ LL | unsafe extern "C" fn f3_1(x: isize, ...) {} | ++++++ error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:24:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 | LL | extern "C" fn f3_2(...) {} | ^^^ @@ -61,25 +75,25 @@ LL | unsafe extern "C" fn f3_2(...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:27:20 + --> $DIR/variadic-ffi-semantic-restrictions.rs:30:20 | LL | extern "C" fn f3_3(..., x: isize) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:33: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:34:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:1 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:34:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ @@ -90,19 +104,19 @@ LL | const unsafe extern "C" fn f4_2(x: isize, ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:26 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42: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:39:1 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:1 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^^^ `const` because of this ^^^ C-variadic because of this error: functions with a C variable argument list must be unsafe - --> $DIR/variadic-ffi-semantic-restrictions.rs:39:41 + --> $DIR/variadic-ffi-semantic-restrictions.rs:42:41 | LL | const extern "C" fn f4_3(..., x: isize, ...) {} | ^^^ @@ -113,49 +127,49 @@ LL | const unsafe extern "C" fn f4_3(..., x: isize, ...) {} | ++++++ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:45:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:48:13 | LL | fn e_f2(..., x: isize); | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:52:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:55:23 | LL | fn i_f1(x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:54:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:57:13 | LL | fn i_f2(...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:56:28 + --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 | LL | fn i_f3(..., x: isize, ...) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:13 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:59:28 + --> $DIR/variadic-ffi-semantic-restrictions.rs:62:28 | LL | fn i_f4(..., x: isize, ...) {} | ^^^ error: functions cannot be both `const` and C-variadic - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:5 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:5 | LL | const fn i_f5(x: isize, ...) {} | ^^^^^ ^^^ C-variadic because of this @@ -163,49 +177,49 @@ LL | const fn i_f5(x: isize, ...) {} | `const` because of this error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:62:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65:29 | LL | const fn i_f5(x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:69:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:72:23 | LL | fn t_f1(x: isize, ...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:71:23 + --> $DIR/variadic-ffi-semantic-restrictions.rs:74:23 | LL | fn t_f2(x: isize, ...); | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:73:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:76:13 | LL | fn t_f3(...) {} | ^^^ error: associated functions cannot have a C variable argument list - --> $DIR/variadic-ffi-semantic-restrictions.rs:75:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:78:13 | LL | fn t_f4(...); | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:77:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:80:13 | LL | fn t_f5(..., x: isize) {} | ^^^ error: `...` must be the last argument of a C-variadic function - --> $DIR/variadic-ffi-semantic-restrictions.rs:79:13 + --> $DIR/variadic-ffi-semantic-restrictions.rs:82:13 | LL | fn t_f6(..., x: isize); | ^^^ error[E0493]: destructor of `VaListImpl<'_>` cannot be evaluated at compile-time - --> $DIR/variadic-ffi-semantic-restrictions.rs:30:43 + --> $DIR/variadic-ffi-semantic-restrictions.rs:33:43 | LL | const unsafe extern "C" fn f4_1(x: isize, ...) {} | ^^^ - value is dropped here @@ -213,7 +227,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:34:36 + --> $DIR/variadic-ffi-semantic-restrictions.rs:37:36 | LL | const extern "C" fn f4_2(x: isize, ...) {} | ^^^ - value is dropped here @@ -221,13 +235,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:62:29 + --> $DIR/variadic-ffi-semantic-restrictions.rs:65: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 32 previous errors +error: aborting due to 33 previous errors For more information about this error, try `rustc --explain E0493`.