Skip to content

Commit

Permalink
Rollup merge of rust-lang#89234 - nbdd0121:discr, r=jackh726
Browse files Browse the repository at this point in the history
Disallow non-c-like but "fieldless" ADTs from being casted to integer if they use arbitrary enum discriminant

Code like

```rust
#[repr(u8)]
enum Enum {
    Foo /* = 0 */,
    Bar(),
    Baz{}
}

let x = Enum::Bar() as u8;
```

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~~ (stabilization was reverted).

Related: rust-lang#88621

`@rustbot` label +T-lang
  • Loading branch information
matthiaskrgr committed Dec 2, 2021
2 parents d9baa36 + f7ef1c9 commit 0666a33
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 14 deletions.
18 changes: 17 additions & 1 deletion compiler/rustc_middle/src/ty/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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())
}

Expand Down
13 changes: 13 additions & 0 deletions src/test/ui/cast/issue-88621.rs
Original file line number Diff line number Diff line change
@@ -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
}
9 changes: 9 additions & 0 deletions src/test/ui/cast/issue-88621.stderr
Original file line number Diff line number Diff line change
@@ -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`.
13 changes: 0 additions & 13 deletions src/test/ui/enum-discriminant/arbitrary_enum_discriminant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}

0 comments on commit 0666a33

Please sign in to comment.