about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-06-25 15:12:11 +0000
committerbors <bors@rust-lang.org>2019-06-25 15:12:11 +0000
commit303f77ee1d60e1b351ff6478143dd866403c27f5 (patch)
tree349f1f0c4084a7097d55dac1840a42d3ecad6349 /src
parent40ab9d2bd57a2203131abd723f7120e960299fae (diff)
parentac98342e846e79985f0c4969a2d546dee24a70d1 (diff)
downloadrust-303f77ee1d60e1b351ff6478143dd866403c27f5.tar.gz
rust-303f77ee1d60e1b351ff6478143dd866403c27f5.zip
Auto merge of #60732 - jswrenn:arbitrary_enum_discriminant, r=pnkfelix
Implement arbitrary_enum_discriminant

Implements RFC rust-lang/rfcs#2363 (tracking issue #60553).
Diffstat (limited to 'src')
-rw-r--r--src/doc/unstable-book/src/language-features/arbitrary-enum-discriminant.md37
-rw-r--r--src/librustc_typeck/check/mod.rs19
-rw-r--r--src/librustc_typeck/error_codes.rs32
-rw-r--r--src/libsyntax/feature_gate.rs37
-rw-r--r--src/libsyntax/parse/diagnostics.rs48
-rw-r--r--src/libsyntax/parse/parser.rs31
-rw-r--r--src/libsyntax_pos/symbol.rs1
-rw-r--r--src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.rs9
-rw-r--r--src/test/ui/enum-discriminant/arbitrary_enum_discriminant-no-repr.stderr14
-rw-r--r--src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs56
-rw-r--r--src/test/ui/enum-discriminant/discriminant_value-wrapper.rs (renamed from src/test/run-pass/discriminant_value-wrapper.rs)1
-rw-r--r--src/test/ui/enum-discriminant/discriminant_value.rs (renamed from src/test/run-pass/discriminant_value.rs)18
-rw-r--r--src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.rs10
-rw-r--r--src/test/ui/enum-discriminant/feature-gate-arbitrary_enum_discriminant.stderr36
-rw-r--r--src/test/ui/parser/issue-17383.rs2
-rw-r--r--src/test/ui/parser/issue-17383.stderr10
-rw-r--r--src/test/ui/parser/tag-variant-disr-non-nullary.rs2
-rw-r--r--src/test/ui/parser/tag-variant-disr-non-nullary.stderr20
18 files changed, 328 insertions, 55 deletions
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 00000000000..e0bb782270e
--- /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(<int-type>)]` 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 0ec5c9763a0..cef50dc7ed4 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<Discr<'tcx>> = 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 115ee0f72c9..d61cef7f858 100644
--- a/src/librustc_typeck/error_codes.rs
+++ b/src/librustc_typeck/error_codes.rs
@@ -4733,6 +4733,38 @@ if there are multiple variants, it is not clear how the enum should be
 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 8ec07de5fab..c405acd8ee3 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -25,13 +25,14 @@ use crate::source_map::Spanned;
 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 @@ declare_features! (
     // #[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<MultiSpan>>(
     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<MultiSpan>>(
     sess: &'a ParseSess,
     feature: Symbol,
-    span: Span,
+    span: S,
     issue: GateIssue,
     explain: &str,
     level: GateStrength,
@@ -2037,6 +2041,29 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 }
             }
 
+            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 60544cca877..07fe521edb0 100644
--- a/src/libsyntax/parse/diagnostics.rs
+++ b/src/libsyntax/parse/diagnostics.rs
@@ -2,7 +2,7 @@ use crate::ast::{
     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 @@ impl<'a> Parser<'a> {
     }
 
     crate fn maybe_report_invalid_custom_discriminants(
-        &mut self,
-        discriminant_spans: Vec<Span>,
+        sess: &ParseSess,
         variants: &[Spanned<ast::Variant_>],
     ) {
         let has_fields = variants.iter().any(|variant| match variant.node.data {
@@ -548,28 +547,39 @@ impl<'a> Parser<'a> {
             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::<Vec<_>>();
+
         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 f3ace84162e..a1440f2eba4 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -6946,36 +6946,34 @@ impl<'a> Parser<'a> {
     /// 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 @@ impl<'a> Parser<'a> {
             }
         }
         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 756bc8c29d8..bd9a9061b99 100644
--- a/src/libsyntax_pos/symbol.rs
+++ b/src/libsyntax_pos/symbol.rs
@@ -135,6 +135,7 @@ symbols! {
         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 00000000000..4da7b5ab24b
--- /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 00000000000..2db5372da0c
--- /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 00000000000..f2270602d87
--- /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
index 1fb0d1edddf..daef2de87a9 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
index 162ab27cdff..b7000015c71 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 00000000000..3e90af4d36a
--- /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 00000000000..f50ed2c184d
--- /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 f95005cd914..7bf0e64f2c0 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 37abd0ff5e1..486c4055807 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 305edc4ad5a..a9cfdd549c7 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 2d3b2839531..13b46c6e8b3 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`.