Skip to content

Commit 0f1d63d

Browse files
committed
Allow non-literal distriminants without feature flag
The new error message is much less confusing, thus gating this possibility behind a flag is no longer necessary.
1 parent aa0e7e9 commit 0f1d63d

File tree

6 files changed

+31
-46
lines changed

6 files changed

+31
-46
lines changed

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ optional = true
2121

2222
[features]
2323
std = []
24-
not_literal = ["enumflags2_derive/not_literal"]
2524

2625
#[dev-dependencies]
2726
#criterion = "0.3"

enumflags_derive/Cargo.toml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,3 @@ proc-macro = true
1515
syn = "^1.0"
1616
quote = "^1.0"
1717
proc-macro2 = "^1.0"
18-
19-
[features]
20-
not_literal = []

enumflags_derive/src/lib.rs

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ pub fn derive_enum_flags(input: proc_macro::TokenStream)
4242
#[derive(Debug)]
4343
enum EvaluationError {
4444
LiteralOutOfRange(Span),
45-
UnsupportedOperation(Span),
4645
}
4746

4847
impl From<EvaluationError> for TokenStream {
@@ -53,42 +52,44 @@ impl From<EvaluationError> for TokenStream {
5352
LiteralOutOfRange(span) => {
5453
error!(span => "Integer literal out of range")
5554
}
56-
UnsupportedOperation(span) => {
57-
error!(span => "This kind of discriminant expression is \
58-
not supported.\n\
59-
hint: Enable the \"not_literal\" feature to \
60-
use a workaround.\n\
61-
note: This is not enabled by default due to the \
62-
high potential for confusing error messages \
63-
(see documentation).")
64-
}
6555
}
6656
}
6757
}
6858

6959
/// Try to evaluate the expression given.
70-
fn fold_expr(expr: &syn::Expr) -> Result<u64, EvaluationError> {
60+
fn fold_expr(expr: &syn::Expr) -> Result<Option<u64>, EvaluationError> {
61+
/// Recurse, but bubble-up both kinds of errors.
62+
/// (I miss my monad transformers)
63+
macro_rules! fold_expr {
64+
($($x:tt)*) => {
65+
match fold_expr($($x)*)? {
66+
Some(x) => x,
67+
None => return Ok(None),
68+
}
69+
}
70+
}
71+
7172
use syn::Expr;
7273
use crate::EvaluationError::*;
7374
match expr {
7475
Expr::Lit(ref expr_lit) => {
7576
match expr_lit.lit {
7677
syn::Lit::Int(ref lit_int) => {
77-
lit_int.base10_parse()
78-
.map_err(|_| LiteralOutOfRange(expr.span()))
78+
Ok(Some(lit_int.base10_parse()
79+
.map_err(|_| LiteralOutOfRange(expr.span()))?))
7980
}
80-
_ => Err(UnsupportedOperation(expr.span()))
81+
_ => Ok(None),
8182
}
8283
},
8384
Expr::Binary(ref expr_binary) => {
84-
let l = fold_expr(&expr_binary.left)?;
85-
let r = fold_expr(&expr_binary.right)?;
85+
let l = fold_expr!(&expr_binary.left);
86+
let r = fold_expr!(&expr_binary.right);
8687
match &expr_binary.op {
87-
syn::BinOp::Shl(_) => Ok(l << r),
88-
_ => Err(UnsupportedOperation(expr_binary.span()))
88+
syn::BinOp::Shl(_) => Ok(Some(l << r)),
89+
_ => Ok(None),
8990
}
9091
}
91-
_ => Err(UnsupportedOperation(expr.span()))
92+
_ => Ok(None),
9293
}
9394
}
9495

@@ -121,42 +122,38 @@ fn extract_repr(attrs: &[syn::Attribute])
121122
.transpose()
122123
}
123124

