Skip to content

Commit c345f8c

Browse files
committed
Major rework based on marker types instead of const generics
1 parent 96367f0 commit c345f8c

9 files changed

Lines changed: 600 additions & 340 deletions

File tree

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,20 @@ rustc_version = "0.4"
2828
default = []
2929
stable = []
3030
nightly = []
31+
abi_vectorcall = ["nightly"]
3132
max-arity-12 = []
3233

3334
[lints.rust]
3435
unexpected_cfgs = { level = "warn", check-cfg = [
3536
'cfg(nightly_build)',
37+
38+
'cfg(has_abi_c_unwind)',
39+
'cfg(has_abi_system_unwind)',
3640
'cfg(has_abi_cdecl)',
3741
'cfg(has_abi_stdcall)',
3842
'cfg(has_abi_fastcall)',
43+
'cfg(has_abi_thiscall)',
44+
'cfg(has_abi_vectorcall)',
3945
'cfg(has_abi_win64)',
4046
'cfg(has_abi_sysv64)',
4147
'cfg(has_abi_aapcs)'

build.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,28 @@ fn main() {
88
cargo_emit::rustc_cfg!("nightly");
99
}
1010

11+
// from https://github.com/rust-lang/rust/blob/873122c006315e541c30809210089606877122c5/tests/ui/abi/unsupported.rs
1112
let t = build_target::target();
1213

13-
// x86: cdecl, stdcall and fastcall
1414
if t.arch == Arch::X86 {
1515
cargo_emit::rustc_cfg!("has_abi_cdecl");
1616
cargo_emit::rustc_cfg!("has_abi_stdcall");
1717
cargo_emit::rustc_cfg!("has_abi_fastcall");
18+
cargo_emit::rustc_cfg!("has_abi_thiscall");
19+
}
20+
21+
if matches!(t.arch, Arch::X86 | Arch::X86_64) && cfg!(feature = "abi_vectorcall") {
22+
cargo_emit::rustc_cfg!("has_abi_vectorcall");
1823
}
1924

20-
// x86_64: Windows uses the Win64 ABI, other OSes use the SysV AMD64 ABI
2125
if t.arch == Arch::X86_64 {
2226
if t.os == Os::Windows {
23-
cargo_emit::rustc_cfg!("has_abi_win64");
27+
cargo_emit::rustc_cfg!("has_abi_win64")
2428
} else {
25-
cargo_emit::rustc_cfg!("has_abi_sysv64");
29+
cargo_emit::rustc_cfg!("has_abi_sysv64")
2630
}
2731
}
2832

29-
// ARM (32-bit) has aapcs
3033
if t.arch == Arch::Arm {
3134
cargo_emit::rustc_cfg!("has_abi_aapcs");
3235
}

src/abi.rs

Lines changed: 140 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,204 +1,172 @@
1-
#[cfg(nightly_build)]
2-
use core::marker::ConstParamTy;
31
use core::{
4-
fmt::{Debug, Display},
2+
fmt::{self, Debug, Display},
53
str::FromStr,
64
};
75

8-
use const_panic::concat_panic;
9-
10-
/// The abi or calling convention of a function pointer.
11-
#[repr(u8)]
12-
#[cfg_attr(nightly_build, derive(ConstParamTy))]
13-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
6+
/// The ABI or calling convention of a function pointer.
7+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8+
// from https://github.com/rust-lang/rust/blob/4fa80a5e733e2202d7ca4c203c2fdfda41cfe7dc/compiler/rustc_abi/src/extern_abi.rs#L21
149
pub enum Abi {
15-
/// The default ABI when you write a normal `fn foo()` in any Rust code.
16-
Rust = 0,
10+
/* universal */
1711
/// This is the same as `extern fn foo()`; whatever the default your C compiler supports.
18-
C = 1,
19-
/// Usually the same as [`extern "C"`](Abi::C), except on Win32, in which case it's [`"stdcall"`](Abi::Stdcall), or what you should use to link to the Windows API itself.
20-
System = 2,
21-
/// The default for C code on `x86_64` Windows.
22-
Win64 = 3,
23-
/// The default for C code on non-Windows `x86_64`.
24-
Sysv64 = 4,
12+
C { unwind: bool },
13+
/// Usually the same as [`extern "C"`](Abi::C), except on Win32, in which case it's
14+
/// [`"stdcall"`](Abi::Stdcall), or what you should use to link to the Windows API itself.
15+
System { unwind: bool },
16+
17+
/// The default ABI when you write a normal `fn foo()` in any Rust code.
18+
Rust,
19+
20+
/* arm */
2521
/// The default for ARM.
26-
Aapcs = 5,
22+
Aapcs { unwind: bool },
23+
24+
/* x86 */
2725
/// The default for `x86_32` C code.
28-
Cdecl = 6,
26+
Cdecl { unwind: bool },
2927
/// The default for the Win32 API on `x86_32`.
30-
Stdcall = 7,
31-
/// The `fastcall` ABI -- corresponds to MSVC's `__fastcall` and GCC and clang's `__attribute__((fastcall))`
32-
Fastcall = 8,
33-
/// The `vectorcall` ABI -- corresponds to MSVC's `__vectorcall` and GCC and clang's `__attribute__((vectorcall))`
34-
Vectorcall = 9,
35-
}
28+
Stdcall { unwind: bool },
29+
/// The `fastcall` ABI.
30+
Fastcall { unwind: bool },
31+
/// The Windows C++ ABI.
32+
Thiscall { unwind: bool },
33+
/// The `vectorcall` ABI.
34+
Vectorcall { unwind: bool },
3635

37-
impl Abi {
38-
/// Returns the string representation of this ABI.
39-
#[must_use]
40-
pub const fn to_str(&self) -> &'static str {
41-
match self {
42-
Abi::Rust => "Rust",
43-
Abi::C => "C",
44-
Abi::System => "system",
45-
Abi::Win64 => "win64",
46-
Abi::Sysv64 => "sysv64",
47-
Abi::Aapcs => "aapcs",
48-
Abi::Cdecl => "cdecl",
49-
Abi::Stdcall => "stdcall",
50-
Abi::Fastcall => "fastcall",
51-
Abi::Vectorcall => "vectorcall",
52-
}
53-
}
36+
/* x86_64 */
37+
/// The default for C code on non-Windows `x86_64`.
38+
SysV64 { unwind: bool },
39+
/// The default for C code on `x86_64` Windows.
40+
Win64 { unwind: bool },
5441
}
5542

5643
impl Abi {
57-
/// Returns true if this ABI is an alias.
5844
#[must_use]
59-
pub fn is_alias(&self) -> bool {
60-
matches!(self, Abi::C | Abi::System)
61-
}
45+
pub fn canonize(self, has_c_varargs: bool) -> Option<Abi> {
46+
// from https://github.com/rust-lang/rust/blob/4fa80a5e733e2202d7ca4c203c2fdfda41cfe7dc/compiler/rustc_target/src/spec/abi_map.rs#L79
47+
let os_windows = cfg!(target_os = "windows");
48+
let os_vexos = cfg!(target_os = "vexos");
6249

63-
/// Returns true if this ABI is a concrete ABI, not an alias.
64-
#[must_use]
65-
pub fn is_concrete(&self) -> bool {
66-
!self.is_alias()
67-
}
50+
let arch_x86 = cfg!(target_arch = "x86");
51+
let arch_x86_64 = cfg!(target_arch = "x86_64");
52+
let arch_arm = cfg!(target_arch = "arm");
53+
let arch_aarch64 = cfg!(target_arch = "aarch64");
54+
let arch_arm_any = arch_arm || arch_aarch64;
6855

69-
/// Returns the concrete ABI for this ABI on the current target.
70-
#[must_use]
71-
pub fn concrete(&self) -> Abi {
72-
match self {
73-
Abi::C => {
74-
// "C" maps to platform default for C
75-
if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") {
76-
Abi::Win64
77-
} else if cfg!(target_arch = "x86_64") {
78-
Abi::Sysv64
79-
} else if cfg!(target_arch = "x86") {
80-
Abi::Cdecl
81-
} else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
82-
Abi::Aapcs
83-
} else {
84-
Abi::Cdecl // fallback
85-
}
56+
let out = match self {
57+
Abi::C { unwind } => Abi::C { unwind },
58+
Abi::Rust => Abi::Rust,
59+
Abi::System { unwind } if arch_x86 && os_windows && !has_c_varargs => {
60+
Abi::Stdcall { unwind }
8661
}
87-
Abi::System => {
88-
if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") {
89-
Abi::Win64
90-
} else if cfg!(target_os = "windows") && cfg!(target_arch = "x86") {
91-
Abi::Stdcall
92-
} else if cfg!(target_arch = "x86_64") {
93-
Abi::Sysv64
94-
} else if cfg!(target_arch = "x86") {
95-
Abi::Cdecl
96-
} else if cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64") {
97-
Abi::Aapcs
98-
} else {
99-
Abi::Cdecl // fallback
100-
}
62+
Abi::System { unwind } if arch_arm && os_vexos => Abi::Aapcs { unwind },
63+
Abi::System { unwind } => Abi::C { unwind },
64+
65+
// arm
66+
Abi::Aapcs { unwind } if arch_arm_any => Abi::Aapcs { unwind },
67+
Abi::Aapcs { .. } => return None,
68+
69+
// x86
70+
Abi::Cdecl { unwind } if arch_x86 => Abi::C { unwind },
71+
Abi::Cdecl { unwind } => Abi::C { unwind },
72+
73+
Abi::Fastcall { unwind } if arch_x86 => Abi::Fastcall { unwind },
74+
Abi::Fastcall { unwind } if os_windows => Abi::C { unwind },
75+
Abi::Fastcall { .. } => return None,
76+
77+
Abi::Stdcall { unwind } if arch_x86 => Abi::Stdcall { unwind },
78+
Abi::Stdcall { unwind } if os_windows => Abi::C { unwind },
79+
Abi::Stdcall { .. } => return None,
80+
81+
Abi::Thiscall { unwind } if arch_x86 => Abi::Thiscall { unwind },
82+
Abi::Thiscall { .. } => return None,
83+
84+
Abi::Vectorcall { unwind } if arch_x86 || arch_x86_64 => {
85+
Abi::Vectorcall { unwind }
10186
}
102-
other => *other,
103-
}
104-
}
105-
}
87+
Abi::Vectorcall { .. } => return None,
10688

107-
impl FromStr for Abi {
108-
type Err = ();
109-
110-
fn from_str(s: &str) -> Result<Self, Self::Err> {
111-
match s {
112-
"" | "Rust" => Ok(Abi::Rust),
113-
"C" => Ok(Abi::C),
114-
"system" => Ok(Abi::System),
115-
"win64" => Ok(Abi::Win64),
116-
"sysv64" => Ok(Abi::Sysv64),
117-
"aapcs" => Ok(Abi::Aapcs),
118-
"cdecl" => Ok(Abi::Cdecl),
119-
"stdcall" => Ok(Abi::Stdcall),
120-
"fastcall" => Ok(Abi::Fastcall),
121-
"vectorcall" => Ok(Abi::Vectorcall),
122-
_ => Err(()),
123-
}
89+
Abi::SysV64 { unwind } if arch_x86_64 => Abi::SysV64 { unwind },
90+
Abi::Win64 { unwind } if arch_x86_64 => Abi::Win64 { unwind },
91+
Abi::SysV64 { .. } | Abi::Win64 { .. } => return None,
92+
};
93+
94+
Some(out)
12495
}
12596
}
12697

