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.

Reply via email to