1
1
//! Functions for reading and writing discriminants of multi-variant layouts (enums and generators).
2
2
3
- use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt } ;
3
+ use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt , TyAndLayout } ;
4
4
use rustc_middle:: { mir, ty} ;
5
5
use rustc_target:: abi:: { self , TagEncoding } ;
6
6
use rustc_target:: abi:: { VariantIdx , Variants } ;
@@ -93,7 +93,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
93
93
pub fn read_discriminant (
94
94
& self ,
95
95
op : & OpTy < ' tcx , M :: Provenance > ,
96
- ) -> InterpResult < ' tcx , ( Scalar < M :: Provenance > , VariantIdx ) > {
96
+ ) -> InterpResult < ' tcx , VariantIdx > {
97
97
trace ! ( "read_discriminant_value {:#?}" , op. layout) ;
98
98
// Get type and layout of the discriminant.
99
99
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> {
106
106
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
107
107
let ( tag_scalar_layout, tag_encoding, tag_field) = match op. layout . variants {
108
108
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) )
119
116
}
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) )
124
122
}
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) )
131
123
}
132
- return Ok ( ( discr , index) ) ;
124
+ return Ok ( index) ;
133
125
}
134
126
Variants :: Multiple { tag, ref tag_encoding, tag_field, .. } => {
135
127
( tag, tag_encoding, tag_field)
@@ -155,7 +147,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
155
147
trace ! ( "tag value: {}" , tag_val) ;
156
148
157
149
// Figure out which discriminant and variant this corresponds to.
158
- let ( discr , index) = match * tag_encoding {
150
+ let index = match * tag_encoding {
159
151
TagEncoding :: Direct => {
160
152
let scalar = tag_val. to_scalar ( ) ;
161
153
// 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> {
183
175
}
184
176
. ok_or_else ( || err_ub ! ( InvalidTag ( Scalar :: from_uint( tag_bits, tag_layout. size) ) ) ) ?;
185
177
// Return the cast value, and the index.
186
- ( discr_val , index. 0 )
178
+ index. 0
187
179
}
188
180
TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } => {
189
181
let tag_val = tag_val. to_scalar ( ) ;
@@ -241,13 +233,33 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
241
233
// Compute the size of the scalar we need to return.
242
234
// No need to cast, because the variant index directly serves as discriminant and is
243
235
// encoded in the tag.
244
- ( Scalar :: from_uint ( variant. as_u32 ( ) , discr_layout . size ) , variant )
236
+ variant
245
237
}
246
238
} ;
247
239
// 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.
248
240
if op. layout . for_variant ( self , index) . abi . is_uninhabited ( ) {
249
241
throw_ub ! ( UninhabitedEnumVariantRead ( index) )
250
242
}
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
+ } )
252
264
}
253
265
}
0 commit comments