diff --git a/src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md b/src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md new file mode 100644 index 0000000000000000000000000000000000000000..e0bb782270e22821f416ac44a45d91236e985ffd --- /dev/null +++ b/src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md @@ -0,0 +1,37 @@ +# `arbitrary_enum_discriminant` + +The tracking issue for this feature is: [#60553] + +[#60553]: https://github.com/rust-lang/rust/issues/60553 + +------------------------ + +The `arbitrary_enum_discriminant` feature permits tuple-like and +struct-like enum variants with `#[repr()]` to have explicit discriminants. + +## Examples + +```rust +#![feature(arbitrary_enum_discriminant)] + +#[allow(dead_code)] +#[repr(u8)] +enum Enum { + Unit = 3, + Tuple(u16) = 2, + Struct { + a: u8, + b: u16, + } = 1, +} + +impl Enum { + fn tag(&self) -> u8 { + unsafe { *(self as *const Self as *const u8) } + } +} + +assert_eq!(3, Enum::Unit.tag()); +assert_eq!(2, Enum::Tuple(5).tag()); +assert_eq!(1, Enum::Struct{a: 7, b: 11}.tag()); +``` diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 0ec5c9763a0fb1714af4aa5e9cb15321cfa0c5f5..cef50dc7ed41e5a1df225fdbb22100ad37bfebcb 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1935,6 +1935,25 @@ pub fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, vs: &'tcx [hir::Variant], i } } + if tcx.adt_def(def_id).repr.int.is_none() && tcx.features().arbitrary_enum_discriminant { + let is_unit = + |var: &hir::Variant| match var.node.data { + hir::VariantData::Unit(..) => true, + _ => false + }; + + let has_disr = |var: &hir::Variant| var.node.disr_expr.is_some(); + let has_non_units = vs.iter().any(|var| !is_unit(var)); + let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var)); + let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var)); + + if disr_non_unit || (disr_units && has_non_units) { + let mut err = struct_span_err!(tcx.sess, sp, E0732, + "`#[repr(inttype)]` must be specified"); + err.emit(); + } + } + let mut disr_vals: Vec> = Vec::with_capacity(vs.len()); for ((_, discr), v) in def.discriminants(tcx).zip(vs) { // Check for duplicate discriminant values diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs index 115ee0f72c9c1c114762ee562994061703418fae..d61cef7f858d66d9f22b0b5ae4bc9f3500234f8f 100644 --- a/src/librustc_typeck/error_codes.rs +++ b/src/librustc_typeck/error_codes.rs @@ -4733,6 +4733,38 @@ enum Status { // error: transparent enum needs exactly one variant, but has 2 represented. "##, +E0732: r##" +An `enum` with a discriminant must specify a `#[repr(inttype)]`. + +A `#[repr(inttype)]` must be provided on an `enum` if it has a non-unit +variant with a discriminant, or where there are both unit variants with +discriminants and non-unit variants. This restriction ensures that there +is a well-defined way to extract a variant's discriminant from a value; +for instance: + +``` +#![feature(arbitrary_enum_discriminant)] + +#[repr(u8)] +enum Enum { + Unit = 3, + Tuple(u16) = 2, + Struct { + a: u8, + b: u16, + } = 1, +} + +fn discriminant(v : &Enum) -> u8 { + unsafe { *(v as *const Enum as *const u8) } +} + +assert_eq!(3, discriminant(&Enum::Unit)); +assert_eq!(2, discriminant(&Enum::Tuple(5))); +assert_eq!(1, discriminant(&Enum::Struct{a: 7, b: 11})); +``` +"##, + } register_diagnostics! { diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 8ec07de5fab73d926967966fd88e89a2b7443df2..c405acd8ee3f619fd414072d28fba35fc3f9845c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -25,13 +25,14 @@ use crate::edition::{ALL_EDITIONS, Edition}; use crate::visit::{self, FnKind, Visitor}; use crate::parse::{token, ParseSess}; +use crate::parse::parser::Parser; use crate::symbol::{Symbol, sym}; use crate::tokenstream::TokenTree; use errors::{Applicability, DiagnosticBuilder, Handler}; use rustc_data_structures::fx::FxHashMap; use rustc_target::spec::abi::Abi; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::{Span, DUMMY_SP, MultiSpan}; use log::debug; use lazy_static::lazy_static; @@ -569,6 +570,9 @@ pub fn walk_feature_fields(&self, mut f: F) // #[repr(transparent)] on unions. (active, transparent_unions, "1.37.0", Some(60405), None), + // Allows explicit discriminants on non-unit enum variants. + (active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -1709,20 +1713,20 @@ pub fn emit_feature_err( feature_err(sess, feature, span, issue, explain).emit(); } -pub fn feature_err<'a>( +pub fn feature_err<'a, S: Into>( sess: &'a ParseSess, feature: Symbol, - span: Span, + span: S, issue: GateIssue, explain: &str, ) -> DiagnosticBuilder<'a> { leveled_feature_err(sess, feature, span, issue, explain, GateStrength::Hard) } -fn leveled_feature_err<'a>( +fn leveled_feature_err<'a, S: Into>( sess: &'a ParseSess, feature: Symbol, - span: Span, + span: S, issue: GateIssue, explain: &str, level: GateStrength, @@ -2037,6 +2041,29 @@ fn visit_item(&mut self, i: &'a ast::Item) { } } + ast::ItemKind::Enum(ast::EnumDef{ref variants, ..}, ..) => { + for variant in variants { + match (&variant.node.data, &variant.node.disr_expr) { + (ast::VariantData::Unit(..), _) => {}, + (_, Some(disr_expr)) => + gate_feature_post!( + &self, + arbitrary_enum_discriminant, + disr_expr.value.span, + "discriminants on non-unit variants are experimental"), + _ => {}, + } + } + + let has_feature = self.context.features.arbitrary_enum_discriminant; + if !has_feature && !i.span.allows_unstable(sym::arbitrary_enum_discriminant) { + Parser::maybe_report_invalid_custom_discriminants( + self.context.parse_sess, + &variants, + ); + } + } + ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => { if polarity == ast::ImplPolarity::Negative { gate_feature_post!(&self, optin_builtin_traits, diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index 60544cca8772936afbbb703864b6a86a4ad57a6d..07fe521edb03788fe343baaceb440d892703805e 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -2,7 +2,7 @@ self, Arg, BinOpKind, BindingMode, BlockCheckMode, Expr, ExprKind, Ident, Item, ItemKind, Mutability, Pat, PatKind, PathSegment, QSelf, Ty, TyKind, VariantData, }; -use crate::parse::{SeqSep, PResult, Parser}; +use crate::parse::{SeqSep, PResult, Parser, ParseSess}; use crate::parse::parser::{BlockMode, PathStyle, SemiColonMode, TokenType, TokenExpectType}; use crate::parse::token::{self, TokenKind}; use crate::print::pprust; @@ -539,8 +539,7 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { } crate fn maybe_report_invalid_custom_discriminants( - &mut self, - discriminant_spans: Vec, + sess: &ParseSess, variants: &[Spanned], ) { let has_fields = variants.iter().any(|variant| match variant.node.data { @@ -548,28 +547,39 @@ fn tokens_to_string(tokens: &[TokenType]) -> String { VariantData::Unit(..) => false, }); + let discriminant_spans = variants.iter().filter(|variant| match variant.node.data { + VariantData::Tuple(..) | VariantData::Struct(..) => false, + VariantData::Unit(..) => true, + }) + .filter_map(|variant| variant.node.disr_expr.as_ref().map(|c| c.value.span)) + .collect::>(); + if !discriminant_spans.is_empty() && has_fields { - let mut err = self.struct_span_err( + let mut err = crate::feature_gate::feature_err( + sess, + sym::arbitrary_enum_discriminant, discriminant_spans.clone(), - "custom discriminant values are not allowed in enums with fields", + crate::feature_gate::GateIssue::Language, + "custom discriminant values are not allowed in enums with tuple or struct variants", ); for sp in discriminant_spans { - err.span_label(sp, "invalid custom discriminant"); + err.span_label(sp, "disallowed custom discriminant"); } for variant in variants.iter() { - if let VariantData::Struct(fields, ..) | VariantData::Tuple(fields, ..) = - &variant.node.data - { - let fields = if fields.len() > 1 { - "fields" - } else { - "a field" - }; - err.span_label( - variant.span, - &format!("variant with {fields} defined here", fields = fields), - ); - + match &variant.node.data { + VariantData::Struct(..) => { + err.span_label( + variant.span, + "struct variant defined here", + ); + } + VariantData::Tuple(..) => { + err.span_label( + variant.span, + "tuple variant defined here", + ); + } + VariantData::Unit(..) => {} } } err.emit(); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f3ace84162ed096bc91d929ecdc6fac0ae17e8a4..a1440f2eba47e237b72601bf7b66c63ee826fa0d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -6946,36 +6946,34 @@ fn parse_existential_or_alias( /// Parses the part of an enum declaration following the `{`. fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<'a, EnumDef> { let mut variants = Vec::new(); - let mut any_disr = vec![]; while self.token != token::CloseDelim(token::Brace) { let variant_attrs = self.parse_outer_attributes()?; let vlo = self.token.span; - let struct_def; - let mut disr_expr = None; self.eat_bad_pub(); let ident = self.parse_ident()?; - if self.check(&token::OpenDelim(token::Brace)) { + + let struct_def = if self.check(&token::OpenDelim(token::Brace)) { // Parse a struct variant. let (fields, recovered) = self.parse_record_struct_body()?; - struct_def = VariantData::Struct(fields, recovered); + VariantData::Struct(fields, recovered) } else if self.check(&token::OpenDelim(token::Paren)) { - struct_def = VariantData::Tuple( + VariantData::Tuple( self.parse_tuple_struct_body()?, ast::DUMMY_NODE_ID, - ); - } else if self.eat(&token::Eq) { - disr_expr = Some(AnonConst { + ) + } else { + VariantData::Unit(ast::DUMMY_NODE_ID) + }; + + let disr_expr = if self.eat(&token::Eq) { + Some(AnonConst { id: ast::DUMMY_NODE_ID, value: self.parse_expr()?, - }); - if let Some(sp) = disr_expr.as_ref().map(|c| c.value.span) { - any_disr.push(sp); - } - struct_def = VariantData::Unit(ast::DUMMY_NODE_ID); + }) } else { - struct_def = VariantData::Unit(ast::DUMMY_NODE_ID); - } + None + }; let vr = ast::Variant_ { ident, @@ -7003,7 +7001,6 @@ fn parse_enum_def(&mut self, _generics: &ast::Generics) -> PResult<'a, EnumDef> } } self.expect(&token::CloseDelim(token::Brace))?; - self.maybe_report_invalid_custom_discriminants(any_disr, &variants); Ok(ast::EnumDef { variants }) } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 756bc8c29d8287b739ffe87528a64970f434ffd4..bd9a9061b99edc4de114362264730dd9faf89346 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -135,6 +135,7 @@ always, and, any, + arbitrary_enum_discriminant, arbitrary_self_types, Arguments, ArgumentV1, diff --git a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs new file mode 100644 index 0000000000000000000000000000000000000000..4da7b5ab24b29fce0decbe9110aab24c77fdf972 --- /dev/null +++ b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs @@ -0,0 +1,9 @@ +#![crate_type="lib"] +#![feature(arbitrary_enum_discriminant)] + +enum Enum { +//~^ ERROR `#[repr(inttype)]` must be specified + Unit = 1, + Tuple() = 2, + Struct{} = 3, +} diff --git a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr new file mode 100644 index 0000000000000000000000000000000000000000..2db5372da0c6edcb498b03472725b84e1698cb4b --- /dev/null +++ b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr @@ -0,0 +1,14 @@ +error[E0732]: `#[repr(inttype)]` must be specified + --> $DIR/arbitrary_enum_discriminant-no-repr.rs:4:1 + | +LL | / enum Enum { +LL | | +LL | | Unit = 1, +LL | | Tuple() = 2, +LL | | Struct{} = 3, +LL | | } + | |_^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0732`. diff --git a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs new file mode 100644 index 0000000000000000000000000000000000000000..f2270602d87ebce98a3f9cbd0438dbf96b1967e2 --- /dev/null +++ b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs @@ -0,0 +1,56 @@ +// run-pass +#![feature(arbitrary_enum_discriminant, const_raw_ptr_deref, test)] + +extern crate test; + +use test::black_box; + +#[allow(dead_code)] +#[repr(u8)] +enum Enum { + Unit = 3, + Tuple(u16) = 2, + Struct { + a: u8, + b: u16, + } = 1, +} + +impl Enum { + const unsafe fn tag(&self) -> u8 { + *(self as *const Self as *const u8) + } +} + +#[allow(dead_code)] +#[repr(u8)] +enum FieldlessEnum { + Unit = 3, + Tuple() = 2, + Struct {} = 1, +} + +fn main() { + const UNIT: Enum = Enum::Unit; + const TUPLE: Enum = Enum::Tuple(5); + const STRUCT: Enum = Enum::Struct{a: 7, b: 11}; + + // Ensure discriminants are correct during runtime execution + assert_eq!(3, unsafe { black_box(UNIT).tag() }); + assert_eq!(2, unsafe { black_box(TUPLE).tag() }); + assert_eq!(1, unsafe { black_box(STRUCT).tag() }); + + // Ensure discriminants are correct during CTFE + const UNIT_TAG: u8 = unsafe { UNIT.tag() }; + const TUPLE_TAG: u8 = unsafe { TUPLE.tag() }; + const STRUCT_TAG: u8 = unsafe { STRUCT.tag() }; + + assert_eq!(3, UNIT_TAG); + assert_eq!(2, TUPLE_TAG); + assert_eq!(1, STRUCT_TAG); + + // Ensure `as` conversions are correct + assert_eq!(3, FieldlessEnum::Unit as u8); + assert_eq!(2, FieldlessEnum::Tuple() as u8); + assert_eq!(1, FieldlessEnum::Struct{} as u8); +} diff --git a/src/test/run-pass/discriminant_value-wrapper.rs b/src/test/ui/enum-discriminant/discriminant_value-wrapper.rs similarity index 97% rename from src/test/run-pass/discriminant_value-wrapper.rs rename to src/test/ui/enum-discriminant/discriminant_value-wrapper.rs index 1fb0d1edddf26dec8abafab0dbb427831c03e054..daef2de87a9c2f4af3e8bb1d9a117c9765b0bdbb 100644 --- a/src/test/run-pass/discriminant_value-wrapper.rs +++ b/src/test/ui/enum-discriminant/discriminant_value-wrapper.rs @@ -1,3 +1,4 @@ +// run-pass use std::mem; enum ADT { diff --git a/src/test/run-pass/discriminant_value.rs b/src/test/ui/enum-discriminant/discriminant_value.rs similarity index 78% rename from src/test/run-pass/discriminant_value.rs rename to src/test/ui/enum-discriminant/discriminant_value.rs index 162ab27cdff2550e95eb74a7b0f8b2ab97653b92..b7000015c71db5cb1efccf0e2e3ae74f16841831 100644 --- a/src/test/run-pass/discriminant_value.rs +++ b/src/test/ui/enum-discriminant/discriminant_value.rs @@ -1,5 +1,6 @@ +// run-pass #![allow(stable_features)] -#![feature(core, core_intrinsics)] +#![feature(arbitrary_enum_discriminant, core, core_intrinsics)] extern crate core; use core::intrinsics::discriminant_value; @@ -38,6 +39,17 @@ enum NullablePointer { static CONST : u32 = 0xBEEF; +#[allow(dead_code)] +#[repr(isize)] +enum Mixed { + Unit = 3, + Tuple(u16) = 2, + Struct { + a: u8, + b: u16, + } = 1, +} + pub fn main() { unsafe { @@ -64,5 +76,9 @@ pub fn main() { assert_eq!(discriminant_value(&10), 0); assert_eq!(discriminant_value(&"test"), 0); + + assert_eq!(3, discriminant_value(&Mixed::Unit)); + assert_eq!(2, discriminant_value(&Mixed::Tuple(5))); + assert_eq!(1, discriminant_value(&Mixed::Struct{a: 7, b: 11})); } } diff --git a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs new file mode 100644 index 0000000000000000000000000000000000000000..3e90af4d36af3fddc8a6de00a048e8ff1eb9d4be --- /dev/null +++ b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs @@ -0,0 +1,10 @@ +#![crate_type="lib"] + +enum Enum { + Unit = 1, + //~^ ERROR custom discriminant values are not allowed in enums with tuple or struct variants + Tuple() = 2, + //~^ ERROR discriminants on non-unit variants are experimental + Struct{} = 3, + //~^ ERROR discriminants on non-unit variants are experimental +} diff --git a/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr new file mode 100644 index 0000000000000000000000000000000000000000..f50ed2c184dafcad4e883b546dba0bf9d16c56df --- /dev/null +++ b/src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr @@ -0,0 +1,36 @@ +error[E0658]: discriminants on non-unit variants are experimental + --> $DIR/feature-gate-arbitrary_enum_discriminant.rs:6:13 + | +LL | Tuple() = 2, + | ^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/60553 + = help: add #![feature(arbitrary_enum_discriminant)] to the crate attributes to enable + +error[E0658]: discriminants on non-unit variants are experimental + --> $DIR/feature-gate-arbitrary_enum_discriminant.rs:8:14 + | +LL | Struct{} = 3, + | ^ + | + = note: for more information, see https://github.com/rust-lang/rust/issues/60553 + = help: add #![feature(arbitrary_enum_discriminant)] to the crate attributes to enable + +error[E0658]: custom discriminant values are not allowed in enums with tuple or struct variants + --> $DIR/feature-gate-arbitrary_enum_discriminant.rs:4:10 + | +LL | Unit = 1, + | ^ disallowed custom discriminant +LL | +LL | Tuple() = 2, + | ----------- tuple variant defined here +LL | +LL | Struct{} = 3, + | ------------ struct variant defined here + | + = note: for more information, see https://github.com/rust-lang/rust/issues/60553 + = help: add #![feature(arbitrary_enum_discriminant)] to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/parser/issue-17383.rs b/src/test/ui/parser/issue-17383.rs index f95005cd914838605d401bc8ff71edb7c4f1f2ee..7bf0e64f2c0a30eecf3963b7bee955d7059ce399 100644 --- a/src/test/ui/parser/issue-17383.rs +++ b/src/test/ui/parser/issue-17383.rs @@ -1,6 +1,6 @@ enum X { A = 3, - //~^ ERROR custom discriminant values are not allowed in enums with fields + //~^ ERROR custom discriminant values are not allowed in enums with tuple or struct variants B(usize) } diff --git a/src/test/ui/parser/issue-17383.stderr b/src/test/ui/parser/issue-17383.stderr index 37abd0ff5e1f49cb8248347aae0f20f7d6bfb6d2..486c4055807c9e4eef14e1a19e4488e6f9c306dd 100644 --- a/src/test/ui/parser/issue-17383.stderr +++ b/src/test/ui/parser/issue-17383.stderr @@ -1,11 +1,15 @@ -error: custom discriminant values are not allowed in enums with fields +error[E0658]: custom discriminant values are not allowed in enums with tuple or struct variants --> $DIR/issue-17383.rs:2:9 | LL | A = 3, - | ^ invalid custom discriminant + | ^ disallowed custom discriminant LL | LL | B(usize) - | -------- variant with a field defined here + | -------- tuple variant defined here + | + = note: for more information, see https://github.com/rust-lang/rust/issues/60553 + = help: add #![feature(arbitrary_enum_discriminant)] to the crate attributes to enable error: aborting due to previous error +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/parser/tag-variant-disr-non-nullary.rs b/src/test/ui/parser/tag-variant-disr-non-nullary.rs index 305edc4ad5a0489d3e178abfad86a064ba651ec2..a9cfdd549c752233f40a6608474f7a9d362ee375 100644 --- a/src/test/ui/parser/tag-variant-disr-non-nullary.rs +++ b/src/test/ui/parser/tag-variant-disr-non-nullary.rs @@ -1,6 +1,6 @@ enum Color { Red = 0xff0000, - //~^ ERROR custom discriminant values are not allowed in enums with fields + //~^ ERROR custom discriminant values are not allowed in enums with tuple or struct variants Green = 0x00ff00, Blue = 0x0000ff, Black = 0x000000, diff --git a/src/test/ui/parser/tag-variant-disr-non-nullary.stderr b/src/test/ui/parser/tag-variant-disr-non-nullary.stderr index 2d3b28395312438439814cdf3067eb0c6a8aab5c..13b46c6e8b3e5990fe96310e8ce063bbeda6ebce 100644 --- a/src/test/ui/parser/tag-variant-disr-non-nullary.stderr +++ b/src/test/ui/parser/tag-variant-disr-non-nullary.stderr @@ -1,21 +1,25 @@ -error: custom discriminant values are not allowed in enums with fields +error[E0658]: custom discriminant values are not allowed in enums with tuple or struct variants --> $DIR/tag-variant-disr-non-nullary.rs:2:11 | LL | Red = 0xff0000, - | ^^^^^^^^ invalid custom discriminant + | ^^^^^^^^ disallowed custom discriminant LL | LL | Green = 0x00ff00, - | ^^^^^^^^ invalid custom discriminant + | ^^^^^^^^ disallowed custom discriminant LL | Blue = 0x0000ff, - | ^^^^^^^^ invalid custom discriminant + | ^^^^^^^^ disallowed custom discriminant LL | Black = 0x000000, - | ^^^^^^^^ invalid custom discriminant + | ^^^^^^^^ disallowed custom discriminant LL | White = 0xffffff, - | ^^^^^^^^ invalid custom discriminant + | ^^^^^^^^ disallowed custom discriminant LL | Other(usize), - | ------------ variant with a field defined here + | ------------ tuple variant defined here LL | Other2(usize, usize), - | -------------------- variant with fields defined here + | -------------------- tuple variant defined here + | + = note: for more information, see https://github.com/rust-lang/rust/issues/60553 + = help: add #![feature(arbitrary_enum_discriminant)] to the crate attributes to enable error: aborting due to previous error +For more information about this error, try `rustc --explain E0658`.