Consider the following snippet trimmed down to illustrate the problem:
#include <cstdio>
struct A {
union {
struct { int a0, a1; } aa;
int bb[2];
};
#if 1 //bug
constexpr A(int a0, int a1) : aa{a0, a1} {}
#else //OK
constexpr A(int a0, int a1) : bb{a0, a1} {}
#endif
};
int main() {
A t0{2, 3}; std::printf("%d %d\n", t0.aa.a0, t0.aa.a1); //2 3 (OK)
static A t1{2, 3}; std::printf("%d %d\n", t1.aa.a0, t1.aa.a1); //2 3 (OK)
constexpr A t2{2, 3}; std::printf("%d %d\n", t2.aa.a0, t2.aa.a1); //2 3 (OK)
static constexpr A t3{2, 3}; std::printf("%d %d\n", t3.aa.a0, t3.aa.a1); //0 0 (??)
}
msvc 2015 update 3 and 2017 rc1 silently generate incorrect code by zero-initializing t3 instead of properly initializing it with the given values. gcc and clang are fine.
I looked into reporting a bug but it is too much hassle (I don't use the IDE). If you care, please confirm this is a bug, and let it be known to whom it may concern at microsoft.
I can reproduce this on VS 2015, with both the x86-32 and x86-64 compilers.
I can also confirm that current versions of GCC, Clang, and ICC compile it correctly. Nor can I find anything in the language standard that prohibits what you're doing. The implementation is allowed some leeway in whether it initializes the object at compile-time (as you would expect) or at run-time, but MSVC isn't initializing it at all.
As might be expected, the same problem is observed when the object's declaration is moved to namespace scope, e.g.:
…as well as if the object is declared as a static member of an otherwise empty struct.
Although the visible effect is a zero-initialization, that does not actually appear to be what the compiler is doing. Rather, it fails to emit a value at all in the corresponding data segment, so when the code attempts to load a value from that address to push onto the stack when making the call to
printf
, it ends up pushing a zero.It seems that the
union
here is key to reproducing the bug. IfA
contains only the structaa
, then there is no problem. Simply removingbb
to leave a one-member union also solves the problem.Note that you have everything you need here to submit a bug report to Microsoft for the VS 2017 compiler (use of the IDE is not required). The code included in the question is a self-contained example that demonstrates the problem perfectly well. If you really don't want to do it yourself, let me know and I'll submit the bug.