Hi! C++ has https://eel.is/c++draft/dcl.init#general-6.2 https://eel.is/c++draft/dcl.init#general-6.3 which says that during zero-initialization padding bits of structures and unions are zero initialized, and in https://eel.is/c++draft/dcl.init#general-9.3 says that in certain cases value-initialization is zero-initialization. E.g. () initialization is value-initialization.
Now, looking at C23, it has something similar, called default initialization, which also requires zeroing padding bits, unlike C++ in different cases. Initialization with {} initializer new in C23 is default-initialization, and if an initializer initializes some members but not others, the rest are also default-initialized. Reading C17, that was apparently the case already there, eventhough it wasn't called default initialization - "the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration" and the static/thread_local storage duration initialization talking about the clearing of padding bits. Of course, if this is initialization of file/namespace scope or thread_local variable, we do zero initialize padding bits forever. A different thing are automatic variable initializers. For structures I think whether we clear the padding bits or not is right now purely an optimization decision during gimplification, whether we decide to optimize by clearing the whole structure or not from performance POV. And for unions we did clear the padding (the whole union; the patch I've just posted changes that though). And I think D has something similar. My main question is when the standards say that the padding bits need to be cleared, whether a valid program can actually observe that. I'd hope that structure assignment is element-wise copying and so doesn't need to preserve those bits. What about memcpy, or *(unsigned char *), or for C++ std::bit_cast inspection of the bits (constexpr for C++ or not)? Or is the wording about clearing padding bits in both standards just useless and it is still UB to depend on the value of the padding bits? struct A { unsigned char a; unsigned int b; }; struct B { unsigned char c; struct A d; struct A e; unsigned long long f; }; void foo (void) { #ifdef __cplusplus struct B a (); // value-initialization -> zero-initialization struct B b = { 0 }; // I think this is zero-initialization of d and e and f. #else struct B a = {}; // New in C23, default-initialization of whole. struct B b = { 0 }; // I think this is default-initialization of d and e and f, // even in C17 and possibly earlier. #endif /* I think both standards say that the padding in between c and d here is cleared, can one inspect it like this? Similarly padding between e and f. */ if (*((unsigned char *)&a + 1) != 0) abort (); /* My reading of either of the standards is that the padding in between b.c and b.d and b.e and b.f is uninitialized. */ /* My reading of either of the standards is that the padding in between b.d.a and b.d.b and padding between b.e.a and b.e.b is zero initialized (and similarly also for a.d.a and a.d.b etc.). */ if (*((unsigned char *)&b + offsetof (struct B, e) + 1) != 0) abort (); } If the padding bits need to be cleared and it needs to be observable for C++ zero-initialization and for C default-initialization, I wonder if we shouldn't introduce CONSTRUCTOR_CLEAR_PADDING flag, let the FEs set it on CONSTRUCTORs where the standards guarantee clearing of padding bits and during gimplification don't take it just as an optimization, but as a conformance issue (say in the code in expr.cc I was touching inside of CONSTRUCTOR_CLEAR_PADDING ctors never set *p_complete to -1, but set it to 0 in those cases instead to force the zeroing. And a question is if we should have some flag too for whether missing constructor elts result in those elements to be effectively CONSTRUCTOR_CLEAR_PADDING or not (or whether it is enough that we set *p_complete = 0 in that case anyway). And another question is whether we want to keep such flag on the CONSTRUCTOR preserved in GIMPLE, or whether we declare a mere struct whatever a = {}; in GIMPLE clears the padding too, or whether we use MEM <char[sizeof (struct whatever)]> [&a, 0] = {}; for that. And whether the DSE memory trimming shouldn't use some other representation if the padding bits in it aren't needed to be cleared. And whether we perform some optimizations which might break the padding bits, SRA, or the DSE memory trimming (in case there is a store but some padding bits left somewhere), etc. Thoughts on this? Jakub