From f7ef1c9f4163df30ae67ab177d5c306cb90b3d47 Mon Sep 17 00:00:00 2001
From: Gary Guo <gary@garyguo.net>
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                | 13 +++++++++++++
 src/test/ui/cast/issue-88621.stderr            |  9 +++++++++
 .../arbitrary_enum_discriminant.rs             | 13 -------------
 4 files changed, 39 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 44f741c5df1a2..69eb73b42552c 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -7,7 +7,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_query_system::ich::StableHashingContext;
@@ -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..9242b80e22939
--- /dev/null
+++ b/src/test/ui/cast/issue-88621.rs
@@ -0,0 +1,13 @@
+#![feature(arbitrary_enum_discriminant)]
+
+#[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..e96d866515238
--- /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:11: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 22c5332c9259f..ccc423e4a194c 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);
 }