@@ -742,7 +742,198 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
742742 Some ( layout)
743743 } ;
744744
745- let niche_filling_layout = calculate_niche_filling_layout ( ) ;
745+ let calculate_niche_filling_layout_repacked =
746+ || -> Option < LayoutData < FieldIdx , VariantIdx > > {
747+ struct VariantLayoutInfo {
748+ align_abi : Align ,
749+ }
750+
751+ if repr. inhibit_enum_layout_opt ( ) {
752+ return None ;
753+ }
754+
755+ if variants. len ( ) < 2 {
756+ return None ;
757+ }
758+
759+ let mut align = dl. aggregate_align ;
760+ let mut max_repr_align = repr. align ;
761+ let mut unadjusted_abi_align = align;
762+ let mut combined_seed = repr. field_shuffle_seed ;
763+
764+ let mut variants_info = IndexVec :: < VariantIdx , _ > :: with_capacity ( variants. len ( ) ) ;
765+ let variant_layouts = variants
766+ . iter ( )
767+ . map ( |v| {
768+ let st = self . univariant ( v, repr, StructKind :: AlwaysSized ) . ok ( ) ?;
769+
770+ variants_info. push ( VariantLayoutInfo { align_abi : st. align . abi } ) ;
771+
772+ align = align. max ( st. align . abi ) ;
773+ max_repr_align = max_repr_align. max ( st. max_repr_align ) ;
774+ unadjusted_abi_align = unadjusted_abi_align. max ( st. unadjusted_abi_align ) ;
775+ combined_seed = combined_seed. wrapping_add ( st. randomization_seed ) ;
776+
777+ Some ( VariantLayout :: from_layout ( st) )
778+ } )
779+ . collect :: < Option < IndexVec < VariantIdx , _ > > > ( ) ?;
780+
781+ let max_variant_size = variant_layouts. iter ( ) . map ( |layout| layout. size ) . max ( ) ?;
782+
783+ // Chooses the first max-sized niche-providing variant whose layout succeeds.
784+ for ( largest_variant_index, _) in
785+ variant_layouts. iter_enumerated ( ) . filter ( |& ( _, layout) | {
786+ layout. size == max_variant_size && layout. largest_niche . is_some ( )
787+ } )
788+ {
789+ let mut variant_layouts = variant_layouts. clone ( ) ;
790+
791+ let all_indices = variants. indices ( ) ;
792+ let needs_disc = |index : VariantIdx | {
793+ index != largest_variant_index && !absent ( & variants[ index] )
794+ } ;
795+ let Some ( niche_variants_start) = all_indices. clone ( ) . find ( |v| needs_disc ( * v) )
796+ else {
797+ continue ;
798+ } ;
799+ let Some ( niche_variants_end) = all_indices. rev ( ) . find ( |v| needs_disc ( * v) )
800+ else {
801+ continue ;
802+ } ;
803+ let niche_variants = niche_variants_start..=niche_variants_end;
804+
805+ let count = ( niche_variants. end ( ) . index ( ) as u128
806+ - niche_variants. start ( ) . index ( ) as u128 )
807+ + 1 ;
808+
809+ // Use the largest niche in the largest variant.
810+ let Some ( niche) = variant_layouts[ largest_variant_index] . largest_niche else {
811+ continue ;
812+ } ;
813+ let Some ( ( niche_start, niche_scalar) ) = niche. reserve ( dl, count) else {
814+ continue ;
815+ } ;
816+ let niche_offset = niche. offset ;
817+ let niche_size = niche. value . size ( dl) ;
818+ let size = variant_layouts[ largest_variant_index] . size . align_to ( align) ;
819+
820+ let all_variants_fit =
821+ variant_layouts. iter_enumerated_mut ( ) . all ( |( i, layout) | {
822+ if i == largest_variant_index {
823+ return true ;
824+ }
825+
826+ layout. largest_niche = None ;
827+
828+ if layout. size <= niche_offset {
829+ // This variant will fit before the niche.
830+ return true ;
831+ }
832+
833+ // Determine if it'll fit after the niche.
834+ let this_align = variants_info[ i] . align_abi ;
835+ let this_offset = ( niche_offset + niche_size) . align_to ( this_align) ;
836+
837+ if this_offset + layout. size > size {
838+ // The ordinary niche-filling path can only move a non-largest variant as a
839+ // whole before or after the chosen niche. If that fails, try placing the
840+ // variant's fields individually while treating the niche bytes as reserved.
841+ if let Some ( repacked) = self . try_layout_variant_around_niche (
842+ & variants[ i] ,
843+ repr,
844+ i,
845+ size,
846+ align,
847+ niche_offset,
848+ niche_size,
849+ ) {
850+ * layout = VariantLayout :: from_layout ( repacked) ;
851+ return true ;
852+ }
853+ return false ;
854+ }
855+
856+ // It'll fit, but we need to make some adjustments.
857+ for offset in layout. field_offsets . iter_mut ( ) {
858+ * offset += this_offset;
859+ }
860+
861+ // It can't be a Scalar or ScalarPair because the offset isn't 0.
862+ if !layout. is_uninhabited ( ) {
863+ layout. backend_repr = BackendRepr :: Memory { sized : true } ;
864+ }
865+ layout. size += this_offset;
866+
867+ true
868+ } ) ;
869+
870+ if !all_variants_fit {
871+ continue ;
872+ }
873+
874+ let largest_niche = Niche :: from_scalar ( dl, niche_offset, niche_scalar) ;
875+
876+ let others_zst = variant_layouts
877+ . iter_enumerated ( )
878+ . all ( |( i, layout) | i == largest_variant_index || layout. size == Size :: ZERO ) ;
879+ let same_size = size == variant_layouts[ largest_variant_index] . size ;
880+ let same_align = align == variants_info[ largest_variant_index] . align_abi ;
881+
882+ let uninhabited = variant_layouts. iter ( ) . all ( |v| v. is_uninhabited ( ) ) ;
883+ let abi = if same_size && same_align && others_zst {
884+ match variant_layouts[ largest_variant_index] . backend_repr {
885+ // When the total alignment and size match, we can use the
886+ // same ABI as the scalar variant with the reserved niche.
887+ BackendRepr :: Scalar ( _) => BackendRepr :: Scalar ( niche_scalar) ,
888+ BackendRepr :: ScalarPair ( first, second) => {
889+ // Only the niche is guaranteed to be initialised,
890+ // so use union layouts for the other primitive.
891+ if niche_offset == Size :: ZERO {
892+ BackendRepr :: ScalarPair ( niche_scalar, second. to_union ( ) )
893+ } else {
894+ BackendRepr :: ScalarPair ( first. to_union ( ) , niche_scalar)
895+ }
896+ }
897+ _ => BackendRepr :: Memory { sized : true } ,
898+ }
899+ } else {
900+ BackendRepr :: Memory { sized : true }
901+ } ;
902+
903+ return Some ( LayoutData {
904+ variants : Variants :: Multiple {
905+ tag : niche_scalar,
906+ tag_encoding : TagEncoding :: Niche {
907+ untagged_variant : largest_variant_index,
908+ niche_variants,
909+ niche_start,
910+ } ,
911+ tag_field : FieldIdx :: new ( 0 ) ,
912+ variants : variant_layouts,
913+ } ,
914+ fields : FieldsShape :: Arbitrary {
915+ offsets : [ niche_offset] . into ( ) ,
916+ in_memory_order : [ FieldIdx :: new ( 0 ) ] . into ( ) ,
917+ } ,
918+ backend_repr : abi,
919+ largest_niche,
920+ uninhabited,
921+ size,
922+ align : AbiAlign :: new ( align) ,
923+ max_repr_align,
924+ unadjusted_abi_align,
925+ randomization_seed : combined_seed,
926+ } ) ;
927+ }
928+
929+ None
930+ } ;
931+
932+ let niche_filling_layout = if repr. can_repack_variant_around_niche ( ) {
933+ calculate_niche_filling_layout_repacked ( )
934+ } else {
935+ calculate_niche_filling_layout ( )
936+ } ;
746937
747938 let discr_type = repr. discr_type ( ) ;
748939 let discr_int = Integer :: from_attr ( dl, discr_type) ;
@@ -1091,6 +1282,121 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
10911282 Ok ( best_layout)
10921283 }
10931284
1285+ fn try_layout_variant_around_niche <
1286+ ' a ,
1287+ FieldIdx : Idx ,
1288+ VariantIdx : Idx ,
1289+ F : Deref < Target = & ' a LayoutData < FieldIdx , VariantIdx > > + fmt:: Debug + Copy ,
1290+ > (
1291+ & self ,
1292+ fields : & IndexSlice < FieldIdx , F > ,
1293+ repr : & ReprOptions ,
1294+ variant_index : VariantIdx ,
1295+ total_size : Size ,
1296+ total_align : Align ,
1297+ niche_offset : Size ,
1298+ niche_size : Size ,
1299+ ) -> Option < LayoutData < FieldIdx , VariantIdx > > {
1300+ let dl = self . cx . data_layout ( ) ;
1301+
1302+ if repr. inhibit_struct_field_reordering ( ) {
1303+ return None ;
1304+ }
1305+ if fields. iter ( ) . any ( |f| f. is_unsized ( ) ) {
1306+ return None ;
1307+ }
1308+
1309+ // This is only a necessary condition: alignment can still make placement fail below.
1310+ let min_used_size = fields. iter ( ) . try_fold ( niche_size, |size, field| {
1311+ if field. is_zst ( ) { Some ( size) } else { size. checked_add ( field. size , dl) }
1312+ } ) ?;
1313+ if min_used_size > total_size {
1314+ return None ;
1315+ }
1316+
1317+ let mut offsets = IndexVec :: from_elem ( Size :: ZERO , fields) ;
1318+ let mut in_memory_order: IndexVec < u32 , FieldIdx > = fields. indices ( ) . collect ( ) ;
1319+
1320+ in_memory_order. raw . sort_by_key ( |& i| {
1321+ let field = & fields[ i] ;
1322+
1323+ let field_align = if let Some ( pack) = repr. pack {
1324+ field. align . min ( AbiAlign :: new ( pack) )
1325+ } else {
1326+ field. align
1327+ } ;
1328+
1329+ ( cmp:: Reverse ( field_align. abi . bytes ( ) ) , cmp:: Reverse ( field. size . bytes ( ) ) )
1330+ } ) ;
1331+
1332+ let niche_end = niche_offset. checked_add ( niche_size, dl) ?;
1333+ let mut occupied = vec ! [ ( niche_offset, niche_end) ] ;
1334+
1335+ let mut max_repr_align = repr. align ;
1336+ let mut unadjusted_abi_align =
1337+ if repr. pack . is_some ( ) { dl. i8_align } else { dl. aggregate_align } ;
1338+
1339+ for & i in & in_memory_order {
1340+ let field = & fields[ i] ;
1341+
1342+ let field_align = if let Some ( pack) = repr. pack {
1343+ field. align . min ( AbiAlign :: new ( pack) )
1344+ } else {
1345+ field. align
1346+ } ;
1347+
1348+ max_repr_align = max_repr_align. max ( field. max_repr_align ) ;
1349+ unadjusted_abi_align = unadjusted_abi_align. max ( field_align. abi ) ;
1350+
1351+ let mut candidate = Size :: ZERO ;
1352+ ' search: loop {
1353+ candidate = candidate. align_to ( field_align. abi ) ;
1354+
1355+ let end = candidate. checked_add ( field. size , dl) ?;
1356+ if end > total_size {
1357+ return None ;
1358+ }
1359+
1360+ for & ( occupied_start, occupied_end) in & occupied {
1361+ if end <= occupied_start {
1362+ break ;
1363+ }
1364+ if candidate < occupied_end {
1365+ candidate = occupied_end;
1366+ continue ' search;
1367+ }
1368+ }
1369+
1370+ offsets[ i] = candidate;
1371+
1372+ if !field. is_zst ( ) {
1373+ occupied. push ( ( candidate, end) ) ;
1374+ occupied. sort_by_key ( |& ( start, _end) | start) ;
1375+ }
1376+
1377+ break ;
1378+ }
1379+ }
1380+
1381+ in_memory_order. raw . sort_by_key ( |& i| offsets[ i] ) ;
1382+
1383+ Some ( LayoutData {
1384+ variants : Variants :: Single { index : variant_index } ,
1385+ fields : FieldsShape :: Arbitrary { offsets, in_memory_order } ,
1386+ backend_repr : BackendRepr :: Memory { sized : true } ,
1387+ largest_niche : None ,
1388+ uninhabited : fields. iter ( ) . any ( |f| f. is_uninhabited ( ) ) ,
1389+ align : AbiAlign :: new ( total_align) ,
1390+ size : total_size,
1391+ max_repr_align,
1392+ unadjusted_abi_align,
1393+ randomization_seed : fields
1394+ . iter ( )
1395+ . fold ( Hash64 :: ZERO , |acc, f| acc. wrapping_add ( f. randomization_seed ) )
1396+ . wrapping_add ( repr. field_shuffle_seed ) ,
1397+ } )
1398+ }
1399+
10941400 fn univariant_biased <
10951401 ' a ,
10961402 FieldIdx : Idx ,
0 commit comments