Skip to content
/ rust Public
forked from rust-lang/rust

Commit 27dc222

Browse files
Rollup merge of rust-lang#136901 - workingjubilee:stabilize-externabi-hashing-forever, r=compiler-errors
compiler: give `ExternAbi` truly stable `Hash` and `Ord` Currently, `ExternAbi` has a bunch of code to handle the reality that, as an enum, adding more variants to it will risk it hashing differently. It forces all of those variants to be added in a fixed order, except this means that the order of the variants doesn't correspond to any logical order except "historical accident". This is all to avoid having to rebless two tests. Perhaps there were more, once upon a time? But then we invented normalization in our test suite to handle exactly this sort of issue in a more general way. There are two options here: - Get rid of all the logical overhead and shrug, embracing blessing a couple of tests sometimes - Change `ExternAbi` to have an ordering and hash that doesn't depend on the number of variants As `ExternAbi` is essentially a strongly-typed string, and thus no two strings can be identical, this implements the second of the two by hand-implementing `Ord` and `Hash` to make the hashing and comparison based on the string! This will diff the current hashes, but they will diff no more after this.
2 parents 269d784 + f8570e8 commit 27dc222

File tree

15 files changed

+163
-176
lines changed

15 files changed

+163
-176
lines changed

Cargo.lock

+1-2
Original file line numberDiff line numberDiff line change
@@ -3409,7 +3409,6 @@ dependencies = [
34093409
"rustc_parse",
34103410
"rustc_session",
34113411
"rustc_span",
3412-
"rustc_target",
34133412
"thin-vec",
34143413
]
34153414

