22
33use rustc_middle:: mir;
44use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt } ;
5- use rustc_middle:: ty:: { self , Ty } ;
5+ use rustc_middle:: ty:: { self , ScalarInt , Ty } ;
66use rustc_target:: abi:: { self , TagEncoding } ;
77use rustc_target:: abi:: { VariantIdx , Variants } ;
88
@@ -28,78 +28,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
2828 throw_ub ! ( UninhabitedEnumVariantWritten ( variant_index) )
2929 }
3030
31- match dest. layout ( ) . variants {
32- abi:: Variants :: Single { index } => {
33- assert_eq ! ( index, variant_index) ;
34- }
35- abi:: Variants :: Multiple {
36- tag_encoding : TagEncoding :: Direct ,
37- tag : tag_layout,
38- tag_field,
39- ..
40- } => {
31+ match self . tag_for_variant ( dest. layout ( ) . ty , variant_index) ? {
32+ Some ( ( tag, tag_field) ) => {
4133 // No need to validate that the discriminant here because the
42- // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
43-
44- let discr_val = dest
45- . layout ( )
46- . ty
47- . discriminant_for_variant ( * self . tcx , variant_index)
48- . unwrap ( )
49- . val ;
50-
51- // raw discriminants for enums are isize or bigger during
52- // their computation, but the in-memory tag is the smallest possible
53- // representation
54- let size = tag_layout. size ( self ) ;
55- let tag_val = size. truncate ( discr_val) ;
56-
34+ // `TyAndLayout::for_variant()` call earlier already checks the
35+ // variant is valid.
5736 let tag_dest = self . project_field ( dest, tag_field) ?;
58- self . write_scalar ( Scalar :: from_uint ( tag_val , size ) , & tag_dest) ? ;
37+ self . write_scalar ( tag , & tag_dest)
5938 }
60- abi:: Variants :: Multiple {
61- tag_encoding :
62- TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } ,
63- tag : tag_layout,
64- tag_field,
65- ..
66- } => {
67- // No need to validate that the discriminant here because the
68- // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
69-
70- if variant_index != untagged_variant {
71- let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
72- let variant_index_relative = variant_index
73- . as_u32 ( )
74- . checked_sub ( variants_start)
75- . expect ( "overflow computing relative variant idx" ) ;
76- // We need to use machine arithmetic when taking into account `niche_start`:
77- // tag_val = variant_index_relative + niche_start_val
78- let tag_layout = self . layout_of ( tag_layout. primitive ( ) . to_int_ty ( * self . tcx ) ) ?;
79- let niche_start_val = ImmTy :: from_uint ( niche_start, tag_layout) ;
80- let variant_index_relative_val =
81- ImmTy :: from_uint ( variant_index_relative, tag_layout) ;
82- let tag_val = self . wrapping_binary_op (
83- mir:: BinOp :: Add ,
84- & variant_index_relative_val,
85- & niche_start_val,
86- ) ?;
87- // Write result.
88- let niche_dest = self . project_field ( dest, tag_field) ?;
89- self . write_immediate ( * tag_val, & niche_dest) ?;
90- } else {
91- // The untagged variant is implicitly encoded simply by having a value that is
92- // outside the niche variants. But what if the data stored here does not
93- // actually encode this variant? That would be bad! So let's double-check...
94- let actual_variant = self . read_discriminant ( & dest. to_op ( self ) ?) ?;
95- if actual_variant != variant_index {
96- throw_ub ! ( InvalidNichedEnumVariantWritten { enum_ty: dest. layout( ) . ty } ) ;
97- }
39+ None => {
40+ // No need to write the tag here, because an untagged variant is
41+ // implicitly encoded. For `Niche`-optimized enums, it's by
42+ // simply by having a value that is outside the niche variants.
43+ // But what if the data stored here does not actually encode
44+ // this variant? That would be bad! So let's double-check...
45+ let actual_variant = self . read_discriminant ( & dest. to_op ( self ) ?) ?;
46+ if actual_variant != variant_index {
47+ throw_ub ! ( InvalidNichedEnumVariantWritten { enum_ty: dest. layout( ) . ty } ) ;
9848 }
49+ Ok ( ( ) )
9950 }
10051 }
101-
102- Ok ( ( ) )
10352 }
10453
10554 /// Read discriminant, return the runtime value as well as the variant index.
@@ -277,4 +226,77 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
277226 } ;
278227 Ok ( ImmTy :: from_scalar ( discr_value, discr_layout) )
279228 }
229+
230+ /// Computes the tag value and its field number (if any) of a given variant
231+ /// of type `ty`.
232+ pub ( crate ) fn tag_for_variant (
233+ & self ,
234+ ty : Ty < ' tcx > ,
235+ variant_index : VariantIdx ,
236+ ) -> InterpResult < ' tcx , Option < ( ScalarInt , usize ) > > {
237+ match self . layout_of ( ty) ?. variants {
238+ abi:: Variants :: Single { index } => {
239+ assert_eq ! ( index, variant_index) ;
240+ Ok ( None )
241+ }
242+
243+ abi:: Variants :: Multiple {
244+ tag_encoding : TagEncoding :: Direct ,
245+ tag : tag_layout,
246+ tag_field,
247+ ..
248+ } => {
249+ // raw discriminants for enums are isize or bigger during
250+ // their computation, but the in-memory tag is the smallest possible
251+ // representation
252+ let discr = self . discriminant_for_variant ( ty, variant_index) ?;
253+ let discr_size = discr. layout . size ;
254+ let discr_val = discr. to_scalar ( ) . to_bits ( discr_size) ?;
255+ let tag_size = tag_layout. size ( self ) ;
256+ let tag_val = tag_size. truncate ( discr_val) ;
257+ let tag = ScalarInt :: try_from_uint ( tag_val, tag_size) . unwrap ( ) ;
258+ Ok ( Some ( ( tag, tag_field) ) )
259+ }
260+
261+ abi:: Variants :: Multiple {
262+ tag_encoding : TagEncoding :: Niche { untagged_variant, .. } ,
263+ ..
264+ } if untagged_variant == variant_index => {
265+ // The untagged variant is implicitly encoded simply by having a
266+ // value that is outside the niche variants.
267+ Ok ( None )
268+ }
269+
270+ abi:: Variants :: Multiple {
271+ tag_encoding :
272+ TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } ,
273+ tag : tag_layout,
274+ tag_field,
275+ ..
276+ } => {
277+ assert ! ( variant_index != untagged_variant) ;
278+ let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
279+ let variant_index_relative = variant_index
280+ . as_u32 ( )
281+ . checked_sub ( variants_start)
282+ . expect ( "overflow computing relative variant idx" ) ;
283+ // We need to use machine arithmetic when taking into account `niche_start`:
284+ // tag_val = variant_index_relative + niche_start_val
285+ let tag_layout = self . layout_of ( tag_layout. primitive ( ) . to_int_ty ( * self . tcx ) ) ?;
286+ let niche_start_val = ImmTy :: from_uint ( niche_start, tag_layout) ;
287+ let variant_index_relative_val =
288+ ImmTy :: from_uint ( variant_index_relative, tag_layout) ;
289+ let tag = self
290+ . wrapping_binary_op (
291+ mir:: BinOp :: Add ,
292+ & variant_index_relative_val,
293+ & niche_start_val,
294+ ) ?
295+ . to_scalar ( )
296+ . try_to_int ( )
297+ . unwrap ( ) ;
298+ Ok ( Some ( ( tag, tag_field) ) )
299+ }
300+ }
301+ }
280302}
0 commit comments