From 996e5edcf37a8605dab3d1db3c73d3d35b5a36bb Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Fri, 24 Sep 2021 21:58:25 +0100 Subject: [PATCH] Disallow non-c-like but "fieldless" ADTs from being casted to integer... ... if they use arbitrary enum discriminant. Code like ```rust enum Enum { Foo = 1, Bar(), Baz{} } ``` seems to be unintentionally allowed so we couldn't disallow them now, but we could disallow them if arbitrary enum discriminant is used before 1.56 hits stable. --- compiler/rustc_middle/src/ty/adt.rs | 18 +++++++++++++++++- src/test/ui/cast/issue-88621.rs | 11 +++++++++++ src/test/ui/cast/issue-88621.stderr | 9 +++++++++ .../arbitrary_enum_discriminant.rs | 13 ------------- 4 files changed, 37 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/cast/issue-88621.rs create mode 100644 src/test/ui/cast/issue-88621.stderr diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index c32f0ea9ca55a..0e0fd3c21261e 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_errors::ErrorReported; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_index::vec::{Idx, IndexVec}; use rustc_serialize::{self, Encodable, Encoder}; @@ -314,6 +314,22 @@ impl<'tcx> AdtDef { /// Whether the ADT lacks fields. Note that this includes uninhabited enums, /// e.g., `enum Void {}` is considered payload free as well. pub fn is_payloadfree(&self) -> bool { + // Treat the ADT as not payload-free if arbitrary_enum_discriminant is used (#88621). + // This would disallow the following kind of enum from being casted into integer. + // ``` + // enum Enum { + // Foo() = 1, + // Bar{} = 2, + // Baz = 3, + // } + // ``` + if self + .variants + .iter() + .any(|v| matches!(v.discr, VariantDiscr::Explicit(_)) && v.ctor_kind != CtorKind::Const) + { + return false; + } self.variants.iter().all(|v| v.fields.is_empty()) } diff --git a/src/test/ui/cast/issue-88621.rs b/src/test/ui/cast/issue-88621.rs new file mode 100644 index 0000000000000..1679793ee6834 --- /dev/null +++ b/src/test/ui/cast/issue-88621.rs @@ -0,0 +1,11 @@ +#[repr(u8)] +enum Kind2 { + Foo() = 1, + Bar{} = 2, + Baz = 3, +} + +fn main() { + let _ = Kind2::Foo() as u8; + //~^ ERROR non-primitive cast +} diff --git a/src/test/ui/cast/issue-88621.stderr b/src/test/ui/cast/issue-88621.stderr new file mode 100644 index 0000000000000..886145c1bafee --- /dev/null +++ b/src/test/ui/cast/issue-88621.stderr @@ -0,0 +1,9 @@ +error[E0605]: non-primitive cast: `Kind2` as `u8` + --> $DIR/issue-88621.rs:9:13 + | +LL | let _ = Kind2::Foo() as u8; + | ^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0605`. diff --git a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs index 360bddb7bd1e4..0f438f8dbadd8 100644 --- a/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs +++ b/src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs @@ -22,14 +22,6 @@ impl Enum { } } -#[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); @@ -48,9 +40,4 @@ fn main() { 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); }