12798
impl Display for Abi {
128-
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
99+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129100
write!(f, "{}", self.to_str())
130101
}
131102
}
132103

133-
/// Parse a string into an [`Abi`] if known, otherwise returns `None`.
134-
#[must_use]
135-
pub const fn parse(conv: &'static str) -> Option<Abi> {
136-
if konst::eq_str(conv, "") || konst::eq_str(conv, "Rust") {
137-
Some(Abi::Rust)
138-
} else if konst::eq_str(conv, "C") {
139-
Some(Abi::C)
140-
} else if konst::eq_str(conv, "system") {
141-
Some(Abi::System)
142-
} else if konst::eq_str(conv, "win64") {
143-
Some(Abi::Win64)
144-
} else if konst::eq_str(conv, "sysv64") {
145-
Some(Abi::Sysv64)
146-
} else if konst::eq_str(conv, "aapcs") {
147-
Some(Abi::Aapcs)
148-
} else if konst::eq_str(conv, "cdecl") {
149-
Some(Abi::Cdecl)
150-
} else if konst::eq_str(conv, "stdcall") {
151-
Some(Abi::Stdcall)
152-
} else if konst::eq_str(conv, "fastcall") {
153-
Some(Abi::Fastcall)
154-
} else if konst::eq_str(conv, "vectorcall") {
155-
Some(Abi::Vectorcall)
156-
} else {
157-
None
158-
}
159-
}
160-
161-
/// Parse a string into an [`Abi`] and panic if unknown.
162-
#[must_use]
163-
pub const fn parse_or_fail(conv: &'static str) -> Abi {
164-
if let Some(c) = parse(conv) {
165-
c
166-
} else {
167-
concat_panic!("invalid or unknown abi", conv)
168-
}
169-
}
104+
macro_rules! abi_kind_impl {
105+
(
106+
$t:ty => {
107+
$(
108+
$variant:ident $( { unwind: $uw:literal } )? => $tok:literal,
109+
)*
110+
}
111+
) => {
112+
impl $t {
113+
/// Returns the string representation of this ABI.
114+
#[must_use]
115+
pub const fn to_str(&self) -> &'static str {
116+
match self {
117+
$( Self::$variant $( { unwind: $uw } )? => $tok, )*
118+
}
119+
}
170120

171-
#[cfg(nightly_build)]
172-
pub(crate) type AbiKey = Abi;
173-
#[cfg(not(nightly_build))]
174-
pub(crate) type AbiKey = u8;
121+
/// The same as the [`FromStr`] implementation, but (only!) for use in `const` contexts.
122+
#[must_use]
123+
pub const fn from_str_const(conv: &'static str) -> Option<Self> {
124+
$(
125+
if konst::eq_str(conv, $tok) {
126+
return Some(Self::$variant $( { unwind: $uw } )?);
127+
}
128+
)*
129+
None
130+
}
131+
}
175132

176-
/// Returns the value used to designate the given ABI in const generics.
177-
/// For stable or beta builds this returns an [`u8`], while on nightly the [`Abi`] instance is returned.
178-
#[must_use]
179-
pub const fn key(abi: Abi) -> AbiKey {
180-
abi as _
181-
}
133+
impl FromStr for $t {
134+
type Err = ();
182135

183-
/// Converts an ABI string like "C" into the corresponding value for use in const generics.
184-
/// This is most useful for stable rust since there [`u8`]s are used.
185-
///
186-
/// # Example
187-
/// ```rust
188-
/// use fn_ptr::{abi, FnPtr};
189-
///
190-
/// fn add(a: i32, b: i32) -> i32 {
191-
/// a + b
192-
/// }
193-
///
194-
/// let f: fn(i32, i32) -> i32 = add;
195-
///
196-
/// let c_fn: extern "C" fn(i32, i32) -> i32 =
197-
/// unsafe { f.with_abi::<{ abi!("C") }>() };
198-
/// ```
199-
#[macro_export]
200-
macro_rules! abi {
201-
( $abi:literal ) => {
202-
$crate::abi::key($crate::abi::parse_or_fail($abi))
136+
fn from_str(s: &str) -> Result<Self, Self::Err> {
137+
match s {
138+
$( $tok => Ok(Self::$variant $( { unwind: $uw } )?), )*
139+
_ => Err(()),
140+
}
141+
}
142+
}
203143
};
204144
}
145+
146+
abi_kind_impl!(Abi => {
147+
Rust => "Rust",
148+
149+
C { unwind: false } => "C",
150+
C { unwind: true } => "C-unwind",
151+
System { unwind: false } => "system",
152+
System { unwind: true } => "system-unwind",
153+
154+
Aapcs { unwind: false } => "aapcs",
155+
Aapcs { unwind: true } => "aapcs-unwind",
156+
157+
Cdecl { unwind: false } => "cdecl",
158+
Cdecl { unwind: true } => "cdecl-unwind",
159+
Stdcall { unwind: false } => "stdcall",
160+
Stdcall { unwind: true } => "stdcall-unwind",
161+
Fastcall { unwind: false } => "fastcall",
162+
Fastcall { unwind: true } => "fastcall-unwind",
163+
Thiscall { unwind: false } => "thiscall",
164+
Thiscall { unwind: true } => "thiscall-unwind",
165+
Vectorcall { unwind: false } => "vectorcall",
166+
Vectorcall { unwind: true } => "vectorcall-unwind",
167+
168+
SysV64 { unwind: false } => "sysv64",
169+
SysV64 { unwind: true } => "sysv64-unwind",
170+
Win64 { unwind: false } => "win64",
171+
Win64 { unwind: true } => "win64-unwind",
172+
});

0 commit comments

Comments
 (0)