Skip to content

Commit 6ca8655

Browse files
daxpeddaModProg
andauthored
Build manual discriminant order if discriminant isn't available (#77)
Co-authored-by: Roland Fredenhagen <[email protected]>
1 parent 773a6df commit 6ca8655

33 files changed

+3548
-508
lines changed

.config/topic.dic

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Changelog
44
CHANGELOG
55
derive_bounded
66
destructure/G
7+
discriminants
78
enum/S
89
FQS
910
invariants

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Fixed
11+
- Take representation into account when determining the discriminant type.
12+
13+
### Changed
1114
- Use stable methods to retrieve the discriminant in `PartialOrd` and `Ord`
12-
implementations. Safe methods are now also used with the `safe` crate
13-
feature, significantly improving the performance there.
15+
implementations. Safe methods used with the `safe` crate feature don't use
16+
recursive matches anymore, significantly improving the performance.
1417

1518
## [1.2.3] - 2023-08-23
1619

README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,13 @@ supported.
274274

275275
Unions only support [`Clone`] and [`Copy`].
276276

277+
[`PartialOrd`] and [`Ord`] need to determine the discriminant type to
278+
function correctly. Unfortunately, according to the specification, the C
279+
representation without an integer representation doesn't have a
280+
platform-independent discriminant type. Therefor a check is inserted to
281+
ascertain that discriminants of enums with a C representation have the
282+
[`isize`] type, which is currently the case for all known platforms.
283+
277284
### `no_std` support
278285

279286
`no_std` support is provided by default.
@@ -282,12 +289,9 @@ Unions only support [`Clone`] and [`Copy`].
282289

283290
- `nightly`: Implements [`Ord`] and [`PartialOrd`] with the help of
284291
[`core::intrinsics::discriminant_value`], which is what Rust does by
285-
default too. Without this feature [`transmute`] is
286-
used to convert [`Discriminant`] to a [`i32`],
287-
which is the underlying type.
292+
default too. This requires a nightly version of the Rust compiler.
288293
- `safe`: `safe`: Uses only safe ways to access the discriminant of the enum
289-
for [`Ord`] and [`PartialOrd`]. This is much slower, but might be
290-
preferred if you don't trust derive-where. It also replaces all cases of
294+
for [`Ord`] and [`PartialOrd`]. It also replaces all cases of
291295
[`core::hint::unreachable_unchecked`] in [`Ord`], [`PartialEq`] and
292296
[`PartialOrd`], which is what std uses, with [`unreachable`].
293297
- `zeroize`: Allows deriving [`Zeroize`] and [`zeroize`][method@zeroize] on
@@ -349,6 +353,7 @@ conditions.
349353
[`Drop`]: https://doc.rust-lang.org/core/ops/trait.Drop.html
350354
[`Eq`]: https://doc.rust-lang.org/core/cmp/trait.Eq.html
351355
[`i32`]: https://doc.rust-lang.org/core/primitive.i32.html
356+
[`isize`]: https://doc.rust-lang.org/core/primitive.isize.html
352357
[`Ord`]: https://doc.rust-lang.org/core/cmp/trait.Ord.html
353358
[`PartialEq`]: https://doc.rust-lang.org/core/cmp/trait.PartialEq.html
354359
[`PartialOrd`]: https://doc.rust-lang.org/core/cmp/trait.PartialOrd.html

src/data.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ mod field;
44
mod fields;
55

66
use proc_macro2::Span;
7-
use syn::{FieldsNamed, Ident, Pat, PatPath, Path, Result, Variant};
7+
use syn::{Expr, FieldsNamed, Ident, Pat, PatPath, Path, Result, Variant};
88

99
pub use self::{
1010
field::{Field, Member},
@@ -27,6 +27,8 @@ pub struct Data<'a> {
2727
pub path: Path,
2828
/// [Type](DataType) of this struct, union or variant.
2929
pub type_: DataType<'a>,
30+
/// Discriminant of this variant.
31+
pub discriminant: Option<&'a Expr>,
3032
}
3133

