about summary refs log tree commit diff
diff options
context:
space:
mode:
authorOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2022-06-29 14:18:55 +0000
committerOli Scherer <git-spam-no-reply9815368754983@oli-obk.de>2022-06-30 07:47:07 +0000
commit7839cb963f747edff90b72efc6094e7184f6ef0e (patch)
tree5bf0f8fa756c9bfd3867445cd5a97c91066a5d04
parent0e674b3ec5e2c238e6756d512fa39efc1ceeb74a (diff)
downloadrust-7839cb963f747edff90b72efc6094e7184f6ef0e.tar.gz
rust-7839cb963f747edff90b72efc6094e7184f6ef0e.zip
Change enum->int casts to not go through MIR casts.
Instead we generate a discriminant rvalue and cast the result of that.
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs23
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs78
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs25
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs32
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs29
-rw-r--r--src/test/codegen/enum-bounds-check-derived-idx.rs6
-rw-r--r--src/test/codegen/enum-bounds-check-issue-13926.rs3
-rw-r--r--src/test/codegen/enum-bounds-check-issue-82871.rs9
-rw-r--r--src/test/codegen/enum-bounds-check.rs3
-rw-r--r--src/test/mir-opt/enum_cast.bar.mir_map.0.mir13
-rw-r--r--src/test/mir-opt/enum_cast.boo.mir_map.0.mir13
-rw-r--r--src/test/mir-opt/enum_cast.droppy.mir_map.0.mir54
-rw-r--r--src/test/mir-opt/enum_cast.foo.mir_map.0.mir13
-rw-r--r--src/test/mir-opt/enum_cast.rs50
-rw-r--r--src/test/run-pass-valgrind/cast-enum-with-dtor.rs2
15 files changed, 221 insertions, 132 deletions
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index fbe830b2b10..a07ed405aa4 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -635,29 +635,6 @@ fn codegen_stmt<'tcx>(
                             let (ptr, _extra) = operand.load_scalar_pair(fx);
                             lval.write_cvalue(fx, CValue::by_val(ptr, dest_layout))
                         }
