Skip to content

Commit 55bdda7

Browse files
Rollup merge of rust-lang#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.
2 parents d4e9952 + de78c32 commit 55bdda7

File tree

12 files changed

+173
-34
lines changed

12 files changed

+173
-34
lines changed

Diff for: compiler/rustc_feature/src/active.rs

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

Diff for: compiler/rustc_hir_analysis/src/lib.rs

+33-13
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ use rustc_middle::middle;
106106
use rustc_middle::ty::query::Providers;
107107
use rustc_middle::ty::{self, Ty, TyCtxt};
108108
use rustc_middle::util;
109-
use rustc_session::config::EntryFnType;
109+
use rustc_session::{config::EntryFnType, parse::feature_err};
110110
use rustc_span::{symbol::sym, Span, DUMMY_SP};
111111
use rustc_target::spec::abi::Abi;
112112
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
@@ -118,20 +118,40 @@ use astconv::AstConv;
118118
use bounds::Bounds;
119119

120120
fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
121-
match (decl.c_variadic, abi) {
122-
// The function has the correct calling convention, or isn't a "C-variadic" function.
123-
(false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {}
124-
// The function is a "C-variadic" function with an incorrect calling convention.
125-
(true, _) => {
126-
let mut err = struct_span_err!(
127-
tcx.sess,
121+
const ERROR_HEAD: &str = "C-variadic function must have a compatible calling convention";
122+
const CONVENTIONS_UNSTABLE: &str = "`C`, `cdecl`, `win64`, `sysv64` or `efiapi`";
123+
const CONVENTIONS_STABLE: &str = "`C` or `cdecl`";
124+
const UNSTABLE_EXPLAIN: &str =
125+
"using calling conventions other than `C` or `cdecl` for varargs functions is unstable";
126+
127+
if !decl.c_variadic || matches!(abi, Abi::C { .. } | Abi::Cdecl { .. }) {
128+
return;
129+
}
130+
131+
let extended_abi_support = tcx.features().extended_varargs_abi_support;
132+
let conventions = match (extended_abi_support, abi.supports_varargs()) {
133+
// User enabled additional ABI support for varargs and function ABI matches those ones.
134+
(true, true) => return,
135+
136+
// Using this ABI would be ok, if the feature for additional ABI support was enabled.
137+
// Return CONVENTIONS_STABLE, because we want the other error to look the same.
138+
(false, true) => {
139+
feature_err(
140+
&tcx.sess.parse_sess,
141+
sym::extended_varargs_abi_support,
128142
span,
129-
E0045,
130-
"C-variadic function must have C or cdecl calling convention"
131-
);
132-
err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
143+
UNSTABLE_EXPLAIN,
144+
)
145+
.emit();
146+
CONVENTIONS_STABLE
133147
}
134-
}
148+
149+
(false, false) => CONVENTIONS_STABLE,
150+
(true, false) => CONVENTIONS_UNSTABLE,
151+
};
152+
153+
let mut err = struct_span_err!(tcx.sess, span, E0045, "{}, like {}", ERROR_HEAD, conventions);
154+
err.span_label(span, ERROR_HEAD).emit();
135155
}
136156

137157
fn require_same_types<'tcx>(

Diff for: compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ symbols! {
694694
export_name,
695695
expr,
696696
extended_key_value_attributes,
697+
extended_varargs_abi_support,
697698
extern_absolute_paths,
698699
extern_crate_item_prelude,
699700
extern_crate_self,

Diff for: compiler/rustc_target/src/spec/abi.rs

+22
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,28 @@ pub enum Abi {
4040
RustCold,
4141
}
4242

43+
impl Abi {
44+
pub fn supports_varargs(self) -> bool {
45+
// * C and Cdecl obviously support varargs.
46+
// * C can be based on SysV64 or Win64, so they must support varargs.
47+
// * EfiApi is based on Win64 or C, so it also supports it.
48+
//
49+
// * Stdcall does not, because it would be impossible for the callee to clean
50+
// up the arguments. (callee doesn't know how many arguments are there)
51+
// * Same for Fastcall, Vectorcall and Thiscall.
52+
// * System can become Stdcall, so is also a no-no.
53+
// * Other calling conventions are related to hardware or the compiler itself.
54+
match self {
55+
Self::C { .. }
56+
| Self::Cdecl { .. }
57+
| Self::Win64 { .. }
58+
| Self::SysV64 { .. }
59+
| Self::EfiApi => true,
60+
_ => false,
61+
}
62+
}
63+
}
64+
4365
#[derive(Copy, Clone)]
4466
pub struct AbiData {
4567
abi: Abi,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# `extended_varargs_abi_support`
2+
3+
The tracking issue for this feature is: [#100189]
4+
5+
[#100189]: https://github.com/rust-lang/rust/issues/100189
6+
7+
------------------------
8+
9+
This feature adds the possibility of using `sysv64`, `win64` or `efiapi` calling
10+
conventions on functions with varargs.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#![feature(abi_efiapi)]
2+
3+
fn efiapi(f: extern "efiapi" fn(usize, ...)) {
4+
//~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
5+
//~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
6+
f(22, 44);
7+
}
8+
fn sysv(f: extern "sysv64" fn(usize, ...)) {
9+
//~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
10+
//~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
11+
f(22, 44);
12+
}
13+
fn win(f: extern "win64" fn(usize, ...)) {
14+
//~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
15+
//~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
16+
f(22, 44);
17+
}
18+
19+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
2+
--> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
3+
|
4+
LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
8+
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
9+
10+
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
11+
--> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
12+
|
13+
LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
15+
16+
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
17+
--> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
18+
|
19+
LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
21+
|
22+
= note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
23+
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
24+
25+
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
26+
--> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
27+
|
28+
LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
30+
31+
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
32+
--> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
33+
|
34+
LL | fn win(f: extern "win64" fn(usize, ...)) {
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
36+
|
37+
= note: see issue #100189 <https://github.com/rust-lang/rust/issues/100189> for more information
38+
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
39+
40+
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
41+
--> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
42+
|
43+
LL | fn win(f: extern "win64" fn(usize, ...)) {
44+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
45+
46+
error: aborting due to 6 previous errors
47+
48+
Some errors have detailed explanations: E0045, E0658.
49+
For more information about an error, try `rustc --explain E0045`.

Diff for: src/test/ui/c-variadic/variadic-ffi-1.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
trait Sized { }
77

88
extern "stdcall" {
9-
fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C or cdecl calling
9+
fn printf(_: *const u8, ...);
10+
//~^ ERROR: C-variadic function must have a compatible calling convention,
11+
// like C, cdecl, win64, sysv64 or efiapi
1012
}
1113

1214
extern "C" {

Diff for: src/test/ui/c-variadic/variadic-ffi-1.stderr

+14-14
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
error[E0045]: C-variadic function must have C or cdecl calling convention
1+
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
22
--> $DIR/variadic-ffi-1.rs:9:5
33
|
44
LL | fn printf(_: *const u8, ...);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
66

77
error[E0060]: this function takes at least 2 arguments but 0 arguments were supplied
8-
--> $DIR/variadic-ffi-1.rs:20:9
8+
--> $DIR/variadic-ffi-1.rs:22:9
99
|
1010
LL | foo();
1111
| ^^^-- two arguments of type `isize` and `u8` are missing
1212
|
1313
note: function defined here
14-
--> $DIR/variadic-ffi-1.rs:13:8
14+
--> $DIR/variadic-ffi-1.rs:15:8
1515
|
1616
LL | fn foo(f: isize, x: u8, ...);
1717
| ^^^
@@ -21,13 +21,13 @@ LL | foo(/* isize */, /* u8 */);
2121
| ~~~~~~~~~~~~~~~~~~~~~~~
2222

2323
error[E0060]: this function takes at least 2 arguments but 1 argument was supplied
24-
--> $DIR/variadic-ffi-1.rs:21:9
24+
--> $DIR/variadic-ffi-1.rs:23:9
2525
|
2626
LL | foo(1);
2727
| ^^^--- an argument of type `u8` is missing
2828
|
2929
note: function defined here
30-
--> $DIR/variadic-ffi-1.rs:13:8
30+
--> $DIR/variadic-ffi-1.rs:15:8
3131
|
3232
LL | fn foo(f: isize, x: u8, ...);
3333
| ^^^
@@ -37,7 +37,7 @@ LL | foo(1, /* u8 */);
3737
| ~~~~~~~~~~~~~
3838

3939
error[E0308]: mismatched types
40-
--> $DIR/variadic-ffi-1.rs:23:56
40+
--> $DIR/variadic-ffi-1.rs:25:56
4141
|
4242
LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
4343
| ------------------------------------- ^^^ expected non-variadic fn, found variadic function
@@ -48,7 +48,7 @@ LL | let x: unsafe extern "C" fn(f: isize, x: u8) = foo;
4848
found fn item `unsafe extern "C" fn(_, _, ...) {foo}`
4949

5050
error[E0308]: mismatched types
51-
--> $DIR/variadic-ffi-1.rs:24:54
51+
--> $DIR/variadic-ffi-1.rs:26:54
5252
|
5353
LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
5454
| ----------------------------------- ^^^ expected variadic fn, found non-variadic function
@@ -59,37 +59,37 @@ LL | let y: extern "C" fn(f: isize, x: u8, ...) = bar;
5959
found fn item `extern "C" fn(_, _) {bar}`
6060

6161
error[E0617]: can't pass `f32` to variadic function
62-
--> $DIR/variadic-ffi-1.rs:26:19
62+
--> $DIR/variadic-ffi-1.rs:28:19
6363
|
6464
LL | foo(1, 2, 3f32);
6565
| ^^^^ help: cast the value to `c_double`: `3f32 as c_double`
6666

6767
error[E0617]: can't pass `bool` to variadic function
68-
--> $DIR/variadic-ffi-1.rs:27:19
68+
--> $DIR/variadic-ffi-1.rs:29:19
6969
|
7070
LL | foo(1, 2, true);
7171
| ^^^^ help: cast the value to `c_int`: `true as c_int`
7272

7373
error[E0617]: can't pass `i8` to variadic function
74-
--> $DIR/variadic-ffi-1.rs:28:19
74+
--> $DIR/variadic-ffi-1.rs:30:19
7575
|
7676
LL | foo(1, 2, 1i8);
7777
| ^^^ help: cast the value to `c_int`: `1i8 as c_int`
7878

7979
error[E0617]: can't pass `u8` to variadic function
80-
--> $DIR/variadic-ffi-1.rs:29:19
80+
--> $DIR/variadic-ffi-1.rs:31:19
8181
|
8282
LL | foo(1, 2, 1u8);
8383
| ^^^ help: cast the value to `c_uint`: `1u8 as c_uint`
8484

8585
error[E0617]: can't pass `i16` to variadic function
86-
--> $DIR/variadic-ffi-1.rs:30:19
86+
--> $DIR/variadic-ffi-1.rs:32:19
8787
|
8888
LL | foo(1, 2, 1i16);
8989
| ^^^^ help: cast the value to `c_int`: `1i16 as c_int`
9090

9191
error[E0617]: can't pass `u16` to variadic function
92-
--> $DIR/variadic-ffi-1.rs:31:19
92+
--> $DIR/variadic-ffi-1.rs:33:19
9393
|
9494
LL | foo(1, 2, 1u16);
9595
| ^^^^ help: cast the value to `c_uint`: `1u16 as c_uint`

Diff for: src/test/ui/c-variadic/variadic-ffi-2.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,20 @@
11
// ignore-arm stdcall isn't supported
2+
#![feature(extended_varargs_abi_support)]
3+
#![feature(abi_efiapi)]
24

35
fn baz(f: extern "stdcall" fn(usize, ...)) {
4-
//~^ ERROR: variadic function must have C or cdecl calling convention
6+
//~^ ERROR: C-variadic function must have a compatible calling convention,
7+
// like C, cdecl, win64, sysv64 or efiapi
8+
f(22, 44);
9+
}
10+
11+
fn sysv(f: extern "sysv64" fn(usize, ...)) {
12+
f(22, 44);
13+
}
14+
fn win(f: extern "win64" fn(usize, ...)) {
15+
f(22, 44);
16+
}
17+
fn efiapi(f: extern "efiapi" fn(usize, ...)) {
518
f(22, 44);
619
}
720

Diff for: src/test/ui/c-variadic/variadic-ffi-2.stderr

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

77
error: aborting due to previous error
88

Diff for: src/test/ui/error-codes/E0045.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error[E0045]: C-variadic function must have C or cdecl calling convention
1+
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
22
--> $DIR/E0045.rs:1:17
33
|
44
LL | extern "Rust" { fn foo(x: u8, ...); }
5-
| ^^^^^^^^^^^^^^^^^^^ C-variadics require C or cdecl calling convention
5+
| ^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
66

77
error: aborting due to previous error
88

0 commit comments

Comments
 (0)