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
```