2
2
3
3
use rustc_middle:: mir;
4
4
use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt } ;
5
- use rustc_middle:: ty:: { self , Ty } ;
5
+ use rustc_middle:: ty:: { self , ScalarInt , Ty } ;
6
6
use rustc_target:: abi:: { self , TagEncoding } ;
7
7
use rustc_target:: abi:: { VariantIdx , Variants } ;
8
8
@@ -28,78 +28,27 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
28
28
throw_ub ! ( UninhabitedEnumVariantWritten ( variant_index) )
29
29
}
30
30
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) ) => {
41
33
// 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.
57
36
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)
59
38
}
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 } ) ;
98
48
}
49
+ Ok ( ( ) )
99
50
}
100
51
}
101
-
102
- Ok ( ( ) )
103
52
}
104
53
105
54
/// 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> {
277
226
} ;
278
227
Ok ( ImmTy :: from_scalar ( discr_value, discr_layout) )
279
228
}
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
+ }
280
302
}
0 commit comments