https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70792

--- Comment #8 from Matthijs van Duin <matthijsvanduin at gmail dot com> ---
(In reply to Matthijs van Duin from comment #4)
>       return std::pair{ ++i, ++i }.first;

My bad! This isn't an exhibit of the bug. I simply forgot that std::pair is not
really a struct, and this isn't aggregate initialization: the constructor takes
references, so correct code is generated in this case.

And in fact, if you do use an aggregate, the test works correctly.

However, if you replace std::pair by a class whose constructor takes (int,
int), similar to the one used in the existing testcase
(g++.dg/cpp0x/initlist86.C) then it fails again.

Looking at the disassembly (on ARM since I don't know x86 asm) shows that gcc
loads both arguments from the storage allocated for i, after both increments
have been done. Effectively it's copy-constructing the first argument too late.

The more general issue appears to be that if the arguments are trivially
copyable lvalues, then gcc keeps these as lvalues and copy-constructs the
actual arguments way too late. If I look at this disassembly of this code:

  struct Foo {
    char x[64]; // too big to pass in register
    Foo( Foo const &other ) = default; // but still trivially copyable
    Foo &mutate();
  };

  struct Pair {
    Pair( Foo x, Foo y );
  };

  void test( Foo &foo ) {
    Pair{ foo.mutate(), foo.mutate() };
  }

Then test() effectively does:

  Foo &temp1 = foo.mutate();
  Foo &temp2 = foo.mutate();
  Pair{ temp1, temp2 }  // copy-construct arguments and call Pair constructor

(Also, interestingly, temp2 is copy-constructed before temp1 is!)

If Foo is not trivially copyable, even if merely due to the presence of a
destructor, then the problem disappears.

Reply via email to