@@ -219,26 +219,47 @@ impl CastTarget {
219219 }
220220}
221221
222- /// Returns value from the `homogeneous_aggregate` test function.
222+ /// Return value from the `homogeneous_aggregate` test function.
223223#[ derive( Copy , Clone , Debug ) ]
224224pub enum HomogeneousAggregate {
225225 /// Yes, all the "leaf fields" of this struct are passed in the
226226 /// same way (specified in the `Reg` value).
227227 Homogeneous ( Reg ) ,
228228
229- /// There are distinct leaf fields passed in different ways,
230- /// or this is uninhabited.
231- Heterogeneous ,
232-
233229 /// There are no leaf fields at all.
234230 NoData ,
235231}
236232
233+ /// Error from the `homogeneous_aggregate` test function, indicating
234+ /// there are distinct leaf fields passed in different ways,
235+ /// or this is uninhabited.
236+ #[ derive( Copy , Clone , Debug ) ]
237+ pub struct Heterogeneous ;
238+
237239impl HomogeneousAggregate {
238240 /// If this is a homogeneous aggregate, returns the homogeneous
239241 /// unit, else `None`.
240242 pub fn unit ( self ) -> Option < Reg > {
241- if let HomogeneousAggregate :: Homogeneous ( r) = self { Some ( r) } else { None }
243+ match self {
244+ HomogeneousAggregate :: Homogeneous ( reg) => Some ( reg) ,
245+ HomogeneousAggregate :: NoData => None ,
246+ }
247+ }
248+
249+ /// Try to combine two `HomogeneousAggregate`s, e.g. from two fields in
250+ /// the same `struct`. Only succeeds if only one of them has any data,
251+ /// or both units are identical.
252+ fn merge ( self , other : HomogeneousAggregate ) -> Result < HomogeneousAggregate , Heterogeneous > {
253+ match ( self , other) {
254+ ( x, HomogeneousAggregate :: NoData ) | ( HomogeneousAggregate :: NoData , x) => Ok ( x) ,
255+
256+ ( HomogeneousAggregate :: Homogeneous ( a) , HomogeneousAggregate :: Homogeneous ( b) ) => {
257+ if a != b {
258+ return Err ( Heterogeneous ) ;
259+ }
260+ Ok ( self )
261+ }
262+ }
242263 }
243264}
244265
@@ -250,8 +271,8 @@ impl<'a, Ty> TyLayout<'a, Ty> {
250271 }
251272 }
252273
253- /// Returns `true ` if this layout is an aggregate containing fields of only
254- /// a single type (e.g., `(u32, u32)`). Such aggregates are often
274+ /// Returns `Homogeneous ` if this layout is an aggregate containing fields of
275+ /// only a single type (e.g., `(u32, u32)`). Such aggregates are often
255276 /// special-cased in ABIs.
256277 ///
257278 /// Note: We generally ignore fields of zero-sized type when computing
@@ -260,94 +281,118 @@ impl<'a, Ty> TyLayout<'a, Ty> {
260281 /// This is public so that it can be used in unit tests, but
261282 /// should generally only be relevant to the ABI details of
262283 /// specific targets.
263- pub fn homogeneous_aggregate < C > ( & self , cx : & C ) -> HomogeneousAggregate
284+ pub fn homogeneous_aggregate < C > ( & self , cx : & C ) -> Result < HomogeneousAggregate , Heterogeneous >
264285 where
265286 Ty : TyLayoutMethods < ' a , C > + Copy ,
266287 C : LayoutOf < Ty = Ty , TyLayout = Self > ,
267288 {
268289 match self . abi {
269- Abi :: Uninhabited => HomogeneousAggregate :: Heterogeneous ,
290+ Abi :: Uninhabited => Err ( Heterogeneous ) ,
270291
271292 // The primitive for this algorithm.
272293 Abi :: Scalar ( ref scalar) => {
273294 let kind = match scalar. value {
274295 abi:: Int ( ..) | abi:: Pointer => RegKind :: Integer ,
275296 abi:: F32 | abi:: F64 => RegKind :: Float ,
276297 } ;
277- HomogeneousAggregate :: Homogeneous ( Reg { kind, size : self . size } )
298+ Ok ( HomogeneousAggregate :: Homogeneous ( Reg { kind, size : self . size } ) )
278299 }
279300
280301 Abi :: Vector { .. } => {
281302 assert ! ( !self . is_zst( ) ) ;
282- HomogeneousAggregate :: Homogeneous ( Reg { kind : RegKind :: Vector , size : self . size } )
303+ Ok ( HomogeneousAggregate :: Homogeneous ( Reg {
304+ kind : RegKind :: Vector ,
305+ size : self . size ,
306+ } ) )
283307 }
284308
285309 Abi :: ScalarPair ( ..) | Abi :: Aggregate { .. } => {
286- let mut total = Size :: ZERO ;
287- let mut result = None ;
288-
289- let is_union = match self . fields {
290- FieldPlacement :: Array { count, .. } => {
291- if count > 0 {
292- return self . field ( cx, 0 ) . homogeneous_aggregate ( cx) ;
293- } else {
294- return HomogeneousAggregate :: NoData ;
295- }
296- }
297- FieldPlacement :: Union ( _) => true ,
298- FieldPlacement :: Arbitrary { .. } => false ,
299- } ;
310+ // Helper for computing `homogenous_aggregate`, allowing a custom
311+ // starting offset (used below for handling variants).
312+ let from_fields_at =
313+ |layout : Self ,
314+ start : Size |
315+ -> Result < ( HomogeneousAggregate , Size ) , Heterogeneous > {
316+ let is_union = match layout. fields {
317+ FieldPlacement :: Array { count, .. } => {
318+ assert_eq ! ( start, Size :: ZERO ) ;
319+
320+ let result = if count > 0 {
321+ layout. field ( cx, 0 ) . homogeneous_aggregate ( cx) ?
322+ } else {
323+ HomogeneousAggregate :: NoData
324+ } ;
325+ return Ok ( ( result, layout. size ) ) ;
326+ }
327+ FieldPlacement :: Union ( _) => true ,
328+ FieldPlacement :: Arbitrary { .. } => false ,
329+ } ;
300330
301- for i in 0 ..self . fields . count ( ) {
302- if !is_union && total != self . fields . offset ( i) {
303- return HomogeneousAggregate :: Heterogeneous ;
304- }
331+ let mut result = HomogeneousAggregate :: NoData ;
332+ let mut total = start;
305333
306- let field = self . field ( cx, i) ;
334+ for i in 0 ..layout. fields . count ( ) {
335+ if !is_union && total != layout. fields . offset ( i) {
336+ return Err ( Heterogeneous ) ;
337+ }
307338
308- match ( result, field. homogeneous_aggregate ( cx) ) {
309- ( _, HomogeneousAggregate :: NoData ) => {
310- // Ignore fields that have no data
311- }
312- ( _, HomogeneousAggregate :: Heterogeneous ) => {
313- // The field itself must be a homogeneous aggregate.
314- return HomogeneousAggregate :: Heterogeneous ;
315- }
316- // If this is the first field, record the unit.
317- ( None , HomogeneousAggregate :: Homogeneous ( unit) ) => {
318- result = Some ( unit) ;
319- }
320- // For all following fields, the unit must be the same.
321- ( Some ( prev_unit) , HomogeneousAggregate :: Homogeneous ( unit) ) => {
322- if prev_unit != unit {
323- return HomogeneousAggregate :: Heterogeneous ;
339+ let field = layout. field ( cx, i) ;
340+
341+ result = result. merge ( field. homogeneous_aggregate ( cx) ?) ?;
342+
343+ // Keep track of the offset (without padding).
344+ let size = field. size ;
345+ if is_union {
346+ total = total. max ( size) ;
347+ } else {
348+ total += size;
324349 }
325350 }
326- }
327351
328- // Keep track of the offset (without padding).
329- let size = field. size ;
330- if is_union {
331- total = total. max ( size) ;
332- } else {
333- total += size;
352+ Ok ( ( result, total) )
353+ } ;
354+
355+ let ( mut result, mut total) = from_fields_at ( * self , Size :: ZERO ) ?;
356+
357+ match & self . variants {
358+ abi:: Variants :: Single { .. } => { }
359+ abi:: Variants :: Multiple { variants, .. } => {
360+ // Treat enum variants like union members.
361+ // HACK(eddyb) pretend the `enum` field (discriminant)
362+ // is at the start of every variant (otherwise the gap
363+ // at the start of all variants would disqualify them).
364+ //
365+ // NB: for all tagged `enum`s (which include all non-C-like
366+ // `enum`s with defined FFI representation), this will
367+ // match the homogenous computation on the equivalent
368+ // `struct { tag; union { variant1; ... } }` and/or
369+ // `union { struct { tag; variant1; } ... }`
370+ // (the offsets of variant fields should be identical
371+ // between the two for either to be a homogenous aggregate).
372+ let variant_start = total;
373+ for variant_idx in variants. indices ( ) {
374+ let ( variant_result, variant_total) =
375+ from_fields_at ( self . for_variant ( cx, variant_idx) , variant_start) ?;
376+
377+ result = result. merge ( variant_result) ?;
378+ total = total. max ( variant_total) ;
379+ }
334380 }
335381 }
336382
337383 // There needs to be no padding.
338384 if total != self . size {
339- HomogeneousAggregate :: Heterogeneous
385+ Err ( Heterogeneous )
340386 } else {
341387 match result {
342- Some ( reg ) => {
388+ HomogeneousAggregate :: Homogeneous ( _ ) => {
343389 assert_ne ! ( total, Size :: ZERO ) ;
344- HomogeneousAggregate :: Homogeneous ( reg)
345390 }
346- None => {
391+ HomogeneousAggregate :: NoData => {
347392 assert_eq ! ( total, Size :: ZERO ) ;
348- HomogeneousAggregate :: NoData
349393 }
350394 }
395+ Ok ( result)
351396 }
352397 }
353398 }
0 commit comments