@@ -4423,6 +4422,7 @@ version = "0.0.0"
44234422
dependencies = [
44244423
"parking_lot",
44254424
"rustc-rayon-core",
4425+
"rustc_abi",
44264426
"rustc_ast",
44274427
"rustc_data_structures",
44284428
"rustc_errors",
@@ -4434,7 +4434,6 @@ dependencies = [
44344434
"rustc_serialize",
44354435
"rustc_session",
44364436
"rustc_span",
4437-
"rustc_target",
44384437
"smallvec",
44394438
"thin-vec",
44404439
"tracing",

compiler/rustc_abi/src/extern_abi.rs

+131-132
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1+
use std::cmp::Ordering;
12
use std::fmt;
3+
use std::hash::{Hash, Hasher};
24

3-
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
5+
#[cfg(feature = "nightly")]
6+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, StableOrd};
7+
#[cfg(feature = "nightly")]
8+
use rustc_macros::{Decodable, Encodable};
49

510
#[cfg(test)]
611
mod tests;
712

813
use ExternAbi as Abi;
914

10-
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
11-
#[derive(HashStable_Generic, Encodable, Decodable)]
15+
#[derive(Clone, Copy, Debug)]
16+
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable))]
1217
pub enum ExternAbi {
1318
// Some of the ABIs come first because every time we add a new ABI, we have to re-bless all the
1419
// hashing tests. These are used in many places, so giving them stable values reduces test
@@ -68,7 +73,124 @@ pub enum ExternAbi {
6873
RiscvInterruptS,
6974
}
7075

71-
impl Abi {
76+
macro_rules! abi_impls {
77+
($e_name:ident = {
78+
$($variant:ident $({ unwind: $uw:literal })? =><= $tok:literal,)*
79+
}) => {
80+
impl $e_name {
81+
pub const ALL_VARIANTS: &[Self] = &[
82+
$($e_name::$variant $({ unwind: $uw })*,)*
83+
];
84+
pub const fn as_str(&self) -> &'static str {
85+
match self {
86+
$($e_name::$variant $( { unwind: $uw } )* => $tok,)*
87+
}
88+
}
89+
}
90+
91+
impl ::core::str::FromStr for $e_name {
92+
type Err = AbiFromStrErr;
93+
fn from_str(s: &str) -> Result<$e_name, Self::Err> {
94+
match s {
95+
$($tok => Ok($e_name::$variant $({ unwind: $uw })*),)*
96+
_ => Err(AbiFromStrErr::Unknown),
97+
}
98+
}
99+
}
100+
}
101+
}
102+
103+
#[derive(Debug)]
104+
pub enum AbiFromStrErr {
105+
Unknown,
106+
}
107+
108+
abi_impls! {
109+
ExternAbi = {
110+
C { unwind: false } =><= "C",
111+
CCmseNonSecureCall =><= "C-cmse-nonsecure-call",
112+
CCmseNonSecureEntry =><= "C-cmse-nonsecure-entry",
113+
C { unwind: true } =><= "C-unwind",
114+
Rust =><= "Rust",
115+
Aapcs { unwind: false } =><= "aapcs",
116+
Aapcs { unwind: true } =><= "aapcs-unwind",
117+
AvrInterrupt =><= "avr-interrupt",
118+
AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
119+
Cdecl { unwind: false } =><= "cdecl",
120+
Cdecl { unwind: true } =><= "cdecl-unwind",
121+
EfiApi =><= "efiapi",
122+
Fastcall { unwind: false } =><= "fastcall",
123+
Fastcall { unwind: true } =><= "fastcall-unwind",
124+
GpuKernel =><= "gpu-kernel",
125+
Msp430Interrupt =><= "msp430-interrupt",
126+
PtxKernel =><= "ptx-kernel",
127+
RiscvInterruptM =><= "riscv-interrupt-m",
128+
RiscvInterruptS =><= "riscv-interrupt-s",
129+
RustCall =><= "rust-call",
130+
RustCold =><= "rust-cold",
131+
RustIntrinsic =><= "rust-intrinsic",
132+
Stdcall { unwind: false } =><= "stdcall",
133+
Stdcall { unwind: true } =><= "stdcall-unwind",
134+
System { unwind: false } =><= "system",
135+
System { unwind: true } =><= "system-unwind",
136+
SysV64 { unwind: false } =><= "sysv64",
137+
SysV64 { unwind: true } =><= "sysv64-unwind",
138+
Thiscall { unwind: false } =><= "thiscall",
139+
Thiscall { unwind: true } =><= "thiscall-unwind",
140+
Unadjusted =><= "unadjusted",
141+
Vectorcall { unwind: false } =><= "vectorcall",
142+
Vectorcall { unwind: true } =><= "vectorcall-unwind",
143+
Win64 { unwind: false } =><= "win64",
144+
Win64 { unwind: true } =><= "win64-unwind",
145+
X86Interrupt =><= "x86-interrupt",
146+
}
147+
}
148+
149+
impl Ord for ExternAbi {
150+
fn cmp(&self, rhs: &Self) -> Ordering {
151+
self.as_str().cmp(rhs.as_str())
152+
}
153+
}
154+
155+
impl PartialOrd for ExternAbi {
156+
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
157+
Some(self.cmp(rhs))
158+
}
159+
}
160+
161+
impl PartialEq for ExternAbi {
162+
fn eq(&self, rhs: &Self) -> bool {
163+
self.cmp(rhs) == Ordering::Equal
164+
}
165+
}
166+
167+
impl Eq for ExternAbi {}
168+
169+
impl Hash for ExternAbi {
170+
fn hash<H: Hasher>(&self, state: &mut H) {
171+
self.as_str().hash(state);
172+
// double-assurance of a prefix breaker
173+
u32::from_be_bytes(*b"ABI\0").hash(state);
174+
}
175+
}
176+
177+
#[cfg(feature = "nightly")]
178+
impl<C> HashStable<C> for ExternAbi {
179+
#[inline]
180+
fn hash_stable(&self, _: &mut C, hasher: &mut StableHasher) {
181+
Hash::hash(self, hasher);
182+
}
183+
}
184+
185+
#[cfg(feature = "nightly")]
186+
impl StableOrd for ExternAbi {
187+
const CAN_USE_UNSTABLE_SORT: bool = true;
188+
189+
// because each ABI is hashed like a string, there is no possible instability
190+
const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
191+
}
192+
193+
impl ExternAbi {
72194
pub fn supports_varargs(self) -> bool {
73195
// * C and Cdecl obviously support varargs.
74196
// * C can be based on Aapcs, SysV64 or Win64, so they must support varargs.
@@ -92,144 +214,21 @@ impl Abi {
92214
}
93215
}
94216

95-
#[derive(Copy, Clone)]
96-
pub struct AbiData {
97-
pub abi: Abi,
98-
99-
/// Name of this ABI as we like it called.
100-
pub name: &'static str,
101-
}
102-
103-
#[allow(non_upper_case_globals)]
104-
pub const AbiDatas: &[AbiData] = &[
105-
AbiData { abi: Abi::Rust, name: "Rust" },
106-
AbiData { abi: Abi::C { unwind: false }, name: "C" },
107-
AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" },
108-
AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" },
109-
AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" },
110-
AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" },
111-
AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" },
112-
AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" },
113-
AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" },
114-
AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" },
115-
AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" },
116-
AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" },
117-
AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" },
118-
AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" },
119-
AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" },
120-
AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" },
121-
AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" },
122-
AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" },
123-
AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" },
124-
AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" },
125-
AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" },
126-
AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" },
127-
AbiData { abi: Abi::GpuKernel, name: "gpu-kernel" },
128-
AbiData { abi: Abi::EfiApi, name: "efiapi" },
129-
AbiData { abi: Abi::AvrInterrupt, name: "avr-interrupt" },
130-
AbiData { abi: Abi::AvrNonBlockingInterrupt, name: "avr-non-blocking-interrupt" },
131-
AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call" },
132-
AbiData { abi: Abi::CCmseNonSecureEntry, name: "C-cmse-nonsecure-entry" },
133-
AbiData { abi: Abi::System { unwind: false }, name: "system" },
134-
AbiData { abi: Abi::System { unwind: true }, name: "system-unwind" },
135-
AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic" },
136-
AbiData { abi: Abi::RustCall, name: "rust-call" },
137-
AbiData { abi: Abi::Unadjusted, name: "unadjusted" },
138-
AbiData { abi: Abi::RustCold, name: "rust-cold" },
139-
AbiData { abi: Abi::RiscvInterruptM, name: "riscv-interrupt-m" },
140-
AbiData { abi: Abi::RiscvInterruptS, name: "riscv-interrupt-s" },
141-
];
142-
143-
#[derive(Copy, Clone, Debug)]
144-
pub struct AbiUnsupported {}
145-
/// Returns the ABI with the given name (if any).
146-
pub fn lookup(name: &str) -> Result<Abi, AbiUnsupported> {
147-
AbiDatas
148-
.iter()
149-
.find(|abi_data| name == abi_data.name)
150-
.map(|&x| x.abi)
151-
.ok_or_else(|| AbiUnsupported {})
152-
}
153-
154217
pub fn all_names() -> Vec<&'static str> {
155-
AbiDatas.iter().map(|d| d.name).collect()
218+
ExternAbi::ALL_VARIANTS.iter().map(|abi| abi.as_str()).collect()
156219
}
157220

