|
1 | | -#[cfg(nightly_build)] |
2 | | -use core::marker::ConstParamTy; |
3 | 1 | use core::{ |
4 | | - fmt::{Debug, Display}, |
| 2 | + fmt::{self, Debug, Display}, |
5 | 3 | str::FromStr, |
6 | 4 | }; |
7 | 5 |
|
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 |
14 | 9 | pub enum Abi { |
15 | | - /// The default ABI when you write a normal `fn foo()` in any Rust code. |
16 | | - Rust = 0, |
| 10 | + /* universal */ |
17 | 11 | /// 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 */ |
25 | 21 | /// The default for ARM. |
26 | | - Aapcs = 5, |
| 22 | + Aapcs { unwind: bool }, |
| 23 | + |
| 24 | + /* x86 */ |
27 | 25 | /// The default for `x86_32` C code. |
28 | | - Cdecl = 6, |
| 26 | + Cdecl { unwind: bool }, |
29 | 27 | /// 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 }, |
36 | 35 |
|
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 }, |
54 | 41 | } |
55 | 42 |
|
56 | 43 | impl Abi { |
57 | | - /// Returns true if this ABI is an alias. |
58 | 44 | #[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"); |
62 | 49 |
|
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; |
68 | 55 |
|
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 } |
86 | 61 | } |
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 } |
101 | 86 | } |
102 | | - other => *other, |
103 | | - } |
104 | | - } |
105 | | -} |
| 87 | + Abi::Vectorcall { .. } => return None, |
106 | 88 |
|
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) |
124 | 95 | } |
125 | 96 | } |
126 | 97 |
|
127 | 98 | 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 { |
129 | 100 | write!(f, "{}", self.to_str()) |
130 | 101 | } |
131 | 102 | } |
132 | 103 |
|
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 | + } |
170 | 120 |
|
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 | + } |
175 | 132 |
|
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 = (); |
182 | 135 |
|
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 | + } |
203 | 143 | }; |
204 | 144 | } |
| 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