Skip to content

Commit

Permalink
Rollup merge of rust-lang#97971 - Soveu:varargs, r=jackh726
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
compiler-errors authored Oct 26, 2022
2 parents 0a6b941 + de78c32 commit 8b001e6
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 34 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
46 changes: 33 additions & 13 deletions compiler/rustc_hir_analysis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _;
Expand All @@ -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,
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,
E0045,
"C-variadic function must have C or cdecl calling convention"
);
err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
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>(
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 22 additions & 0 deletions compiler/rustc_target/src/spec/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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`.
4 changes: 3 additions & 1 deletion src/test/ui/c-variadic/variadic-ffi-1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down
28 changes: 14 additions & 14 deletions src/test/ui/c-variadic/variadic-ffi-1.stderr
Original file line number Diff line number Diff line change
@@ -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, ...);
| ^^^
Expand All @@ -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, ...);
| ^^^
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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`
Expand Down
15 changes: 14 additions & 1 deletion src/test/ui/c-variadic/variadic-ffi-2.rs
Original file line number Diff line number Diff line change
@@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/c-variadic/variadic-ffi-2.stderr
Original file line number Diff line number Diff line change
@@ -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

Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/error-codes/E0045.stderr
Original file line number Diff line number Diff line change
@@ -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

Expand Down

0 comments on commit 8b001e6

Please sign in to comment.