-                    } else if let ty::Adt(adt_def, _substs) = from_ty.kind() {
-                        // enum -> discriminant value
-                        assert!(adt_def.is_enum());
-                        match to_ty.kind() {
-                            ty::Uint(_) | ty::Int(_) => {}
-                            _ => unreachable!("cast adt {} -> {}", from_ty, to_ty),
-                        }
-                        let to_clif_ty = fx.clif_type(to_ty).unwrap();
-
-                        let discriminant = crate::discriminant::codegen_get_discriminant(
-                            fx,
-                            operand,
-                            fx.layout_of(operand.layout().ty.discriminant_ty(fx.tcx)),
-                        )
-                        .load_scalar(fx);
-
-                        let res = crate::cast::clif_intcast(
-                            fx,
-                            discriminant,
-                            to_clif_ty,
-                            to_ty.is_signed(),
-                        );
-                        lval.write_cvalue(fx, CValue::by_val(res, dest_layout));
                     } else {
                         let to_clif_ty = fx.clif_type(to_ty).unwrap();
                         let from = operand.load_scalar(fx);
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 6a87da3ba89..e3a055b619a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -12,7 +12,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
 use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
 use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
 use rustc_span::source_map::{Span, DUMMY_SP};
-use rustc_target::abi::{Abi, Int, Variants};
 
 impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     #[instrument(level = "debug", skip(self, bx))]
@@ -283,74 +282,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             CastTy::from_ty(operand.layout.ty).expect("bad input type for cast");
                         let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast");
                         let ll_t_in = bx.cx().immediate_backend_type(operand.layout);
-                        match operand.layout.variants {
-                            Variants::Single { index } => {
-                                if let Some(discr) =
-                                    operand.layout.ty.discriminant_for_variant(bx.tcx(), index)
-                                {
-                                    let discr_layout = bx.cx().layout_of(discr.ty);
-                                    let discr_t = bx.cx().immediate_backend_type(discr_layout);
-                                    let discr_val = bx.cx().const_uint_big(discr_t, discr.val);
-                                    let discr_val =
-                                        bx.intcast(discr_val, ll_t_out, discr.ty.is_signed());
-
-                                    return (
-                                        bx,
-                                        OperandRef {
-                                            val: OperandValue::Immediate(discr_val),
-                                            layout: cast,
-                                        },
-                                    );
-                                }
-                            }
-                            Variants::Multiple { .. } => {}
-                        }
                         let llval = operand.immediate();
 
-                        let mut signed = false;
-                        if let Abi::Scalar(scalar) = operand.layout.abi {
-                            if let Int(_, s) = scalar.primitive() {
-                                // We use `i1` for bytes that are always `0` or `1`,
-                                // e.g., `#[repr(i8)] enum E { A, B }`, but we can't
-                                // let LLVM interpret the `i1` as signed, because
-                                // then `i1 1` (i.e., E::B) is effectively `i8 -1`.
-                                signed = !scalar.is_bool() && s;
-
-                                if !scalar.is_always_valid(bx.cx())
-                                    && scalar.valid_range(bx.cx()).end
-                                        >= scalar.valid_range(bx.cx()).start
-                                {
-                                    // We want `table[e as usize ± k]` to not
-                                    // have bound checks, and this is the most
-                                    // convenient place to put the `assume`s.
-                                    if scalar.valid_range(bx.cx()).start > 0 {
-                                        let enum_value_lower_bound = bx.cx().const_uint_big(
-                                            ll_t_in,
-                                            scalar.valid_range(bx.cx()).start,
-                                        );
-                                        let cmp_start = bx.icmp(
-                                            IntPredicate::IntUGE,
-                                            llval,
-                                            enum_value_lower_bound,
-                                        );
-                                        bx.assume(cmp_start);
-                                    }
-
-                                    let enum_value_upper_bound = bx
-                                        .cx()
-                                        .const_uint_big(ll_t_in, scalar.valid_range(bx.cx()).end);
-                                    let cmp_end = bx.icmp(
-                                        IntPredicate::IntULE,
-                                        llval,
-                                        enum_value_upper_bound,
-                                    );
-                                    bx.assume(cmp_end);
-                                }
-                            }
-                        }
-
                         let newval = match (r_t_in, r_t_out) {
-                            (CastTy::Int(_), CastTy::Int(_)) => bx.intcast(llval, ll_t_out, signed),
+                            (CastTy::Int(i), CastTy::Int(_)) => {
+                                bx.intcast(llval, ll_t_out, matches!(i, IntTy::I))
+                            }
                             (CastTy::Float, CastTy::Float) => {
                                 let srcsz = bx.cx().float_width(ll_t_in);
                                 let dstsz = bx.cx().float_width(ll_t_out);
@@ -362,8 +299,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                                     llval
                                 }
                             }
-                            (CastTy::Int(_), CastTy::Float) => {
-                                if signed {
+                            (CastTy::Int(i), CastTy::Float) => {
+                                if matches!(i, IntTy::I) {
                                     bx.sitofp(llval, ll_t_out)
                                 } else {
                                     bx.uitofp(llval, ll_t_out)
@@ -372,8 +309,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                             (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => {
                                 bx.pointercast(llval, ll_t_out)
                             }
-                            (CastTy::Int(_), CastTy::Ptr(_)) => {
-                                let usize_llval = bx.intcast(llval, bx.cx().type_isize(), signed);
+                            (CastTy::Int(i), CastTy::Ptr(_)) => {
+                                let usize_llval =
+                                    bx.intcast(llval, bx.cx().type_isize(), matches!(i, IntTy::I));
                                 bx.inttoptr(usize_llval, ll_t_out)
                             }
                             (CastTy::Float, CastTy::Int(IntTy::I)) => {
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index fb484fba9fd..4835b7969b2 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -8,7 +8,7 @@ use rustc_middle::mir::CastKind;
 use rustc_middle::ty::adjustment::PointerCast;
 use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
 use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut};
-use rustc_target::abi::{Integer, Variants};
+use rustc_target::abi::Integer;
 use rustc_type_ir::sty::TyKind::*;
 
 use super::{
@@ -127,12 +127,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             Float(FloatTy::F64) => {
                 return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into());
             }
-            // The rest is integer/pointer-"like", including fn ptr casts and casts from enums that
-            // are represented as integers.
+            // The rest is integer/pointer-"like", including fn ptr casts
             _ => assert!(
                 src.layout.ty.is_bool()
                     || src.layout.ty.is_char()
-                    || src.layout.ty.is_enum()
                     || src.layout.ty.is_integral()
                     || src.layout.ty.is_any_ptr(),
                 "Unexpected cast from type {:?}",
@@ -142,25 +140,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
 
         // # First handle non-scalar source values.
 
-        // Handle cast from a ZST enum (0 or 1 variants).
-        match src.layout.variants {
-            Variants::Single { index } => {
-                if src.layout.abi.is_uninhabited() {
-                    // This is dead code, because an uninhabited enum is UB to
-                    // instantiate.
-                    throw_ub!(Unreachable);
-                }
-                if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) {
-                    assert!(src.layout.is_zst());
-                    let discr_layout = self.layout_of(discr.ty)?;
-
-                    let scalar = Scalar::from_uint(discr.val, discr_layout.layout.size());
-                    return Ok(self.cast_from_int_like(scalar, discr_layout, cast_ty)?.into());
-                }
-            }
-            Variants::Multiple { .. } => {}
-        }
-
         // Handle casting any ptr to raw ptr (might be a fat ptr).
         if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() {
             let dest_layout = self.layout_of(cast_ty)?;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 66d66ea9510..dce4a68e811 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -7,9 +7,9 @@ use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
 use rustc_middle::mir::{
-    traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass,
-    MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement,
-    StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
+    traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, Local, Location,
+    MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope,
+    Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
 };
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
@@ -361,6 +361,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     );
                 }
             }
+            Rvalue::Ref(..) => {}
             Rvalue::Len(p) => {
                 let pty = p.ty(&self.body.local_decls, self.tcx).ty;
                 check_kinds!(
@@ -503,7 +504,30 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 let a = operand.ty(&self.body.local_decls, self.tcx);
                 check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..));
             }
-            _ => {}
+            Rvalue::Cast(kind, operand, target_type) => {
+                match kind {
+                    CastKind::Misc => {
+                        let op_ty = operand.ty(self.body, self.tcx);
+                        if op_ty.is_enum() {
+                            self.fail(
+                                location,
+                                format!(
+                                    "enum -> int casts should go through `Rvalue::Discriminant`: {operand:?}:{op_ty} as {target_type}",
+                                ),
+                            );
+                        }
+                    }
+                    // Nothing to check here
+                    CastKind::PointerFromExposedAddress
+                    | CastKind::PointerExposeAddress
+                    | CastKind::Pointer(_) => {}
+                }
+            }
+            Rvalue::Repeat(_, _)
+            | Rvalue::ThreadLocalRef(_)
+            | Rvalue::AddressOf(_, _)
+            | Rvalue::NullaryOp(_, _)
+            | Rvalue::Discriminant(_) => {}
         }
         self.super_rvalue(rvalue, location);
     }
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 8e87ecd27d2..e3a383f86a7 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -1,6 +1,7 @@
 //! See docs in `build/expr/mod.rs`.
 
 use rustc_index::vec::Idx;
+use rustc_middle::ty::util::IntTypeExt;
 
 use crate::build::expr::as_place::PlaceBase;
 use crate::build::expr::category::{Category, RvalueFunc};
@@ -190,7 +191,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
             ExprKind::Cast { source } => {
                 let source = &this.thir[source];
-                let from_ty = CastTy::from_ty(source.ty);
+
+                // Casting an enum to an integer is equivalent to computing the discriminant and casting the
+                // discriminant. Previously every backend had to repeat the logic for this operation. Now we
+                // create all the steps directly in MIR with operations all backends need to support anyway.
+                let (source, ty) = if let ty::Adt(adt_def, ..) = source.ty.kind() && adt_def.is_enum() {
+                    let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
+                    let place = unpack!(block = this.as_place(block, source));
+                    let discr = this.temp(discr_ty, source.span);
+                    this.cfg.push_assign(
+                        block,
+                        source_info,
+                        discr,
+                        Rvalue::Discriminant(place),
+                    );
+
+                    (Operand::Move(discr), discr_ty)
+                } else {
+                    let ty = source.ty;
+                    let source = unpack!(
+                        block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
+                    );
+                    (source, ty)
+                };
+                let from_ty = CastTy::from_ty(ty);
                 let cast_ty = CastTy::from_ty(expr.ty);
                 let cast_kind = match (from_ty, cast_ty) {
                     (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
@@ -201,9 +225,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     }
                     (_, _) => CastKind::Misc,
                 };
-                let source = unpack!(
-                    block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
-                );
                 block.and(Rvalue::Cast(cast_kind, source, expr.ty))
             }
             ExprKind::Pointer { cast, source } => {
diff --git a/src/test/codegen/enum-bounds-check-derived-idx.rs b/src/test/codegen/enum-bounds-check-derived-idx.rs
index aa66c2ed08e..fe02aeb5f62 100644
--- a/src/test/codegen/enum-bounds-check-derived-idx.rs
+++ b/src/test/codegen/enum-bounds-check-derived-idx.rs
@@ -12,13 +12,15 @@ pub enum Bar {
 // CHECK-LABEL: @lookup_inc
 #[no_mangle]
 pub fn lookup_inc(buf: &[u8; 5], f: Bar) -> u8 {
-    // CHECK-NOT: panic_bounds_check
+    // FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
+    // CHECK: panic_bounds_check
     buf[f as usize + 1]
 }
 
 // CHECK-LABEL: @lookup_dec
 #[no_mangle]
 pub fn lookup_dec(buf: &[u8; 5], f: Bar) -> u8 {
-    // CHECK-NOT: panic_bounds_check
+    // FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
+    // CHECK: panic_bounds_check
     buf[f as usize - 1]
 }
diff --git a/src/test/codegen/enum-bounds-check-issue-13926.rs b/src/test/codegen/enum-bounds-check-issue-13926.rs
index b26945bc549..1aec41d5441 100644
--- a/src/test/codegen/enum-bounds-check-issue-13926.rs
+++ b/src/test/codegen/enum-bounds-check-issue-13926.rs
@@ -13,6 +13,7 @@ pub enum Exception {
 // CHECK-LABEL: @access
 #[no_mangle]
 pub fn access(array: &[usize; 12], exc: Exception) -> usize {
-    // CHECK-NOT: panic_bounds_check
+    // FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
+    // CHECK: panic_bounds_check
     array[(exc as u8 - 4) as usize]
 }
diff --git a/src/test/codegen/enum-bounds-check-issue-82871.rs b/src/test/codegen/enum-bounds-check-issue-82871.rs
index a1fa1387d94..32fdc4a5f4f 100644
--- a/src/test/codegen/enum-bounds-check-issue-82871.rs
+++ b/src/test/codegen/enum-bounds-check-issue-82871.rs
@@ -1,4 +1,4 @@
-// compile-flags: -O
+// compile-flags: -C opt-level=0
 
 #![crate_type = "lib"]
 
@@ -9,7 +9,10 @@ pub enum E {
 
 // CHECK-LABEL: @index
 #[no_mangle]
-pub fn index(x: &[u32; 3], ind: E) -> u32{
-    // CHECK-NOT: panic_bounds_check
+pub fn index(x: &[u32; 3], ind: E) -> u32 {
+    // Canary: we should be able to optimize out the bounds check, but we need
+    // to track the range of the discriminant result in order to be able to do that.
+    // oli-obk tried to add that, but that caused miscompilations all over the place.
+    // CHECK: panic_bounds_check
     x[ind as usize]
 }
diff --git a/src/test/codegen/enum-bounds-check.rs b/src/test/codegen/enum-bounds-check.rs
index 17322d5911b..f85c6817ded 100644
--- a/src/test/codegen/enum-bounds-check.rs
+++ b/src/test/codegen/enum-bounds-check.rs
@@ -21,6 +21,7 @@ pub enum Bar {
 // CHECK-LABEL: @lookup_unmodified
 #[no_mangle]
 pub fn lookup_unmodified(buf: &[u8; 5], f: Bar) -> u8 {
-    // CHECK-NOT: panic_bounds_check
+    // FIXME: panic check can be removed by adding the assumes back after https://github.com/rust-lang/rust/pull/98332
+    // CHECK: panic_bounds_check
     buf[f as usize]
 }
diff --git a/src/test/mir-opt/enum_cast.bar.mir_map.0.mir b/src/test/mir-opt/enum_cast.bar.mir_map.0.mir
new file mode 100644
index 00000000000..1b4a469135c
--- /dev/null
+++ b/src/test/mir-opt/enum_cast.bar.mir_map.0.mir
@@ -0,0 +1,13 @@
+// MIR for `bar` 0 mir_map
+
+fn bar(_1: Bar) -> usize {
+    debug bar => _1;                     // in scope 0 at $DIR/enum_cast.rs:22:8: 22:11
+    let mut _0: usize;                   // return place in scope 0 at $DIR/enum_cast.rs:22:21: 22:26
+    let mut _2: isize;                   // in scope 0 at $DIR/enum_cast.rs:23:5: 23:8
+
+    bb0: {
+        _2 = discriminant(_1);           // scope 0 at $DIR/enum_cast.rs:23:5: 23:17
+        _0 = move _2 as usize (Misc);    // scope 0 at $DIR/enum_cast.rs:23:5: 23:17
+        return;                          // scope 0 at $DIR/enum_cast.rs:24:2: 24:2
+    }
+}
diff --git a/src/test/mir-opt/enum_cast.boo.mir_map.0.mir b/src/test/mir-opt/enum_cast.boo.mir_map.0.mir
new file mode 100644
index 00000000000..7724e89a228
--- /dev/null
+++ b/src/test/mir-opt/enum_cast.boo.mir_map.0.mir
@@ -0,0 +1,13 @@
+// MIR for `boo` 0 mir_map
+
+fn boo(_1: Boo) -> usize {
+    debug boo => _1;                     // in scope 0 at $DIR/enum_cast.rs:26:8: 26:11
+    let mut _0: usize;                   // return place in scope 0 at $DIR/enum_cast.rs:26:21: 26:26
+    let mut _2: u8;                      // in scope 0 at $DIR/enum_cast.rs:27:5: 27:8
+
+    bb0: {
+        _2 = discriminant(_1);           // scope 0 at $DIR/enum_cast.rs:27:5: 27:17
+        _0 = move _2 as usize (Misc);    // scope 0 at $DIR/enum_cast.rs:27:5: 27:17
+        return;                          // scope 0 at $DIR/enum_cast.rs:28:2: 28:2
+    }
+}
diff --git a/src/test/mir-opt/enum_cast.droppy.mir_map.0.mir b/src/test/mir-opt/enum_cast.droppy.mir_map.0.mir
new file mode 100644
index 00000000000..a9dcfadae75
--- /dev/null
+++ b/src/test/mir-opt/enum_cast.droppy.mir_map.0.mir
@@ -0,0 +1,54 @@
+// MIR for `droppy` 0 mir_map
+
+fn droppy() -> () {
+    let mut _0: ();                      // return place in scope 0 at $DIR/enum_cast.rs:39:13: 39:13
+    let _1: ();                          // in scope 0 at $DIR/enum_cast.rs:40:5: 45:6
+    let _2: Droppy;                      // in scope 0 at $DIR/enum_cast.rs:41:13: 41:14
+    let mut _4: isize;                   // in scope 0 at $DIR/enum_cast.rs:44:17: 44:18
+    let _5: Droppy;                      // in scope 0 at $DIR/enum_cast.rs:46:9: 46:10
+    scope 1 {
+        debug x => _2;                   // in scope 1 at $DIR/enum_cast.rs:41:13: 41:14
+        scope 2 {
+            debug y => _3;               // in scope 2 at $DIR/enum_cast.rs:44:13: 44:14
+        }
+        scope 3 {
+            let _3: usize;               // in scope 3 at $DIR/enum_cast.rs:44:13: 44:14
+        }
+    }
+    scope 4 {
+        debug z => _5;                   // in scope 4 at $DIR/enum_cast.rs:46:9: 46:10
+    }
+
+    bb0: {
+        StorageLive(_1);                 // scope 0 at $DIR/enum_cast.rs:40:5: 45:6
+        StorageLive(_2);                 // scope 0 at $DIR/enum_cast.rs:41:13: 41:14
+        _2 = Droppy::C;                  // scope 0 at $DIR/enum_cast.rs:41:17: 41:26
+        FakeRead(ForLet(None), _2);      // scope 0 at $DIR/enum_cast.rs:41:13: 41:14
+        StorageLive(_3);                 // scope 3 at $DIR/enum_cast.rs:44:13: 44:14
+        _4 = discriminant(_2);           // scope 3 at $DIR/enum_cast.rs:44:17: 44:27
+        _3 = move _4 as usize (Misc);    // scope 3 at $DIR/enum_cast.rs:44:17: 44:27
+        FakeRead(ForLet(None), _3);      // scope 3 at $DIR/enum_cast.rs:44:13: 44:14
+        _1 = const ();                   // scope 0 at $DIR/enum_cast.rs:40:5: 45:6
+        StorageDead(_3);                 // scope 1 at $DIR/enum_cast.rs:45:5: 45:6
+        drop(_2) -> [return: bb1, unwind: bb3]; // scope 0 at $DIR/enum_cast.rs:45:5: 45:6
+    }
+
+    bb1: {
+        StorageDead(_2);                 // scope 0 at $DIR/enum_cast.rs:45:5: 45:6
+        StorageDead(_1);                 // scope 0 at $DIR/enum_cast.rs:45:5: 45:6
+        StorageLive(_5);                 // scope 0 at $DIR/enum_cast.rs:46:9: 46:10
+        _5 = Droppy::B;                  // scope 0 at $DIR/enum_cast.rs:46:13: 46:22
+        FakeRead(ForLet(None), _5);      // scope 0 at $DIR/enum_cast.rs:46:9: 46:10
+        _0 = const ();                   // scope 0 at $DIR/enum_cast.rs:39:13: 47:2
+        drop(_5) -> [return: bb2, unwind: bb3]; // scope 0 at $DIR/enum_cast.rs:47:1: 47:2
+    }
+
+    bb2: {
+        StorageDead(_5);                 // scope 0 at $DIR/enum_cast.rs:47:1: 47:2
+        return;                          // scope 0 at $DIR/enum_cast.rs:47:2: 47:2
+    }
+
+    bb3 (cleanup): {
+        resume;                          // scope 0 at $DIR/enum_cast.rs:39:1: 47:2
+    }
+}
diff --git a/src/test/mir-opt/enum_cast.foo.mir_map.0.mir b/src/test/mir-opt/enum_cast.foo.mir_map.0.mir
new file mode 100644
index 00000000000..d89dc951923
--- /dev/null
+++ b/src/test/mir-opt/enum_cast.foo.mir_map.0.mir
@@ -0,0 +1,13 @@
+// MIR for `foo` 0 mir_map
+
+fn foo(_1: Foo) -> usize {
+    debug foo => _1;                     // in scope 0 at $DIR/enum_cast.rs:18:8: 18:11
+    let mut _0: usize;                   // return place in scope 0 at $DIR/enum_cast.rs:18:21: 18:26
+    let mut _2: isize;                   // in scope 0 at $DIR/enum_cast.rs:19:5: 19:8
+
+    bb0: {
+        _2 = discriminant(_1);           // scope 0 at $DIR/enum_cast.rs:19:5: 19:17
+        _0 = move _2 as usize (Misc);    // scope 0 at $DIR/enum_cast.rs:19:5: 19:17
+        return;                          // scope 0 at $DIR/enum_cast.rs:20:2: 20:2
+    }
+}
diff --git a/src/test/mir-opt/enum_cast.rs b/src/test/mir-opt/enum_cast.rs
new file mode 100644
index 00000000000..090142aaf35
--- /dev/null
+++ b/src/test/mir-opt/enum_cast.rs
@@ -0,0 +1,50 @@
+// EMIT_MIR enum_cast.foo.mir_map.0.mir
+// EMIT_MIR enum_cast.bar.mir_map.0.mir
+// EMIT_MIR enum_cast.boo.mir_map.0.mir
+
+enum Foo {
+    A
+}
+
+enum Bar {
+    A, B
+}
+
+#[repr(u8)]
+enum Boo {
+    A, B
+}
+
+fn foo(foo: Foo) -> usize {
+    foo as usize
+}
+
+fn bar(bar: Bar) -> usize {
+    bar as usize
+}
+
+fn boo(boo: Boo) -> usize {
+    boo as usize
+}
+
+// EMIT_MIR enum_cast.droppy.mir_map.0.mir
+enum Droppy {
+    A, B, C
+}
+
+impl Drop for Droppy {
+    fn drop(&mut self) {}
+}
+
+fn droppy() {
+    {
+        let x = Droppy::C;
+        // remove this entire test once `cenum_impl_drop_cast` becomes a hard error
+        #[allow(cenum_impl_drop_cast)]
+        let y = x as usize;
+    }
+    let z = Droppy::B;
+}
+
+fn main() {
+}
diff --git a/src/test/run-pass-valgrind/cast-enum-with-dtor.rs b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
index 998d202b117..f29bc50e84c 100644
--- a/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
+++ b/src/test/run-pass-valgrind/cast-enum-with-dtor.rs
@@ -30,5 +30,5 @@ fn main() {
         assert_eq!(e as u32, 2);
         assert_eq!(FLAG.load(Ordering::SeqCst), 0);
     }
-    assert_eq!(FLAG.load(Ordering::SeqCst), 0);
+    assert_eq!(FLAG.load(Ordering::SeqCst), 1);
 }