Skip to content

Commit cdb683f

Browse files
committed
Auto merge of rust-lang#122024 - clubby789:remove-spec-option-pe, r=jhpratt
Remove SpecOptionPartialEq With the recent LLVM bump, the specialization for Option::partial_eq on types with niches is no longer necessary. I kept the manual implementation as it still gives us better codegen than the derive (will look at this seperately). Also implemented PartialOrd/Ord by hand as it _somewhat_ improves codegen for rust-lang#49892: https://godbolt.org/z/vx5Y6oW4Y
2 parents b57a10c + f8fd23a commit cdb683f

File tree

5 files changed

+71
-127
lines changed

5 files changed

+71
-127
lines changed

compiler/rustc_index_macros/src/lib.rs

+1-7
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,7 @@ mod newtype;
3434
#[proc_macro]
3535
#[cfg_attr(
3636
feature = "nightly",
37-
allow_internal_unstable(
38-
step_trait,
39-
rustc_attrs,
40-
trusted_step,
41-
spec_option_partial_eq,
42-
min_specialization
43-
)
37+
allow_internal_unstable(step_trait, rustc_attrs, trusted_step, min_specialization)
4438
)]
4539
pub fn newtype_index(input: TokenStream) -> TokenStream {
4640
newtype::newtype(input)

compiler/rustc_index_macros/src/newtype.rs

-28
Original file line numberDiff line numberDiff line change
@@ -156,32 +156,6 @@ impl Parse for Newtype {
156156
}
157157
};
158158

