Bug 15172 - [3.3/3.4 regression] Copy constructor optimization in aggregate initialization
Summary: [3.3/3.4 regression] Copy constructor optimization in aggregate initialization
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 3.3.3
: P2 critical
Target Milestone: 3.4.3
Assignee: Mark Mitchell
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2004-04-27 16:59 UTC by Michael Spencer
Modified: 2004-11-01 00:14 UTC (History)
3 users (show)

See Also:
Host: *-*-*
Target: *-*-*
Build: *-*-*
Known to work: 2.95 4.0.0
Known to fail: 3.2.3 3.3.5 3.4.2
Last reconfirmed: 2004-10-16 23:28:23


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Spencer 2004-04-27 16:59:35 UTC
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.
Comment 1 Andrew Pinski 2004-04-27 17:24:17 UTC
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.
Comment 2 Michael Spencer 2004-04-27 18:30:09 UTC
(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
Comment 3 Giovanni Bajo 2004-04-28 10:50:26 UTC
I will agree it makes sense, it's a code pessimization regression if it works 
in 2.95.
Comment 4 Andrew Pinski 2004-06-03 22:57:00 UTC
Confirmed.
Comment 5 Ali Cehreli 2004-10-16 22:57:31 UTC
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>
Comment 6 Wolfgang Bangerth 2004-10-16 23:28:22 UTC
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. 
Comment 7 Wolfgang Bangerth 2004-10-16 23:34:35 UTC
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. 
Comment 8 Ali Cehreli 2004-10-17 01:01:55 UTC
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
Comment 9 Giovanni Bajo 2004-10-17 11:21:06 UTC
> 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
Comment 10 Eric Botcazou 2004-10-26 08:30:52 UTC
Is this problem specific to SPARC?  If no, I'd suggest to change the triplets.
Comment 11 Eric Botcazou 2004-10-27 17:59:37 UTC
Reproducible on x86 too.
Comment 12 Benoît Dejean 2004-10-29 15:15:21 UTC
and ppc32
Comment 13 Mark Mitchell 2004-10-31 01:58:05 UTC
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.
Comment 14 Mark Mitchell 2004-10-31 19:02:35 UTC
Working on a fix.
Comment 15 GCC Commits 2004-11-01 00:02:32 UTC
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

Comment 16 GCC Commits 2004-11-01 00:07:15 UTC
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

Comment 17 Mark Mitchell 2004-11-01 00:14:03 UTC
Fixed in GCC 3.4.3.