[gnu.g++.bug] Bug giving peculiar visibility of function

jj@idris.id.dk (Jesper Joergensen [ris]) (12/09/89)

Hi again,

I'm sorry if this bug report seems a bit large, but don't worry!!
I've thoroughly isolated the BUGs as best as I can and included some
SIMPLE explanatory examples, which takes up some space, but should
otherwise be quite clear and save you some troubles and tests. Hope it
helps ...

Contents:
  1: Description of a bug that gives a function peculiar visibility outside
     the module in which it is declared. Source and assembler output
     for two examples are included.
  2: Description of a bug concerning the use of 'operator=' in derived classes.
     Source and execution output included for two examples.

Please notify me when you have received this letter, so that I can
cancel them from my 'pending bug reports' list. We've been having some
trouble with the local networks lately, so I would like to know if I
can reach you (and the rest of the world) at all. I hope this isn't too
much inconvenience.

All examples are run with the '-v' compiler option so that you can see
what happens and identify the compiler and environment. The only
specialty is that my '/usr/local/lib/gcc-as' is a link to
'/usr/local/bin/gas' so I'm using the GNU assembler (version 1.34, 
though I don't believe the assembler is 'guilty' in this case, but now
you know).


1: Function visibility
======================

Below is a sample session collected by GNU emacs in shell mode, with
source and assembler output included by  'cat'. Here we go:

*** SAMPLE SESSION 1a START ***
% pwd
/usr/users/jj/GNUmail/g++errs
% cat testlink.cc
class Some {
  int member ;
public:
  int change(int) ;
} ;

int Some::change(int nember) {
  int tember = member ;
  member = nember ;
  return tember ;
}

class Some_other {
  int member ;
public:
  const int change(int nember) ;
} ;

int Some_other::change(int nember) {
  int tember = member ;
  member = nember ;
  return tember ;
}

class Some_other_again {
  int member ;
public:
  const int change(int nember) ;
} ;

const int Some_other_again::change(int nember) {
  int tember = member ;
  member = nember ;
  return tember ;
}

% g++ -v testlink.cc -O -S
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__ testlink.cc /usr/tmp/cc007031.cpp
GNU CPP version 1.36
 /usr/local/lib/gcc-cc1plus /usr/tmp/cc007031.cpp -quiet -dumpbase testlink.cc -O -version -o testlink.s
GNU C++ version 1.36.1 (based on GCC 1.36) (vax) compiled by GNU C version 1.36.
default target switches: -munix
% cat testlink.s
#NO_APP
gcc_compiled.:
.text
	.align 1
.globl _change__4Somei
_change__4Somei:
	.word 0x0
	movl 4(ap),r1
	movl (r1),r0
	movl 8(ap),(r1)
	ret
	.align 1
.globl _change__10Some_otheri
_change__10Some_otheri:
	.word 0x0
	movl 4(ap),r1
	movl (r1),r0
	movl 8(ap),(r1)
	ret
	.align 1
_change__16Some_other_againi:
	.word 0x0
	movl 4(ap),r1
	movl (r1),r0
	movl 8(ap),(r1)
	ret
**** SAMPLE SESSION 1a END ****

Don't worry about the functionality of the assembler code, it's
perfectly OK !!!

The primary problem is that the third version of the 'change' function
(in class Some_other_again) doesn't get a  '.globl'  statement for its
name in the assembler output. The only difference between this and the
preceeding (in class Some_other) is that the DEFinition of the
function includes  'const'.

The secondary problem is that the compiler doesn't complain about the
difference between the DECLaration (within the class) and DEFinition
(outside the class) of the second version of 'change' for the class
'Some_other'. The DECLaration includes 'const', while the DEFinition
doesn't, but the compiler seems to forget this. NOTE: I've included
this problem here because I think it might be correlated to the
primary one, it was just a hunch I got.

I've checked the  ChangeLog  for version 1.36.1 which says:
*** QUOTE BEGIN ***
Mon Nov  6 00:51:43 1989  Michael Tiemann  (tiemann at arkesden)

	:
	Some other stuff deleted.
	:

	* cplus-decl2.c (grok_method_quals): New function.  Incorporate
	`const' and `volatile' qualifiers into FUNCTION_DECLs and
	TYPE_DECLs.  Uses code broken out of `grokclassfn'.
	* cplus-decl2.c (grokclassfn): Caller changed.
	* cplus-decl.c (grokdeclarator): Call `grok_method_quals' when
	QUALS is non-zero in declarator grokking loop.
