Skip to content

Commit ce57c04

Browse files
committed
Stop emitting Assumes into MIR when as-casting enums.
1 parent 3ea711f commit ce57c04

13 files changed

+281
-163
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
2323
use rustc_middle::mir::*;
2424
use rustc_middle::traits::query::NoSolution;
2525
use rustc_middle::ty::adjustment::PointerCoercion;
26-
use rustc_middle::ty::cast::CastTy;
26+
use rustc_middle::ty::cast::{CastTy, IntTy};
2727
use rustc_middle::ty::fold::fold_regions;
2828
use rustc_middle::ty::visit::TypeVisitableExt;
2929
use rustc_middle::ty::{
@@ -2183,11 +2183,24 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
21832183
}
21842184
}
21852185
CastKind::Transmute => {
2186-
span_mirbug!(
2187-
self,
2188-
rvalue,
2189-
"Unexpected CastKind::Transmute, which is not permitted in Analysis MIR",
2190-
);
2186+
let ty_from = op.ty(body, tcx);
2187+
let cast_ty_from = CastTy::from_ty(ty_from);
2188+
let cast_ty_to = CastTy::from_ty(*ty);
2189+
match (cast_ty_from, cast_ty_to) {
2190+
(
2191+
Some(CastTy::Int(IntTy::CEnum)),
2192+
Some(CastTy::Int(IntTy::U(_) | IntTy::I)),
2193+
) => (),
2194+
_ => {
2195+
span_mirbug!(
2196+
self,
2197+
rvalue,
2198+
"Invalid CastKind::Transmute cast {:?} -> {:?}",
2199+
ty_from,
2200+
ty
2201+
)
2202+
}
2203+
}
21912204
}
21922205
}
21932206
}

compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs

