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 } ;
46use rustc_hir:: lang_items:: LangItem ;
57use rustc_index:: { Idx , IndexVec } ;
68use rustc_middle:: bug;
@@ -9,8 +11,8 @@ use rustc_middle::mir::interpret::Scalar;
911use rustc_middle:: mir:: * ;
1012use rustc_middle:: thir:: * ;
1113use 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 ;
1416use rustc_middle:: ty:: { self , Ty , UpvarArgs } ;
1517use rustc_span:: source_map:: Spanned ;
1618use 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 ! (
0 commit comments