Skip to content

Commit f0345de

Browse files
authored
Fix breaking change: Do not enforce discriminant to be a literal + don't expect usize. (#695)
* fix * tests * fix ui test error * more fix * add test * fmt * fix typo * wip * fix * remove dead code * last fix * remove convertion * fix * refactor * fix conflicting implementation of len() * license and doc * trigger clippy again * Revert "trigger clippy again" This reverts commit 97246b8.
1 parent 16dd072 commit f0345de

File tree

7 files changed

+83
-45
lines changed

7 files changed

+83
-45
lines changed

Cargo.lock

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ proptest = "1.6.0"
3131
trybuild = "1.0.103"
3232
paste = "1"
3333
rustversion = "1"
34+
enumflags2 = "0.7.11"
3435

3536
[build-dependencies]
3637
rustversion = "1"

derive/src/decode.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub fn quote(
5959

6060
quote_spanned! { v.span() =>
6161
#[allow(clippy::unnecessary_cast)]
62-
__codec_x_edqy if __codec_x_edqy == #index as ::core::primitive::u8 => {
62+
__codec_x_edqy if __codec_x_edqy == (#index) as ::core::primitive::u8 => {
6363
// NOTE: This lambda is necessary to work around an upstream bug
6464
// where each extra branch results in excessive stack usage:
6565
// https://github.com/rust-lang/rust/issues/34283

derive/src/encode.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,8 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS
333333
let encoding_names = names.clone();
334334
let encoding = quote_spanned! { f.span() =>
335335
#type_name :: #name { #( ref #encoding_names, )* } => {
336-
#dest.push_byte(#index as ::core::primitive::u8);
336+
#[allow(clippy::unnecessary_cast)]
337+
#dest.push_byte((#index) as ::core::primitive::u8);
337338
#encode_fields
338339
}
339340
};
@@ -366,7 +367,8 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS
366367
let encoding_names = names.clone();
367368
let encoding = quote_spanned! { f.span() =>
368369
#type_name :: #name ( #( ref #encoding_names, )* ) => {
369-
#dest.push_byte(#index as ::core::primitive::u8);
370+
#[allow(clippy::unnecessary_cast)]
371+
#dest.push_byte((#index) as ::core::primitive::u8);
370372
#encode_fields
371373
}
372374
};
@@ -383,7 +385,7 @@ fn impl_encode(data: &Data, type_name: &Ident, crate_path: &syn::Path) -> TokenS
383385
let encoding = quote_spanned! { f.span() =>
384386
#type_name :: #name => {
385387
#[allow(clippy::unnecessary_cast)]
386-
#dest.push_byte(#index as ::core::primitive::u8);
388+
#dest.push_byte((#index) as ::core::primitive::u8);
387389
}
388390
};
389391

derive/src/utils.rs

