1
Fork 0

Change enum->int casts to not go through MIR casts.

Instead we generate a discriminant rvalue and cast the result of that.
This commit is contained in:
Oli Scherer 2022-06-29 14:18:55 +00:00
parent 0e674b3ec5
commit 7839cb963f
15 changed files with 221 additions and 132 deletions

View file

@ -635,29 +635,6 @@ fn codegen_stmt<'tcx>(
let (ptr, _extra) = operand.load_scalar_pair(fx); let (ptr, _extra) = operand.load_scalar_pair(fx);
lval.write_cvalue(fx, CValue::by_val(ptr, dest_layout)) 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 { } else {
let to_clif_ty = fx.clif_type(to_ty).unwrap(); let to_clif_ty = fx.clif_type(to_ty).unwrap();
let from = operand.load_scalar(fx); let from = operand.load_scalar(fx);

View file

@ -12,7 +12,6 @@ use rustc_middle::ty::cast::{CastTy, IntTy};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt}; use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
use rustc_span::source_map::{Span, DUMMY_SP}; 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> { impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[instrument(level = "debug", skip(self, 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"); 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 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); 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 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) { 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) => { (CastTy::Float, CastTy::Float) => {
let srcsz = bx.cx().float_width(ll_t_in); let srcsz = bx.cx().float_width(ll_t_in);
let dstsz = bx.cx().float_width(ll_t_out); 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 llval
} }
} }
(CastTy::Int(_), CastTy::Float) => { (CastTy::Int(i), CastTy::Float) => {
if signed { if matches!(i, IntTy::I) {
bx.sitofp(llval, ll_t_out) bx.sitofp(llval, ll_t_out)
} else { } else {
bx.uitofp(llval, ll_t_out) 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(_)) => { (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Ptr(_)) => {
bx.pointercast(llval, ll_t_out) bx.pointercast(llval, ll_t_out)
} }
(CastTy::Int(_), CastTy::Ptr(_)) => { (CastTy::Int(i), CastTy::Ptr(_)) => {
let usize_llval = bx.intcast(llval, bx.cx().type_isize(), signed); let usize_llval =
bx.intcast(llval, bx.cx().type_isize(), matches!(i, IntTy::I));
bx.inttoptr(usize_llval, ll_t_out) bx.inttoptr(usize_llval, ll_t_out)
} }
(CastTy::Float, CastTy::Int(IntTy::I)) => { (CastTy::Float, CastTy::Int(IntTy::I)) => {

View file

@ -8,7 +8,7 @@ use rustc_middle::mir::CastKind;
use rustc_middle::ty::adjustment::PointerCast; use rustc_middle::ty::adjustment::PointerCast;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty, TypeAndMut}; 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 rustc_type_ir::sty::TyKind::*;
use super::{ use super::{
@ -127,12 +127,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Float(FloatTy::F64) => { Float(FloatTy::F64) => {
return Ok(self.cast_from_float(src.to_scalar()?.to_f64()?, cast_ty).into()); 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 // The rest is integer/pointer-"like", including fn ptr casts
// are represented as integers.
_ => assert!( _ => assert!(
src.layout.ty.is_bool() src.layout.ty.is_bool()
|| src.layout.ty.is_char() || src.layout.ty.is_char()
|| src.layout.ty.is_enum()
|| src.layout.ty.is_integral() || src.layout.ty.is_integral()
|| src.layout.ty.is_any_ptr(), || src.layout.ty.is_any_ptr(),
"Unexpected cast from type {:?}", "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. // # 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). // Handle casting any ptr to raw ptr (might be a fat ptr).
if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() { if src.layout.ty.is_any_ptr() && cast_ty.is_unsafe_ptr() {
let dest_layout = self.layout_of(cast_ty)?; let dest_layout = self.layout_of(cast_ty)?;

View file

@ -7,9 +7,9 @@ use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo; use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{ use rustc_middle::mir::{
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass, traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, Local, Location,
MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement, MirPass, MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope,
StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, Statement, StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK,
}; };
use rustc_middle::ty::fold::BottomUpFolder; use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; 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) => { Rvalue::Len(p) => {
let pty = p.ty(&self.body.local_decls, self.tcx).ty; let pty = p.ty(&self.body.local_decls, self.tcx).ty;
check_kinds!( 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); let a = operand.ty(&self.body.local_decls, self.tcx);
check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..)); 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); self.super_rvalue(rvalue, location);
} }

View file

@ -1,6 +1,7 @@
//! See docs in `build/expr/mod.rs`. //! See docs in `build/expr/mod.rs`.
use rustc_index::vec::Idx; use rustc_index::vec::Idx;
use rustc_middle::ty::util::IntTypeExt;
use crate::build::expr::as_place::PlaceBase; use crate::build::expr::as_place::PlaceBase;
use crate::build::expr::category::{Category, RvalueFunc}; use crate::build::expr::category::{Category, RvalueFunc};
@ -190,7 +191,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
ExprKind::Cast { source } => { ExprKind::Cast { source } => {
let source = &this.thir[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_ty = CastTy::from_ty(expr.ty);
let cast_kind = match (from_ty, cast_ty) { let cast_kind = match (from_ty, cast_ty) {
(Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => { (Some(CastTy::Ptr(_) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
@ -201,9 +225,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
(_, _) => CastKind::Misc, (_, _) => CastKind::Misc,
}; };
let source = unpack!(
block = this.as_operand(block, scope, source, None, NeedsTemporary::No)
);
block.and(Rvalue::Cast(cast_kind, source, expr.ty)) block.and(Rvalue::Cast(cast_kind, source, expr.ty))
} }
ExprKind::Pointer { cast, source } => { ExprKind::Pointer { cast, source } => {

View file

@ -12,13 +12,15 @@ pub enum Bar {
// CHECK-LABEL: @lookup_inc // CHECK-LABEL: @lookup_inc
#[no_mangle] #[no_mangle]
pub fn lookup_inc(buf: &[u8; 5], f: Bar) -> u8 { 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] buf[f as usize + 1]
} }
// CHECK-LABEL: @lookup_dec // CHECK-LABEL: @lookup_dec
#[no_mangle] #[no_mangle]
pub fn lookup_dec(buf: &[u8; 5], f: Bar) -> u8 { 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] buf[f as usize - 1]
} }

View file

@ -13,6 +13,7 @@ pub enum Exception {
// CHECK-LABEL: @access // CHECK-LABEL: @access
#[no_mangle] #[no_mangle]
pub fn access(array: &[usize; 12], exc: Exception) -> usize { 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] array[(exc as u8 - 4) as usize]
} }

View file

@ -1,4 +1,4 @@
// compile-flags: -O // compile-flags: -C opt-level=0
#![crate_type = "lib"] #![crate_type = "lib"]
@ -9,7 +9,10 @@ pub enum E {
// CHECK-LABEL: @index // CHECK-LABEL: @index
#[no_mangle] #[no_mangle]
pub fn index(x: &[u32; 3], ind: E) -> u32{ pub fn index(x: &[u32; 3], ind: E) -> u32 {
// CHECK-NOT: panic_bounds_check // 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] x[ind as usize]
} }

View file

@ -21,6 +21,7 @@ pub enum Bar {
// CHECK-LABEL: @lookup_unmodified // CHECK-LABEL: @lookup_unmodified
#[no_mangle] #[no_mangle]
pub fn lookup_unmodified(buf: &[u8; 5], f: Bar) -> u8 { 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] buf[f as usize]
} }

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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() {
}

View file

@ -30,5 +30,5 @@ fn main() {
assert_eq!(e as u32, 2); 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), 0); assert_eq!(FLAG.load(Ordering::SeqCst), 1);
} }