Lines changed: 51 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! See docs in `build/expr/mod.rs`.
22
3-
use rustc_abi::{BackendRepr, FieldIdx, Primitive};
3+
use std::assert_matches::assert_matches;
4+
5+
use rustc_abi::{BackendRepr, FieldIdx, TagEncoding, Variants};
46
use rustc_hir::lang_items::LangItem;
57
use rustc_index::{Idx, IndexVec};
68
use rustc_middle::bug;
@@ -9,8 +11,8 @@ use rustc_middle::mir::interpret::Scalar;
911
use rustc_middle::mir::*;
1012
use rustc_middle::thir::*;
1113
use rustc_middle::ty::cast::{CastTy, mir_cast_kind};
12-
use rustc_middle::ty::layout::IntegerExt;
13-
use rustc_middle::ty::util::IntTypeExt;
14+
use rustc_middle::ty::layout::PrimitiveExt;
15+
use rustc_middle::ty::util::Discr;
1416
use rustc_middle::ty::{self, Ty, UpvarArgs};
1517
use rustc_span::source_map::Spanned;
1618
use rustc_span::{DUMMY_SP, Span};
@@ -197,97 +199,64 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
197199
ExprKind::Cast { source } => {
198200
let source_expr = &this.thir[source];
199201

200-
// Casting an enum to an integer is equivalent to computing the discriminant and casting the
201-
// discriminant. Previously every backend had to repeat the logic for this operation. Now we
202-
// create all the steps directly in MIR with operations all backends need to support anyway.
203202
let (source, ty) = if let ty::Adt(adt_def, ..) = source_expr.ty.kind()
204203
&& adt_def.is_enum()
205204
{
206-
let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
207205
let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not));
208-
let layout =
209-
this.tcx.layout_of(this.typing_env().as_query_input(source_expr.ty));
210-
let discr = this.temp(discr_ty, source_expr.span);
211-
this.cfg.push_assign(
212-
block,
213-
source_info,
214-
discr,
215-
Rvalue::Discriminant(temp.into()),
216-
);
217-
let (op, ty) = (Operand::Move(discr), discr_ty);
218-
219-
if let BackendRepr::Scalar(scalar) = layout.unwrap().backend_repr
220-
&& !scalar.is_always_valid(&this.tcx)
221-
&& let Primitive::Int(int_width, _signed) = scalar.primitive()
222-
{
223-
let unsigned_ty = int_width.to_ty(this.tcx, false);
224-
let unsigned_place = this.temp(unsigned_ty, expr_span);
225-
this.cfg.push_assign(
226-
block,
227-
source_info,
228-
unsigned_place,
229-
Rvalue::Cast(CastKind::IntToInt, Operand::Copy(discr), unsigned_ty),
230-
);
231-
232-
let bool_ty = this.tcx.types.bool;
233-
let range = scalar.valid_range(&this.tcx);
234-
let merge_op =
235-
if range.start <= range.end { BinOp::BitAnd } else { BinOp::BitOr };
236-
237-
let mut comparer = |range: u128, bin_op: BinOp| -> Place<'tcx> {
238-
// We can use `ty::TypingEnv::fully_monomorphized()` here
239-
// as we only need it to compute the layout of a primitive.
240-
let range_val = Const::from_bits(
241-
this.tcx,
242-
range,
243-
ty::TypingEnv::fully_monomorphized(),
244-
unsigned_ty,
245-
);
246-
let lit_op = this.literal_operand(expr.span, range_val);
247-
let is_bin_op = this.temp(bool_ty, expr_span);
248-
this.cfg.push_assign(
206+
let layout = this
207+
.tcx
208+
.layout_of(this.typing_env().as_query_input(source_expr.ty))
209+
.unwrap();
210+
match layout.variants {
211+
// Uninhabited enum, so we're in dead code.
212+
Variants::Empty => {
213+
let false_lit = this.zero_literal(expr_span, this.tcx.types.bool);
214+
this.cfg.push(
249215
block,
250-
source_info,
251-
is_bin_op,
252-
Rvalue::BinaryOp(
253-
bin_op,
254-
Box::new((Operand::Copy(unsigned_place), lit_op)),
255-
),
216+
Statement {
217+
source_info,
218+
kind: StatementKind::Intrinsic(Box::new(
219+
NonDivergingIntrinsic::Assume(false_lit),
220+
)),
221+
},
256222
);
257-
is_bin_op
258-
};
259-
let assert_place = if range.start == 0 {
260-
comparer(range.end, BinOp::Le)
261-
} else {
262-
let start_place = comparer(range.start, BinOp::Ge);
263-
let end_place = comparer(range.end, BinOp::Le);
264-
let merge_place = this.temp(bool_ty, expr_span);
223+
// We still need to emit *something*, to keep the following MIR legal,
224+
// so give a zero of the type the cast was asking for.
225+
(this.zero_literal(expr_span, expr.ty), expr.ty)
226+
}
227+
// Only one legal variant, so we can just look up its
228+
// discriminant directly and return it as a constant.
229+
// (In the discriminant's type, not the cast-to type,
230+
// to avoid worrying about truncation or extension.)
231+
Variants::Single { index } => {
232+
let Discr { val, ty } =
233+
adt_def.discriminant_for_variant(this.tcx, index);
234+
let val = Const::from_bits(this.tcx, val, this.typing_env(), ty);
235+
(this.literal_operand(expr_span, val), ty)
236+
}
237+
// Casting an enum to an integer is only supported for enums which
238+
// have no fields, so we can transmute the stored tag.
239+
// This used to emit `Assume`s into the MIR, but that bloated it,
240+
// so now we re-use the `Transmute` checks that backends ought to
241+
// support anyway for polymorphic MIR cases.
242+
Variants::Multiple { tag, ref tag_encoding, .. } => {
243+
assert_matches!(tag_encoding, TagEncoding::Direct);
244+
assert_matches!(layout.backend_repr, BackendRepr::Scalar(repr) if repr == tag);
245+
let tag_ty = tag.primitive().to_ty(this.tcx);
246+
let tag = this.temp(tag_ty, expr_span);
265247
this.cfg.push_assign(
266248
block,
267249
source_info,
268-
merge_place,
269-
Rvalue::BinaryOp(
270-
merge_op,
271-
Box::new((
272-
Operand::Move(start_place),
273-
Operand::Move(end_place),
274-
)),
250+
tag,
251+
Rvalue::Cast(
252+
CastKind::Transmute,
253+
Operand::Move(Place::from(temp)),
254+
tag_ty,
275255
),
276256
);
277-
merge_place
278-
};
279-
this.cfg.push(
280-
block,
281-
Statement {
282-
source_info,
283-
kind: StatementKind::Intrinsic(Box::new(
284-
NonDivergingIntrinsic::Assume(Operand::Move(assert_place)),
285-
)),
286-
},
287-
);
257+
(Operand::Move(tag), tag_ty)
258+
}
288259
}
289-
290-
(op, ty)
291260
} else {
292261
let ty = source_expr.ty;
293262
let source = unpack!(

compiler/rustc_mir_transform/src/validate.rs

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,37 +1303,27 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
13031303
}
13041304
}
13051305
CastKind::Transmute => {
1306-
if let MirPhase::Runtime(..) = self.body.phase {
1307-
// Unlike `mem::transmute`, a MIR `Transmute` is well-formed
1308-
// for any two `Sized` types, just potentially UB to run.
1309-
1310-
if !self
1311-
.tcx
1312-
.normalize_erasing_regions(self.typing_env, op_ty)
1313-
.is_sized(self.tcx, self.typing_env)
1314-
{
1315-
self.fail(
1316-
location,
1317-
format!("Cannot transmute from non-`Sized` type {op_ty:?}"),
1318-
);
1319-
}
1320-
if !self
1321-
.tcx
1322-
.normalize_erasing_regions(self.typing_env, *target_type)
1323-
.is_sized(self.tcx, self.typing_env)
1324-
{
1325-
self.fail(
1326-
location,
1327-
format!("Cannot transmute to non-`Sized` type {target_type:?}"),
1328-
);
1329-
}
1330-
} else {
1306+
// Unlike `mem::transmute`, a MIR `Transmute` is well-formed
1307+
// for any two `Sized` types, just potentially UB to run.
1308+
1309+
if !self
1310+
.tcx
1311+
.normalize_erasing_regions(self.typing_env, op_ty)
1312+
.is_sized(self.tcx, self.typing_env)
1313+
{
13311314
self.fail(
13321315
location,
1333-
format!(
1334-
"Transmute is not supported in non-runtime phase {:?}.",
1335-
self.body.phase
1336-
),
1316+
format!("Cannot transmute from non-`Sized` type {op_ty:?}"),
1317+
);
1318+
}
1319+
if !self
1320+
.tcx
1321+
.normalize_erasing_regions(self.typing_env, *target_type)
1322+
.is_sized(self.tcx, self.typing_env)
1323+
{
1324+
self.fail(
1325+
location,
1326+
format!("Cannot transmute to non-`Sized` type {target_type:?}"),
13371327
);
13381328
}
13391329
}

tests/mir-opt/building/enum_cast.bar.built.after.mir

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,12 @@ fn bar(_1: Bar) -> usize {
44
debug bar => _1;
55
let mut _0: usize;
66
let _2: Bar;
7-
let mut _3: isize;
8-
let mut _4: u8;
9-
let mut _5: bool;
7+
let mut _3: u8;
108

119
bb0: {
1210
StorageLive(_2);
1311
_2 = move _1;
14-
_3 = discriminant(_2);
15-
_4 = copy _3 as u8 (IntToInt);
16-
_5 = Le(copy _4, const 1_u8);
17-
assume(move _5);
12+
_3 = move _2 as u8 (Transmute);
1813
_0 = move _3 as usize (IntToInt);
1914
StorageDead(_2);
2015
return;

tests/mir-opt/building/enum_cast.boo.built.after.mir

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,11 @@ fn boo(_1: Boo) -> usize {
55
let mut _0: usize;
66
let _2: Boo;
77
let mut _3: u8;
8-
let mut _4: u8;
9-
let mut _5: bool;
108

119
bb0: {
1210
StorageLive(_2);
1311
_2 = move _1;
14-
_3 = discriminant(_2);
15-
_4 = copy _3 as u8 (IntToInt);
16-
_5 = Le(copy _4, const 1_u8);
17-
assume(move _5);
12+
_3 = move _2 as u8 (Transmute);
1813
_0 = move _3 as usize (IntToInt);
1914
StorageDead(_2);
2015
return;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// MIR for `custom_single_variant` after built
2+
3+
fn custom_single_variant(_1: SingleVariantWithCustomDiscriminant, _2: SingleVariantWithCustomDiscriminantAndRepr) -> impl Sized {
4+
debug a => _1;
5+
debug b => _2;
6+
let mut _0: impl Sized;
7+
let mut _3: isize;
8+
let _4: SingleVariantWithCustomDiscriminant;
9+
let mut _5: usize;
10+
let _6: SingleVariantWithCustomDiscriminant;
11+
let mut _7: i16;
12+
let _8: SingleVariantWithCustomDiscriminant;
13+
let mut _9: u16;
14+
let _10: SingleVariantWithCustomDiscriminant;
15+
let mut _11: isize;
16+
let _12: SingleVariantWithCustomDiscriminantAndRepr;
17+
let mut _13: u16;
18+
let mut _14: usize;
19+
let _15: SingleVariantWithCustomDiscriminantAndRepr;
20+
let mut _16: u16;
21+
let mut _17: i16;
22+
let _18: SingleVariantWithCustomDiscriminantAndRepr;
23+
let mut _19: u16;
24+
let mut _20: u16;
25+
let _21: SingleVariantWithCustomDiscriminantAndRepr;
26+
let mut _22: u16;
27+
28+
bb0: {
29+
StorageLive(_3);
30+
StorageLive(_4);
31+
_4 = copy _1;
32+
_3 = const 42_isize as isize (IntToInt);
33+
StorageDead(_4);
34+
StorageLive(_5);
35+
StorageLive(_6);
36+
_6 = copy _1;
37+
_5 = const 42_isize as usize (IntToInt);
38+
StorageDead(_6);
39+
StorageLive(_7);
40+
StorageLive(_8);
41+
_8 = copy _1;
42+
_7 = const 42_isize as i16 (IntToInt);
43+
StorageDead(_8);
44+
StorageLive(_9);
45+
StorageLive(_10);
46+
_10 = copy _1;
47+
_9 = const 42_isize as u16 (IntToInt);
48+
StorageDead(_10);
49+
StorageLive(_11);
50+
StorageLive(_12);
51+
_12 = copy _2;
52+
_13 = move _12 as u16 (Transmute);
53+
_11 = move _13 as isize (IntToInt);
54+
StorageDead(_12);
55+
StorageLive(_14);
56+
StorageLive(_15);
57+
_15 = copy _2;
58+
_16 = move _15 as u16 (Transmute);
59+
_14 = move _16 as usize (IntToInt);
60+
StorageDead(_15);
61+
StorageLive(_17);
62+
StorageLive(_18);
63+
_18 = copy _2;
64+
_19 = move _18 as u16 (Transmute);
65+
_17 = move _19 as i16 (IntToInt);
66+
StorageDead(_18);
67+
StorageLive(_20);
68+
StorageLive(_21);
69+
_21 = copy _2;
70+
_22 = move _21 as u16 (Transmute);
71+
_20 = move _22 as u16 (IntToInt);
72+
StorageDead(_21);
73+
_0 = (move _3, move _5, move _7, move _9, move _11, move _14, move _17, move _20);
74+
StorageDead(_20);
75+
StorageDead(_17);
76+
StorageDead(_14);
77+
StorageDead(_11);
78+
StorageDead(_9);
79+
StorageDead(_7);
80+
StorageDead(_5);
81+
StorageDead(_3);
82+
return;
83+
}
84+
}

0 commit comments

Comments
 (0)