1
1
//! See docs in `build/expr/mod.rs`.
2
2
3
- use rustc_abi:: { BackendRepr , FieldIdx , Primitive } ;
3
+ use std:: assert_matches:: assert_matches;
4
+
5
+ use rustc_abi:: { BackendRepr , FieldIdx , TagEncoding , Variants } ;
4
6
use rustc_hir:: lang_items:: LangItem ;
5
7
use rustc_index:: { Idx , IndexVec } ;
6
8
use rustc_middle:: bug;
@@ -9,8 +11,8 @@ use rustc_middle::mir::interpret::Scalar;
9
11
use rustc_middle:: mir:: * ;
10
12
use rustc_middle:: thir:: * ;
11
13
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 , IntTypeExt } ;
14
16
use rustc_middle:: ty:: { self , Ty , UpvarArgs } ;
15
17
use rustc_span:: source_map:: Spanned ;
16
18
use rustc_span:: { DUMMY_SP , Span } ;
@@ -197,97 +199,78 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
197
199
ExprKind :: Cast { source } => {
198
200
let source_expr = & this. thir [ source] ;
199
201
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.
203
202
let ( source, ty) = if let ty:: Adt ( adt_def, ..) = source_expr. ty . kind ( )
204
203
&& adt_def. is_enum ( )
205
204
{
206
- let discr_ty = adt_def. repr ( ) . discr_type ( ) . to_ty ( this. tcx ) ;
207
205
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 (
249
215
block,
250
- source_info,
251
- is_bin_op,
252
- Rvalue :: BinaryOp (
253
- bin_op,
254
- Box :: new ( ( Operand :: Copy ( unsigned_place) , lit_op) ) ,
255
- ) ,
256
- ) ;
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) ;
265
- this. cfg . push_assign (
266
- block,
267
- source_info,
268
- merge_place,
269
- Rvalue :: BinaryOp (
270
- merge_op,
271
- Box :: new ( (
272
- Operand :: Move ( start_place) ,
273
- Operand :: Move ( end_place) ,
216
+ Statement {
217
+ source_info,
218
+ kind : StatementKind :: Intrinsic ( Box :: new (
219
+ NonDivergingIntrinsic :: Assume ( false_lit) ,
274
220
) ) ,
275
- ) ,
221
+ } ,
276
222
) ;
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
- ) ;
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
+ if let BackendRepr :: Scalar ( repr) = layout. backend_repr {
245
+ assert_eq ! ( repr, tag) ;
246
+ let tag_ty = tag. primitive ( ) . to_ty ( this. tcx ) ;
247
+ let tag = this. temp ( tag_ty, expr_span) ;
248
+ this. cfg . push_assign (
249
+ block,
250
+ source_info,
251
+ tag,
252
+ Rvalue :: Cast (
253
+ CastKind :: Transmute ,
254
+ Operand :: Move ( Place :: from ( temp) ) ,
255
+ tag_ty,
256
+ ) ,
257
+ ) ;
258
+ ( Operand :: Move ( tag) , tag_ty)
259
+ } else {
260
+ // FIXME: One `Transmute` works union-style (so it can truncate
261
+ // the padding down to just the tag) we can remove this fallback.
262
+ let discr_ty = adt_def. repr ( ) . discr_type ( ) . to_ty ( this. tcx ) ;
263
+ let discr = this. temp ( discr_ty, source_expr. span ) ;
264
+ this. cfg . push_assign (
265
+ block,
266
+ source_info,
267
+ discr,
268
+ Rvalue :: Discriminant ( temp. into ( ) ) ,
269
+ ) ;
270
+ ( Operand :: Move ( discr) , discr_ty)
271
+ }
272
+ }
288
273
}
289
-
290
- ( op, ty)
291
274
} else {
292
275
let ty = source_expr. ty ;
293
276
let source = unpack ! (
0 commit comments