**** QUOTE END ****
is it possible that this change is causing the error, so that a
'const' is interpreted as an 'extern' DECLaration ?????????????

Below is an example causing exactly the same problem, but without the
use of classes and members:

*** SAMPLE SESSION 1b START ***
% pwd
/usr/users/jj/GNUmail/g++errs
% cat testfunc.cc
int change1(int nember) {
  return nember ;
}

const int change2(int nember) {
  return nember ;
}
% g++ -v testfunc.cc -O -S
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__ testfunc.cc /usr/tmp/cc007056.cpp
GNU CPP version 1.36
 /usr/local/lib/gcc-cc1plus /usr/tmp/cc007056.cpp -quiet -dumpbase testfunc.cc -O -version -o testfunc.s
GNU C++ version 1.36.1 (based on GCC 1.36) (vax) compiled by GNU C version 1.36.
default target switches: -munix
% cat testfunc.s
#NO_APP
gcc_compiled.:
.text
	.align 1
.globl _change1__Fi
_change1__Fi:
	.word 0x0
	movl 4(ap),r0
	ret
	.align 1
_change2__Fi:
	.word 0x0
	movl 4(ap),r0
	ret
**** SAMPLE SESSION 1b END ****



2: Bug in derived class' use of assignment
==========================================

Below is a sample program which illustrates the missing import of the
assignment member function 'operator=' from the base to the derived
class. The base class assignment operator changes the sign of the data
member as a side effect, so that its invocation can be detected.

*** SAMPLE SESSION 2a START ***
% pwd
/usr/users/jj/GNUmail/g++errs
% 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(23) ;

  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/cc007100.cpp
GNU CPP version 1.36
 /usr/local/lib/gcc-cc1plus /usr/tmp/cc007100.cpp -quiet -dumpbase testbase.cc -O -version -o /usr/tmp/cc007100.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/cc007100.s
 /usr/local/lib/gcc-ld -o testbase /lib/crt0.o testbase.o -lg++ /usr/local/lib/gcc-gnulib -lc
% testbase
member == 23
member == 23
member == -23
member == 23
**** SAMPLE SESSION 2a END ****

Notice that the base class of the derived is 'public', so there is a
conversion from the derived objects 'dpub' to the base class argument
of 'Base::operator=', and the latter is a public member of the base
class and should therefore be imported by the derived. As you can see
from the output it obviously doesn't; bitwise copy is used instead.

Now follows another example, where the derived class has a data member
of its own, carefully observe what happens:

*** SAMPLE SESSION 2b START ***
% pwd
/usr/users/jj/GNUmail/g++errs
% cat testbase2.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 {
  int member2 ;
public:
  Dpub(int x, int y) : (x) {
    member2 = y ;
  }
  void print_member() {
    Base::print_member() ;
    printf ("member2 == %d\n", member2) ;
  }
} ;

int main() {
  Base b(23) ;
  Dpub dpub(23,24) ;

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

  b = b ;
  dpub = dpub ;

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

  return 0 ;
}
% g++ -v testbase2.cc -O -o testbase2
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__ testbase2.cc /usr/tmp/cc007123.cpp
GNU CPP version 1.36
 /usr/local/lib/gcc-cc1plus /usr/tmp/cc007123.cpp -quiet -dumpbase testbase2.cc -O -version -o /usr/tmp/cc007123.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 testbase2.o /usr/tmp/cc007123.s
 /usr/local/lib/gcc-ld -o testbase2 /lib/crt0.o testbase2.o -lg++ /usr/local/lib/gcc-gnulib -lc
% testbase2
member == 23
member == 23
member2 == 24
member == -23
member == -23
member2 == 24
**** SAMPLE PROGRAM 2b END ****

Now the assignment operator IS used for the base part of the derived
object. The reason for this very peculiar behavior is totally beyond
my wildest imaginations, I haven't got the faintest idea of what might
be causing this. Sorry, but I can't isolate the bug further.


That's all i had to say this time. Thanks for your attention and an
otherwise very useful compiler. I hope you'll respond as soon as
possible so that I know when you've received this bug report.

So long ...

	Jesper Jorgensen (known as 'JJ the famous')

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