@@ -783,6 +783,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
783783 } ) ;
784784 trace ! ( ?largest_niche) ;
785785
786+ let single_variant_layout_eligible =
787+ !repr. inhibit_enum_layout_opt ( ) && valid_discriminants. len ( ) == 1 ;
788+
786789 // `max` is the last valid discriminant before the largest niche
787790 // `min` is the first valid discriminant after the largest niche
788791 let ( max, min) = largest_niche
@@ -815,10 +818,15 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
815818 }
816819
817820 // Create the set of structs that represent each variant.
821+ let mut single_inhabited_variant_no_tag_layout = None ;
818822 let mut layout_variants = variants
819823 . iter_enumerated ( )
820824 . map ( |( i, field_layouts) | {
821825 let uninhabited = field_layouts. iter ( ) . any ( |f| f. is_uninhabited ( ) ) ;
826+ if !uninhabited && single_variant_layout_eligible {
827+ single_inhabited_variant_no_tag_layout =
828+ Some ( ( i, self . univariant ( field_layouts, repr, StructKind :: AlwaysSized ) ) ) ;
829+ }
822830 // We don't need to encode the tag in uninhabited variants in repr(Rust) enums
823831 let struct_kind = if uninhabited && !repr. inhibit_enum_layout_opt ( ) {
824832 StructKind :: AlwaysSized
@@ -845,6 +853,62 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
845853 } )
846854 . collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
847855
856+ // If there is a single uninhabited variant, we can use it mostly unchanged as the layout,
857+ // without using a tag or niche.
858+ //
859+ // We do still need to modify it to make all the uninhabited variants fit so they
860+ // can be partially-initialized.
861+ //
862+ // We keep this as a prospective layout, and don't assume it's better than the tagged
863+ // layout and return it immediately; e.g. it's worse for `enum Foo { A, B(i32, !) }`
864+ // because it has no niche.
865+ let no_tag_layout = if let Some ( ( single_inhabited_variant_idx, Ok ( mut st) ) ) =
866+ single_inhabited_variant_no_tag_layout
867+ {
868+ // Keep track of original variant layouts (including the inhabited one)
869+ // for `offset_of!`.
870+ let mut variants = layout_variants. clone ( ) ;
871+ variants[ single_inhabited_variant_idx] = st. clone ( ) ;
872+
873+ // We know that every other variant is uninhabited, and thus does not have a
874+ // prefix for the tag, so we can use them to find the necessary size.
875+ for ( idx, layout) in layout_variants. iter_enumerated ( ) {
876+ if idx != single_inhabited_variant_idx {
877+ st. size = cmp:: max ( st. size , layout. size ) ;
878+ st. align = st. align . max ( layout. align ) ;
879+ st. max_repr_align = st. max_repr_align . max ( layout. max_repr_align ) ;
880+ st. unadjusted_abi_align =
881+ st. unadjusted_abi_align . max ( layout. unadjusted_abi_align ) ;
882+ }
883+ }
884+
885+ // Align the maximum variant size to the largest alignment.
886+ st. size = st. size . align_to ( st. align . abi ) ;
887+
888+ // If the inhabited variant's layout would use a non-Memory BackendRepr,
889+ // but we made the enum layout bigger or more-aligned due to uninhabited variants,
890+ // force the enum to be BackendRepr::Memory.
891+ //
892+ // FIXME: does this need to care about `max_repr_align` and `unadjusted_abi_align`?
893+ // The untagged layout is only used for `repr(Rust)` enums,
894+ // so `max_repr_align` and `unadjusted_abi_align` might be irrelevant.
895+ if st. size != variants[ single_inhabited_variant_idx] . size
896+ || st. align != variants[ single_inhabited_variant_idx] . align
897+ || st. max_repr_align != variants[ single_inhabited_variant_idx] . max_repr_align
898+ || st. unadjusted_abi_align
899+ != variants[ single_inhabited_variant_idx] . unadjusted_abi_align
900+ {
901+ st. backend_repr = BackendRepr :: Memory { sized : true } ;
902+ }
903+
904+ st. variants =
905+ Variants :: Single { index : single_inhabited_variant_idx, variants : Some ( variants) } ;
906+
907+ Some ( st)
908+ } else {
909+ None
910+ } ;
911+
848912 // Align the maximum variant size to the largest alignment.
849913 size = size. align_to ( align) ;
850914
@@ -1125,22 +1189,36 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
11251189 randomization_seed : combined_seed,
11261190 } ;
11271191
1128- let best_layout = match ( tagged_layout, niche_filling_layout) {
1129- ( tl, Some ( nl) ) => {
1130- // Pick the smaller layout; otherwise,
1131- // pick the layout with the larger niche; otherwise,
1132- // pick tagged as it has simpler codegen.
1192+ // Pick the smallest layout; otherwise,
1193+ // pick the layout with the largest niche; otherwise,
1194+ // pick no_tag as it has simpler codegen than tagged and niched; otherwise,
1195+ // pick tagged as it has simpler codegen than niched.
1196+
1197+ let better_layout_or_first =
1198+ |l1 : LayoutData < FieldIdx , VariantIdx > , l2 : LayoutData < FieldIdx , VariantIdx > | {
11331199 use cmp:: Ordering :: * ;
11341200 let niche_size = |l : & LayoutData < FieldIdx , VariantIdx > | {
11351201 l. largest_niche . map_or ( 0 , |n| n. available ( dl) )
11361202 } ;
1137- match ( tl . size . cmp ( & nl . size ) , niche_size ( & tl ) . cmp ( & niche_size ( & nl ) ) ) {
1138- ( Greater , _) => nl ,
1139- ( Equal , Less ) => nl ,
1140- _ => tl ,
1203+ match ( l1 . size . cmp ( & l2 . size ) , niche_size ( & l1 ) . cmp ( & niche_size ( & l2 ) ) ) {
1204+ ( Greater , _) => l2 ,
1205+ ( Equal , Less ) => l2 ,
1206+ _ => l1 ,
11411207 }
1142- }
1143- ( tl, None ) => tl,
1208+ } ;
1209+
1210+ let best_layout = match niche_filling_layout {
1211+ None => tagged_layout,
1212+ // Prefer tagged over niched if they have the same size and niche size,
1213+ // as the tagged layout has simpler codegen.
1214+ Some ( niched_layout) => better_layout_or_first ( tagged_layout, niched_layout) ,
1215+ } ;
1216+
1217+ let best_layout = match no_tag_layout {
1218+ None => best_layout,
1219+ // Prefer no-tag over tagged/niched if they have the same size and niche size,
1220+ // as the no-tag layout has simpler codegen.
1221+ Some ( no_tag_layout) => better_layout_or_first ( no_tag_layout, best_layout) ,
11441222 } ;
11451223
11461224 Ok ( best_layout)
0 commit comments