@@ -7,15 +7,15 @@ use std::fmt::Write;
7
7
use rustc_errors:: ErrorReported ;
8
8
use rustc_hir:: def:: Namespace ;
9
9
use rustc_macros:: HashStable ;
10
- use rustc_middle:: ty:: layout:: { IntegerExt , PrimitiveExt , TyAndLayout } ;
10
+ use rustc_middle:: ty:: layout:: { PrimitiveExt , TyAndLayout } ;
11
11
use rustc_middle:: ty:: print:: { FmtPrinter , PrettyPrinter , Printer } ;
12
12
use rustc_middle:: ty:: Ty ;
13
13
use rustc_middle:: { mir, ty} ;
14
- use rustc_target:: abi:: { Abi , DiscriminantKind , HasDataLayout , Integer , LayoutOf , Size } ;
14
+ use rustc_target:: abi:: { Abi , DiscriminantKind , HasDataLayout , LayoutOf , Size } ;
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 ,
18
+ from_known_layout, mir_assign_valid_types , ConstValue , GlobalId , InterpCx , InterpResult ,
19
19
MPlaceTy , Machine , MemPlace , Place , PlaceTy , Pointer , Scalar , ScalarMaybeUninit ,
20
20
} ;
21
21
@@ -469,6 +469,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
469
469
. try_fold ( base_op, |op, elem| self . operand_projection ( op, elem) ) ?;
470
470
471
471
trace ! ( "eval_place_to_op: got {:?}" , * op) ;
472
+ // Sanity-check the type we ended up with.
473
+ debug_assert ! ( mir_assign_valid_types(
474
+ * self . tcx,
475
+ self . layout_of( self . subst_from_current_frame_and_normalize_erasing_regions(
476
+ place. ty( & self . frame( ) . body. local_decls, * self . tcx) . ty
477
+ ) ) ?,
478
+ op. layout,
479
+ ) ) ;
472
480
Ok ( op)
473
481
}
474
482
@@ -576,98 +584,113 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
576
584
/// Read discriminant, return the runtime value as well as the variant index.
577
585
pub fn read_discriminant (
578
586
& self ,
579
- rval : OpTy < ' tcx , M :: PointerTag > ,
580
- ) -> InterpResult < ' tcx , ( u128 , VariantIdx ) > {
581
- trace ! ( "read_discriminant_value {:#?}" , rval. layout) ;
582
-
583
- let ( discr_layout, discr_kind, discr_index) = match rval. layout . variants {
587
+ op : OpTy < ' tcx , M :: PointerTag > ,
588
+ ) -> InterpResult < ' tcx , ( Scalar < M :: PointerTag > , VariantIdx ) > {
589
+ trace ! ( "read_discriminant_value {:#?}" , op. layout) ;
590
+
591
+ // Get type and layout of the discriminant.
592
+ let discr_layout = self . layout_of ( op. layout . ty . discriminant_ty ( * self . tcx ) ) ?;
593
+ trace ! ( "discriminant type: {:?}" , discr_layout. ty) ;
594
+
595
+ // We use "discriminant" to refer to the value associated with a particular enum variant.
596
+ // This is not to be confused with its "variant index", which is just determining its position in the
597
+ // declared list of variants -- they can differ with explicitly assigned discriminants.
598
+ // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
599
+ // straight-forward (`DiscriminantKind::Tag`) or with a niche (`DiscriminantKind::Niche`).
600
+ // Unfortunately, the rest of the compiler calls the latter "discriminant", too, which makes things
601
+ // rather confusing.
602
+ let ( tag_scalar_layout, tag_kind, tag_index) = match op. layout . variants {
584
603
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) ) ;
604
+ let discr = match op. layout . ty . discriminant_for_variant ( * self . tcx , index) {
605
+ Some ( discr) => {
606
+ // This type actually has discriminants.
607
+ assert_eq ! ( discr. ty, discr_layout. ty) ;
608
+ Scalar :: from_uint ( discr. val , discr_layout. size )
609
+ }
610
+ None => {
611
+ // On a type without actual discriminants, variant is 0.
612
+ assert_eq ! ( index. as_u32( ) , 0 ) ;
613
+ Scalar :: from_uint ( index. as_u32 ( ) , discr_layout. size )
614
+ }
615
+ } ;
616
+ return Ok ( ( discr, index) ) ;
591
617
}
592
- Variants :: Multiple { discr : ref discr_layout , ref discr_kind, discr_index, .. } => {
593
- ( discr_layout , discr_kind, discr_index)
618
+ Variants :: Multiple { ref discr , ref discr_kind, discr_index, .. } => {
619
+ ( discr , discr_kind, discr_index)
594
620
}
595
621
} ;
596
622
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
603
- Ok ( match * discr_kind {
623
+ // There are *three* layouts that come into play here:
624
+ // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
625
+ // the `Scalar` we return.
626
+ // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
627
+ // and used to interpret the value we read from the tag field.
628
+ // For the return value, a cast to `discr_layout` is performed.
629
+ // - The field storing the tag has a layout, which is very similar to `tag_layout` but
630
+ // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
631
+
632
+ // Get layout for tag.
633
+ let tag_layout = self . layout_of ( tag_scalar_layout. value . to_int_ty ( * self . tcx ) ) ?;
634
+
635
+ // Read tag and sanity-check `tag_layout`.
636
+ let tag_val = self . read_immediate ( self . operand_field ( op, tag_index) ?) ?;
637
+ assert_eq ! ( tag_layout. size, tag_val. layout. size) ;
638
+ assert_eq ! ( tag_layout. abi. is_signed( ) , tag_val. layout. abi. is_signed( ) ) ;
639
+ let tag_val = tag_val. to_scalar ( ) ?;
640
+ trace ! ( "tag value: {:?}" , tag_val) ;
641
+
642
+ // Figure out which discriminant and variant this corresponds to.
643
+ Ok ( match * tag_kind {
604
644
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
627
- let index = match rval. layout . ty . kind {
645
+ let tag_bits = self
646
+ . force_bits ( tag_val, tag_layout. size )
647
+ . map_err ( |_| err_ub ! ( InvalidDiscriminant ( tag_val. erase_tag( ) ) ) ) ?;
648
+ // Cast bits from tag layout to discriminant layout.
649
+ let discr_val_cast = self . cast_from_scalar ( tag_bits, tag_layout, discr_layout. ty ) ;
650
+ let discr_bits = discr_val_cast. assert_bits ( discr_layout. size ) ;
651
+ // Convert discriminant to variant index, and catch invalid discriminants.
652
+ let index = match op. layout . ty . kind {
628
653
ty:: Adt ( adt, _) => {
629
- adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == real_discr )
654
+ adt. discriminants ( self . tcx . tcx ) . find ( |( _, var) | var. val == discr_bits )
630
655
}
631
656
ty:: Generator ( def_id, substs, _) => {
632
657
let substs = substs. as_generator ( ) ;
633
658
substs
634
659
. discriminants ( def_id, self . tcx . tcx )
635
- . find ( |( _, var) | var. val == real_discr )
660
+ . find ( |( _, var) | var. val == discr_bits )
636
661
}
637
662
_ => bug ! ( "tagged layout for non-adt non-generator" ) ,
638
663
}
639
- . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( raw_discr. erase_tag( ) ) ) ) ?;
640
- ( real_discr, index. 0 )
664
+ . ok_or_else ( || err_ub ! ( InvalidDiscriminant ( tag_val. erase_tag( ) ) ) ) ?;
665
+ // Return the cast value, and the index.
666
+ ( discr_val_cast, index. 0 )
641
667
}
642
668
DiscriminantKind :: Niche { dataful_variant, ref niche_variants, niche_start } => {
669
+ // Compute the variant this niche value/"tag" corresponds to. With niche layout,
670
+ // discriminant (encoded in niche/tag) and variant index are the same.
643
671
let variants_start = niche_variants. start ( ) . as_u32 ( ) ;
644
672
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 ) {
673
+ let variant = match tag_val. to_bits_or_ptr ( tag_layout. size , self ) {
649
674
Err ( ptr) => {
650
675
// The niche must be just 0 (which an inbounds pointer value never is)
651
676
let ptr_valid = niche_start == 0
652
677
&& variants_start == variants_end
653
678
&& !self . memory . ptr_may_be_null ( ptr) ;
654
679
if !ptr_valid {
655
- throw_ub ! ( InvalidDiscriminant ( raw_discr . erase_tag( ) . into ( ) ) )
680
+ throw_ub ! ( InvalidDiscriminant ( tag_val . erase_tag( ) ) )
656
681
}
657
- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
682
+ dataful_variant
658
683
}
659
- Ok ( raw_discr ) => {
684
+ Ok ( tag_bits ) => {
660
685
// We need to use machine arithmetic to get the relative variant idx:
661
- // 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) ;
665
- let niche_start_val = ImmTy :: from_uint ( niche_start, discr_layout) ;
686
+ // variant_index_relative = tag_val - niche_start_val
687
+ let tag_val = ImmTy :: from_uint ( tag_bits, tag_layout) ;
688
+ let niche_start_val = ImmTy :: from_uint ( niche_start, tag_layout) ;
666
689
let variant_index_relative_val =
667
- self . binary_op ( mir:: BinOp :: Sub , discr_val , niche_start_val) ?;
690
+ self . binary_op ( mir:: BinOp :: Sub , tag_val , niche_start_val) ?;
668
691
let variant_index_relative = variant_index_relative_val
669
692
. to_scalar ( ) ?
670
- . assert_bits ( discr_val . layout . size ) ;
693
+ . assert_bits ( tag_val . layout . size ) ;
671
694
// Check if this is in the range that indicates an actual discriminant.
672
695
if variant_index_relative <= u128:: from ( variants_end - variants_start) {
673
696
let variant_index_relative = u32:: try_from ( variant_index_relative)
@@ -676,20 +699,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
676
699
let variant_index = variants_start
677
700
. checked_add ( variant_index_relative)
678
701
. expect ( "overflow computing absolute variant idx" ) ;
679
- let variants_len = rval
702
+ let variants_len = op
680
703
. layout
681
704
. ty
682
705
. ty_adt_def ( )
683
706
. expect ( "tagged layout for non adt" )
684
707
. variants
685
708
. len ( ) ;
686
709
assert ! ( usize :: try_from( variant_index) . unwrap( ) < variants_len) ;
687
- ( u128 :: from ( variant_index ) , VariantIdx :: from_u32 ( variant_index) )
710
+ VariantIdx :: from_u32 ( variant_index)
688
711
} else {
689
- ( u128 :: from ( dataful_variant. as_u32 ( ) ) , dataful_variant )
712
+ dataful_variant
690
713
}
691
714
}
692
- }
715
+ } ;
716
+ // Compute the size of the scalar we need to return.
717
+ // No need to cast, because the variant index directly serves as discriminant and is
718
+ // encoded in the tag.
719
+ ( Scalar :: from_uint ( variant. as_u32 ( ) , discr_layout. size ) , variant)
693
720
}
694
721
} )
695
722
}
0 commit comments