11//! Functions for reading and writing discriminants of multi-variant layouts (enums and generators).
22
3- use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt } ;
3+ use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt , TyAndLayout } ;
44use rustc_middle:: { mir, ty} ;
55use rustc_target:: abi:: { self , TagEncoding } ;
66use rustc_target:: abi:: { VariantIdx , Variants } ;
@@ -93,7 +93,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
9393 pub fn read_discriminant (
9494 & self ,
9595 op : & OpTy < ' tcx , M :: Provenance > ,
96- ) -> InterpResult < ' tcx , ( Scalar < M :: Provenance > , VariantIdx ) > {
96+ ) -> InterpResult < ' tcx , VariantIdx > {
9797 trace ! ( "read_discriminant_value {:#?}" , op. layout) ;
9898 // Get type and layout of the discriminant.
9999 let discr_layout = self . layout_of ( op. layout . ty . discriminant_ty ( * self . tcx ) ) ?;
@@ -106,30 +106,22 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
106106 // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
107107 let ( tag_scalar_layout, tag_encoding, tag_field) = match op. layout . variants {
108108 Variants :: Single { index } => {
109- // Hilariously, `Single` is used even for 0-variant enums.
110- // (See https://github.com/rust-lang/rust/issues/89765).
111- if matches ! ( op. layout. ty. kind( ) , ty:: Adt ( def, ..) if def. variants( ) . is_empty( ) ) {
112- throw_ub ! ( UninhabitedEnumVariantRead ( index) )
113- }
114- let discr = match op. layout . ty . discriminant_for_variant ( * self . tcx , index) {
115- Some ( discr) => {
116- // This type actually has discriminants.
117- assert_eq ! ( discr. ty, discr_layout. ty) ;
118- Scalar :: from_uint ( discr. val , discr_layout. size )
109+ // Do some extra checks on enums.
110+ if op. layout . ty . is_enum ( ) {
111+ // Hilariously, `Single` is used even for 0-variant enums.
112+ // (See https://github.com/rust-lang/rust/issues/89765).
113+ if matches ! ( op. layout. ty. kind( ) , ty:: Adt ( def, ..) if def. variants( ) . is_empty( ) )
114+ {
115+ throw_ub ! ( UninhabitedEnumVariantRead ( index) )
119116 }
120- None => {
121- // On a type without actual discriminants, variant is 0.
122- assert_eq ! ( index. as_u32( ) , 0 ) ;
123- Scalar :: from_uint ( index. as_u32 ( ) , discr_layout. size )
117+ // For consisteny with `write_discriminant`, and to make sure that
118+ // `project_downcast` cannot fail due to strange layouts, we declare immediate UB
119+ // for uninhabited variants.
120+ if op. layout . for_variant ( self , index) . abi . is_uninhabited ( ) {
121+ throw_ub ! ( UninhabitedEnumVariantRead ( index) )
124122 }
125- } ;
126- // For consisteny with `write_discriminant`, and to make sure that
127- // `project_downcast` cannot fail due to strange layouts, we declare immediate UB
128- // for uninhabited variants.
129- if op. layout . ty . is_enum ( ) && op. layout . for_variant ( self , index) . abi . is_uninhabited ( ) {
130- throw_ub ! ( UninhabitedEnumVariantRead ( index) )
131123 }
132- return Ok ( ( discr , index) ) ;
124+ return Ok ( index) ;
133125 }
134126 Variants :: Multiple { tag, ref tag_encoding, tag_field, .. } => {
135127 ( tag, tag_encoding, tag_field)
@@ -155,7 +147,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
155147 trace ! ( "tag value: {}" , tag_val) ;
156148
157149 // Figure out which discriminant and variant this corresponds to.
158- let ( discr , index) = match * tag_encoding {
150+ let index = match * tag_encoding {
159151 TagEncoding :: Direct => {
160152 let scalar = tag_val. to_scalar ( ) ;
161153 // Generate a specific error if `tag_val` is not an integer.
@@ -183,7 +175,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
183175 }
184176 . ok_or_else ( || err_ub ! ( InvalidTag ( Scalar :: from_uint( tag_bits, tag_layout. size) ) ) ) ?;
185177 // Return the cast value, and the index.
186- ( discr_val , index. 0 )
178+ index. 0
187179 }
188180 TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } => {
189181 let tag_val = tag_val. to_scalar ( ) ;
@@ -241,13 +233,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
241233 // Compute the size of the scalar we need to return.
242234 // No need to cast, because the variant index directly serves as discriminant and is
243235 // encoded in the tag.
244- ( Scalar :: from_uint ( variant. as_u32 ( ) , discr_layout . size ) , variant )
236+ variant
245237 }
246238 } ;
247239 // For consisteny with `write_discriminant`, and to make sure that `project_downcast` cannot fail due to strange layouts, we declare immediate UB for uninhabited variants.
248240 if op. layout . for_variant ( self , index) . abi . is_uninhabited ( ) {
249241 throw_ub ! ( UninhabitedEnumVariantRead ( index) )
250242 }
251- Ok ( ( discr, index) )
243+ Ok ( index)
244+ }
245+
246+ pub fn discriminant_for_variant (
247+ & self ,
248+ layout : TyAndLayout < ' tcx > ,
249+ variant : VariantIdx ,
250+ ) -> InterpResult < ' tcx , Scalar < M :: Provenance > > {
251+ let discr_layout = self . layout_of ( layout. ty . discriminant_ty ( * self . tcx ) ) ?;
252+ Ok ( match layout. ty . discriminant_for_variant ( * self . tcx , variant) {
253+ Some ( discr) => {
254+ // This type actually has discriminants.
255+ assert_eq ! ( discr. ty, discr_layout. ty) ;
256+ Scalar :: from_uint ( discr. val , discr_layout. size )
257+ }
258+ None => {
259+ // On a type without actual discriminants, variant is 0.
260+ assert_eq ! ( variant. as_u32( ) , 0 ) ;
261+ Scalar :: from_uint ( variant. as_u32 ( ) , discr_layout. size )
262+ }
263+ } )
252264 }
253265}
0 commit comments