124-
/// Returns Ok with deferred checks (not_literal), or Err with error!
125+
/// Returns deferred checks
125126
fn verify_flag_values<'a>(
126-
// starts with underscore to silence warnings when not_literal
127-
// are disabled
128-
_type_name: &Ident,
127+
type_name: &Ident,
129128
variants: impl Iterator<Item=&'a syn::Variant>
130129
) -> Result<TokenStream, TokenStream> {
131-
#[cfg_attr(not(feature = "not_literal"), allow(unused_mut))]
132130
let mut deferred_checks: Vec<TokenStream> = vec![];
133131
for variant in variants {
134132
let discr = variant.discriminant.as_ref()
135133
.ok_or_else(|| error!(variant.span() =>
136134
"Please add an explicit discriminant"))?;
137135
match fold_expr(&discr.1) {
138-
Ok(flag) => {
136+
Ok(Some(flag)) => {
139137
if !flag.is_power_of_two() {
140138
return Err(error!(variant.discriminant.as_ref()
141139
.unwrap().1.span() =>
142140
"Flags must have exactly one set bit."));
143141
}
144142
}
145-
#[cfg(feature = "not_literal")]
146-
Err(EvaluationError::UnsupportedOperation(_)) => {
143+
Ok(None) => {
147144
let variant_name = &variant.ident;
148145
// TODO: Remove this madness when Debian ships a new compiler.
149146
let assertion_name = syn::Ident::new(
150147
&format!("__enumflags_assertion_{}_{}",
151-
_type_name, variant_name),
148+
type_name, variant_name),
152149
Span::call_site()); // call_site because def_site is unstable
153150
// adapted from static-assertions-rs by nvzqz (MIT/Apache-2.0)
154151
deferred_checks.push(quote_spanned!(variant.span() =>
155152
const #assertion_name: fn() = || {
156153
::enumflags2::_internal::assert_exactly_one_bit_set::<[(); (
157-
(#_type_name::#variant_name as u64).wrapping_sub(1) &
158-
(#_type_name::#variant_name as u64) == 0 &&
159-
(#_type_name::#variant_name as u64) != 0
154+
(#type_name::#variant_name as u64).wrapping_sub(1) &
155+
(#type_name::#variant_name as u64) == 0 &&
156+
(#type_name::#variant_name as u64) != 0
160157
) as usize]>();
161158
};
162159
));

src/lib.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,6 @@
4848
//! - [`serde`](https://serde.rs/) implements `Serialize` and `Deserialize`
4949
//! for `BitFlags<T>`.
5050
//! - `std` implements `std::error::Error` for `FromBitsError`.
51-
//! - `not_literal` enables a workaround that allows using discriminant
52-
//! expressions that can't be evaluated at macro expansion time. Notably,
53-
//! this includes using pre-existing constants.
54-
//!
55-
//! This is disabled by default because of the high potential for confusing
56-
//! error messages - if a flag doesn't have exactly one bit set, the error
57-
//! message will be "attempt to subtract with overflow", pointing at the
58-
//! relevant flag.
5951
//!
6052
//! ### Migrating from 0.5
6153
//!

test_suite/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ publish = false
55

66
[dependencies.enumflags2]
77
path = "../"
8-
features = ["serde", "not_literal"]
8+
features = ["serde"]
99

1010
[dependencies.serde]
1111
version = "1"

test_suite/ui/multiple_bits_deferred.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0277]: the trait bound `enumflags2::_internal::AssertionFailed: enumflags
44
6 | Three = THREE,
55
| ^^^^^ the trait `enumflags2::_internal::ExactlyOneBitSet` is not implemented for `enumflags2::_internal::AssertionFailed`
66
|
7-
::: $WORKSPACE/src/lib.rs:236:34
7+
::: $WORKSPACE/src/lib.rs:228:34
88
|
9-
236 | where Assertion::Status: ExactlyOneBitSet {}
9+
228 | where Assertion::Status: ExactlyOneBitSet {}
1010
| ---------------- required by this bound in `enumflags2::_internal::assert_exactly_one_bit_set`

0 commit comments

Comments
 (0)