From 568c6ed8c97e398aff6d17c3fe6492dfc1256d33 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 9 Nov 2025 13:26:40 +0100 Subject: [PATCH 1/3] propagate function attributes in ast visitor --- compiler/rustc_ast/src/visit.rs | 17 +++++++++++------ compiler/rustc_ast_passes/src/ast_validation.rs | 6 +++--- compiler/rustc_ast_passes/src/feature_gate.rs | 5 ++--- compiler/rustc_lint/src/early.rs | 4 ++-- compiler/rustc_passes/src/input_stats.rs | 4 ++-- .../rustc_resolve/src/build_reduced_graph.rs | 2 +- compiler/rustc_resolve/src/def_collector.rs | 2 +- compiler/rustc_resolve/src/late.rs | 2 +- 8 files changed, 23 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 4ae193a42ff28..094875f38d741 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -645,8 +645,9 @@ macro_rules! common_visitor_and_walkers { fn visit_fn( &mut self, fk: FnKind<$($lt)? $(${ignore($mut)} '_)?>, + _: &AttrVec, _: Span, - _: NodeId + _: NodeId, ) -> Self::Result { walk_fn(self, fk) } @@ -740,6 +741,7 @@ macro_rules! common_visitor_and_walkers { type Ctxt; fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -773,7 +775,7 @@ macro_rules! common_visitor_and_walkers { ) -> V::Result { let Item { attrs, id, kind, vis, span, tokens: _ } = item; visit_visitable!($($mut)? visitor, id, attrs, vis); - try_visit!(kind.walk(*span, *id, vis, ctxt, visitor)); + try_visit!(kind.walk(attrs, *span, *id, vis, ctxt, visitor)); visit_visitable!($($mut)? visitor, span); V::Result::output() } @@ -799,6 +801,7 @@ macro_rules! common_visitor_and_walkers { type Ctxt = (); fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -808,7 +811,7 @@ macro_rules! common_visitor_and_walkers { match self { ItemKind::Fn(func) => { let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func); - try_visit!(vis.visit_fn(kind, span, id)); + try_visit!(vis.visit_fn(kind, attrs, span, id)); } ItemKind::ExternCrate(orig_name, ident) => visit_visitable!($($mut)? vis, orig_name, ident), @@ -856,6 +859,7 @@ macro_rules! common_visitor_and_walkers { type Ctxt = AssocCtxt; fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -867,7 +871,7 @@ macro_rules! common_visitor_and_walkers { visit_visitable!($($mut)? vis, item), AssocItemKind::Fn(func) => { let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func); - try_visit!(vis.visit_fn(kind, span, id)) + try_visit!(vis.visit_fn(kind, attrs, span, id)) } AssocItemKind::Type(alias) => visit_visitable!($($mut)? vis, alias), @@ -886,6 +890,7 @@ macro_rules! common_visitor_and_walkers { type Ctxt = (); fn walk<$($lt,)? V: $Visitor$(<$lt>)?>( &$($lt)? $($mut)? self, + attrs: &AttrVec, span: Span, id: NodeId, visibility: &$($lt)? $($mut)? Visibility, @@ -897,7 +902,7 @@ macro_rules! common_visitor_and_walkers { visit_visitable!($($mut)? vis, item), ForeignItemKind::Fn(func) => { let kind = FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func); - try_visit!(vis.visit_fn(kind, span, id)) + try_visit!(vis.visit_fn(kind, attrs, span, id)) } ForeignItemKind::TyAlias(alias) => visit_visitable!($($mut)? vis, alias), @@ -999,7 +1004,7 @@ macro_rules! common_visitor_and_walkers { }) => { visit_visitable!($($mut)? vis, constness, movability, capture_clause); let kind = FnKind::Closure(binder, coroutine_kind, fn_decl, body); - try_visit!(vis.visit_fn(kind, *span, *id)); + try_visit!(vis.visit_fn(kind, attrs, *span, *id)); visit_visitable!($($mut)? vis, fn_decl_span, fn_arg_span); } ExprKind::Block(block, opt_label) => diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 7a79accc66c2e..e7d066ea97cb1 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1104,7 +1104,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func); - self.visit_fn(kind, item.span, item.id); + self.visit_fn(kind, &item.attrs, item.span, item.id); } ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => { let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span)); @@ -1478,7 +1478,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_param_bound(self, bound) } - fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) { + fn visit_fn(&mut self, fk: FnKind<'a>, _attrs: &AttrVec, span: Span, id: NodeId) { // Only associated `fn`s can have `self` parameters. let self_semantic = match fk.ctxt() { Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes, @@ -1648,7 +1648,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { { self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident); let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func); - self.visit_fn(kind, item.span, item.id); + self.visit_fn(kind, &item.attrs, item.span, item.id); } AssocItemKind::Type(_) => { let disallowed = (!parent_is_const).then(|| match self.outer_trait_or_trait_impl { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index b8a29a9a08f43..2d87d8c84d7c9 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -1,6 +1,5 @@ -use rustc_ast as ast; use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor}; -use rustc_ast::{NodeId, PatKind, attr, token}; +use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token}; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_warn}; @@ -392,7 +391,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_poly_trait_ref(self, t); } - fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { + fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) { if let Some(_header) = fn_kind.header() { // Stability of const fn methods are covered in `visit_assoc_item` below. } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index dff1fc4367021..6cb5aea798407 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -5,7 +5,7 @@ //! syntactical lints. use rustc_ast::visit::{self as ast_visit, Visitor, walk_list}; -use rustc_ast::{self as ast, HasAttrs}; +use rustc_ast::{self as ast, AttrVec, HasAttrs}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer}; use rustc_feature::Features; @@ -135,7 +135,7 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast> }); } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) { + fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, _: &AttrVec, span: Span, id: ast::NodeId) { lint_callback!(self, check_fn, fk, span, id); ast_visit::walk_fn(self, fk); } diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index c83610da1aad2..fe50b730256b7 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -3,7 +3,7 @@ // completely accurate (some things might be counted twice, others missed). use rustc_ast::visit::BoundKind; -use rustc_ast::{self as ast, NodeId, visit as ast_visit}; +use rustc_ast::{self as ast, AttrVec, NodeId, visit as ast_visit}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::thousands::usize_with_underscores; use rustc_hir::{self as hir, AmbigArg, HirId, intravisit as hir_visit}; @@ -709,7 +709,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ast_visit::walk_where_predicate(self, p) } - fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) { + fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: &AttrVec, _: Span, _: NodeId) { self.record("FnDecl", None, fk.decl()); ast_visit::walk_fn(self, fk) } diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 886ffcffbb4d9..cd12d5ad10cf7 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1358,7 +1358,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // Visit attributes after items for backward compatibility. // This way they can use `macro_rules` defined later. self.visit_vis(&item.vis); - item.kind.walk(item.span, item.id, &item.vis, (), self); + item.kind.walk(&item.attrs, item.span, item.id, &item.vis, (), self); visit::walk_list!(self, visit_attribute, &item.attrs); } _ => visit::walk_item(self, item), diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 14538df8187d8..ea64a1e6c64dd 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -193,7 +193,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { }); } - fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { + fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) { match fn_kind { FnKind::Fn( _ctxt, diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 16cbfe3b3c743..107803c5c2651 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1034,7 +1034,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc } } } - fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, fn_id: NodeId) { + fn visit_fn(&mut self, fn_kind: FnKind<'ast>, _: &AttrVec, sp: Span, fn_id: NodeId) { let previous_value = self.diag_metadata.current_function; match fn_kind { // Bail if the function is foreign, and thus cannot validly have From ebd173f5129935007ab1a7d1273a10ef40da4a84 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 9 Nov 2025 14:22:45 +0100 Subject: [PATCH 2/3] add `feature(c_variadic_naked_functions)` --- compiler/rustc_ast_passes/messages.ftl | 4 + .../rustc_ast_passes/src/ast_validation.rs | 111 +++++++++++++----- compiler/rustc_ast_passes/src/errors.rs | 13 +- compiler/rustc_feature/src/unstable.rs | 3 + compiler/rustc_span/src/symbol.rs | 1 + tests/ui/c-variadic/naked-invalid.rs | 44 +++++++ tests/ui/c-variadic/naked-invalid.stderr | 31 +++++ tests/ui/c-variadic/naked.rs | 19 ++- ...feature-gate-c_variadic-naked-functions.rs | 27 +++++ ...ure-gate-c_variadic-naked-functions.stderr | 13 ++ 10 files changed, 234 insertions(+), 32 deletions(-) create mode 100644 tests/ui/c-variadic/naked-invalid.rs create mode 100644 tests/ui/c-variadic/naked-invalid.stderr create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.rs create mode 100644 tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.stderr diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 5e10c5a77d97d..e3189b9275c0c 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -68,6 +68,10 @@ ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` .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_bad_naked_extern = `...` is not supported for `extern "{$abi}"` naked functions + .label = `extern "{$abi}"` because of this + .help = C-variadic function must have a compatible calling convention + 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 diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index e7d066ea97cb1..cfa8fd39fdeed 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -21,7 +21,7 @@ use std::ops::{Deref, DerefMut}; use std::str::FromStr; use itertools::{Either, Itertools}; -use rustc_abi::{CanonAbi, ExternAbi, InterruptKind}; +use rustc_abi::{CVariadicStatus, CanonAbi, ExternAbi, InterruptKind}; use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list}; use rustc_ast::*; use rustc_ast_pretty::pprust::{self, State}; @@ -35,6 +35,7 @@ use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, }; +use rustc_session::parse::feature_err; use rustc_span::{Ident, Span, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use thin_vec::thin_vec; @@ -659,7 +660,7 @@ impl<'a> AstValidator<'a> { /// C-variadics must be: /// - Non-const /// - Either foreign, or free and `unsafe extern "C"` semantically - fn check_c_variadic_type(&self, fk: FnKind<'a>) { + fn check_c_variadic_type(&self, fk: FnKind<'a>, attrs: &'a AttrVec) { // `...` 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, @@ -691,36 +692,92 @@ impl<'a> AstValidator<'a> { match fn_ctxt { FnCtxt::Foreign => return, - FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext { - Extern::Implicit(_) => { - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); + FnCtxt::Free | FnCtxt::Assoc(_) => { + match sig.header.ext { + Extern::Implicit(_) => { + if !matches!(sig.header.safety, Safety::Unsafe(_)) { + self.dcx().emit_err(errors::CVariadicMustBeUnsafe { + span: variadic_param.span, + unsafe_span: sig.safety_span(), + }); + } + } + Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { + // Just bail if the ABI is not even recognized. + let Ok(abi) = ExternAbi::from_str(symbol_unescaped.as_str()) else { + return; + }; + + self.check_c_variadic_abi(abi, attrs, variadic_param.span, sig); + + 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 }; + self.dcx().emit_err(err); } } - Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => { - if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) { - self.dcx().emit_err(errors::CVariadicBadExtern { - span: variadic_param.span, - abi: symbol_unescaped, - extern_span: sig.extern_span(), - }); + } + } + } + + fn check_c_variadic_abi( + &self, + abi: ExternAbi, + attrs: &'a AttrVec, + dotdotdot_span: Span, + sig: &FnSig, + ) { + // For naked functions we accept any ABI that is accepted on c-variadic + // foreign functions, if the c_variadic_naked_functions feature is enabled. + if attr::contains_name(attrs, sym::naked) { + match abi.supports_c_variadic() { + CVariadicStatus::Stable if let ExternAbi::C { .. } = abi => { + // With `c_variadic` naked c-variadic `extern "C"` functions are allowed. + } + CVariadicStatus::Stable => { + // For e.g. aapcs or sysv64 `c_variadic_naked_functions` must also be enabled. + if !self.features.enabled(sym::c_variadic_naked_functions) { + let msg = format!("Naked c-variadic `extern {abi}` functions are unstable"); + feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg) + .emit(); + } + } + CVariadicStatus::Unstable { feature } => { + // Some ABIs need additional features. + if !self.features.enabled(sym::c_variadic_naked_functions) { + let msg = format!("Naked c-variadic `extern {abi}` functions are unstable"); + feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg) + .emit(); } - if !matches!(sig.header.safety, Safety::Unsafe(_)) { - self.dcx().emit_err(errors::CVariadicMustBeUnsafe { - span: variadic_param.span, - unsafe_span: sig.safety_span(), - }); + if !self.features.enabled(feature) { + let msg = format!( + "C-variadic functions with the {abi} calling convention are unstable" + ); + feature_err(&self.sess, feature, sig.span, msg).emit(); } } - Extern::None => { - let err = errors::CVariadicNoExtern { span: variadic_param.span }; - self.dcx().emit_err(err); + CVariadicStatus::NotSupported => { + // Some ABIs, e.g. `extern "Rust"`, never support c-variadic functions. + self.dcx().emit_err(errors::CVariadicBadNakedExtern { + span: dotdotdot_span, + abi: abi.as_str(), + extern_span: sig.extern_span(), + }); } - }, + } + } else if !matches!(abi, ExternAbi::C { .. }) { + self.dcx().emit_err(errors::CVariadicBadExtern { + span: dotdotdot_span, + abi: abi.as_str(), + extern_span: sig.extern_span(), + }); } } @@ -1478,7 +1535,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_param_bound(self, bound) } - fn visit_fn(&mut self, fk: FnKind<'a>, _attrs: &AttrVec, span: Span, id: NodeId) { + fn visit_fn(&mut self, fk: FnKind<'a>, attrs: &AttrVec, span: Span, id: NodeId) { // Only associated `fn`s can have `self` parameters. let self_semantic = match fk.ctxt() { Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes, @@ -1497,7 +1554,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig); } - self.check_c_variadic_type(fk); + self.check_c_variadic_type(fk, attrs); // Functions cannot both be `const async` or `const gen` if let Some(&FnHeader { diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index fd75e999d1381..9329b915ce72b 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -347,7 +347,18 @@ pub(crate) struct CVariadicMustBeUnsafe { pub(crate) struct CVariadicBadExtern { #[primary_span] pub span: Span, - pub abi: Symbol, + pub abi: &'static str, + #[label] + pub extern_span: Span, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_c_variadic_bad_naked_extern)] +#[help] +pub(crate) struct CVariadicBadNakedExtern { + #[primary_span] + pub span: Span, + pub abi: &'static str, #[label] pub extern_span: Span, } diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8397cd294e0a9..f51bc7031c31d 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -410,6 +410,9 @@ declare_features! ( (unstable, avx10_target_feature, "1.88.0", Some(138843)), /// Allows using C-variadics. (unstable, c_variadic, "1.34.0", Some(44930)), + /// Allows defining c-variadic naked functions with any extern ABI that is allowed + /// on c-variadic foreign functions. + (unstable, c_variadic_naked_functions, "CURRENT_RUSTC_VERSION", Some(148767)), /// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled. (unstable, cfg_contract_checks, "1.86.0", Some(128044)), /// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 94cacf8b40e22..9d121513246ed 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -611,6 +611,7 @@ symbols! { c_str_literals, c_unwind, c_variadic, + c_variadic_naked_functions, c_void, call, call_mut, diff --git a/tests/ui/c-variadic/naked-invalid.rs b/tests/ui/c-variadic/naked-invalid.rs new file mode 100644 index 0000000000000..2cc4aaafbfcc0 --- /dev/null +++ b/tests/ui/c-variadic/naked-invalid.rs @@ -0,0 +1,44 @@ +//@ add-minicore +//@ compile-flags: --target x86_64-unknown-linux-gnu +//@ needs-llvm-components: x86 +//@ ignore-backends: gcc + +#![feature(no_core, lang_items, rustc_attrs)] +#![feature(c_variadic, c_variadic_naked_functions, abi_x86_interrupt, naked_functions_rustic_abi)] +#![crate_type = "rlib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[repr(C)] +#[lang = "va_list"] +pub struct VaList; + +#[unsafe(naked)] +unsafe extern "sysv64" fn c_variadic_sysv64(_: ...) { + naked_asm!("ret") +} + +#[unsafe(naked)] +unsafe extern "C" fn c_variadic_c(_: ...) { + naked_asm!("ret") +} + +#[unsafe(naked)] +unsafe extern "Rust" fn c_variadic_rust(_: ...) { + //~^ ERROR `...` is not supported for `extern "Rust"` naked functions + naked_asm!("ret") +} + +#[unsafe(naked)] +unsafe extern "x86-interrupt" fn c_variadic_x86_interrupt(_: ...) { + //~^ ERROR `...` is not supported for `extern "x86-interrupt"` naked functions + naked_asm!("ret") +} + +#[unsafe(naked)] +unsafe extern "nonsense" fn c_variadic_x86_nonsense(_: ...) { + //~^ ERROR invalid ABI: found `nonsense` + naked_asm!("ret") +} diff --git a/tests/ui/c-variadic/naked-invalid.stderr b/tests/ui/c-variadic/naked-invalid.stderr new file mode 100644 index 0000000000000..a1fc8c1664272 --- /dev/null +++ b/tests/ui/c-variadic/naked-invalid.stderr @@ -0,0 +1,31 @@ +error: `...` is not supported for `extern "Rust"` naked functions + --> $DIR/naked-invalid.rs:29:41 + | +LL | unsafe extern "Rust" fn c_variadic_rust(_: ...) { + | ------------- ^^^^^^ + | | + | `extern "Rust"` because of this + | + = help: C-variadic function must have a compatible calling convention + +error: `...` is not supported for `extern "x86-interrupt"` naked functions + --> $DIR/naked-invalid.rs:35:59 + | +LL | unsafe extern "x86-interrupt" fn c_variadic_x86_interrupt(_: ...) { + | ---------------------- ^^^^^^ + | | + | `extern "x86-interrupt"` because of this + | + = help: C-variadic function must have a compatible calling convention + +error[E0703]: invalid ABI: found `nonsense` + --> $DIR/naked-invalid.rs:41:15 + | +LL | unsafe extern "nonsense" fn c_variadic_x86_nonsense(_: ...) { + | ^^^^^^^^^^ invalid ABI + | + = note: invoke `rustc --print=calling-conventions` for a full list of supported calling conventions + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0703`. diff --git a/tests/ui/c-variadic/naked.rs b/tests/ui/c-variadic/naked.rs index 46b59395485c5..73664206d9ced 100644 --- a/tests/ui/c-variadic/naked.rs +++ b/tests/ui/c-variadic/naked.rs @@ -1,14 +1,14 @@ //@ run-pass //@ only-x86_64 //@ only-linux -#![feature(c_variadic)] +#![feature(c_variadic, c_variadic_naked_functions)] #[repr(C)] #[derive(Debug, PartialEq)] struct Data(i32, f64); #[unsafe(naked)] -unsafe extern "C" fn c_variadic(_: ...) -> Data { +unsafe extern "sysv64" fn c_variadic_sysv64(_: ...) -> Data { // This assembly was generated with GCC, because clang/LLVM is unable to // optimize out the spilling of all registers to the stack. core::arch::naked_asm!( @@ -32,9 +32,20 @@ unsafe extern "C" fn c_variadic(_: ...) -> Data { ) } +#[unsafe(naked)] +unsafe extern "C" fn c_variadic_c(_: ...) -> Data { + core::arch::naked_asm!( + "jmp {}", + sym c_variadic_sysv64, + ) +} + fn main() { unsafe { - assert_eq!(c_variadic(1, 2.0), Data(1, 2.0)); - assert_eq!(c_variadic(123, 4.56), Data(123, 4.56)); + assert_eq!(c_variadic_sysv64(1, 2.0), Data(1, 2.0)); + assert_eq!(c_variadic_sysv64(123, 4.56), Data(123, 4.56)); + + assert_eq!(c_variadic_c(1, 2.0), Data(1, 2.0)); + assert_eq!(c_variadic_c(123, 4.56), Data(123, 4.56)); } } diff --git a/tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.rs b/tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.rs new file mode 100644 index 0000000000000..bf52c4d0cf526 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.rs @@ -0,0 +1,27 @@ +//@ add-minicore +//@ compile-flags: --target x86_64-unknown-linux-gnu +//@ needs-llvm-components: x86 +//@ ignore-backends: gcc + +#![feature(no_core, lang_items, rustc_attrs)] +#![feature(c_variadic, abi_x86_interrupt, naked_functions_rustic_abi)] +#![crate_type = "rlib"] +#![no_core] + +extern crate minicore; +use minicore::*; + +#[repr(C)] +#[lang = "va_list"] +pub struct VaList; + +#[unsafe(naked)] +unsafe extern "sysv64" fn c_variadic_sysv64(_: ...) { + //~^ ERROR Naked c-variadic `extern "sysv64"` functions are unstable + naked_asm!("ret") +} + +#[unsafe(naked)] +unsafe extern "C" fn c_variadic_c(_: ...) { + naked_asm!("ret") +} diff --git a/tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.stderr b/tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.stderr new file mode 100644 index 0000000000000..fc44747a4fadb --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-c_variadic-naked-functions.stderr @@ -0,0 +1,13 @@ +error[E0658]: Naked c-variadic `extern "sysv64"` functions are unstable + --> $DIR/feature-gate-c_variadic-naked-functions.rs:19:1 + | +LL | unsafe extern "sysv64" fn c_variadic_sysv64(_: ...) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #148767 for more information + = help: add `#![feature(c_variadic_naked_functions)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. From 9129b902d07ba4fa7d77016bae4166a6cdad1c9d Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 9 Nov 2025 14:34:41 +0100 Subject: [PATCH 3/3] use `feature(c_variadic_naked_functions)` in tests --- .../same-program-multiple-abis-arm.rs | 28 ++++++++--------- .../same-program-multiple-abis-x86_64.rs | 30 +++++++++---------- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs index 1445bbb47bbd3..fbc5573c5d76c 100644 --- a/tests/ui/c-variadic/same-program-multiple-abis-arm.rs +++ b/tests/ui/c-variadic/same-program-multiple-abis-arm.rs @@ -2,6 +2,7 @@ //@ only-arm //@ ignore-thumb (this test uses arm assembly) //@ only-eabihf (the assembly below requires float hardware support) +#![feature(c_variadic, c_variadic_naked_functions)] // Check that multiple c-variadic calling conventions can be used in the same program. // @@ -9,14 +10,6 @@ // argument list, so C programs that use multiple c-variadic calling conventions are unlikely // to come up. Here we validate that our codegen backends do in fact generate correct code. -extern "C" { - fn variadic_c(_: f64, _: ...) -> f64; -} - -extern "aapcs" { - fn variadic_aapcs(_: f64, _: ...) -> f64; -} - fn main() { unsafe { assert_eq!(variadic_c(1.0, 2.0, 3.0), 1.0 + 2.0 + 3.0); @@ -51,10 +44,11 @@ fn main() { // // > This section applies only to non-variadic functions. For a variadic function the base standard // > is always used both for argument passing and result return. -core::arch::global_asm!( - r#" -{variadic_c}: -{variadic_aapcs}: + +#[unsafe(naked)] +unsafe extern "aapcs" fn variadic_aapcs(_: f64, _: ...) -> f64 { + core::arch::naked_asm!( + r#" sub sp, sp, #12 stmib sp, {{r2, r3}} vmov d0, r0, r1 @@ -71,6 +65,10 @@ core::arch::global_asm!( add sp, sp, #12 bx lr "#, - variadic_c = sym variadic_c, - variadic_aapcs = sym variadic_aapcs, -); + ) +} + +#[unsafe(naked)] +unsafe extern "C" fn variadic_c(_: f64, _: ...) -> f64 { + core::arch::naked_asm!("b {}", sym variadic_aapcs) +} diff --git a/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs index aff8ab613c431..b54b63289f343 100644 --- a/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs +++ b/tests/ui/c-variadic/same-program-multiple-abis-x86_64.rs @@ -1,5 +1,6 @@ //@ run-pass //@ only-x86_64 +#![feature(c_variadic, c_variadic_naked_functions)] // Check that multiple c-variadic calling conventions can be used in the same program. // @@ -7,14 +8,6 @@ // argument list, so C programs that use multiple c-variadic calling conventions are unlikely // to come up. Here we validate that our codegen backends do in fact generate correct code. -extern "sysv64" { - fn variadic_sysv64(_: u32, _: ...) -> u32; -} - -extern "win64" { - fn variadic_win64(_: u32, _: ...) -> u32; -} - fn main() { unsafe { assert_eq!(variadic_win64(1, 2, 3), 1 + 2 + 3); @@ -37,9 +30,11 @@ fn main() { // a + b + c // } // ``` -core::arch::global_asm!( - r#" -{variadic_sysv64}: + +#[unsafe(naked)] +unsafe extern "sysv64" fn variadic_sysv64(_: u32, _: ...) -> u32 { + core::arch::naked_asm!( + r#" sub rsp, 88 test al, al je .LBB0_7 @@ -93,8 +88,14 @@ core::arch::global_asm!( add eax, dword ptr [rcx] add rsp, 88 ret + "#, + ) +} -{variadic_win64}: +#[unsafe(naked)] +unsafe extern "win64" fn variadic_win64(_: u32, _: ...) -> u32 { + core::arch::naked_asm!( + r#" push rax mov qword ptr [rsp + 40], r9 mov qword ptr [rsp + 24], rdx @@ -106,6 +107,5 @@ core::arch::global_asm!( pop rcx ret "#, - variadic_win64 = sym variadic_win64, - variadic_sysv64 = sym variadic_sysv64, -); + ) +}