Bug 17435 - [4.0 Regression] Binding a temporary of derived type to reference of base
[4.0 Regression] Binding a temporary of derived type to reference of base
Status: RESOLVED FIXED
Product: gcc
Classification: Unclassified
Component: c++
4.0.0
: P2 critical
: 4.0.0
Assigned To: Mark Mitchell
: wrong-code
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2004-09-12 11:05 UTC by vlukas
Modified: 2004-10-28 05:26 UTC (History)
1 user (show)

See Also:
Host: i686-pc-linux-gnu
Target: i686-pc-linux-gnu
Build: i686-pc-linux-gnu
Known to work: 3.3.4 3.4.2
Known to fail: 4.0.0
Last reconfirmed: 2004-10-15 00:44:03


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description vlukas 2004-09-12 11:05:23 UTC
If a reference to const is bound to an temporary object of derived type, it 
seems as if the lifetime of the temporary is not extended to the lifetime of 
the reference. 
 
The small program at the bottom, compiled with gcc version 4.0.0 20040912 
(experimental) gives this Output: 
 
~A() 
 A::func() 
 
With gcc 3.3.4 the same program generates this Output: 
 
 A::func() 
~A() 
 
I think this is the correct behaviour. 
 
 
To reproduce, save the following snippet to a file "name.cpp" and compile with 
gcc version 4.0.0 20040912: 
c++ -Wall -o name name.cpp 
Then execute "name". 
 
------------------------------------------------------ 
 
int printf(char const*, ...); 
 
struct A  
{ 
 
void func() const 
 { 
 printf(" A::func()\n"); 
 } 
 
~A() 
 { 
 printf("~A()\n"); 
 } 
 
}; 
 
struct B : public A  
{ 
 
}; 
 
int main() 
{ 
A const& r1 = B(); 
r1.func(); 
} 
 
----------------------------------------------------------
Comment 1 Wolfgang Bangerth 2004-09-12 23:21:56 UTC
This example makes it slightly clearer what happens: 
------------------ 
#include <iostream> 
#include <typeinfo> 
 
struct A { 
    virtual ~A() {}; 
};  
  
struct B : public A  { };  
  
int main() {  
  A const& r1 = B();  
  std::cout << typeid(r1).name() << std::endl; 
}  
------------------------ 
 
g/x> /home/bangerth/bin/gcc-3.5-pre/bin/c++ x.cc 
g/x> ./a.out  
1A 
g/x> /home/bangerth/bin/gcc-3.3.4-pre/bin/c++ x.cc 
g/x> ./a.out  
1B 
g/x> icc -Xc -ansi -c x.cc 
g/x> ./a.out  
1B 
 
In contrast to older versions of gcc and to icc, gcc chooses to copy the 
A part of the object created by B() when it assigns to the reference. 
 
I believe that this is standard conforming, and that gcc is allowed (though 
not required) to do so, but someone else will have to look up the respective 
sentence of the standard. 
 
W. 
Comment 2 Gabriel Dos Reis 2004-09-12 23:49:21 UTC
Subject: Re:  Temporay bound to reference is immediately destructed

"bangerth at dealii dot org" <gcc-bugzilla@gcc.gnu.org> writes:

| This example makes it slightly clearer what happens: 
| ------------------ 
| #include <iostream> 
| #include <typeinfo> 
|  
| struct A { 
|     virtual ~A() {}; 
| };  
|   
| struct B : public A  { };  
|   
| int main() {  
|   A const& r1 = B();  
|   std::cout << typeid(r1).name() << std::endl; 
| }  
| ------------------------ 
|  
| g/x> /home/bangerth/bin/gcc-3.5-pre/bin/c++ x.cc 
| g/x> ./a.out  
| 1A 
| g/x> /home/bangerth/bin/gcc-3.3.4-pre/bin/c++ x.cc 
| g/x> ./a.out  
| 1B 
| g/x> icc -Xc -ansi -c x.cc 
| g/x> ./a.out  
| 1B 
|  
| In contrast to older versions of gcc and to icc, gcc chooses to copy the 
| A part of the object created by B() when it assigns to the reference. 
|  
| I believe that this is standard conforming, and that gcc is allowed (though 
| not required) to do so, but someone else will have to look up the respective 
| sentence of the standard. 

I believe this is a bug in GCC, according to 8.5.3/5, third
alternative in the first bullet:

  A reference to type "cv1 T1" is initialized by an expression of type
  "cv2 T2"as follows:
  -- If the initializer expression
     [...]
     -- If the initializer expression is an rvalue, with T2 a class
        type, and "cv1 T1" is reference-compatible with "cv2 T2",
        the reference is bound in one of the following ways (the
        choice is implementation-defined):
        -- The reference is bound to the object represented by the
           rvalue (see 3.10) or to a sub-object within that object.

        -- A temporary of type "cv1 T2" [sic] is created, and a
           constructor is called to copy the entire rvalue object into
           the temporary. The reference is bound to the temporary or
           to a sub-object within the temporary.93)


[that part of the standard is a clear abuse of switch-statements :-)]

The bottom line is that even if a copy occurs, the whole object is
occupied, not just the "T1" subobject.  This new GCC behaviour is just
at odd with C++ semantics from the dark ages.

