msvc visual c++ silent incorrect code generation when constexpr initializing struct within union

274 views Asked by At

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.

1

There are 1 answers

1
Cody Gray - on strike On BEST ANSWER

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.:

#include <cstdio>

struct A {
    union {
        struct { int a0, a1; } aa;
        int bb[2];
    };

    constexpr A(int a0, int a1) : aa{a0, a1} { }
};

namespace {
    constexpr A t3 { 2, 3 };
};

int main() {
    std::printf("%d %d\n", t3.aa.a0, t3.aa.a1);
}

…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. If A contains only the struct aa, then there is no problem. Simply removing bb 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.