159-
let spec_partial_eq_impl = if let Lit::Int(max) = &max {
160-
if let Ok(max_val) = max.base10_parse::<u32>() {
161-
quote! {
162-
#gate_rustc_only
163-
impl core::option::SpecOptionPartialEq for #name {
164-
#[inline]
165-
fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
166-
if #max_val < u32::MAX {
167-
l.map(|i| i.as_u32()).unwrap_or(#max_val+1) == r.map(|i| i.as_u32()).unwrap_or(#max_val+1)
168-
} else {
169-
match (l, r) {
170-
(Some(l), Some(r)) => r == l,
171-
(None, None) => true,
172-
_ => false
173-
}
174-
}
175-
}
176-
}
177-
}
178-
} else {
179-
quote! {}
180-
}
181-
} else {
182-
quote! {}
183-
};
184-
185159
Ok(Self(quote! {
186160
#(#attrs)*
187161
#[derive(Clone, Copy, PartialEq, Eq, Hash, #(#derive_paths),*)]
@@ -283,8 +257,6 @@ impl Parse for Newtype {
283257

284258
#step
285259

286-
#spec_partial_eq_impl
287-
288260
impl From<#name> for u32 {
289261
#[inline]
290262
fn from(v: #name) -> u32 {

library/core/src/option.rs

+30-62
Original file line numberDiff line numberDiff line change
@@ -558,17 +558,16 @@ use crate::panicking::{panic, panic_str};
558558
use crate::pin::Pin;
559559
use crate::{
560560
cmp, convert, hint, mem,
561-
num::NonZero,
562561
ops::{self, ControlFlow, Deref, DerefMut},
563562
slice,
564563
};
565564

566565
/// The `Option` type. See [the module level documentation](self) for more.
567-
#[derive(Copy, PartialOrd, Eq, Ord, Debug, Hash)]
566+
#[derive(Copy, Eq, Debug, Hash)]
568567
#[rustc_diagnostic_item = "Option"]
569568
#[lang = "Option"]
570569
#[stable(feature = "rust1", since = "1.0.0")]
571-
#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is specialized
570+
#[allow(clippy::derived_hash_with_manual_eq)] // PartialEq is manually implemented equivalently
572571
pub enum Option<T> {
573572
/// No value.
574573
#[lang = "None"]
@@ -2146,83 +2145,52 @@ impl<'a, T> From<&'a mut Option<T>> for Option<&'a mut T> {
21462145
}
21472146
}
21482147

2148+
// Ideally, LLVM should be able to optimize our derive code to this.
2149+
// Once https://github.com/llvm/llvm-project/issues/52622 is fixed, we can
2150+
// go back to deriving `PartialEq`.
21492151
#[stable(feature = "rust1", since = "1.0.0")]
21502152
impl<T> crate::marker::StructuralPartialEq for Option<T> {}
21512153
#[stable(feature = "rust1", since = "1.0.0")]
21522154
impl<T: PartialEq> PartialEq for Option<T> {
21532155
#[inline]
21542156
fn eq(&self, other: &Self) -> bool {
2155-
SpecOptionPartialEq::eq(self, other)
2156-
}
2157-
}
2158-
2159-
/// This specialization trait is a workaround for LLVM not currently (2023-01)
2160-
/// being able to optimize this itself, even though Alive confirms that it would
2161-
/// be legal to do so: <https://github.com/llvm/llvm-project/issues/52622>
2162-
///
2163-
/// Once that's fixed, `Option` should go back to deriving `PartialEq`, as
2164-
/// it used to do before <https://github.com/rust-lang/rust/pull/103556>.
2165-
#[unstable(feature = "spec_option_partial_eq", issue = "none", reason = "exposed only for rustc")]
2166-
#[doc(hidden)]
2167-
pub trait SpecOptionPartialEq: Sized {
2168-
fn eq(l: &Option<Self>, other: &Option<Self>) -> bool;
2169-
}
2170-
2171-
#[unstable(feature = "spec_option_partial_eq", issue = "none", reason = "exposed only for rustc")]
2172-
impl<T: PartialEq> SpecOptionPartialEq for T {
2173-
#[inline]
2174-
default fn eq(l: &Option<T>, r: &Option<T>) -> bool {
2175-
match (l, r) {
2157+
// Spelling out the cases explicitly optimizes better than
2158+
// `_ => false`
2159+
match (self, other) {
21762160
(Some(l), Some(r)) => *l == *r,
2161+
(Some(_), None) => false,
2162+
(None, Some(_)) => false,
21772163
(None, None) => true,
2178-
_ => false,
21792164
}
21802165
}
21812166
}
21822167

2183-
macro_rules! non_zero_option {
2184-
( $( #[$stability: meta] $NZ:ty; )+ ) => {
2185-
$(
2186-
#[$stability]
2187-
impl SpecOptionPartialEq for $NZ {
2188-
#[inline]
2189-
fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
2190-
l.map(Self::get).unwrap_or(0) == r.map(Self::get).unwrap_or(0)
2191-
}
2192-
}
2193-
)+
2194-
};
2195-
}
2196-
2197-
non_zero_option! {
2198-
#[stable(feature = "nonzero", since = "1.28.0")] NonZero<u8>;
2199-
#[stable(feature = "nonzero", since = "1.28.0")] NonZero<u16>;
2200-
#[stable(feature = "nonzero", since = "1.28.0")] NonZero<u32>;
2201-
#[stable(feature = "nonzero", since = "1.28.0")] NonZero<u64>;
2202-
#[stable(feature = "nonzero", since = "1.28.0")] NonZero<u128>;
2203-
#[stable(feature = "nonzero", since = "1.28.0")] NonZero<usize>;
2204-
#[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i8>;
2205-
#[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i16>;
2206-
#[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i32>;
2207-
#[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i64>;
2208-
#[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<i128>;
2209-
#[stable(feature = "signed_nonzero", since = "1.34.0")] NonZero<isize>;
2210-
}
2211-
2212-
#[stable(feature = "nonnull", since = "1.25.0")]
2213-
impl<T> SpecOptionPartialEq for crate::ptr::NonNull<T> {
2168+
// Manually implementing here somewhat improves codegen for
2169+
// https://github.com/rust-lang/rust/issues/49892, although still
2170+
// not optimal.
2171+
#[stable(feature = "rust1", since = "1.0.0")]
2172+
impl<T: PartialOrd> PartialOrd for Option<T> {
22142173
#[inline]
2215-
fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
2216-
l.map(Self::as_ptr).unwrap_or_else(|| crate::ptr::null_mut())
2217-
== r.map(Self::as_ptr).unwrap_or_else(|| crate::ptr::null_mut())
2174+
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
2175+
match (self, other) {
2176+
(Some(l), Some(r)) => l.partial_cmp(r),
2177+
(Some(_), None) => Some(cmp::Ordering::Greater),
2178+
(None, Some(_)) => Some(cmp::Ordering::Less),
2179+
(None, None) => Some(cmp::Ordering::Equal),
2180+
}
22182181
}
22192182
}
22202183

22212184
#[stable(feature = "rust1", since = "1.0.0")]
2222-
impl SpecOptionPartialEq for cmp::Ordering {
2185+
impl<T: Ord> Ord for Option<T> {
22232186
#[inline]
2224-
fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
2225-
l.map_or(2, |x| x as i8) == r.map_or(2, |x| x as i8)
2187+
fn cmp(&self, other: &Self) -> cmp::Ordering {
2188+
match (self, other) {
2189+
(Some(l), Some(r)) => l.cmp(r),
2190+
(Some(_), None) => cmp::Ordering::Greater,
2191+
(None, Some(_)) => cmp::Ordering::Less,
2192+
(None, None) => cmp::Ordering::Equal,
2193+
}
22262194
}
22272195
}
22282196

tests/assembly/option-nonzero-eq.rs

-27
This file was deleted.

tests/codegen/option-nonzero-eq.rs renamed to tests/codegen/option-niche-eq.rs

+40-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//@ compile-flags: -O -Zmerge-functions=disabled
2+
//@ min-llvm-version: 18
23
#![crate_type = "lib"]
34
#![feature(generic_nonzero)]
45

@@ -7,9 +8,6 @@ use core::cmp::Ordering;
78
use core::ptr::NonNull;
89
use core::num::NonZero;
910

10-
// See also tests/assembly/option-nonzero-eq.rs, for cases with `assume`s in the
11-
// LLVM and thus don't optimize down clearly here, but do in assembly.
12-
1311
// CHECK-lABEL: @non_zero_eq
1412
#[no_mangle]
1513
pub fn non_zero_eq(l: Option<NonZero<u32>>, r: Option<NonZero<u32>>) -> bool {
@@ -36,3 +34,42 @@ pub fn non_null_eq(l: Option<NonNull<u8>>, r: Option<NonNull<u8>>) -> bool {
3634
// CHECK-NEXT: ret i1
3735
l == r
3836
}
37+
38+
// CHECK-lABEL: @ordering_eq
39+
#[no_mangle]
40+
pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool {
41+
// CHECK: start:
42+
// CHECK-NEXT: icmp eq i8
43+
// CHECK-NEXT: ret i1
44+
l == r
45+
}
46+
47+
#[derive(PartialEq)]
48+
pub enum EnumWithNiche {
49+
A,
50+
B,
51+
C,
52+
D,
53+
E,
54+
F,
55+
G,
56+
}
57+
58+
// CHECK-lABEL: @niche_eq
59+
#[no_mangle]
60+
pub fn niche_eq(l: Option<EnumWithNiche>, r: Option<EnumWithNiche>) -> bool {
61+
// CHECK: start:
62+
// CHECK-NEXT: icmp eq i8
63+
// CHECK-NEXT: ret i1
64+
l == r
65+
}
66+
67+
// FIXME: This should work too
68+
// // FIXME-CHECK-lABEL: @bool_eq
69+
// #[no_mangle]
70+
// pub fn bool_eq(l: Option<bool>, r: Option<bool>) -> bool {
71+
// // FIXME-CHECK: start:
72+
// // FIXME-CHECK-NEXT: icmp eq i8
73+
// // FIXME-CHECK-NEXT: ret i1
74+
// l == r
75+
// }

0 commit comments

Comments
 (0)