158-
impl Abi {
221+
impl ExternAbi {
159222
/// Default ABI chosen for `extern fn` declarations without an explicit ABI.
160223
pub const FALLBACK: Abi = Abi::C { unwind: false };
161224

162-
#[inline]
163-
pub fn index(self) -> usize {
164-
// N.B., this ordering MUST match the AbiDatas array above.
165-
// (This is ensured by the test indices_are_correct().)
166-
use Abi::*;
167-
let i = match self {
168-
// Cross-platform ABIs
169-
Rust => 0,
170-
C { unwind: false } => 1,
171-
C { unwind: true } => 2,
172-
// Platform-specific ABIs
173-
Cdecl { unwind: false } => 3,
174-
Cdecl { unwind: true } => 4,
175-
Stdcall { unwind: false } => 5,
176-
Stdcall { unwind: true } => 6,
177-
Fastcall { unwind: false } => 7,
178-
Fastcall { unwind: true } => 8,
179-
Vectorcall { unwind: false } => 9,
180-
Vectorcall { unwind: true } => 10,
181-
Thiscall { unwind: false } => 11,
182-
Thiscall { unwind: true } => 12,
183-
Aapcs { unwind: false } => 13,
184-
Aapcs { unwind: true } => 14,
185-
Win64 { unwind: false } => 15,
186-
Win64 { unwind: true } => 16,
187-
SysV64 { unwind: false } => 17,
188-
SysV64 { unwind: true } => 18,
189-
PtxKernel => 19,
190-
Msp430Interrupt => 20,
191-
X86Interrupt => 21,
192-
GpuKernel => 22,
193-
EfiApi => 23,
194-
AvrInterrupt => 24,
195-
AvrNonBlockingInterrupt => 25,
196-
CCmseNonSecureCall => 26,
197-
CCmseNonSecureEntry => 27,
198-
// Cross-platform ABIs
199-
System { unwind: false } => 28,
200-
System { unwind: true } => 29,
201-
RustIntrinsic => 30,
202-
RustCall => 31,
203-
Unadjusted => 32,
204-
RustCold => 33,
205-
RiscvInterruptM => 34,
206-
RiscvInterruptS => 35,
207-
};
208-
debug_assert!(
209-
AbiDatas
210-
.iter()
211-
.enumerate()
212-
.find(|(_, AbiData { abi, .. })| *abi == self)
213-
.map(|(index, _)| index)
214-
.expect("abi variant has associated data")
215-
== i,
216-
"Abi index did not match `AbiDatas` ordering"
217-
);
218-
i
219-
}
220-
221-
#[inline]
222-
pub fn data(self) -> &'static AbiData {
223-
&AbiDatas[self.index()]
224-
}
225-
226225
pub fn name(self) -> &'static str {
227-
self.data().name
226+
self.as_str()
228227
}
229228
}
230229