+19-41
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,13 @@ pub fn const_eval_check_variant_indexes(
4545
let mut recurse_indices = vec![];
4646
for (ident, index) in recurse_variant_indices {
4747
let ident_str = ident.to_string();
48-
recurse_indices.push(quote! { (#index, #ident_str) });
48+
// We convert to u8 same as in the generated code.
49+
recurse_indices.push(quote_spanned! { ident.span() =>
50+
(
51+
(#index) as ::core::primitive::u8,
52+
#ident_str
53+
)
54+
});
4955
}
5056
let len = recurse_indices.len();
5157

@@ -55,11 +61,12 @@ pub fn const_eval_check_variant_indexes(
5561

5662
quote! {
5763
const _: () = {
58-
const indices: [(usize, &'static str); #len] = [#( #recurse_indices ,)*];
64+
#[allow(clippy::unnecessary_cast)]
65+
const indices: [(u8, &'static str); #len] = [#( #recurse_indices ,)*];
5966

6067
// Returns if there is duplicate, and if there is some the duplicate indexes.
61-
const fn duplicate_info(array: &[(usize, &'static str); #len]) -> (bool, usize, usize) {
62-
let len = array.len();
68+
const fn duplicate_info(array: &[(u8, &'static str); #len]) -> (bool, usize, usize) {
69+
let len = #len;
6370
let mut i = 0;
6471
while i < len {
6572
let mut j = i + 1;
@@ -97,7 +104,7 @@ pub fn const_eval_check_variant_indexes(
97104
/// is found, fall back to the discriminant or just the variant index.
98105
pub fn variant_index(v: &Variant, i: usize) -> TokenStream {
99106
// first look for an attribute
100-
let mut index = find_meta_item(v.attrs.iter(), |meta| {
107+
let index = find_meta_item(v.attrs.iter(), |meta| {
101108
if let Meta::NameValue(ref nv) = meta {
102109
if nv.path.is_ident("index") {
103110
if let Expr::Lit(ExprLit { lit: Lit::Int(ref v), .. }) = nv.value {
@@ -112,21 +119,13 @@ pub fn variant_index(v: &Variant, i: usize) -> TokenStream {
112119
None
113120
});
114121

115-
// if no attribute, we fall back to an explicit discriminant (ie 'enum A { Foo = 1u32 }').
116-
if index.is_none() {
117-
if let Some((_, Expr::Lit(ExprLit { lit: Lit::Int(disc_lit), .. }))) = &v.discriminant {
118-
index = disc_lit.base10_parse::<usize>().ok()
119-
}
120-
}
121-
122-
// fall back to the variant index if no attribute or explicit discriminant is found.
123-
let index = index.unwrap_or(i);
124-
125-
// output the index with no suffix (ie 1 rather than 1usize) so that these can be quoted into
126-
// an array of usizes and will work regardless of what type the discriminant values actually
127-
// are.
128-
let s = proc_macro2::Literal::usize_unsuffixed(index);
129-
quote! { #s }
122+
// then fallback to discriminant or just index
123+
index.map(|i| quote! { #i }).unwrap_or_else(|| {
124+
v.discriminant
125+
.as_ref()
126+
.map(|(_, expr)| quote! { #expr })
127+
.unwrap_or_else(|| quote! { #i })
128+
})
130129
}
131130

132131
/// Look for a `#[codec(encoded_as = "SomeType")]` outer attribute on the given
@@ -394,12 +393,6 @@ pub fn check_attributes(input: &DeriveInput) -> syn::Result<()> {
394393
check_field_attribute(attr)?;
395394
}
396395
}
397-
// While we're checking things, also ensure that
398-
// any explicit discriminants are within 0..=255
399-
let discriminant = variant.discriminant.as_ref().map(|(_, d)| d);
400-
if let Some(expr) = discriminant {
401-
check_variant_discriminant(expr)?;
402-
}
403396
},
404397
Data::Union(_) => (),
405398
}
@@ -479,21 +472,6 @@ fn check_variant_attribute(attr: &Attribute) -> syn::Result<()> {
479472
}
480473
}
481474

482-
// Ensure a variant discriminant, if provided, can be parsed into
483-
// something in the range 0..255.
484-
fn check_variant_discriminant(discriminant: &Expr) -> syn::Result<()> {
485-
if let Expr::Lit(ExprLit { lit: Lit::Int(lit_int), .. }) = discriminant {
486-
lit_int.base10_parse::<u8>().map(|_| ()).map_err(|_| {
487-
syn::Error::new(lit_int.span(), "Discriminant index must be in the range 0..255")
488-
})
489-
} else {
490-
Err(syn::Error::new(
491-
discriminant.span(),
492-
"Discriminant must be an integer literal in the range 0..255",
493-
))
494-
}
495-
}
496-
497475
// Only `#[codec(dumb_trait_bound)]` is accepted as top attribute
498476
fn check_top_attribute(attr: &Attribute) -> syn::Result<()> {
499477
let top_error = "Invalid attribute: only `#[codec(dumb_trait_bound)]`, \

tests/bitflags.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2017, 2018 Parity Technologies
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
use parity_scale_codec_derive::{Decode, Encode};
16+
17+
// test for regression
18+
#[enumflags2::bitflags]
19+
#[repr(u64)]
20+
#[derive(Copy, Clone, Encode, Decode)]
21+
pub enum EnumWithU64ReprAndBitflags {
22+
Variant1,
23+
Variant2,
24+
Variant3,
25+
Variant4,
26+
}

tests/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -931,3 +931,13 @@ fn deserializing_of_big_recursively_nested_enum_works() {
931931
let obj_d2 = Enum::decode_with_depth_limit(40, &mut &data[..]).unwrap();
932932
assert!(obj == obj_d2);
933933
}
934+
935+
#[test]
936+
fn non_literal_variant_discriminant() {
937+
const A: isize = 1;
938+
#[derive(PartialEq, Eq, DeriveDecode, DeriveDecodeWithMemTracking, DeriveEncode)]
939+
enum Enum {
940+
A = A,
941+
B = A + 1,
942+
}
943+
}

0 commit comments

Comments
 (0)