Skip to content

Commit 023309d

Browse files
daxpeddaModProg
authored andcommitted
Take representation into account for (Partial)Ord
1 parent 7eaf870 commit 023309d

File tree

15 files changed

+430
-196
lines changed

15 files changed

+430
-196
lines changed

src/data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ impl<'a> Data<'a> {
255255
}
256256

257257
/// Returns the destructuring `other` pattern of this [`Data`].
258-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
258+
#[cfg(not(feature = "nightly"))]
259259
pub fn other_pattern_skip(&self) -> &Pat {
260260
match self.fields() {
261261
Either::Left(fields) => &fields.other_pattern_skip,

src/data/fields.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! Storage for items or variants with data.
22
3+
use std::iter;
4+
35
use syn::{
6+
punctuated::Punctuated,
47
token::{Brace, Paren},
5-
FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatStruct, PatTupleStruct, Path,
6-
Result, Token,
8+
FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatRest, PatStruct, PatTupleStruct,
9+
Path, Result, Token,
710
};
8-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
9-
use {std::iter, syn::punctuated::Punctuated, syn::PatRest};
1011

1112
use crate::{DeriveWhere, Field, Skip, Trait};
1213

@@ -19,7 +20,6 @@ pub struct Fields<'a> {
1920
pub other_pattern: Pat,
2021
/// [Pattern](Pat) to use in a match arm to destructure `other` but skipping
2122
/// all fields.
22-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
2323
pub other_pattern_skip: Pat,
2424
/// [`Field`]s of this struct, union or variant.
2525
pub fields: Vec<Field<'a>>,
@@ -36,7 +36,6 @@ impl<'a> Fields<'a> {
3636
let fields = Field::from_named(derive_wheres, skip_inner, fields)?;
3737

3838
let self_pattern = Self::struct_pattern(path.clone(), &fields, |field| &field.self_ident);
39-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
4039
let other_pattern_skip = Pat::Struct(PatStruct {
4140
attrs: Vec::new(),
4241
qself: None,
@@ -53,7 +52,6 @@ impl<'a> Fields<'a> {
5352
Ok(Self {
5453
self_pattern,
5554
other_pattern,
56-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
5755
other_pattern_skip,
5856
fields,
5957
})
@@ -69,7 +67,6 @@ impl<'a> Fields<'a> {
6967
let fields = Field::from_unnamed(derive_wheres, skip_inner, fields)?;
7068

7169
let self_pattern = Self::tuple_pattern(path.clone(), &fields, |field| &field.self_ident);
72-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
7370
let other_pattern_skip = Pat::TupleStruct(PatTupleStruct {
7471
attrs: Vec::new(),
7572
qself: None,
@@ -86,7 +83,6 @@ impl<'a> Fields<'a> {
8683
Ok(Self {
8784
self_pattern,
8885
other_pattern,
89-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
9086
other_pattern_skip,
9187
fields,
9288
})

src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ impl Error {
212212
syn::Error::new(span, "duplicate trait with the same bound")
213213
}
214214

215+
/// `repr` attribute that isn't a meta list.
216+
pub fn repr(span: Span) -> syn::Error {
217+
syn::Error::new(span, "unable to parse `repr` attribute")
218+
}
219+
215220
/// Unsupported default option if [`Default`] isn't implemented.
216221
pub fn default(span: Span) -> syn::Error {
217222
syn::Error::new(

src/input.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use syn::{DeriveInput, GenericParam, Generics, Result};
55

66
#[cfg(feature = "zeroize")]
77
use crate::DeriveTrait;
8-
use crate::{Data, DeriveWhere, Either, Error, Item, ItemAttr, Trait};
8+
use crate::{Data, DeriveWhere, Discriminant, Either, Error, Item, ItemAttr, Trait};
99

1010
/// Parsed input.
1111
pub struct Input<'a> {
@@ -51,6 +51,8 @@ impl<'a> Input<'a> {
5151
)
5252
.map(Item::Item)?,
5353
syn::Data::Enum(data) => {
54+
let discriminant = Discriminant::parse(attrs, &data.variants)?;
55+
5456
let variants = data
5557
.variants
5658
.iter()
@@ -97,6 +99,7 @@ impl<'a> Input<'a> {
9799
}
98100

99101
Item::Enum {
102+
discriminant,
100103
ident,
101104
variants,
102105
incomparable,

src/item.rs

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
//! Intermediate representation of item data.
22
3-
use syn::Ident;
3+
use proc_macro2::TokenStream;
4+
use quote::{quote, ToTokens};
5+
use syn::{
6+
punctuated::Punctuated, spanned::Spanned, Attribute, Fields, Ident, Meta, Result, Token,
7+
Variant,
8+
};
49

5-
use crate::{attr::Incomparable, Data, Trait};
10+
use crate::{Data, Error, Incomparable, Trait};
611

712
/// Fields or variants of an item.
813
#[cfg_attr(test, derive(Debug))]
914
#[allow(clippy::large_enum_variant)]
1015
pub enum Item<'a> {
1116
/// Enum.
1217
Enum {
18+
/// Type of discriminant used.
19+
discriminant: Discriminant,
1320
/// [`struct@Ident`] of this enum.
1421
ident: &'a Ident,
1522
/// [`Incomparable`] attribute of this enum.
@@ -88,3 +95,146 @@ impl Item<'_> {
8895
}
8996
}
9097
}
98+
99+
/// Type of discriminant used.
100+
#[cfg_attr(test, derive(Debug))]
101+
pub enum Discriminant {
102+
/// The enum has only a single variant.
103+
Single,
104+
/// The enum uses the default representation and has only unit variants.
105+
UnitDefault,
106+
/// The enum uses the default representation but has a non-unit variant.
107+
Default,
108+
/// The enum uses a non-default representation and has only unit variants.
109+
UnitRepr(Representation),
110+
/// The enum uses a non-default representation and has a non-unit variant.
111+
Repr(Representation),
112+
}
113+
114+
impl Discriminant {
115+
/// Parse the representation of an item.
116+
pub fn parse(attrs: &[Attribute], variants: &Punctuated<Variant, Token![,]>) -> Result<Self> {
117+
if variants.len() == 1 {
118+
return Ok(Self::Single);
119+
}
120+
121+
let mut has_repr = None;
122+
123+
for attr in attrs {
124+
if attr.path().is_ident("repr") {
125+
if let Meta::List(list) = &attr.meta {
126+
let list =
127+
list.parse_args_with(Punctuated::<Ident, Token![,]>::parse_terminated)?;
128+
129+
for ident in list {
130+
if let Some(repr) = Representation::parse(&ident) {
131+
has_repr = Some(repr);
132+
break;
133+
}
134+
}
135+
} else {
136+
return Err(Error::repr(attr.span()));
137+
}
138+
}
139+
}
140+
141+
let is_unit = variants.iter().all(|variant| match &variant.fields {
142+
Fields::Named(fields) => fields.named.is_empty(),
143+
Fields::Unnamed(fields) => fields.unnamed.is_empty(),
144+
Fields::Unit => true,
145+
});
146+
147+
Ok(if let Some(repr) = has_repr {
148+
if is_unit {
149+
Self::UnitRepr(repr)
150+
} else {
151+
Self::Repr(repr)
152+
}
153+
} else if is_unit {
154+
Self::UnitDefault
155+
} else {
156+
Self::Default
157+
})
158+
}
159+
}
160+
161+
/// The type used to represent an enum.
162+
#[cfg_attr(test, derive(Debug))]
163+
pub enum Representation {
164+
/// [`u8`].
165+
U8,
166+
/// [`u16`].
167+
U16,
168+
/// [`u32`].
169+
U32,
170+
/// [`u64`].
171+
U64,
172+
/// [`u128`].
173+
U128,
174+
/// [`usize`].
175+
USize,
176+
/// [`i8`].
177+
I8,
178+
/// [`i16`].
179+
I16,
180+
/// [`i32`].
181+
I32,
182+
/// [`i64`].
183+
I64,
184+
/// [`i128`].
185+
I128,
186+
/// [`isize`].
187+
ISize,
188+
}
189+
190+
impl Representation {
191+
/// Parse an [`struct@Ident`] to a valid representation if it is.
192+
fn parse(ident: &Ident) -> Option<Self> {
193+
Some(if ident == "u8" {
194+
Self::U8
195+
} else if ident == "u16" {
196+
Self::U16
197+
} else if ident == "u32" {
198+
Self::U32
199+
} else if ident == "u64" {
200+
Self::U64
201+
} else if ident == "u128" {
202+
Self::U128
203+
} else if ident == "usize" {
204+
Self::USize
205+
} else if ident == "i8" {
206+
Self::I8
207+
} else if ident == "i16" {
208+
Self::I16
209+
} else if ident == "i32" {
210+
Self::I32
211+
} else if ident == "i64" {
212+
Self::I64
213+
} else if ident == "i128" {
214+
Self::I128
215+
} else if ident == "isize" {
216+
Self::ISize
217+
} else {
218+
return None;
219+
})
220+
}
221+
}
222+
223+
impl ToTokens for Representation {
224+
fn to_tokens(&self, tokens: &mut TokenStream) {
225+
match self {
226+
Representation::U8 => tokens.extend(quote! { u8 }),
227+
Representation::U16 => tokens.extend(quote! { u16 }),
228+
Representation::U32 => tokens.extend(quote! { u32 }),
229+
Representation::U64 => tokens.extend(quote! { u64 }),
230+
Representation::U128 => tokens.extend(quote! { u128 }),
231+
Representation::USize => tokens.extend(quote! { usize }),
232+
Representation::I8 => tokens.extend(quote! { i8 }),
233+
Representation::I16 => tokens.extend(quote! { i16 }),
234+
Representation::I32 => tokens.extend(quote! { i32 }),
235+
Representation::I64 => tokens.extend(quote! { i64 }),
236+
Representation::I128 => tokens.extend(quote! { i128 }),
237+
Representation::ISize => tokens.extend(quote! { isize }),
238+
}
239+
}
240+
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ use self::{
413413
data::{Data, DataType, Field, SimpleType},
414414
error::Error,
415415
input::Input,
416-
item::Item,
416+
item::{Discriminant, Item},
417417
trait_::{Trait, TraitImpl},
418418
util::Either,
419419
};

src/test/basic.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -193,15 +193,7 @@ fn enum_() -> Result<()> {
193193
let ord = quote! {
194194
::core::cmp::Ord::cmp(&__self_disc, &__other_disc)
195195
};
196-
#[cfg(not(any(feature = "nightly", feature = "safe")))]
197-
let ord = quote! {
198-
::core::cmp::Ord::cmp(
199-
&unsafe { ::core::mem::transmute::<_, isize>(__self_disc) },
200-
&unsafe { ::core::mem::transmute::<_, isize>(__other_disc) },
201-
)
202-
};
203-
204-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
196+
#[cfg(not(feature = "nightly"))]
205197
let ord = quote! {
206198
match self {
207199
Test::A { field: ref __field_field } => ::core::cmp::Ordering::Less,

src/test/enum_.rs

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,7 @@ fn two_data() -> Result<()> {
124124
let partial_ord = quote! {
125125
::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc)
126126
};
127-
#[cfg(not(any(feature = "nightly", feature = "safe")))]
128-
let partial_ord = quote! {
129-
::core::cmp::PartialOrd::partial_cmp(
130-
&unsafe { ::core::mem::transmute::<_, isize>(__self_disc) },
131-
&unsafe { ::core::mem::transmute::<_, isize>(__other_disc) },
132-
)
133-
};
134-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
127+
#[cfg(not(feature = "nightly"))]
135128
let partial_ord = quote! {
136129
match self {
137130
Test::A(ref __field_0) => ::core::option::Option::Some(::core::cmp::Ordering::Less),
@@ -206,14 +199,7 @@ fn unit() -> Result<()> {
206199
let partial_ord = quote! {
207200
::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc)
208201
};
209-
#[cfg(not(any(feature = "nightly", feature = "safe")))]
210-
let partial_ord = quote! {
211-
::core::cmp::PartialOrd::partial_cmp(
212-
&unsafe { ::core::mem::transmute::<_, isize>(__self_disc) },
213-
&unsafe { ::core::mem::transmute::<_, isize>(__other_disc) },
214-
)
215-
};
216-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
202+
#[cfg(not(feature = "nightly"))]
217203
let partial_ord = quote! {
218204
match self {
219205
Test::A(ref __field_0) => ::core::option::Option::Some(::core::cmp::Ordering::Less),
@@ -281,14 +267,7 @@ fn struct_unit() -> Result<()> {
281267
let partial_ord = quote! {
282268
::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc)
283269
};
284-
#[cfg(not(any(feature = "nightly", feature = "safe")))]
285-
let partial_ord = quote! {
286-
::core::cmp::PartialOrd::partial_cmp(
287-
&unsafe { ::core::mem::transmute::<_, isize>(__self_disc) },
288-
&unsafe { ::core::mem::transmute::<_, isize>(__other_disc) },
289-
)
290-
};
291-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
270+
#[cfg(not(feature = "nightly"))]
292271
let partial_ord = quote! {
293272
match self {
294273
Test::A(ref __field_0) => ::core::option::Option::Some(::core::cmp::Ordering::Less),
@@ -356,14 +335,7 @@ fn tuple_unit() -> Result<()> {
356335
let partial_ord = quote! {
357336
::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc)
358337
};
359-
#[cfg(not(any(feature = "nightly", feature = "safe")))]
360-
let partial_ord = quote! {
361-
::core::cmp::PartialOrd::partial_cmp(
362-
&unsafe { ::core::mem::transmute::<_, isize>(__self_disc) },
363-
&unsafe { ::core::mem::transmute::<_, isize>(__other_disc) },
364-
)
365-
};
366-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
338+
#[cfg(not(feature = "nightly"))]
367339
let partial_ord = quote! {
368340
match self {
369341
Test::A(ref __field_0) => ::core::option::Option::Some(::core::cmp::Ordering::Less),

src/test/incomparable.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ fn variants() -> Result<()> {
5555
_ => ::core::option::Option::Some(::core::cmp::Ordering::Equal),
5656
}
5757
} else {
58-
::core::cmp::PartialOrd::partial_cmp(
59-
&unsafe { ::core::mem::transmute::<_, isize>(__self_disc) },
60-
&unsafe { ::core::mem::transmute::<_, isize>(__other_disc) },
61-
)
58+
match self {
59+
Test::A => ::core::option::Option::Some(::core::cmp::Ordering::Less),
60+
Test::C(ref __field_0) => ::core::option::Option::Some(::core::cmp::Ordering::Greater),
61+
_ => unreachable!("incomparable variants should have already returned"),
62+
}
6263
}
6364
}
6465
}

src/test/partial_ord.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,7 @@ fn enum_() -> Result<()> {
6767
let partial_ord = quote! {
6868
::core::cmp::PartialOrd::partial_cmp(&__self_disc, &__other_disc)
6969
};
70-
#[cfg(not(any(feature = "nightly", feature = "safe")))]
71-
let partial_ord = quote! {
72-
::core::cmp::PartialOrd::partial_cmp(
73-
&unsafe { ::core::mem::transmute::<_, isize>(__self_disc) },
74-
&unsafe { ::core::mem::transmute::<_, isize>(__other_disc) },
75-
)
76-
};
77-
#[cfg(all(not(feature = "nightly"), feature = "safe"))]
70+
#[cfg(not(feature = "nightly"))]
7871
let partial_ord = quote! {
7972
match self {
8073
Test::A { field: ref __field_field } => ::core::option::Option::Some(::core::cmp::Ordering::Less),

0 commit comments

Comments
 (0)