On 02/08/2018 06:57 AM, Ole Kniemeyer wrote:
Hi,
I have a small code snippet where I'm not sure if it's a GCC bug or undefined behaviour, so maybe
someone can help? The code is
#include <new>
void Func(void*);
struct A
{
A() = default;
A(const A& src)
{
Func(src.x);
}
void* x;
};
void Test(void* ptr)
{
A tmp;
tmp.x = ptr;
new (&tmp) A(tmp);
}
If you paste this code at gcc.godbolt.org and use -O3, GCC <= 5.X and Clang produce the code which
I'd expect:
Test(void*):
jmp Func(void*)
But GCC 6 and 7 call Func with a nullptr:
Test(void*):
xorl %edi, %edi
jmp Func(void*)
I admit that the code is a bit dubious because it copy-constructs a new A object with itself and
the copy constructor doesn't copy anything, so is this undefined behavior or a GCC bug?
I think it's undefined. If it were to be well-defined then
the copy constructor of every non-trivial type would have
to worry about self-copying not just the way copy assignment
does, but it would also have to create a temporary copy of
its argument before constructing self to avoid clobbering it.
That's a catch-22.
Martin
PS Here's a small test case where GCC detects the undefined
behavior and complains that x i uninitialized. The change
was introduced in GCC 6 via r222135. The change description
explains why:
* constexpr.c (cxx_eval_store_expression): Ignore clobbers.
(build_constexpr_constructor_member_initializers): Loop to find
the BIND_EXPR.
* decl.c (start_preparsed_function): Clobber the object at the
beginning of a constructor.
The constructor marks the object under construction as clobbered
which the -Wuninitialized checker notices.
void* operator new (__SIZE_TYPE__, void *p) { return p; }
struct A
{
A() = default;
A(const A& src) {
x = src.x;
}
void* x;
};
void* Test(void* ptr)
{
A tmp;
tmp.x = ptr;
new (&tmp) A(tmp);
return tmp.x;
}
u.C:18:14: warning: ‘tmp.A::x’ is used uninitialized in this function
[-Wuninitialized]
return tmp.x;
^