231-
impl fmt::Display for Abi {
230+
impl fmt::Display for ExternAbi {
232231
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233-
write!(f, "\"{}\"", self.name())
232+
write!(f, "\"{}\"", self.as_str())
234233
}
235234
}
+12-10
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
use std::assert_matches::assert_matches;
2+
use std::str::FromStr;
23

34
use super::*;
45

56
#[allow(non_snake_case)]
67
#[test]
78
fn lookup_Rust() {
8-
let abi = lookup("Rust");
9-
assert!(abi.is_ok() && abi.unwrap().data().name == "Rust");
9+
let abi = ExternAbi::from_str("Rust");
10+
assert!(abi.is_ok() && abi.unwrap().as_str() == "Rust");
1011
}
1112

1213
#[test]
1314
fn lookup_cdecl() {
14-
let abi = lookup("cdecl");
15-
assert!(abi.is_ok() && abi.unwrap().data().name == "cdecl");
15+
let abi = ExternAbi::from_str("cdecl");
16+
assert!(abi.is_ok() && abi.unwrap().as_str() == "cdecl");
1617
}
1718

1819
#[test]
1920
fn lookup_baz() {
20-
let abi = lookup("baz");
21-
assert_matches!(abi, Err(AbiUnsupported {}));
21+
let abi = ExternAbi::from_str("baz");
22+
assert_matches!(abi, Err(AbiFromStrErr::Unknown));
2223
}
2324

2425
#[test]
25-
fn indices_are_correct() {
26-
for (i, abi_data) in AbiDatas.iter().enumerate() {
27-
assert_eq!(i, abi_data.abi.index());
28-
}
26+
fn guarantee_lexicographic_ordering() {
27+
let abis = ExternAbi::ALL_VARIANTS;
28+
let mut sorted_abis = abis.to_vec();
29+
sorted_abis.sort_unstable();
30+
assert_eq!(abis, sorted_abis);
2931
}

compiler/rustc_abi/src/lib.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,17 @@ use bitflags::bitflags;
5252
use rustc_data_structures::stable_hasher::StableOrd;
5353
use rustc_index::{Idx, IndexSlice, IndexVec};
5454
#[cfg(feature = "nightly")]
55-
use rustc_macros::HashStable_Generic;
56-
#[cfg(feature = "nightly")]
57-
use rustc_macros::{Decodable_Generic, Encodable_Generic};
55+
use rustc_macros::{Decodable_Generic, Encodable_Generic, HashStable_Generic};
5856

5957
mod callconv;
6058
mod layout;
6159
#[cfg(test)]
6260
mod tests;
6361

64-
#[cfg(feature = "nightly")]
6562
mod extern_abi;
6663

6764
pub use callconv::{Heterogeneous, HomogeneousAggregate, Reg, RegKind};
68-
#[cfg(feature = "nightly")]
69-
pub use extern_abi::{AbiDatas, AbiUnsupported, ExternAbi, all_names, lookup};
65+
pub use extern_abi::{ExternAbi, all_names};
7066
#[cfg(feature = "nightly")]
7167
pub use layout::{FIRST_VARIANT, FieldIdx, Layout, TyAbiInterface, TyAndLayout, VariantIdx};
7268
pub use layout::{LayoutCalculator, LayoutCalculatorError};

compiler/rustc_ast_lowering/src/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1475,7 +1475,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
14751475

14761476
pub(super) fn lower_abi(&mut self, abi_str: StrLit) -> ExternAbi {
14771477
let ast::StrLit { symbol_unescaped, span, .. } = abi_str;
1478-
let extern_abi = rustc_abi::lookup(symbol_unescaped.as_str()).unwrap_or_else(|_| {
1478+
let extern_abi = symbol_unescaped.as_str().parse().unwrap_or_else(|_| {
14791479
self.error_on_invalid_abi(abi_str);
14801480
ExternAbi::Rust
14811481
});

0 commit comments

Comments
 (0)