Skip to content

Commit 60b2068

Browse files
authored
Rollup merge of #148770 - folkertdev:naked-c-variadic, r=workingjubilee
implement `feature(c_variadic_naked_functions)` tracking issue: #148767 [#t-lang > C-variadic naked functions](https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/C-variadic.20naked.20functions/with/554593886) This feature allows naked c-variadic function definitions with any ABI that is supported for foreign c-variadic functions. ```rust #![feature(c_variadic, c_variadic_naked_functions)] #[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 mov qword ptr [rsp + 32], r8 lea rax, [rsp + 40] mov qword ptr [rsp], rax lea eax, [rdx + rcx] add eax, r8d pop rcx ret "#, ) } ``` r? ````@workingjubilee````
2 parents ab8bdf7 + 9129b90 commit 60b2068

File tree

19 files changed

+284
-80
lines changed

19 files changed

+284
-80
lines changed

compiler/rustc_ast/src/visit.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -645,8 +645,9 @@ macro_rules! common_visitor_and_walkers {
645645
fn visit_fn(
646646
&mut self,
647647
fk: FnKind<$($lt)? $(${ignore($mut)} '_)?>,
648+
_: &AttrVec,
648649
_: Span,
649-
_: NodeId
650+
_: NodeId,
650651
) -> Self::Result {
651652
walk_fn(self, fk)
652653
}
@@ -740,6 +741,7 @@ macro_rules! common_visitor_and_walkers {
740741
type Ctxt;
741742
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
742743
&$($lt)? $($mut)? self,
744+
attrs: &AttrVec,
743745
span: Span,
744746
id: NodeId,
745747
visibility: &$($lt)? $($mut)? Visibility,
@@ -773,7 +775,7 @@ macro_rules! common_visitor_and_walkers {
773775
) -> V::Result {
774776
let Item { attrs, id, kind, vis, span, tokens: _ } = item;
775777
visit_visitable!($($mut)? visitor, id, attrs, vis);
776-
try_visit!(kind.walk(*span, *id, vis, ctxt, visitor));
778+
try_visit!(kind.walk(attrs, *span, *id, vis, ctxt, visitor));
777779
visit_visitable!($($mut)? visitor, span);
778780
V::Result::output()
779781
}
@@ -799,6 +801,7 @@ macro_rules! common_visitor_and_walkers {
799801
type Ctxt = ();
800802
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
801803
&$($lt)? $($mut)? self,
804+
attrs: &AttrVec,
802805
span: Span,
803806
id: NodeId,
804807
visibility: &$($lt)? $($mut)? Visibility,
@@ -808,7 +811,7 @@ macro_rules! common_visitor_and_walkers {
808811
match self {
809812
ItemKind::Fn(func) => {
810813
let kind = FnKind::Fn(FnCtxt::Free, visibility, &$($mut)? *func);
811-
try_visit!(vis.visit_fn(kind, span, id));
814+
try_visit!(vis.visit_fn(kind, attrs, span, id));
812815
}
813816
ItemKind::ExternCrate(orig_name, ident) =>
814817
visit_visitable!($($mut)? vis, orig_name, ident),
@@ -856,6 +859,7 @@ macro_rules! common_visitor_and_walkers {
856859
type Ctxt = AssocCtxt;
857860
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
858861
&$($lt)? $($mut)? self,
862+
attrs: &AttrVec,
859863
span: Span,
860864
id: NodeId,
861865
visibility: &$($lt)? $($mut)? Visibility,
@@ -867,7 +871,7 @@ macro_rules! common_visitor_and_walkers {
867871
visit_visitable!($($mut)? vis, item),
868872
AssocItemKind::Fn(func) => {
869873
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), visibility, &$($mut)? *func);
870-
try_visit!(vis.visit_fn(kind, span, id))
874+
try_visit!(vis.visit_fn(kind, attrs, span, id))
871875
}
872876
AssocItemKind::Type(alias) =>
873877
visit_visitable!($($mut)? vis, alias),
@@ -886,6 +890,7 @@ macro_rules! common_visitor_and_walkers {
886890
type Ctxt = ();
887891
fn walk<$($lt,)? V: $Visitor$(<$lt>)?>(
888892
&$($lt)? $($mut)? self,
893+
attrs: &AttrVec,
889894
span: Span,
890895
id: NodeId,
891896
visibility: &$($lt)? $($mut)? Visibility,
@@ -897,7 +902,7 @@ macro_rules! common_visitor_and_walkers {
897902
visit_visitable!($($mut)? vis, item),
898903
ForeignItemKind::Fn(func) => {
899904
let kind = FnKind::Fn(FnCtxt::Foreign, visibility, &$($mut)?*func);
900-
try_visit!(vis.visit_fn(kind, span, id))
905+
try_visit!(vis.visit_fn(kind, attrs, span, id))
901906
}
902907
ForeignItemKind::TyAlias(alias) =>
903908
visit_visitable!($($mut)? vis, alias),
@@ -999,7 +1004,7 @@ macro_rules! common_visitor_and_walkers {
9991004
}) => {
10001005
visit_visitable!($($mut)? vis, constness, movability, capture_clause);
10011006
let kind = FnKind::Closure(binder, coroutine_kind, fn_decl, body);
1002-
try_visit!(vis.visit_fn(kind, *span, *id));
1007+
try_visit!(vis.visit_fn(kind, attrs, *span, *id));
10031008
visit_visitable!($($mut)? vis, fn_decl_span, fn_arg_span);
10041009
}
10051010
ExprKind::Block(block, opt_label) =>

compiler/rustc_ast_passes/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"`
6868
.label = `extern "{$abi}"` because of this
6969
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
7070
71+
ast_passes_c_variadic_bad_naked_extern = `...` is not supported for `extern "{$abi}"` naked functions
72+
.label = `extern "{$abi}"` because of this
73+
.help = C-variadic function must have a compatible calling convention
74+
7175
ast_passes_c_variadic_must_be_unsafe =
7276
functions with a C variable argument list must be unsafe
7377
.suggestion = add the `unsafe` keyword to this definition

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 86 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::ops::{Deref, DerefMut};
2121
use std::str::FromStr;
2222

2323
use itertools::{Either, Itertools};
24-
use rustc_abi::{CanonAbi, ExternAbi, InterruptKind};
24+
use rustc_abi::{CVariadicStatus, CanonAbi, ExternAbi, InterruptKind};
2525
use rustc_ast::visit::{AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor, walk_list};
2626
use rustc_ast::*;
2727
use rustc_ast_pretty::pprust::{self, State};
@@ -35,6 +35,7 @@ use rustc_session::lint::builtin::{
3535
DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
3636
PATTERNS_IN_FNS_WITHOUT_BODY,
3737
};
38+
use rustc_session::parse::feature_err;
3839
use rustc_span::{Ident, Span, kw, sym};
3940
use rustc_target::spec::{AbiMap, AbiMapping};
4041
use thin_vec::thin_vec;
@@ -661,7 +662,7 @@ impl<'a> AstValidator<'a> {
661662
/// C-variadics must be:
662663
/// - Non-const
663664
/// - Either foreign, or free and `unsafe extern "C"` semantically
664-
fn check_c_variadic_type(&self, fk: FnKind<'a>) {
665+
fn check_c_variadic_type(&self, fk: FnKind<'a>, attrs: &'a AttrVec) {
665666
// `...` is already rejected when it is not the final parameter.
666667
let variadic_param = match fk.decl().inputs.last() {
667668
Some(param) if matches!(param.ty.kind, TyKind::CVarArgs) => param,
@@ -693,36 +694,92 @@ impl<'a> AstValidator<'a> {
693694

694695
match fn_ctxt {
695696
FnCtxt::Foreign => return,
696-
FnCtxt::Free | FnCtxt::Assoc(_) => match sig.header.ext {
697-
Extern::Implicit(_) => {
698-
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
699-
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
700-
span: variadic_param.span,
701-
unsafe_span: sig.safety_span(),
702-
});
697+
FnCtxt::Free | FnCtxt::Assoc(_) => {
698+
match sig.header.ext {
699+
Extern::Implicit(_) => {
700+
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
701+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
702+
span: variadic_param.span,
703+
unsafe_span: sig.safety_span(),
704+
});
705+
}
706+
}
707+
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
708+
// Just bail if the ABI is not even recognized.
709+
let Ok(abi) = ExternAbi::from_str(symbol_unescaped.as_str()) else {
710+
return;
711+
};
712+
713+
self.check_c_variadic_abi(abi, attrs, variadic_param.span, sig);
714+
715+
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
716+
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
717+
span: variadic_param.span,
718+
unsafe_span: sig.safety_span(),
719+
});
720+
}
721+
}
722+
Extern::None => {
723+
let err = errors::CVariadicNoExtern { span: variadic_param.span };
724+
self.dcx().emit_err(err);
703725
}
704726
}
705-
Extern::Explicit(StrLit { symbol_unescaped, .. }, _) => {
706-
if !matches!(symbol_unescaped, sym::C | sym::C_dash_unwind) {
707-
self.dcx().emit_err(errors::CVariadicBadExtern {
708-
span: variadic_param.span,
709-
abi: symbol_unescaped,
710-
extern_span: sig.extern_span(),
711-
});
727+
}
728+
}
729+
}
730+
731+
fn check_c_variadic_abi(
732+
&self,
733+
abi: ExternAbi,
734+
attrs: &'a AttrVec,
735+
dotdotdot_span: Span,
736+
sig: &FnSig,
737+
) {
738+
// For naked functions we accept any ABI that is accepted on c-variadic
739+
// foreign functions, if the c_variadic_naked_functions feature is enabled.
740+
if attr::contains_name(attrs, sym::naked) {
741+
match abi.supports_c_variadic() {
742+
CVariadicStatus::Stable if let ExternAbi::C { .. } = abi => {
743+
// With `c_variadic` naked c-variadic `extern "C"` functions are allowed.
744+
}
745+
CVariadicStatus::Stable => {
746+
// For e.g. aapcs or sysv64 `c_variadic_naked_functions` must also be enabled.
747+
if !self.features.enabled(sym::c_variadic_naked_functions) {
748+
let msg = format!("Naked c-variadic `extern {abi}` functions are unstable");
749+
feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg)
750+
.emit();
751+
}
752+
}
753+
CVariadicStatus::Unstable { feature } => {
754+
// Some ABIs need additional features.
755+
if !self.features.enabled(sym::c_variadic_naked_functions) {
756+
let msg = format!("Naked c-variadic `extern {abi}` functions are unstable");
757+
feature_err(&self.sess, sym::c_variadic_naked_functions, sig.span, msg)
758+
.emit();
712759
}
713760

714-
if !matches!(sig.header.safety, Safety::Unsafe(_)) {
715-
self.dcx().emit_err(errors::CVariadicMustBeUnsafe {
716-
span: variadic_param.span,
717-
unsafe_span: sig.safety_span(),
718-
});
761+
if !self.features.enabled(feature) {
762+
let msg = format!(
763+
"C-variadic functions with the {abi} calling convention are unstable"
764+
);
765+
feature_err(&self.sess, feature, sig.span, msg).emit();
719766
}
720767
}
721-
Extern::None => {
722-
let err = errors::CVariadicNoExtern { span: variadic_param.span };
723-
self.dcx().emit_err(err);
768+
CVariadicStatus::NotSupported => {
769+
// Some ABIs, e.g. `extern "Rust"`, never support c-variadic functions.
770+
self.dcx().emit_err(errors::CVariadicBadNakedExtern {
771+
span: dotdotdot_span,
772+
abi: abi.as_str(),
773+
extern_span: sig.extern_span(),
774+
});
724775
}
725-
},
776+
}
777+
} else if !matches!(abi, ExternAbi::C { .. }) {
778+
self.dcx().emit_err(errors::CVariadicBadExtern {
779+
span: dotdotdot_span,
780+
abi: abi.as_str(),
781+
extern_span: sig.extern_span(),
782+
});
726783
}
727784
}
728785

@@ -1106,7 +1163,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11061163
}
11071164

11081165
let kind = FnKind::Fn(FnCtxt::Free, &item.vis, &*func);
1109-
self.visit_fn(kind, item.span, item.id);
1166+
self.visit_fn(kind, &item.attrs, item.span, item.id);
11101167
}
11111168
ItemKind::ForeignMod(ForeignMod { extern_span, abi, safety, .. }) => {
11121169
let old_item = mem::replace(&mut self.extern_mod_span, Some(item.span));
@@ -1473,7 +1530,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14731530
visit::walk_param_bound(self, bound)
14741531
}
14751532

1476-
fn visit_fn(&mut self, fk: FnKind<'a>, span: Span, id: NodeId) {
1533+
fn visit_fn(&mut self, fk: FnKind<'a>, attrs: &AttrVec, span: Span, id: NodeId) {
14771534
// Only associated `fn`s can have `self` parameters.
14781535
let self_semantic = match fk.ctxt() {
14791536
Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes,
@@ -1492,7 +1549,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
14921549
self.check_extern_fn_signature(abi, ctxt, &fun.ident, &fun.sig);
14931550
}
14941551

1495-
self.check_c_variadic_type(fk);
1552+
self.check_c_variadic_type(fk, attrs);
14961553

14971554
// Functions cannot both be `const async` or `const gen`
14981555
if let Some(&FnHeader {
@@ -1643,7 +1700,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
16431700
{
16441701
self.visit_attrs_vis_ident(&item.attrs, &item.vis, &func.ident);
16451702
let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.vis, &*func);
1646-
self.visit_fn(kind, item.span, item.id);
1703+
self.visit_fn(kind, &item.attrs, item.span, item.id);
16471704
}
16481705
AssocItemKind::Type(_) => {
16491706
let disallowed = (!parent_is_const).then(|| match self.outer_trait_or_trait_impl {

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,18 @@ pub(crate) struct CVariadicMustBeUnsafe {
347347
pub(crate) struct CVariadicBadExtern {
348348
#[primary_span]
349349
pub span: Span,
350-
pub abi: Symbol,
350+
pub abi: &'static str,
351+
#[label]
352+
pub extern_span: Span,
353+
}
354+
355+
#[derive(Diagnostic)]
356+
#[diag(ast_passes_c_variadic_bad_naked_extern)]
357+
#[help]
358+
pub(crate) struct CVariadicBadNakedExtern {
359+
#[primary_span]
360+
pub span: Span,
361+
pub abi: &'static str,
351362
#[label]
352363
pub extern_span: Span,
353364
}

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use rustc_ast as ast;
21
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
3-
use rustc_ast::{NodeId, PatKind, attr, token};
2+
use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token};
43
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
54
use rustc_session::Session;
65
use rustc_session::parse::{feature_err, feature_warn};
@@ -392,7 +391,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
392391
visit::walk_poly_trait_ref(self, t);
393392
}
394393

395-
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
394+
fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) {
396395
if let Some(_header) = fn_kind.header() {
397396
// Stability of const fn methods are covered in `visit_assoc_item` below.
398397
}

compiler/rustc_feature/src/unstable.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,9 @@ declare_features! (
410410
(unstable, avx10_target_feature, "1.88.0", Some(138843)),
411411
/// Allows using C-variadics.
412412
(unstable, c_variadic, "1.34.0", Some(44930)),
413+
/// Allows defining c-variadic naked functions with any extern ABI that is allowed
414+
/// on c-variadic foreign functions.
415+
(unstable, c_variadic_naked_functions, "CURRENT_RUSTC_VERSION", Some(148767)),
413416
/// Allows the use of `#[cfg(contract_checks)` to check if contract checks are enabled.
414417
(unstable, cfg_contract_checks, "1.86.0", Some(128044)),
415418
/// Allows the use of `#[cfg(overflow_checks)` to check if integer overflow behaviour.

compiler/rustc_lint/src/early.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! syntactical lints.
66
77
use rustc_ast::visit::{self as ast_visit, Visitor, walk_list};
8-
use rustc_ast::{self as ast, HasAttrs};
8+
use rustc_ast::{self as ast, AttrVec, HasAttrs};
99
use rustc_data_structures::stack::ensure_sufficient_stack;
1010
use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
1111
use rustc_feature::Features;
@@ -135,7 +135,7 @@ impl<'ast, 'ecx, 'tcx, T: EarlyLintPass> ast_visit::Visitor<'ast>
135135
});
136136
}
137137

138-
fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, span: Span, id: ast::NodeId) {
138+
fn visit_fn(&mut self, fk: ast_visit::FnKind<'ast>, _: &AttrVec, span: Span, id: ast::NodeId) {
139139
lint_callback!(self, check_fn, fk, span, id);
140140
ast_visit::walk_fn(self, fk);
141141
}

compiler/rustc_passes/src/input_stats.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// completely accurate (some things might be counted twice, others missed).
44

55
use rustc_ast::visit::BoundKind;
6-
use rustc_ast::{self as ast, NodeId, visit as ast_visit};
6+
use rustc_ast::{self as ast, AttrVec, NodeId, visit as ast_visit};
77
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
88
use rustc_data_structures::thousands::usize_with_underscores;
99
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> {
709709
ast_visit::walk_where_predicate(self, p)
710710
}
711711

712-
fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: Span, _: NodeId) {
712+
fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, _: &AttrVec, _: Span, _: NodeId) {
713713
self.record("FnDecl", None, fk.decl());
714714
ast_visit::walk_fn(self, fk)
715715
}

compiler/rustc_resolve/src/build_reduced_graph.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,7 @@ impl<'a, 'ra, 'tcx> Visitor<'a> for BuildReducedGraphVisitor<'a, 'ra, 'tcx> {
13581358
// Visit attributes after items for backward compatibility.
13591359
// This way they can use `macro_rules` defined later.
13601360
self.visit_vis(&item.vis);
1361-
item.kind.walk(item.span, item.id, &item.vis, (), self);
1361+
item.kind.walk(&item.attrs, item.span, item.id, &item.vis, (), self);
13621362
visit::walk_list!(self, visit_attribute, &item.attrs);
13631363
}
13641364
_ => visit::walk_item(self, item),

compiler/rustc_resolve/src/def_collector.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
193193
});
194194
}
195195

196-
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
196+
fn visit_fn(&mut self, fn_kind: FnKind<'a>, _: &AttrVec, span: Span, _: NodeId) {
197197
match fn_kind {
198198
FnKind::Fn(
199199
_ctxt,

0 commit comments

Comments
 (0)