-- Gaby
Comment 3 Andrew Pinski 2004-09-13 07:57:52 UTC
Confirmed, I cannot figure out when this changed but I think it was done after the tree-ssa merge.
Comment 4 Wolfgang Bangerth 2004-09-13 13:11:50 UTC
I think I remember that there was some language in the standard that 
says that the compiler _may_ invoke the copy constructor (which, btw, 
was the reason the copy constructor of the base class has to be 
accessible, one of our constant streams of bug reports). I understand 
that that is a rather vague pointer at the right place of the standard, 
I guess I will have to go look it up some time, just not today. 
 
Even then, the question whether this change is desirable is completely 
orthogonal. 
 
W. 
Comment 5 vlukas 2004-09-13 15:07:50 UTC
(In reply to comment #4) 
> I think I remember that there was some language in the standard that  
> says that the compiler _may_ invoke the copy constructor (which, btw,  
> was the reason the copy constructor of the base class has to be  
> accessible, one of our constant streams of bug reports). I understand  
> that that is a rather vague pointer at the right place of the standard,  
> I guess I will have to go look it up some time, just not today.  
 
I assume you mean 12.2 "Temporary objects". 
  
 
Comment 6 Wolfgang Bangerth 2004-09-13 15:43:20 UTC
Yes, thanks. Specifically 12.2/5 may be read as indicating that the 
compiler may create a temporary of type Base when binding a Derived 
object to reference-to-base. 
 
The case I was really thinking about, though, is 8.5.3/5. The first 
enumeration does not apply (only lvalues as initializers), but the 
second one does: 
      --  If  the  initializer  expression is an rvalue, with T2 a class 
          type, and "cv1 T1" is reference-compatible with "cv2 T2,"  the 
          reference is bound in one of the following ways (the choice is 
          implementation-defined): 
 
          --  The reference is bound to the object  represented  by  the 
              rvalue  (see  _basic.lval_) or to a sub-object within that 
              object. 
 
          --  A temporary of type  "cv1 T2"  [sic]  is  created,  and  a 
              constructor  is  called  to  copy the entire rvalue object 
              into  the  temporary.   The  reference  is  bound  to  the 
              temporary or to a sub-object within the temporary.93) 
 
          The constructor that would be used to make the copy  shall  be 
          callable whether or not the copy is actually done.  [Example: 
 
             struct A { }; 
             struct B : public A { } b; 
             extern B f(); 
             const A& rca = f();             // Either bound to the A 
sub-object of the B rvalue, 
                                             //   or the entire B object is 
copied and the reference 
                                             //   is bound to the A sub-object 
of the copy 
 
           --end example] 
 
Alas, this pretty clearly states that gcc is in error and your expectations 
are correct -- I remembered that there was implementation leeway, but 
misremembered what the implementation was allowed to actually do. 
 
W. 
Comment 7 Andrew Pinski 2004-10-15 00:44:03 UTC
I think the problem is that we are adding a cleanup_point expression 
around A const& r1 = B(); which is correct really (or is it?).
Comment 8 Andrew Pinski 2004-10-15 00:45:52 UTC
I think it is correct but we are generating a TARGET_EXPR which is wrong.
Comment 9 CVS Commits 2004-10-28 05:17:21 UTC
Subject: Bug 17435

CVSROOT:	/cvs/gcc
Module name:	gcc
Changes by:	mmitchel@gcc.gnu.org	2004-10-28 05:17:15

Modified files:
	gcc/cp         : ChangeLog parser.c call.c 
	gcc/testsuite  : ChangeLog 
	gcc/testsuite/g++.dg/template: error10.C 
Added files:
	gcc/testsuite/g++.dg/template: shift1.C 
	gcc/testsuite/g++.dg/init: ref12.C 

Log message:
	PR c++/17435
	* call.c (convert_like_real): Fix formatting.
	(initialize_reference): When binding a temporary to a base class,
	ensure that the nominal copy made is to the derived class, not the
	base class.
	
	PR c++/18140
	* parser.c (cp_parser_next_token_ends_template_argument_p): Do not
	include ">>".
	
	PR c++/17435
	* g++.dg/init/ref12.C: New test.
	
	PR c++/18140
	* g++.dg/template/shift1.C: New test.
	* g++.dg/template/error10.C: Adjust error markers.

Patches:
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/ChangeLog.diff?cvsroot=gcc&r1=1.4458&r2=1.4459
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/parser.c.diff?cvsroot=gcc&r1=1.272&r2=1.273
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/cp/call.c.diff?cvsroot=gcc&r1=1.516&r2=1.517
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/ChangeLog.diff?cvsroot=gcc&r1=1.4508&r2=1.4509
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/shift1.C.diff?cvsroot=gcc&r1=NONE&r2=1.1
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/template/error10.C.diff?cvsroot=gcc&r1=1.2&r2=1.3
http://gcc.gnu.org/cgi-bin/cvsweb.cgi/gcc/gcc/testsuite/g++.dg/init/ref12.C.diff?cvsroot=gcc&r1=NONE&r2=1.1

Comment 10 Mark Mitchell 2004-10-28 05:26:23 UTC
Fixed in GCC 4.0.