gcc configured with: --with-gnu-as --with-gnu-ld A class member of an aggregate appears to be initialized in temporary storage then byte-copied to its final location, as this code shows: int printf (const char *format, ...); struct A { A * _this; A () : _this (this) { printf ("A () : this = %p, _this = %p\n", this, _this); } ~ A () { printf ("~ A () : this = %p, _this = %p\n", this, _this); } void print () { printf ("print () : this = %p, _this = %p\n", this, _this); } A (const A & a) : _this (this) { printf ("A (const A &) : this = %p, _this = %p\n", this, _this); } }; struct B { A a; }; B b = { A () }; int main () { b.a.print (); } When run, the program produces: A () : this = ffbeae00, _this = ffbeae00 print () : this = 20e2c, _this = ffbeae00 ~ A () : this = 20e2c, _this = ffbeae00 I expected to see the "_this" member compare with "this" in all cases. In gcc 2.95 this is true. 12.6.1p2 of the Standard states that class members of aggregates are copy-initialized in this context.
From 12.6.1p2: "each such member is copy-initialized (see 8.5)" From 8.5p14: "The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy- initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8. " What 12.8 says is that it does not have call the copy constructor at all but it still can. Well that this case so this is invalid.
(In reply to comment #1) > What 12.8 says is that it does not have call the copy constructor at all but it still can. > Well that this case so this is invalid. 12.8p15: ... when a temporary class object that has not been bound to a reference (12.2) would be copied to a class object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary object directly into the target of the omitted copy. The problem here is that the temporary is *not* constructed into the target of the omitted copy. If this were the case, I'd expect to see something like the following (this is the run of the same program built with gcc 2.95): A () : this = 27794, _this = 27794 print () : this = 27794, _this = 27794 ~ A () : this = 27794, _this = 27794
I will agree it makes sense, it's a code pessimization regression if it works in 2.95.
Confirmed.
I don't think the paragraphs from the standard that are mentioned above are completely relevant. There isn't an opportunity for optimization in this case. This is aggregate initialization. I found the following paragraphs to be describing this case exactly: 8.5.1/1 8.5.1/13 12.6.1/2 The first example under 12.6.1/2 is directly relevant: <quote> [Example: complex v[6] = { 1,complex(1,2),complex(),2 }; Here, complex::complex(double) is called for the initialization of v[0] and v [3], complex::complex(double,double) is called for the initialization of v[1], complex::complex() is called for the initialization v[2], v[4], and v[5]. </quote>
Something is definitely wrong with this testcase. I think that storing and printing the _this pointer is only obstructing the view of the problem somehow, so consider this example here: -------------------- int printf (const char *format, ...); struct A { A () { printf ("A::A () : this = %p\n", this); } A (const A & a) { printf ("A::A (const A&) : this = %p\n", this); } ~ A () { printf ("A::~A () : this = %p\n", this); } void print () { printf ("print () : this = %p\n", this); } }; struct B { A a; }; B b = { A () }; int main () { b.a.print (); } ------------------------ I think we all agree that we always have to have matching calls to (copy) constructors and destructors for a this pointer, but we really get this here: g/x> c++ x.cc g/x> ./a.out A::A () : this = 0xbfffeaa0 print () : this = 0x80499c8 A::~A () : this = 0x80499c8 I don't think this can ever be right. This used to work in gcc2.95, but is broken between 3.2 and 3.4. It works fine in mainline again, though, so this is a 3.3/3.4 branch regression only. W.
The reason, btw, why I believe that this is wrong code is that the this pointer of the object being constructed can escape, like here: ------------------------------------ extern "C" int printf (const char *format, ...); struct A; void register_A (const A* a) {printf ("registering %p\n", a); } void deregister_A (const A* a) {printf ("deregistering %p\n", a); } struct A { A () { register_A(this); } A (const A & a) { register_A(this); } ~ A () { deregister_A(this); } }; struct B { A a; }; B b = { A () }; int main () {} ------------------------------------ It is certainly more than just a nuisance ("missed optimization") that the calls to register and deregister don't match! g/x> c++ x.cc g/x> ./a.out g/x> c++ x.cc g/x> ./a.out registering 0xbfffeaa0 deregistering 0x8049990 W.
We've seen the problem in 2.95.3 and 3.4.2. There is a recent posting to comp.lang.c++.moderated about the same problem, with the subject "Bitwise copy during aggregate initialization (a compiler bug?)". Ali
> There is a recent posting to comp.lang.c++.moderated about the same problem, > with the subject "Bitwise copy during aggregate initialization (a compiler > bug?)". ... which is here: http://tinyurl.com/4hpdn
Is this problem specific to SPARC? If no, I'd suggest to change the triplets.
Reproducible on x86 too.
and ppc32
The problem here is that expand_expr_real creates a temporary variable and stores the CONSTRCUTOR corresponding to the brace-enclosed initializer there. Then, it copies the temporary to the global variable.
Working on a fix.
Subject: Bug 15172 CVSROOT: /cvs/gcc Module name: gcc Branch: gcc-3_4-branch Changes by: mmitchel@gcc.gnu.org 2004-11-01 00:02:25 Modified files: gcc/cp : ChangeLog typeck2.c gcc/testsuite : ChangeLog Added files: gcc/testsuite/g++.dg/init: aggr2.C Log message: PR c++/15172 * typeck2.c (store_init_value): Use split_nonconstant_init even for types that require construction. PR c++/15172 * g++.dg/init/aggr2.C: New test. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.3892.2.176&r2=1.3892.2.177 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/typeck2.c.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.153.4.3&r2=1.153.4.4 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=1.3389.2.303&r2=1.3389.2.304 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/init/aggr2.C.diff?cvsroot=gcc&only_with_tag=gcc-3_4-branch&r1=NONE&r2=1.1.2.1
Subject: Bug 15172 CVSROOT: /cvs/gcc Module name: gcc Changes by: mmitchel@gcc.gnu.org 2004-11-01 00:07:10 Modified files: gcc/cp : ChangeLog typeck2.c gcc/testsuite : ChangeLog Added files: gcc/testsuite/g++.dg/init: aggr2.C Log message: PR c++/15172 * typeck2.c (store_init_value): Use split_nonconstant_init even for types that require construction. PR c++/15172 * g++.dg/init/aggr2.C: New test. Patches: http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/ChangeLog.diff?cvsroot=gcc&r1=1.4466&r2=1.4467 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/typeck2.c.diff?cvsroot=gcc&r1=1.177&r2=1.178 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&r1=1.4528&r2=1.4529 http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/init/aggr2.C.diff?cvsroot=gcc&r1=1.1&r2=1.2
Fixed in GCC 3.4.3.