@@ -729,42 +729,73 @@ pub trait LayoutCalculator {
729729 align = align. max ( AbiAndPrefAlign :: new ( repr_align) ) ;
730730 }
731731
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+
733742 let mut size = Size :: ZERO ;
734- let mut abi = Abi :: Aggregate { sized : true } ;
735743 let only_variant = & variants[ FIRST_VARIANT ] ;
736744 for field in only_variant {
737745 assert ! ( field. 0 . is_sized( ) ) ;
746+
738747 align = align. max ( field. align ( ) ) ;
748+ size = cmp:: max ( size, field. size ( ) ) ;
739749
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 {
742756 // 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 ( ) ;
751758
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 ) ) ) ;
758777 }
759778 }
760-
761- size = cmp:: max ( size, field. size ( ) ) ;
762779 }
763780
764781 if let Some ( pack) = repr. pack {
765782 align = align. min ( AbiAndPrefAlign :: new ( pack) ) ;
766783 }
767784
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+
768799 Some ( LayoutS {
769800 variants : Variants :: Single { index : FIRST_VARIANT } ,
770801 fields : FieldsShape :: Union ( NonZeroUsize :: new ( only_variant. len ( ) ) ?) ,
0 commit comments