[cxx-abi-dev] Do zero-length arrays make a class type non-empty?

Richard Smith richardsmith at googlers.com
Mon Apr 27 20:33:16 UTC 2015


On 27 April 2015 at 13:23, John McCall <rjmccall at apple.com> wrote:

>
> On Apr 26, 2015, at 2:26 PM, Richard Smith <richardsmith at googlers.com>
> wrote:
>
> On 25 April 2015 at 21:10, John McCall <rjmccall at apple.com> wrote:
>
>> The standard says that std::is_empty<T>::value is true if:
>>   - T is a class type, but not a union type, with no non-static data
>> members other than bit-fields of length 0, no virtual member functions, no
>> virtual base classes, and no base class B for which is_empty<B>::value is
>> false.
>>
>> The standard also says that array bounds must be greater than zero, but
>> there’s a common extension to permit zero-length array types (hereafter,
>> ZLATs).
>>
>> In GCC and Clang, at least, a ZLAT conventionally has size 0, both
>> formally (as a result of sizeof) and for struct layout (a ZLAT field takes
>> up no direct space, although it does cause the next offset to be rounded up
>> to the field’s alignment).  Moreover, a ZLAT field inhibits the general
>> rule rounding a class's size up to at least 1, recursively.
>>
>> Oddly, though, GCC and Clang also say that a struct containing a ZLAT is
>> not empty, at least as far as std::is_empty is concerned.  On the other
>> hand, ICC says that a struct containing only one (or more) ZLATs is empty;
>> as does MSVC, for what it’s worth.
>>
>> This touches on the ABI because:
>>   - the results of these metaprogramming traits can affect the ABI in a
>> number of ways,
>>   - class sizes are also obviously ABI, and
>>   - whether a base class is empty dramatically affects Itanium class
>> layout.
>>
>> The current Itanium definition mirrors the std::is_empty definition:
>>   [a] class with no non-static data members other than zero-width
>> bitfields, no virtual functions, no virtual base classes, and no non-empty
>> non-virtual proper base classes.
>>
>> Now, of course, this is an extension, and we don’t have to standardize
>> behavior on it; but my preference is to specify this sort of common
>> extension wherever possible.
>>
>
> The point of is_empty is to allow detection of EBO opportunities, so I
> think it should return 'true' in all cases where EBO would apply. It seems
> reasonable (if not very worthwhile) to ignore ZLATs when determining
> whether we can apply EBO. And I think this is a common enough extension for
> the ABI to specify how it should behave.
>
>
>> I propose the following changes:
>>
>> 1.  Add “or members of zero-length array type” to the ABI definition of
>> "empty class”.
>>
>
> Is it worth explicitly calling out that flexible array members and
> zero-length array members are handled the same in this regard?
>
>
> Yes, that’s probably a good idea.
>
>
> 2.  Specify std::is_empty to behave as if the same clause were there.
>> 3.  Change the ABI class layout rule (in IV. Finalization) to not require
>> sizeof(C) to be non-zero if C contains a ZLAT subobject.  That is, replace
>> this sentence:
>>   Round sizeof(C) up to a non-zero multiple of align(C).
>> with:
>>   If C does not contain (recursively) a subobject of zero-length array
>> type, and sizeof(C) is 0, set sizeof(C) to align(C); otherwise, round
>> sizeof(C) up to a multiple of align(C).
>
>
> Perhaps:
>
> If sizeof(C) is 0 and C has no non-static data members and no base
> classes, set sizeof(C) to align(C); otherwise, round sizeof(C) up to a
> multiple of align(C).
>
>
> Wouldn’t this trigger for a class with an empty base?
>

For an empty base class D of class C, step 3 says:

"Once offset(D) has been chosen, update sizeof(C) to max (sizeof(C),
offset(D)+sizeof(D)) and align(C) to max (alignof(C), nvalign(D))."

So sizeof(C) == sizeof(D) in the case of an otherwise-empty class with one
base class. Thus sizeof(C) == alignof(C) if D's size was made non-zero, and
is 0 otherwise (if D had a ZLAT member).

> I think that's equivalent, and it seems simpler and more general.
>
> Another problem is in step 2 of the layout algorithm: "Place D at this
> offset unless doing so would result in two components (direct or indirect)
> of the same type having the same offset.". That does the wrong thing for:
>
>   struct A { int n[0]; int m[0]; };
>
> where sizeof(A) should be zero, but will be 4 with this algorithm because
> putting m at offset 0 gives two components of type 'int[0]' at the same
> offset. Perhaps replace "components (direct or indirect)" with "non-array
> subobjects"?
>
>
> That seems reasonable.
>
> John.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://sourcerytools.com/pipermail/cxx-abi-dev/attachments/20150427/f6b08621/attachment-0001.html>


More information about the cxx-abi-dev mailing list