@@ -15,8 +15,8 @@ use rustc_target::abi::{Abi, DiscriminantKind, HasDataLayout, Integer, LayoutOf,
15
15
use rustc_target:: abi:: { VariantIdx , Variants } ;
16
16
17
17
use super :: {
18
- from_known_layout, sign_extend , truncate , ConstValue , GlobalId , InterpCx , InterpResult ,
19
- MPlaceTy , Machine , MemPlace , Place , PlaceTy , Pointer , Scalar , ScalarMaybeUninit ,
18
+ from_known_layout, ConstValue , GlobalId , InterpCx , InterpResult , MPlaceTy , Machine , MemPlace ,
19
+ Place , PlaceTy , Pointer , Scalar , ScalarMaybeUninit ,
20
20
} ;
21
21
22
22
/// An `Immediate` represents a single immediate self-contained Rust value.
@@ -577,91 +577,112 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
577
577
pub fn read_discriminant (
578
578
& self ,
579
579
rval : OpTy < ' tcx , M :: PointerTag > ,
580
- ) -> InterpResult < ' tcx , ( u128 , VariantIdx ) > {
580
+ ) -> InterpResult < ' tcx , ( Scalar < M :: PointerTag > , VariantIdx ) > {
581
581
trace ! ( "read_discriminant_value {:#?}" , rval. layout) ;
582
582
583
- let ( discr_layout , discr_kind, discr_index) = match rval. layout . variants {
583
+ let ( discr_scalar_layout , discr_kind, discr_index) = match rval. layout . variants {
584
584
Variants :: Single { index } => {
585
- let discr_val = rval
586
- . layout
587
- . ty
588
- . discriminant_for_variant ( * self . tcx , index)
589
- . map_or ( u128:: from ( index. as_u32 ( ) ) , |discr| discr. val ) ;
590
- return Ok ( ( discr_val, index) ) ;
585
+ let discr = match rval. layout . ty . discriminant_for_variant ( * self . tcx , index) {
586
+ Some ( discr) => {
587
+ // This type actually has discriminants.
588
+ let discr_layout = self . layout_of ( discr. ty ) ?;
589
+ Scalar :: from_uint ( discr. val , discr_layout. size )
590
+ }
591
+ None => {
592
+ // On a type without actual discriminants, return variant idx as `u8`.
593
+ let discr_layout = self . layout_of ( self . tcx . types . u8 ) ?;
594
+ Scalar :: from_uint ( index. as_u32 ( ) , discr_layout. size )
595
+ }
596
+ } ;
597
+ return Ok ( ( discr, index) ) ;
591
598
}
592
- Variants :: Multiple { discr : ref discr_layout , ref discr_kind, discr_index, .. } => {
593
- ( discr_layout , discr_kind, discr_index)
599
+ Variants :: Multiple { ref discr , ref discr_kind, discr_index, .. } => {
600
+ ( discr , discr_kind, discr_index)
594
601
}
595
602
} ;
596
603
597
- // read raw discriminant value
598
- let discr_op = self . operand_field ( rval, discr_index) ?;
599
- let discr_val = self . read_immediate ( discr_op) ?;
600
- let raw_discr = discr_val. to_scalar_or_undef ( ) ;
601
- trace ! ( "discr value: {:?}" , raw_discr) ;
602
- // post-process
604
+ // There are *three* types/layouts that come into play here:
605
+ // - The field storing the discriminant has a layout, which my be a pointer.
606
+ // This is `discr_val.layout`; we just use it for sanity checks.
607
+ // - The discriminant has a layout for tag storing purposes, which is always an integer.
608
+ // This is `discr_layout` and is used to interpret the value we read from the
609
+ // discriminant field.
610
+ // - The discriminant also has a type for typechecking, and that type's
611
+ // layout can be *different*. This is `discr_ty`, and is used for the `Scalar`
612
+ // we return. If necessary, a cast from `discr_layout` is performed.
613
+
614
+ // Get layout for tag.
615
+ let discr_layout = self . layout_of ( discr_scalar_layout. value . to_int_ty ( * self . tcx ) ) ?;
616
+
617
+ // Read discriminant value and sanity-check `discr_layout`.
618
+ let discr_val = self . read_immediate ( self . operand_field ( rval, discr_index) ?) ?;
619
+ assert_eq ! ( discr_layout. size, discr_val. layout. size) ;
620
+ assert_eq ! ( discr_layout. abi. is_signed( ) , discr_val. layout. abi. is_signed( ) ) ;
621
+ let discr_val = discr_val. to_scalar ( ) ?;
622
+ trace ! ( "discriminant value: {:?}" , discr_val) ;
623
+
624
+ // Get type used by typechecking.
625
+ let discr_ty = match rval. layout . ty . kind {
626
+ ty:: Adt ( adt, _) => {
627
+ let discr_int_ty = Integer :: from_attr ( self , adt. repr . discr_type ( ) ) ;
628
+ // The signedness of tag and discriminant is the same.
629
+ discr_int_ty. to_ty ( * self . tcx , discr_layout. abi . is_signed ( ) )
630
+ }
631
+ ty:: Generator ( _, substs, _) => {
632
+ let substs = substs. as_generator ( ) ;
633
+ substs. discr_ty ( * self . tcx )
634
+ }
635
+ _ => bug ! ( "multiple variants for non-adt non-generator" ) ,
636
+ } ;
637
+
638
+ // Figure out which discriminant and variant this corresponds to.
603
639
Ok ( match * discr_kind {
604
640
DiscriminantKind :: Tag => {
605
- let bits_discr = raw_discr
606
- . not_undef ( )
607
- . and_then ( |raw_discr| self . force_bits ( raw_discr, discr_val. layout . size ) )
608
- . map_err ( |_| err_ub ! ( InvalidDiscriminant ( raw_discr. erase_tag( ) ) ) ) ?;
609
- let real_discr = if discr_val. layout . abi . is_signed ( ) {
610
- // going from layout tag type to typeck discriminant type
611
- // requires first sign extending with the discriminant layout
612
- let sexted = sign_extend ( bits_discr, discr_val. layout . size ) ;
613
- // and then zeroing with the typeck discriminant type
614
- let discr_ty = rval
615
- . layout
616
- . ty
617
- . ty_adt_def ( )
618
- . expect ( "tagged layout corresponds to adt" )
619
- . repr
620
- . discr_type ( ) ;
621
- let size = Integer :: from_attr ( self , discr_ty) . size ( ) ;
622
- truncate ( sexted, size)
623
- } else {
624
- bits_discr
625
- } ;
626
- // Make sure we catch invalid discriminants
641
+ let discr_bits = self
642
+ . force_bits ( discr_val, discr_layout. size )
643
+ . map_err ( |_| err_ub ! ( InvalidDiscriminant ( discr_val. erase_tag( ) ) ) ) ?;
644
+ // Cast discriminant bits to the right type.
645
+ let discr_ty_layout = self . layout_of ( discr_ty) ?;
646
+ let discr_val_cast =
647
+ self . cast_from_scalar ( discr_bits, discr_layout, discr_ty) ;
648
+ let discr_bits = discr_val_cast. assert_bits ( discr_ty_layout. size ) ;
649
+ // Find variant index for this tag, and catch invalid discriminants.
627
650
let index = match rval. layout . ty . kind {
628
651
ty:: Adt ( adt, _) => {
629
- adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == real_discr )
652
+ adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == discr_bits )
630
653
}
631
654
ty:: Generator ( def_id, substs, _) => {
632
655
let substs = substs. as_generator ( ) ;
633
656
substs
634
657
. discriminants ( def_id, self . tcx . tcx )
635
- . find ( |( _, var) | var. val == real_discr )
658
+ . find ( |( _, var) | var. val == discr_bits )
636
659
}
637
660
_ => bug ! ( "tagged layout for non-adt non-generator" ) ,
638
661
}
639
- . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( raw_discr. erase_tag( ) ) ) ) ?;
640
- ( real_discr, index. 0 )
662
+ . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( discr_val. erase_tag( ) ) ) ) ?;
663
+ // Return the cast value, and the index.
664
+ ( discr_val_cast, index. 0 )
641
665
}
642
666
DiscriminantKind :: Niche { dataful_variant, ref niche_variants, niche_start } => {
667
+ // Compute the variant this discriminant corresponds to. With niche layout,
668
+ // tag and variant index are the same.
643
669
let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
644
670
let variants_end = niche_variants. end ( ) . as_u32 ( ) ;
645
- let raw_discr = raw_discr
646
- . not_undef ( )
647
- . map_err ( |_| err_ub ! ( InvalidDiscriminant ( ScalarMaybeUninit :: Uninit ) ) ) ?;
648
- match raw_discr. to_bits_or_ptr ( discr_val. layout . size , self ) {
671
+ let variant = match discr_val. to_bits_or_ptr ( discr_layout. size , self ) {
649
672
Err ( ptr) => {
650
673
// The niche must be just 0 (which an inbounds pointer value never is)
651
674
let ptr_valid = niche_start == 0
652
675
&& variants_start == variants_end
653
676
&& !self . memory . ptr_may_be_null ( ptr) ;
654
677
if !ptr_valid {
655
- throw_ub ! ( InvalidDiscriminant ( raw_discr . erase_tag( ) . into ( ) ) )
678
+ throw_ub ! ( InvalidDiscriminant ( discr_val . erase_tag( ) ) )
656
679
}
657
- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
680
+ dataful_variant
658
681
}
659
- Ok ( raw_discr ) => {
682
+ Ok ( bits_discr ) => {
660
683
// We need to use machine arithmetic to get the relative variant idx:
661
684
// variant_index_relative = discr_val - niche_start_val
662
- let discr_layout =
663
- self . layout_of ( discr_layout. value . to_int_ty ( * self . tcx ) ) ?;
664
- let discr_val = ImmTy :: from_uint ( raw_discr, discr_layout) ;
685
+ let discr_val = ImmTy :: from_uint ( bits_discr, discr_layout) ;
665
686
let niche_start_val = ImmTy :: from_uint ( niche_start, discr_layout) ;
666
687
let variant_index_relative_val =
667
688
self . binary_op ( mir:: BinOp :: Sub , discr_val, niche_start_val) ?;
@@ -684,12 +705,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
684
705
. variants
685
706
. len ( ) ;
686
707
assert ! ( usize :: try_from( variant_index) . unwrap( ) < variants_len) ;
687
- ( u128 :: from ( variant_index ) , VariantIdx :: from_u32 ( variant_index) )
708
+ VariantIdx :: from_u32 ( variant_index)
688
709
} else {
689
- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
710
+ dataful_variant
690
711
}
691
712
}
692
- }
713
+ } ;
714
+ // Compute the size of the scalar we need to return.
715
+ // FIXME: Why do we not need to do a cast here like we do above?
716
+ let size = self . layout_of ( discr_ty) ?. size ;
717
+ ( Scalar :: from_uint ( variant. as_u32 ( ) , size) , variant)
693
718
}
694
719
} )
695
720
}
0 commit comments