Bug 56559 - g++ virtual inheritance bug for temporary object
g++ virtual inheritance bug for temporary object
Status: CLOSED CURRENTRELEASE
Product: Red Hat Linux
Classification: Retired
Component: gcc (Show other bugs)
7.2
i686 Linux
medium Severity high
: ---
: ---
Assigned To: Jakub Jelinek
Brian Brock
:
Depends On:
Blocks:
  Show dependency treegraph
 
Reported: 2001-11-20 19:09 EST by Michel Eyckmans (MCE)
Modified: 2007-04-18 12:38 EDT (History)
0 users

See Also:
Fixed In Version:
Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of:
Environment:
Last Closed: 2002-12-15 13:17:46 EST
Type: ---
Regression: ---
Mount Type: ---
Documentation: ---
CRM:
Verified Versions:
Category: ---
oVirt Team: ---
RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: ---


Attachments (Terms of Use)

  None (edit)
Description Michel Eyckmans (MCE) 2001-11-20 19:09:43 EST
From Bugzilla Helper:
User-Agent: Mozilla/4.78 [en] (X11; U; HP-UX B.10.20 9000/871)

Description of problem:
Cfr. the comments in the demonstrator. 

The problem does not occur with g++ 2.95.x. 
I don't have any of the 3.x versions to test with.

Version-Release number of selected component (if applicable):


How reproducible:
Always

Steps to Reproduce:
1. Save the example as file.c.
2. g++ file.c
3. a.out
4. Study the output and compare with the source.

Demonstrator code follows:

#include <stdlib.h>
#include <iostream.h>

void do_free (void* p)
{
  cerr << "   freeing " << p << endl;
  // free (p);
}

class A
{
 public:

  friend class B;

  A();
  A& operator=(const A&);
  B operator+(const A&);
  ~A();

 protected:

  A(int) { /* do nothing */ };
  A(const A&);
  void* buf;
};

class B : public virtual A  // removing 'virtual' makes the problem go away
{
 public:

  B(A& a);
};

A::A()
{
  buf = malloc(1);
  cerr << "A  " << this << " (" << buf << ")" << endl;
}

A::A(const A& a)
{
  buf = malloc(1);
  cerr << "Ac " << this << " (" << buf << ")" << endl;
}

A::~A()
{
  cerr << "~  " << this << " (" << buf << ")" << endl;
  do_free(buf);
}

B A::operator+(const A& a)
{
  cerr << "+  " << this << ", " << &a << endl;
  A c(a);   // create a temporary to (pretend to) "work with"
  return c;
}

A& A::operator=(const A& a)
{
  cerr << "=  " << this << ", " << &a << endl;
  return *this;
}

B::B(A& a) : A(0)
{
  cerr << "B  " << this << endl;
  cerr << "Ba " << (A*) this << endl;

  // Transfer ownership of the buffer from the argument to ourselves.
  buf = a.buf;
  a.buf = 0;
}
int main()
{
  cerr << "----------" << endl;
  A a1, a2, a3;
  cerr << "----------" << endl;

  //
  //  When operator+() is called for the second time in the
  //  expression below, it is given a bogus 'this' argument,
  //  instead of the temporary object that resulted from the
  //  first call. If do_free() actually calls free(), this
  //  results in the same buffer area being freed twice.
  //
  a1 = a1 + a2 + a3;

  cerr << "----------" << endl;

  return 0;
}



Actual Results: Here's what I get (with some comments added):

----------
A  0xbffff6a0 (0x804a2e0)
A  0xbffff690 (0x804a2f0)
A  0xbffff680 (0x804a300)
----------
+  0xbffff6a0, 0xbffff690
Ac 0xbffff5f0 (0x804a310)
B  0xbffff660
Ba 0xbffff664
~  0xbffff5f0 ((nil))
   freeing (nil)
+  0xbffff654, 0xbffff680  <- first argument should be 0xbffff664!
Ac 0xbffff600 (0x804a320)
B  0xbffff670
Ba 0xbffff674
~  0xbffff600 ((nil))
   freeing (nil)
=  0xbffff6a0, 0xbffff664
~  0xbffff674 (0x804a320)
   freeing 0x804a320       <-- a duplicate free() would result!
~  0xbffff664 (0x804a320)    /
   freeing 0x804a320       <'
----------
~  0xbffff680 (0x804a300)
   freeing 0x804a300
~  0xbffff690 (0x804a2f0)
   freeing 0x804a2f0
~  0xbffff6a0 (0x804a2e0)
   freeing 0x804a2e0


Expected Results:  This is what I get when using non-virtual inheritance.
The results for virtual inheritance should be similar.

----------
A  0xbffff6a0 (0x804a2a0)
A  0xbffff690 (0x804a2b0)
A  0xbffff680 (0x804a2c0)
----------
+  0xbffff6a0, 0xbffff690
Ac 0xbffff600 (0x804a2d0)
B  0xbffff660
Ba 0xbffff660
~  0xbffff600 ((nil))
   freeing (nil)
+  0xbffff660, 0xbffff680  <-- OK, we get the correct first argument!
Ac 0xbffff610 (0x804a2e0)
B  0xbffff670
Ba 0xbffff670
~  0xbffff610 ((nil))
   freeing (nil)
=  0xbffff6a0, 0xbffff670
~  0xbffff670 (0x804a2e0)
   freeing 0x804a2e0       <-- OK, nothing can go wrong here
~  0xbffff660 (0x804a2d0)    /
   freeing 0x804a2d0       <.
----------
~  0xbffff680 (0x804a2c0)
   freeing 0x804a2c0
~  0xbffff690 (0x804a2b0)
   freeing 0x804a2b0
~  0xbffff6a0 (0x804a2a0)
   freeing 0x804a2a0


Additional info:
Comment 1 Alan Cox 2002-12-15 13:17:46 EST
Ok with g++ 3.2

Note You need to log in before you can comment on or make changes to this bug.