Possible GCC bug with copy-construction of object on itself?

Martin Sebor msebor@gmail.com
Thu Feb 8 17:39:00 GMT 2018


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;
               ^



More information about the Gcc-help mailing list