1
1
//! Functions for reading and writing discriminants of multi-variant layouts (enums and coroutines).
2
2
3
3
use rustc_abi:: { self as abi, TagEncoding , VariantIdx , Variants } ;
4
- use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt } ;
4
+ use rustc_middle:: ty:: layout:: { LayoutOf , PrimitiveExt , TyAndLayout } ;
5
5
use rustc_middle:: ty:: { self , CoroutineArgsExt , ScalarInt , Ty } ;
6
6
use rustc_middle:: { mir, span_bug} ;
7
7
use tracing:: { instrument, trace} ;
@@ -21,17 +21,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
21
21
variant_index : VariantIdx ,
22
22
dest : & impl Writeable < ' tcx , M :: Provenance > ,
23
23
) -> InterpResult < ' tcx > {
24
- // Layout computation excludes uninhabited variants from consideration
25
- // therefore there's no way to represent those variants in the given layout.
26
- // Essentially, uninhabited variants do not have a tag that corresponds to their
27
- // discriminant, so we cannot do anything here.
28
- // When evaluating we will always error before even getting here, but ConstProp 'executes'
29
- // dead code, so we cannot ICE here.
30
- if dest. layout ( ) . for_variant ( self , variant_index) . is_uninhabited ( ) {
31
- throw_ub ! ( UninhabitedEnumVariantWritten ( variant_index) )
32
- }
33
-
34
- match self . tag_for_variant ( dest. layout ( ) . ty , variant_index) ? {
24
+ match self . tag_for_variant ( dest. layout ( ) , variant_index) ? {
35
25
Some ( ( tag, tag_field) ) => {
36
26
// No need to validate that the discriminant here because the
37
27
// `TyAndLayout::for_variant()` call earlier already checks the
@@ -80,7 +70,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
80
70
if ty. is_enum ( ) {
81
71
// Hilariously, `Single` is used even for 0-variant enums.
82
72
// (See https://github.com/rust-lang/rust/issues/89765).
83
- if matches ! ( ty. kind ( ) , ty :: Adt ( def , .. ) if def . variants( ) . is_empty( ) ) {
73
+ if ty. ty_adt_def ( ) . unwrap ( ) . variants ( ) . is_empty ( ) {
84
74
throw_ub ! ( UninhabitedEnumVariantRead ( index) )
85
75
}
86
76
// For consistency with `write_discriminant`, and to make sure that
@@ -188,6 +178,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
188
178
let variants =
189
179
ty. ty_adt_def ( ) . expect ( "tagged layout for non adt" ) . variants ( ) ;
190
180
assert ! ( variant_index < variants. next_index( ) ) ;
181
+ if variant_index == untagged_variant {
182
+ // The untagged variant can be in the niche range, but even then it
183
+ // is not a valid encoding.
184
+ throw_ub ! ( InvalidTag ( Scalar :: from_uint( tag_bits, tag_layout. size) ) )
185
+ }
191
186
variant_index
192
187
} else {
193
188
untagged_variant
@@ -236,10 +231,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
236
231
/// given field index.
237
232
pub ( crate ) fn tag_for_variant (
238
233
& self ,
239
- ty : Ty < ' tcx > ,
234
+ layout : TyAndLayout < ' tcx > ,
240
235
variant_index : VariantIdx ,
241
236
) -> InterpResult < ' tcx , Option < ( ScalarInt , usize ) > > {
242
- match self . layout_of ( ty) ?. variants {
237
+ // Layout computation excludes uninhabited variants from consideration.
238
+ // Therefore, there's no way to represent those variants in the given layout.
239
+ // Essentially, uninhabited variants do not have a tag that corresponds to their
240
+ // discriminant, so we have to bail out here.
241
+ if layout. for_variant ( self , variant_index) . is_uninhabited ( ) {
242
+ throw_ub ! ( UninhabitedEnumVariantWritten ( variant_index) )
243
+ }
244
+
245
+ match layout. variants {
243
246
abi:: Variants :: Single { .. } => {
244
247
// The tag of a `Single` enum is like the tag of the niched
245
248
// variant: there's no tag as the discriminant is encoded
@@ -260,7 +263,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
260
263
// raw discriminants for enums are isize or bigger during
261
264
// their computation, but the in-memory tag is the smallest possible
262
265
// representation
263
- let discr = self . discriminant_for_variant ( ty, variant_index) ?;
266
+ let discr = self . discriminant_for_variant ( layout . ty , variant_index) ?;
264
267
let discr_size = discr. layout . size ;
265
268
let discr_val = discr. to_scalar ( ) . to_bits ( discr_size) ?;
266
269
let tag_size = tag_layout. size ( self ) ;
@@ -286,11 +289,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
286
289
..
287
290
} => {
288
291
assert ! ( variant_index != untagged_variant) ;
292
+ // We checked that this variant is inhabited, so it must be in the niche range.
293
+ assert ! (
294
+ niche_variants. contains( & variant_index) ,
295
+ "invalid variant index for this enum"
296
+ ) ;
289
297
let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
290
- let variant_index_relative = variant_index
291
- . as_u32 ( )
292
- . checked_sub ( variants_start)
293
- . expect ( "overflow computing relative variant idx" ) ;
298
+ let variant_index_relative = variant_index. as_u32 ( ) . strict_sub ( variants_start) ;
294
299
// We need to use machine arithmetic when taking into account `niche_start`:
295
300
// tag_val = variant_index_relative + niche_start_val
296
301
let tag_layout = self . layout_of ( tag_layout. primitive ( ) . to_int_ty ( * self . tcx ) ) ?;
0 commit comments