https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93018
Bug ID: 93018 Summary: Zero initialization not occurring for empty struct in member union when converting constructor is used with -O2 Product: gcc Version: 9.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: john at drouhard dot dev Target Milestone: --- Created attachment 47528 --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=47528&action=edit reproducible c++ source When a converting constructor is used to return a struct that contains a bool and a union with an instance of an empty struct as a member, the padding byte for that empty struct member is not zero initialized. See the attached reproducible. I've reproduced this on gcc 8.1-8.3, 9.2, and trunk (using godbolt.org). Here's the godbolt link: https://godbolt.org/z/voL7B5 Compile with -std=c++17 -O2 -DEMPTY (which will use an empty struct in the union). Omit -DEMPTY to see that naming the byte (thus making it part of the value representation instead of the object representation) causes it to correctly zero initialize. It also correctly zero initializes if the function that returns the struct does so by returning a value-initialized Bar (baz1). In baz2, a converting constructor from a null_t to Bar is used, though the same behavior should occur as in baz1. I believe zero initialization is required by the standard for the byte in the empty struct. From the draft standard as of 2017-11-27: ================================= 6.6.2 paragraph 7: Unless it is a bit-field (12.2.4), a most derived object shall have a nonzero size and shall occupy one or more bytes of storage. Base class subobjects may have zero size. An object of trivially copyable or standard-layout type (6.7) shall occupy contiguous bytes of storage. 6.7 paragraph 4: The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T). The value representation of an object is the set of bits that hold the value of type T. Bits in the object representation that are not part of the value representation are padding bits. 11.6 paragraph 6: To zero-initialize an object or reference of type T means: (6.2) — if T is a (possibly cv-qualified) non-union class type, its padding bits (6.7) are initialized to zero bits and each non-static data member, each non-virtual base class subobject, and, if the object is not a base class subobject, each virtual base class subobject is zero-initialized; (6.3) — if T is a (possibly cv-qualified) union type, its padding bits (6.7) are initialized to zero bits and the object’s first non-static named data member is zero-initialized; 11.6 paragraph 8: To value-initialize an object of type T means: (8.1) — if T is a (possibly cv-qualified) class type (Clause 12) with either no default constructor (15.1) or a default constructor that is user-provided or deleted, then the object is default-initialized; (8.2) — if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized; (8.3) — if T is an array type, then each element is value-initialized; (8.4) — otherwise, the object is zero-initialized. ================================= The empty "e" member in the "Foo" union is being value-initialized in the member initializer list of the default constructor of Foo. Value-initialization for an empty struct should cause zero-initialization of the padding bytes, which is the single byte of the object representation for instances of an empty struct. baz1 seems to do this correctly, baz2 does not.