[Bug] Destructor of temporary objects bound to reference variable not called in expected order.

Hi,

This is a gcc test case

extern “C” int printf (const char *, …);

int c;
int r;

struct A
{
A() { printf (“A()\n”); if (c++) r = 1; }
A(const A&) { printf (“A(const A&)\n”); ++c; }
~A() { printf (“~A()\n”); --c; }
};

struct B
{
B(int, const A& = A()) { printf (“B()\n”); }
};

int main()
{
B b[] = { 0, 1 };
return r;
}

Output from Clang (latest trunk):

A()
B()
A()
B()
~A()
~A()

Output from gcc 4.8 :

A()
B()
~A()
A()
B()
~A()

As we can see, the destructor of temporary objects are not called immediately in case of clang.
The temporary A passed to the constructor for b[0] should have been destroyed before going on to construct b[1].

According to standards,

The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
— A temporary bound to a reference member in a constructor’s ctor-initializer ( 12.6.2) persists until the constructor exits.
— A temporary bound to a reference parameter in a function call ( 5.2.2) persists until the completion of the full-expression containing the call.

So even though A’s temporary is bound to reference, it should get destroyed as soon as b[0] gets constructed before going forward to construct b[1].

This seems similar to http://llvm.org/bugs/show_bug.cgi?id=16476

Richard, any comment/help on this from you will be most welcomed (Since bug 16476 was filed by you :)). Does this require major change in code and handling of temporaries?

Is this also related to http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1634 ?

Hi,

This is a gcc test case

*extern "C" int printf (const char *, ...);int c;int r;struct A{ A() {
printf ("A()\n"); if (c++) r = 1; } A(const A&) { printf ("A(const
A&)\n"); ++c; } ~A() { printf ("~A()\n"); --c; }};struct B{ B(int, const
A& = A()) { printf ("B()\n"); } };int main(){ B b = { 0, 1 }; return
r;}*

Output from Clang (latest trunk):

*A()B()A()B()~A()~A()*

Output from gcc 4.8 :

*A() B()~A()A()B()~A() *

As we can see, the destructor of temporary objects are not called
immediately in case of clang.
The temporary A passed to the constructor for b[0] should have been
destroyed before going on to construct b[1].

According to standards,

The temporary to which the reference is bound or the temporary that is the
complete object of a subobject to which the reference is bound persists for
the lifetime of the reference except:
— A temporary bound to a reference member in a constructor’s
ctor-initializer ( 12.6.2) persists until the constructor exits.
— A temporary bound to a reference parameter in a function call ( 5.2.2)
persists until the completion of the full-expression containing the call.

So even though A's temporary is bound to reference, it should get
destroyed as soon as b[0] gets constructed before going forward to
construct b[1].

We (incorrectly, per the C++ standard) treat top-level aggregate
initialization as a full-expression. However, I'm not entirely convinced
that this is a bug in Clang rather than a bug in the standard, especially
in C++11. As a demonstration of why this might be a standard defect:

typedef B BA;

void f() {
  BA x = { 1, 2 }; // A(), B(), ~A(), A(), B(), ~A()
  auto &&y = BA { 1, 2 }; // A(), B(), A(), B(), ~A(), ~A()
} // ~B(), ~B(), ~B(), ~B()

This seems similar to http://llvm.org/bugs/show_bug.cgi?id=16476

It looks similar, but I think they're completely separate. That issue is
about end-of-scope cleanups and interleaving of lifetime-extended
temporaries with array elements. In this case, there is no
lifetime-extension.

Richard, any comment/help on this from you will be most welcomed (Since bug

16476 was filed by you :)). Does this require major change in code and
handling of temporaries?

It would probably be straightforward to track where the full-expression
boundaries actually are during IR generation. As noted above, I'm not yet
convinced that Clang is the right place to fix this. =)

Is this also related to
C++ Standard Core Language Active Issues ?

Not in this instance, but there are nearby issues that are related (in
particular, destruction order if a temporary's destructor throws).

Hi Richard,

Thanks for the reply.