3234
/// Type of this data.
@@ -98,6 +100,7 @@ impl<'a> Data<'a> {
98100
ident,
99101
path,
100102
type_: DataType::Struct(fields),
103+
discriminant: None,
101104
})
102105
}
103106
}
@@ -114,6 +117,7 @@ impl<'a> Data<'a> {
114117
ident,
115118
path,
116119
type_: DataType::Tuple(fields),
120+
discriminant: None,
117121
})
118122
}
119123
}
@@ -127,6 +131,7 @@ impl<'a> Data<'a> {
127131
qself: None,
128132
path,
129133
})),
134+
discriminant: None,
130135
}),
131136
syn::Fields::Unit => Err(Error::item_empty(span)),
132137
}
@@ -153,6 +158,7 @@ impl<'a> Data<'a> {
153158
ident,
154159
path,
155160
type_: DataType::Union(fields),
161+
discriminant: None,
156162
})
157163
}
158164
}
@@ -185,6 +191,7 @@ impl<'a> Data<'a> {
185191
default,
186192
type_: VariantType::Struct(fields),
187193
},
194+
discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr),
188195
})
189196
}
190197
syn::Fields::Unnamed(fields) => {
@@ -200,6 +207,7 @@ impl<'a> Data<'a> {
200207
default,
201208
type_: VariantType::Tuple(fields),
202209
},
210+
discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr),
203211
})
204212
}
205213
syn::Fields::Unit => {
@@ -218,6 +226,7 @@ impl<'a> Data<'a> {
218226
default,
219227
type_: VariantType::Unit(pattern),
220228
},
229+
discriminant: variant.discriminant.as_ref().map(|(_, expr)| expr),
221230
})
222231
}
223232
}
@@ -254,15 +263,6 @@ impl<'a> Data<'a> {
254263
}
255264
}
256265

257-
/// Returns the destructuring `other` pattern of this [`Data`].
258-
#[cfg(not(feature = "nightly"))]
259-
pub fn other_pattern_skip(&self) -> &Pat {
260-
match self.fields() {
261-
Either::Left(fields) => &fields.other_pattern_skip,
262-
Either::Right(pattern) => pattern,
263-
}
264-
}
265-
266266
/// Returns `true` if this variant is marked as the [`struct@Default`]. If
267267
/// not a variant, always returns `true`.
268268
pub fn is_default(&self) -> bool {

src/data/fields.rs

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
//! Storage for items or variants with data.
22
3-
use std::iter;
4-
53
use syn::{
6-
punctuated::Punctuated,
74
token::{Brace, Paren},
8-
FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatRest, PatStruct, PatTupleStruct,
9-
Path, Result, Token,
5+
FieldPat, FieldsNamed, FieldsUnnamed, Ident, Pat, PatIdent, PatStruct, PatTupleStruct, Path,
6+
Result, Token,
107
};
118

129
use crate::{DeriveWhere, Field, Skip, Trait};
@@ -18,9 +15,6 @@ pub struct Fields<'a> {
1815
pub self_pattern: Pat,
1916
/// [Pattern](Pat) to use in a match arm to destructure `other`.
2017
pub other_pattern: Pat,
21-
/// [Pattern](Pat) to use in a match arm to destructure `other` but skipping
22-
/// all fields.
23-
pub other_pattern_skip: Pat,
2418
/// [`Field`]s of this struct, union or variant.
2519
pub fields: Vec<Field<'a>>,
2620
}
@@ -36,23 +30,11 @@ impl<'a> Fields<'a> {
3630
let fields = Field::from_named(derive_wheres, skip_inner, fields)?;
3731

3832
let self_pattern = Self::struct_pattern(path.clone(), &fields, |field| &field.self_ident);
39-
let other_pattern_skip = Pat::Struct(PatStruct {
40-
attrs: Vec::new(),
41-
qself: None,
42-
path: path.clone(),
43-
brace_token: Brace::default(),
44-
fields: Punctuated::new(),
45-
rest: Some(PatRest {
46-
attrs: Vec::new(),
47-
dot2_token: <Token![..]>::default(),
48-
}),
49-
});
5033
let other_pattern = Self::struct_pattern(path, &fields, |field| &field.other_ident);
5134

5235
Ok(Self {
5336
self_pattern,
5437
other_pattern,
55-
other_pattern_skip,
5638
fields,
5739
})
5840
}
@@ -67,23 +49,11 @@ impl<'a> Fields<'a> {
6749
let fields = Field::from_unnamed(derive_wheres, skip_inner, fields)?;
6850

6951
let self_pattern = Self::tuple_pattern(path.clone(), &fields, |field| &field.self_ident);
70-
let other_pattern_skip = Pat::TupleStruct(PatTupleStruct {
71-
attrs: Vec::new(),
72-
qself: None,
73-
path: path.clone(),
74-
paren_token: Paren::default(),
75-
elems: iter::once(Pat::Rest(PatRest {
76-
attrs: Vec::new(),
77-
dot2_token: <Token![..]>::default(),
78-
}))
79-
.collect(),
80-
});
8152
let other_pattern = Self::tuple_pattern(path, &fields, |field| &field.other_ident);
8253

8354
Ok(Self {
8455
self_pattern,
8556
other_pattern,
86-
other_pattern_skip,
8757
fields,
8858
})
8959
}

