@@ -84,37 +84,40 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
8484            debug ! ( "DST {} layout: {:?}" ,  t,  layout) ; 
8585
8686            let  i = layout. fields . count ( )  - 1 ; 
87-             let  sized_size  = layout. fields . offset ( i) . bytes ( ) ; 
87+             let  unsized_offset_unadjusted  = layout. fields . offset ( i) . bytes ( ) ; 
8888            let  sized_align = layout. align . abi . bytes ( ) ; 
89-             debug ! ( "DST {} statically sized prefix size: {} align: {}" ,  t,  sized_size,  sized_align) ; 
90-             let  sized_size = bx. const_usize ( sized_size) ; 
89+             debug ! ( 
90+                 "DST {} offset of dyn field: {}, statically sized align: {}" , 
91+                 t,  unsized_offset_unadjusted,  sized_align
92+             ) ; 
93+             let  unsized_offset_unadjusted = bx. const_usize ( unsized_offset_unadjusted) ; 
9194            let  sized_align = bx. const_usize ( sized_align) ; 
9295
9396            // Recurse to get the size of the dynamically sized field (must be 
9497            // the last field). 
9598            let  field_ty = layout. field ( bx,  i) . ty ; 
9699            let  ( unsized_size,  mut  unsized_align)  = size_and_align_of_dst ( bx,  field_ty,  info) ; 
97100
98-             // FIXME (#26403, #27023): We should be adding padding  
99-              // to `sized_size` (to accommodate the `unsized_align` 
100-             // required of the unsized field that follows) before  
101-             // summing it with `sized_size`. (Note that since #26403 
102-             // is unfixed, we do not yet add the necessary padding 
103-             // here. But this is where the add would go.) 
104- 
105-             // Return the sum of sizes and max of aligns . 
106-             let  size  = bx. add ( sized_size ,  unsized_size ) ; 
107- 
108-             // Packed types ignore the alignment of their fields . 
109-             if   let  ty :: Adt ( def ,  _ )  = t . kind ( )   { 
110-                 if  def . repr ( ) . packed ( )   { 
111-                     unsized_align = sized_align ; 
101+             // # First compute the dynamic alignment  
102+ 
103+             // For packed types, we need to cap the alignment.  
104+             if   let  ty :: Adt ( def ,  _ )  = t . kind ( ) 
105+                 &&  let   Some ( packed )  = def . repr ( ) . pack 
106+             { 
107+                  if  packed . bytes ( )  ==  1   { 
108+                      // We know this will be capped to 1 . 
109+                     unsized_align  = bx. const_usize ( 1 ) ; 
110+                  }   else   { 
111+                      // We have to dynamically compute `min(unsized_align, packed)` . 
112+                      let  packed = bx . const_usize ( packed . bytes ( ) ) ; 
113+                      let  cmp = bx . icmp ( IntPredicate :: IntULT ,  unsized_align ,   packed) ; 
114+                     unsized_align = bx . select ( cmp ,  unsized_align ,  packed ) ; 
112115                } 
113116            } 
114117
115118            // Choose max of two known alignments (combined value must 
116119            // be aligned according to more restrictive of the two). 
117-             let  align  = match  ( 
120+             let  full_align  = match  ( 
118121                bx. const_to_opt_u128 ( sized_align,  false ) , 
119122                bx. const_to_opt_u128 ( unsized_align,  false ) , 
120123            )  { 
@@ -129,6 +132,19 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
129132                } 
130133            } ; 
131134
135+             // # Then compute the dynamic size 
136+ 
137+             // The full formula for the size would be: 
138+             // let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align); 
139+             // let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align); 
140+             // However, `unsized_size` is a multiple of `unsized_align`. 
141+             // Therefore, we can equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`: 
142+             // let full_size = (unsized_offset_unadjusted + unsized_size).align_to(unsized_align).align_to(full_align); 
143+             // Furthermore, `align >= unsized_align`, and therefore we only need to do: 
144+             // let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align); 
145+ 
146+             let  full_size = bx. add ( unsized_offset_unadjusted,  unsized_size) ; 
147+ 
132148            // Issue #27023: must add any necessary padding to `size` 
133149            // (to make it a multiple of `align`) before returning it. 
134150            // 
@@ -140,12 +156,12 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
140156            // 
141157            //   `(size + (align-1)) & -align` 
142158            let  one = bx. const_usize ( 1 ) ; 
143-             let  addend = bx. sub ( align ,  one) ; 
144-             let  add = bx. add ( size ,  addend) ; 
145-             let  neg = bx. neg ( align ) ; 
146-             let  size  = bx. and ( add,  neg) ; 
159+             let  addend = bx. sub ( full_align ,  one) ; 
160+             let  add = bx. add ( full_size ,  addend) ; 
161+             let  neg = bx. neg ( full_align ) ; 
162+             let  full_size  = bx. and ( add,  neg) ; 
147163
148-             ( size ,  align ) 
164+             ( full_size ,  full_align ) 
149165        } 
150166        _ => bug ! ( "size_and_align_of_dst: {t} not supported" ) , 
151167    } 
0 commit comments