@@ -729,42 +729,73 @@ pub trait LayoutCalculator {
729
729
align = align. max ( AbiAndPrefAlign :: new ( repr_align) ) ;
730
730
}
731
731
732
- let optimize = !repr. inhibit_union_abi_opt ( ) ;
732
+ // If all the non-ZST fields have the same ABI and union ABI optimizations aren't
733
+ // disabled, we can use that common ABI for the union as a whole.
734
+ struct AbiMismatch ;
735
+ let mut common_non_zst_abi_and_align = if repr. inhibit_union_abi_opt ( ) {
736
+ // Can't optimize
737
+ Err ( AbiMismatch )
738
+ } else {
739
+ Ok ( None )
740
+ } ;
741
+
733
742
let mut size = Size :: ZERO ;
734
- let mut abi = Abi :: Aggregate { sized : true } ;
735
743
let only_variant = & variants[ FIRST_VARIANT ] ;
736
744
for field in only_variant {
737
745
assert ! ( field. 0 . is_sized( ) ) ;
746
+
738
747
align = align. max ( field. align ( ) ) ;
748
+ size = cmp:: max ( size, field. size ( ) ) ;
739
749
740
- // If all non-ZST fields have the same ABI, forward this ABI
741
- if optimize && !field. 0 . is_zst ( ) {
750
+ if field. 0 . is_zst ( ) {
751
+ // Nothing more to do for ZST fields
752
+ continue ;
753
+ }
754
+
755
+ if let Ok ( common) = common_non_zst_abi_and_align {
742
756
// Discard valid range information and allow undef
743
- let field_abi = match field. abi ( ) {
744
- Abi :: Scalar ( x) => Abi :: Scalar ( x. to_union ( ) ) ,
745
- Abi :: ScalarPair ( x, y) => Abi :: ScalarPair ( x. to_union ( ) , y. to_union ( ) ) ,
746
- Abi :: Vector { element : x, count } => {
747
- Abi :: Vector { element : x. to_union ( ) , count }
748
- }
749
- Abi :: Uninhabited | Abi :: Aggregate { .. } => Abi :: Aggregate { sized : true } ,
750
- } ;
757
+ let field_abi = field. abi ( ) . to_union ( ) ;
751
758
752
- if size == Size :: ZERO {
753
- // first non ZST: initialize 'abi'
754
- abi = field_abi;
755
- } else if abi != field_abi {
756
- // different fields have different ABI: reset to Aggregate
757
- abi = Abi :: Aggregate { sized : true } ;
759
+ if let Some ( ( common_abi, common_align) ) = common {
760
+ if common_abi != field_abi {
761
+ // Different fields have different ABI: disable opt
762
+ common_non_zst_abi_and_align = Err ( AbiMismatch ) ;
763
+ } else {
764
+ // Fields with the same non-Aggregate ABI should also
765
+ // have the same alignment
766
+ if !matches ! ( common_abi, Abi :: Aggregate { .. } ) {
767
+ assert_eq ! (
768
+ common_align,
769
+ field. align( ) . abi,
770
+ "non-Aggregate field with matching ABI but differing alignment"
771
+ ) ;
772
+ }
773
+ }
774
+ } else {
775
+ // First non-ZST field: record its ABI and alignment
776
+ common_non_zst_abi_and_align = Ok ( Some ( ( field_abi, field. align ( ) . abi ) ) ) ;
758
777
}
759
778
}
760
-
761
- size = cmp:: max ( size, field. size ( ) ) ;
762
779
}
763
780
764
781
if let Some ( pack) = repr. pack {
765
782
align = align. min ( AbiAndPrefAlign :: new ( pack) ) ;
766
783
}
767
784
785
+ // If all non-ZST fields have the same ABI, we may forward that ABI
786
+ // for the union as a whole, unless otherwise inhibited.
787
+ let abi = match common_non_zst_abi_and_align {
788
+ Err ( AbiMismatch ) | Ok ( None ) => Abi :: Aggregate { sized : true } ,
789
+ Ok ( Some ( ( abi, _) ) ) => {
790
+ if abi. inherent_align ( dl) . map ( |a| a. abi ) != Some ( align. abi ) {
791
+ // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt
792
+ Abi :: Aggregate { sized : true }
793
+ } else {
794
+ abi
795
+ }
796
+ }
797
+ } ;
798
+
768
799
Some ( LayoutS {
769
800
variants : Variants :: Single { index : FIRST_VARIANT } ,
770
801
fields : FieldsShape :: Union ( NonZeroUsize :: new ( only_variant. len ( ) ) ?) ,
0 commit comments