https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85548
Bug ID: 85548 Summary: Zero-initialization of padding bits of an aggregate class (class A) member of a non-aggregate class (class B) is not performed when B is value-initialized. Product: gcc Version: 7.3.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: jenda.tusil at gmail dot com Target Milestone: --- Consider classes A and B: ``` struct A { char c1; int i1; char c2; int i2; }; struct B { virtual void foo(){} A a; }; ``` The class `A` is an aggregate, `B` is not. Now consider this declaration: ``` B b{}; ``` When an instance of `B` is list-initialized with an empty list, it should be value-initialized, because `B` is not an aggregate, and because it has a default-constructor (the implicitly-generated `B::B()`). In this case, value-initialization means zero-initialization, because `B` is a class-type without deleted or user-provided default-constructor. Therefore the non-static member `B::a` is zero-initialized, as well as the padding bits of `B` (there are none on x86-64). The zero-initialization of 'b.a' means zero-initialization of `c1`, `i1`, `c2`, `i2`, and of all the padding. However, GCC 6 and GCC 7 does not perform the zero-initialization of the padding (clang 5 and clang 6 does). A full program: ``` extern "C" void abort(); #define assert(x) if(!(x)) abort(); struct A { char c1; int i1; char c2; int i2; }; struct B { virtual void foo(){} A a; }; int main() { B my_b{}; assert(my_b.a.c1 == 0); // the members are zero-initialized int const alig_size_1 = int(((char *)&my_b.a.i1 - &my_b.a.c1) - sizeof(my_b.a.c1)); // This does not have to hold, but holds for x86-64 assert(alig_size_1 > 0); for (int i = 0; i < alig_size_1; i++) { assert(*(&my_b.a.c1 + 1 + i) == 0); // fails } // The same for the padding between A::c2 and A::i2 int const alig_size_2 = int(((char *)&my_b.a.i2 - &my_b.a.c2) - sizeof(my_b.a.c2)); assert(alig_size_2 > 0); for (int i = 0; i < alig_size_2; i++) { assert(*(&my_b.a.c2 + 1 + i) == 0); } } ``` Also, when optimizing with -O1, GCC 7.3 gives the following warnings: ``` warning: '*((void*)& my_b +9)' is used uninitialized in this function assert(*(&my_b.a.c1 + 1 + i) == 0); ^ warning: '*((void*)& my_b +10)' may be used uninitialized in this function warning: '*((void*)& my_b +11)' may be used uninitialized in this function warning: '*((void*)& my_b +17)' may be used uninitialized in this function assert(*(&my_b.a.c2 + 1 + i) == 0); ^ warning: '*((void*)& my_b +18)' may be used uninitialized in this function warning: '*((void*)& my_b +19)' may be used uninitialized in this function ```