[gnu.g++.bug] IMPORTANT: BUG LOCATED in compiler source !!!!

jj@idris.id.dk (Jesper Joergensen [ris]) (01/04/90)

ATTN: Michael D. Tiemann

Hope you've had a merry christmas and a happy new year. I'm sorry to have
to pull you back into the painful realities of life, however, I'VE FINALLY
DEBUGGED MY WAY TO THE POINT IN THE COMPILER SOURCE that causes a bug that
I reported a month ago, so I CONSIDER IT TO BE VERY IMPORTANT.

I'm running:	Ultrix V2.2-1 Worksystem V1.1 System #2
on a:		DEC VAXstation 2000
Compiler:	GNU C++ version 1.36.1
Configured for:	vax running BSD, which is appropriate for Ultrix

Before pointing at specific places in the source code of the compiler, I'll
just summarize the problem by reposting my sample session involving a simple
class derivation. Here goes:

====== SAMPLE SESSION START ======
% pwd
/usr/users/jj/GNUinstall
% cat testbase.cc
extern "C" void printf (char *, ...) ;

class Base {
  int member ;
public:
  Base(const Base& x) {
    member = x.member ;
  }
  Base(int x) {
    member = x ;
  }
  void print_member() {
    printf ("member == %d\n", member) ;
  }
  const Base operator=(const Base& x) {
    member = - x.member ;
    return *this ;
  }
} ;

class Dpub : public Base {
public:
  Dpub(int x) : (x) { }
} ;

int main() {
  Base b(23) ;
  Dpub dpub(46) ;

  b.print_member() ;
  dpub.print_member() ;

  b = b ;
  dpub = dpub ;

  b.print_member() ;
  dpub.print_member() ;

  return 0 ;
}
% g++ -v testbase.cc -O -o testbase
gcc version 1.36.1 (based on GCC 1.36)
 /usr/local/lib/gcc-cpp -+ -v -undef -D__GNUC__ -D__GNUG__ -D__cplusplus -Dvax -Dunix -D__vax__ -D__unix__ -D__OPTIMIZE__ testbase.cc /usr/tmp/cc001292.cpp
GNU CPP version 1.36
 /usr/local/lib/gcc-cc1plus /usr/tmp/cc001292.cpp -quiet -dumpbase testbase.cc -O -version -o /usr/tmp/cc001292.s
GNU C++ version 1.36.1 (based on GCC 1.36) (vax) compiled by GNU C version 1.36.
default target switches: -munix
 /usr/local/lib/gcc-as -o testbase.o /usr/tmp/cc001292.s
 /usr/local/lib/gcc-ld -o testbase /lib/crt0.o testbase.o -lg++ /usr/local/lib/gcc-gnulib -lc
% testbase
member == 23
member == 46
member == -23
member == 46
======= SAMPLE SESSION END =======

The assignment operator of class `Base' has a side effect so that its
invocation can be seen. The `Base' and the `Dpub' objects are initialized to
different integer values, so that they are easy to distinguish.

DON'T MODIFY ANYTHING IF YOU TRY TO RUN THE SAMPLE YOURSELF, IT'S VERY TRICKY.

The problem is, as you can easily see, that the special assignment for the
`Base' class has no effect upon the the assignment of the `Dpub' object.
This is in violation of the language rule, which recursively defines the
semantics of assignment to be memberwise assignment (including the hidden
base class member).

The funny thing is that as soon as you add a member to the derived class `Dpub'
the problem goes away. However, I have located the exact spot in the code
that causes this peculiar behavior. Read on ...


In the file `cplus-typeck.c' there is a function called `build_modify_expr',
whose function I don't think I need to explain to you. Lets take a look at
the introductory comment
4530:  /* C++: The semantics of C++ differ from those of C when an
4531:     assignment of an aggregate is desired.  Assignment in C++ is
4532:     now defined as memberwise assignment of non-static members
4533:     and base class objects.  This rule applies recursively
4534:     until a member of a built-in type is found.
4535:
4536:     .... unimportant stuff .... */

After this follows a lot of code testing whether it's a real assignment of a
record type, and `TYPE_GETS_ASSIGNMENT' from one of its sub-components.
If this is the case a loop is started as follows:
4551:      while (TREE_GETS_ASSIGNMENT (lhstype))

In this loop baseclasses are tried first and if they are identical for both the
left and the right hand side they are copied to variables `base_lhs' and
`base_rhs'. Something else happens if the baseclasses are not identical, but
that is irrelevant to this case. (So far it sounds reasonable to me, though I
don't know exactly what is going on).

Right after this two way branch the following code appears:
4585:	      if (! TYPE_FIELDS (lhstype))
4586:		{
4587:		  lhs = base_lhs;
4588:		  newrhs = base_rhs;
4589:		  lhstype = TREE_TYPE (lhs);
4590:		  continue;
4591:		}
4592:	      result = build_modify_expr (base_lhs, modifycode, base_rhs);

THIS IS WHERE THINGS GO WRONG !!!! (I've debugged it)

If TYPE_FIELDS(lhstype) is `null' then some rearrangement is performed and the
loop `continued' by the statement in line 4590. This action skips the call of
`build_modify_expr' in line 4592, which is responsible for making the CORRECT
assignment for the base class.

In the example from above debugging shows that TYPE_FIELDS(lhstype) is `null'
(there are no fields in the derived class), so the CORRECT assignment of the
base portion is not performed by line 4592. But if the derived class has a
data member, then the if statement is skipped and line 4592 creates the
CORRECT assignment for the base component.

I don't know why you test `lhstype' for having no fields, shouldn't it have
been `base_lhs' instead ?????

I've now described the bug as thoroughly as I can, the rest of the details
are not obvious to me. But it seems that that particular piece of code needs
some revision.

If the correction is a simple `typo', then please notify me directly, so
that I can correct it myself. This error is simply driving me nuts, and I
don't think I can wait until the next release (when is it due by the way ?).

If you can't use reply, you can reach me at the following addresses:
	jj@idris.id.dk
	jj@iddth.id.dk
	jj@idau1v.id.dk
	jj@idau2v.id.dk
preferably in the above order of precedence.


Thanks for your attention and an otherwise very good compiler


	Jesper Jorgensen	jj@idris.id.dk

	Research associate
	Department of Computer Science
	Technical University of Denmark
	DK-2800 Lyngby
	DENMARK