Rollup merge of #97971 - Soveu:varargs, r=jackh726

Enable varargs support for calling conventions other than C or cdecl

This patch makes it possible to use varargs for calling conventions,
which are either based on C (efiapi) or C is based on them (sysv64 and win64).

Also pinging ``@phlopsi,`` because he noticed first this oversight when writing a library for UEFI.
This commit is contained in:
Michael Howell 2022-10-30 19:31:36 -07:00 committed by GitHub
commit 9911229650
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 174 additions and 35 deletions

View file

@ -388,6 +388,9 @@ declare_features! (
(active, exclusive_range_pattern, "1.11.0", Some(37854), None),
/// Allows exhaustive pattern matching on types that contain uninhabited types.
(active, exhaustive_patterns, "1.13.0", Some(51085), None),
/// Allows using `efiapi`, `sysv64` and `win64` as calling convention
/// for functions with varargs.
(active, extended_varargs_abi_support, "1.65.0", Some(100189), None),
/// Allows defining `extern type`s.
(active, extern_types, "1.23.0", Some(43467), None),
/// Allows the use of `#[ffi_const]` on foreign functions.

View file

@ -106,7 +106,7 @@ use rustc_middle::middle;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util;
use rustc_session::config::EntryFnType;
use rustc_session::{config::EntryFnType, parse::feature_err};
use rustc_span::{symbol::sym, Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
@ -118,20 +118,40 @@ use astconv::AstConv;
use bounds::Bounds;
fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
match (decl.c_variadic, abi) {
// The function has the correct calling convention, or isn't a "C-variadic" function.
(false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {}
// The function is a "C-variadic" function with an incorrect calling convention.
(true, _) => {
let mut err = struct_span_err!(
tcx.sess,
span,
E0045,
"C-variadic function must have C or cdecl calling convention"
);
err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
}
const ERROR_HEAD: &str = "C-variadic function must have a compatible calling convention";
const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`";
const CONVENTIONS_STABLE: &str = "`C` or `cdecl`";
const UNSTABLE_EXPLAIN: &str =
"using calling conventions other than `C` or `cdecl` for varargs functions is unstable";
if !decl.c_variadic || matches!(abi, Abi::C { .. } | Abi::Cdecl { .. }) {
return;
}
let extended_abi_support = tcx.features().extended_varargs_abi_support;
let conventions = match (extended_abi_support, abi.supports_varargs()) {
// User enabled additional ABI support for varargs and function ABI matches those ones.
(true, true) => return,
// Using this ABI would be ok, if the feature for additional ABI support was enabled.
// Return CONVENTIONS_STABLE, because we want the other error to look the same.
(false, true) => {
feature_err(
&tcx.sess.parse_sess,
sym::extended_varargs_abi_support,
span,
UNSTABLE_EXPLAIN,
)
.emit();
CONVENTIONS_STABLE
}
(false, false) => CONVENTIONS_STABLE,
(true, false) => CONVENTIONS_UNSTABLE,
};
let mut err = struct_span_err!(tcx.sess, span, E0045, "{}, like {}", ERROR_HEAD, conventions);
err.span_label(span, ERROR_HEAD).emit();
}
fn require_same_types<'tcx>(

View file

@ -694,6 +694,7 @@ symbols! {
export_name,
expr,
extended_key_value_attributes,
extended_varargs_abi_support,
extern_absolute_paths,
extern_crate_item_prelude,
extern_crate_self,

View file

@ -40,6 +40,28 @@ pub enum Abi {
RustCold,
}
impl Abi {
pub fn supports_varargs(self) -> bool {
// * C and Cdecl obviously support varargs.
// * C can be based on SysV64 or Win64, so they must support varargs.
// * EfiApi is based on Win64 or C, so it also supports it.
//
// * Stdcall does not, because it would be impossible for the callee to clean
// up the arguments. (callee doesn't know how many arguments are there)
// * Same for Fastcall, Vectorcall and Thiscall.
// * System can become Stdcall, so is also a no-no.
// * Other calling conventions are related to hardware or the compiler itself.
match self {
Self::C { .. }
| Self::Cdecl { .. }
| Self::Win64 { .. }
| Self::SysV64 { .. }
| Self::EfiApi => true,
_ => false,
}
}
}
#[derive(Copy, Clone)]
pub struct AbiData {
abi: Abi,

View file

@ -0,0 +1,10 @@
# `extended_varargs_abi_support`
The tracking issue for this feature is: [#100189]
[#100189]: https://github.com/rust-lang/rust/issues/100189
------------------------
This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling
conventions on functions with varargs.

View file

@ -0,0 +1,19 @@
#![feature(abi_efiapi)]
fn efiapi(f: extern "efiapi" fn(usize, ...)) {
//~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
//~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
f(22, 44);
}
fn sysv(f: extern "sysv64" fn(usize, ...)) {
//~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
//~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
f(22, 44);
}
fn win(f: extern "win64" fn(usize, ...)) {
//~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
//~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
f(22, 44);
}
fn main() {}

View file

@ -0,0 +1,49 @@
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
--> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
|
LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
--> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
|
LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
--> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
|
LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
--> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
|
LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
--> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
|
LL | fn win(f: extern "win64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
--> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
|
LL | fn win(f: extern "win64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0045, E0658.
For more information about an error, try `rustc --explain E0045`.

View file

@ -6,7 +6,9 @@
trait Sized { }
extern "stdcall" {
fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
fn printf(_: *const u8, ...);
//~^ ERROR: C-variadic function must have a compatible calling convention,
// like C, cdecl, win64, sysv64 or efiapi
}
extern "C" {

View file

@ -1,17 +1,17 @@
error[E0045]: C-variadic function must have C or cdecl calling convention
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
--> $DIR/variadic-ffi-1.rs:9:5
|
LL | fn printf(_: *const u8, ...);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error[E0060]: this function takes at least 2 arguments but 0 arguments were supplied
--> $DIR/variadic-ffi-1.rs:20:9
--> $DIR/variadic-ffi-1.rs:22:9
|
LL | foo();
| ^^^-- two arguments of type `isize` and `u8` are missing
|
note: function defined here
--> $DIR/variadic-ffi-1.rs:13:8
--> $DIR/variadic-ffi-1.rs:15:8
|
LL | fn foo(f: isize, x: u8, ...);
| ^^^
@ -21,13 +21,13 @@ LL | foo(/* isize */, /* u8 */);
| ~~~~~~~~~~~~~~~~~~~~~~~
error[E0060]: this function takes at least 2 arguments but 1 argument was supplied
--> $DIR/variadic-ffi-1.rs:21:9
--> $DIR/variadic-ffi-1.rs:23:9
|
LL | foo(1);
| ^^^--- an argument of type `u8` is missing
|
note: function defined here
--> $DIR/variadic-ffi-1.rs:13:8
--> $DIR/variadic-ffi-1.rs:15:8
|
LL | fn foo(f: isize, x: u8, ...);
| ^^^
@ -37,7 +37,7 @@ LL | foo(1, /* u8 */);
| ~~~~~~~~~~~~~
error[E0308]: mismatched types
--> $DIR/variadic-ffi-1.rs:23:56
--> $DIR/variadic-ffi-1.rs:25:56
|
LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
| ------------------------------------- ^^^ expected non-variadic fn, found variadic function
@ -48,7 +48,7 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
found fn item `unsafe extern "C" fn(_, _, ...) {foo}`
error[E0308]: mismatched types
--> $DIR/variadic-ffi-1.rs:24:54
--> $DIR/variadic-ffi-1.rs:26:54
|
LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
| ----------------------------------- ^^^ expected variadic fn, found non-variadic function
@ -59,37 +59,37 @@ LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
found fn item `extern "C" fn(_, _) {bar}`
error[E0617]: can't pass `f32` to variadic function
--> $DIR/variadic-ffi-1.rs:26:19
--> $DIR/variadic-ffi-1.rs:28:19
|
LL | foo(1, 2, 3f32);
| ^^^^ help: cast the value to `c_double`: `3f32 as c_double`
error[E0617]: can't pass `bool` to variadic function
--> $DIR/variadic-ffi-1.rs:27:19
--> $DIR/variadic-ffi-1.rs:29:19
|
LL | foo(1, 2, true);
| ^^^^ help: cast the value to `c_int`: `true as c_int`
error[E0617]: can't pass `i8` to variadic function
--> $DIR/variadic-ffi-1.rs:28:19
--> $DIR/variadic-ffi-1.rs:30:19
|
LL | foo(1, 2, 1i8);
| ^^^ help: cast the value to `c_int`: `1i8 as c_int`
error[E0617]: can't pass `u8` to variadic function
--> $DIR/variadic-ffi-1.rs:29:19
--> $DIR/variadic-ffi-1.rs:31:19
|
LL | foo(1, 2, 1u8);
| ^^^ help: cast the value to `c_uint`: `1u8 as c_uint`
error[E0617]: can't pass `i16` to variadic function
--> $DIR/variadic-ffi-1.rs:30:19
--> $DIR/variadic-ffi-1.rs:32:19
|
LL | foo(1, 2, 1i16);
| ^^^^ help: cast the value to `c_int`: `1i16 as c_int`
error[E0617]: can't pass `u16` to variadic function
--> $DIR/variadic-ffi-1.rs:31:19
--> $DIR/variadic-ffi-1.rs:33:19
|
LL | foo(1, 2, 1u16);
| ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint`

View file

@ -1,7 +1,20 @@
// ignore-arm stdcall isn't supported
#![feature(extended_varargs_abi_support)]
#![feature(abi_efiapi)]
fn baz(f: extern "stdcall" fn(usize, ...)) {
//~^ ERROR: variadic function must have C or cdecl calling convention
//~^ ERROR: C-variadic function must have a compatible calling convention,
// like C, cdecl, win64, sysv64 or efiapi
f(22, 44);
}
fn sysv(f: extern "sysv64" fn(usize, ...)) {
f(22, 44);
}
fn win(f: extern "win64" fn(usize, ...)) {
f(22, 44);
}
fn efiapi(f: extern "efiapi" fn(usize, ...)) {
f(22, 44);
}

View file

@ -1,8 +1,8 @@
error[E0045]: C-variadic function must have C or cdecl calling convention
--> $DIR/variadic-ffi-2.rs:3:11
error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `win64`, `sysv64` or `efiapi`
--> $DIR/variadic-ffi-2.rs:5:11
|
LL | fn baz(f: extern "stdcall" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error: aborting due to previous error

View file

@ -1,8 +1,8 @@
error[E0045]: C-variadic function must have C or cdecl calling convention
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
--> $DIR/E0045.rs:1:17
|
LL | extern "Rust" { fn foo(x: u8, ...); }
| ^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
| ^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error: aborting due to previous error