src/error.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,16 +212,21 @@ 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_parse(span: Span) -> syn::Error {
217-
syn::Error::new(span, "unable to parse `repr` attribute")
218-
}
219-
220215
/// Unknown `repr`.
221216
pub fn repr_unknown(span: Span) -> syn::Error {
222217
syn::Error::new(span, "found unknown representation")
223218
}
224219

220+
/// Invalid enum with non-empty variants and custom discriminants without an
221+
/// integer representation.
222+
pub fn repr_discriminant_invalid(span: Span) -> syn::Error {
223+
syn::Error::new(
224+
span,
225+
"enums with non-empty variants and custom discriminants require a integer \
226+
representation",
227+
)
228+
}
229+
225230
/// Unsupported default option if [`Default`] isn't implemented.
226231
pub fn default(span: Span) -> syn::Error {
227232
syn::Error::new(

src/input.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Parses [`DeriveInput`] into something more useful.
22
33
use proc_macro2::Span;
4-
use syn::{DeriveInput, GenericParam, Generics, Result};
4+
use syn::{DeriveInput, GenericParam, Generics, ImplGenerics, Result, TypeGenerics, WhereClause};
55

66
#[cfg(feature = "zeroize")]
77
use crate::DeriveTrait;
@@ -12,7 +12,7 @@ pub struct Input<'a> {
1212
/// `derive_where` attributes on the item.
1313
pub derive_wheres: Vec<DeriveWhere>,
1414
/// Generics necessary to define for an `impl`.
15-
pub generics: &'a Generics,
15+
pub generics: SplitGenerics<'a>,
1616
/// Fields or variants of this item.
1717
pub item: Item<'a>,
1818
}
@@ -194,10 +194,35 @@ impl<'a> Input<'a> {
194194
}
195195
}
196196

197+
let generics = SplitGenerics::new(generics);
198+
197199
Ok(Self {
198200
derive_wheres,
199201
generics,
200202
item,
201203
})
202204
}
203205
}
206+
207+
/// Stores output of [`Generics::split_for_impl()`].
208+
pub struct SplitGenerics<'a> {
209+
/// Necessary generic definitions.
210+
pub imp: ImplGenerics<'a>,
211+
/// Generics on the type itself.
212+
pub ty: TypeGenerics<'a>,
213+
/// `where` clause.
214+
pub where_clause: Option<&'a WhereClause>,
215+
}
216+
217+
impl<'a> SplitGenerics<'a> {
218+
/// Creates a [`SplitGenerics`] from [`Generics`].
219+
fn new(generics: &'a Generics) -> Self {
220+
let (imp, ty, where_clause) = generics.split_for_impl();
221+
222+
SplitGenerics {
223+
imp,
224+
ty,
225+
where_clause,
226+
}
227+
}
228+
}

0 commit comments

Comments
 (0)