add feature(c_variadic_naked_functions)
This commit is contained in:
parent
568c6ed8c9
commit
ebd173f512
10 changed files with 234 additions and 32 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -611,6 +611,7 @@ symbols! {
|
|||
c_str_literals,
|
||||
c_unwind,
|
||||
c_variadic,
|
||||
c_variadic_naked_functions,
|
||||
c_void,
|
||||
call,
|
||||
call_mut,
|
||||
|
|
|
|||
44
tests/ui/c-variadic/naked-invalid.rs
Normal file
44
tests/ui/c-variadic/naked-invalid.rs
Normal file
|
|
@ -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")
|
||||
}
|
||||
31
tests/ui/c-variadic/naked-invalid.stderr
Normal file
31
tests/ui/c-variadic/naked-invalid.stderr
Normal file
|
|
@ -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`.
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
@ -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 <https://github.com/rust-lang/rust/issues/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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue