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

Reply via email to