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
9
9
use super :: { ImmTy , InterpCx , InterpResult , Machine , Readable , Scalar , Writeable } ;
10
10
11
+ /// The tag of an enum discriminant.
12
+ pub ( crate ) enum Tag {
13
+ /// No tag; the variant is `Single`-encoded.
14
+ None ,
15
+ /// The variant is tagged.
16
+ Tagged { tag : ScalarInt , tag_field : usize } ,
17
+ /// No tag; the variant is identified by its validity.
18
+ Untagged ,
19
+ }
20
+
21
+ impl Tag {
22
+ pub ( crate ) fn value ( self ) -> Option < ScalarInt > {
23
+ if let Self :: Tagged { tag, .. } = self { Some ( tag) } else { None }
24
+ }
25
+ }
26
+
11
27
impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
12
28
/// Writes the discriminant of the given variant.
13
29
///
@@ -28,78 +44,28 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
28
44
throw_ub ! ( UninhabitedEnumVariantWritten ( variant_index) )
29
45
}
30
46
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
- } => {
47
+ let ( tag, tag_field) = match self . tag_for_variant ( dest. layout ( ) . ty , variant_index) ? {
48
+ Tag :: None => return Ok ( ( ) ) ,
49
+ Tag :: Tagged { tag, tag_field } => {
41
50
// 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
-
57
- let tag_dest = self . project_field ( dest, tag_field) ?;
58
- self . write_scalar ( Scalar :: from_uint ( tag_val, size) , & tag_dest) ?;
51
+ // `TyAndLayout::for_variant()` call earlier already checks the
52
+ // variant is valid.
53
+ ( tag, tag_field)
59
54
}
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
- }
55
+ Tag :: Untagged => {
56
+ // The untagged variant is implicitly encoded simply by having a value that is
57
+ // outside the niche variants. But what if the data stored here does not
58
+ // actually encode this variant? That would be bad! So let's double-check...
59
+ let actual_variant = self . read_discriminant ( & dest. to_op ( self ) ?) ?;
60
+ if actual_variant != variant_index {
61
+ throw_ub ! ( InvalidNichedEnumVariantWritten { enum_ty: dest. layout( ) . ty } ) ;
98
62
}
63
+ return Ok ( ( ) ) ;
99
64
}
100
- }
65
+ } ;
101
66
102
- Ok ( ( ) )
67
+ let tag_dest = self . project_field ( dest, tag_field) ?;
68
+ self . write_scalar ( tag, & tag_dest)
103
69
}
104
70
105
71
/// Read discriminant, return the runtime value as well as the variant index.
@@ -277,4 +243,76 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
277
243
} ;
278
244
Ok ( ImmTy :: from_scalar ( discr_value, discr_layout) )
279
245
}
246
+
247
+ /// Computes the tag (if any) of a given variant of type `ty`.
248
+ pub ( crate ) fn tag_for_variant (
249
+ & self ,
250
+ ty : Ty < ' tcx > ,
251
+ variant_index : VariantIdx ,
252
+ ) -> InterpResult < ' tcx , Tag > {
253
+ match self . layout_of ( ty) ?. variants {
254
+ abi:: Variants :: Single { index } => {
255
+ assert_eq ! ( index, variant_index) ;
256
+ Ok ( Tag :: None )
257
+ }
258
+
259
+ abi:: Variants :: Multiple {
260
+ tag_encoding : TagEncoding :: Direct ,
261
+ tag : tag_layout,
262
+ tag_field,
263
+ ..
264
+ } => {
265
+ // raw discriminants for enums are isize or bigger during
266
+ // their computation, but the in-memory tag is the smallest possible
267
+ // representation
268
+ let discr = self . discriminant_for_variant ( ty, variant_index) ?;
269
+ let discr_size = discr. layout . size ;
270
+ let discr_val = discr. to_scalar ( ) . to_bits ( discr_size) ?;
271
+ let tag_size = tag_layout. size ( self ) ;
272
+ let tag_val = tag_size. truncate ( discr_val) ;
273
+ let tag = ScalarInt :: try_from_uint ( tag_val, tag_size) . unwrap ( ) ;
274
+ Ok ( Tag :: Tagged { tag, tag_field } )
275
+ }
276
+
277
+ abi:: Variants :: Multiple {
278
+ tag_encoding : TagEncoding :: Niche { untagged_variant, .. } ,
279
+ ..
280
+ } if untagged_variant == variant_index => {
281
+ // The untagged variant is implicitly encoded simply by having a
282
+ // value that is outside the niche variants.
283
+ Ok ( Tag :: Untagged )
284
+ }
285
+
286
+ abi:: Variants :: Multiple {
287
+ tag_encoding :
288
+ TagEncoding :: Niche { untagged_variant, ref niche_variants, niche_start } ,
289
+ tag : tag_layout,
290
+ tag_field,
291
+ ..
292
+ } => {
293
+ assert ! ( variant_index != untagged_variant) ;
294
+ let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
295
+ let variant_index_relative = variant_index
296
+ . as_u32 ( )
297
+ . checked_sub ( variants_start)
298
+ . expect ( "overflow computing relative variant idx" ) ;
299
+ // We need to use machine arithmetic when taking into account `niche_start`:
300
+ // tag_val = variant_index_relative + niche_start_val
301
+ let tag_layout = self . layout_of ( tag_layout. primitive ( ) . to_int_ty ( * self . tcx ) ) ?;
302
+ let niche_start_val = ImmTy :: from_uint ( niche_start, tag_layout) ;
303
+ let variant_index_relative_val =
304
+ ImmTy :: from_uint ( variant_index_relative, tag_layout) ;
305
+ let tag = self
306
+ . wrapping_binary_op (
307
+ mir:: BinOp :: Add ,
308
+ & variant_index_relative_val,
309
+ & niche_start_val,
310
+ ) ?
311
+ . to_scalar ( )
312
+ . try_to_int ( )
313
+ . unwrap ( ) ;
314
+ Ok ( Tag :: Tagged { tag, tag_field } )
315
+ }
316
+ }
317
+ }
280
318
}
0 commit comments