jj@idris.id.dk (Jesper Joergensen [ris]) (01/17/90)
ATTN: Michael Tiemann I've just come accross another BUG, which bites in the destructor for a class derived from an abstract base class with a pure virtual destructor. The destructors for the other base classes, however, seems to be called quite normally before the program fails. The below sample session contains a complete executable example with trace output on the cout ostream: ***** SAMPLE SESSION START ***** % pwd /usr/users/jj/GNUmail/g++errs % cat virtdtortest.cc #include <stream.h> class Abstract { public: virtual ~Abstract() = 0 ; } ; class Base { int *member ; public: Base() { member = new int ; *member = 0 ; (cout << "Base::Base() executed\n").flush() ; } Base(int member1) { member = new int ; *member = member1 ; (cout << "Base::Base(int) executed\n").flush() ; } ~Base() { delete member ; (cout << "Base::~Base() executed\n").flush() ; } } ; class Derived : public Abstract, public Base { public: Derived() { (cout << "Derived::Derived() executed\n").flush() ; } Derived(int x) : Base(x) { (cout << "Derived::Derived(int) executed\n").flush() ; } ~Derived() { (cout << "Derived::~Derived() executed\n").flush() ; } } ; int main(int argc, const char *argv) { (cout << "Derived d ;\n").flush() ; Derived d ; (cout << "Derived d1 = d ;\n").flush() ; Derived d1 = d ; (cout << "Derived d2 = 12 ;\n").flush() ; Derived d2 = 12 ; (cout << "Abstract *aptr = new Derived(12) ;\n").flush() ; Abstract *aptr = new Derived(12) ; (cout << "delete aptr ;\n").flush() ; delete aptr ; (cout << "return 0 ;\n").flush() ; return 0 ; } % g++ -v virtdtortest.cc -o virtdtortest g++ version 1.36.3- (based on GCC 1.36) /usr/local/lib/gcc-cpp -+ -v -undef -D__GNUC__ -D__GNUG__ -D__cplusplus -Dvax -Dunix -D__vax__ -D__unix__ virtdtortest.cc /usr/tmp/cc018935.cpp GNU CPP version 1.36 /usr/local/lib/gcc-cc1plus /usr/tmp/cc018935.cpp -quiet -dumpbase virtdtortest.cc -version -o /usr/tmp/cc018935.s GNU C++ version 1.36.3- (based on GCC 1.36) (vax) compiled by GNU C version 1.36. default target switches: -munix /usr/local/lib/gcc-as -o virtdtortest.o /usr/tmp/cc018935.s /usr/local/lib/gcc-ld -o virtdtortest /lib/crt0.o virtdtortest.o -lg++ /usr/local/lib/gcc-gnulib -lc % virtdtortest Derived d ; Base::Base() executed Derived::Derived() executed Derived d1 = d ; Derived d2 = 12 ; Base::Base(int) executed Derived::Derived(int) executed Abstract *aptr = new Derived(12) ; Base::Base(int) executed Derived::Derived(int) executed delete aptr ; Derived::~Derived() executed Base::~Base() executed Illegal instruction (core dumped) ****** SAMPLE SESSION END ****** I have analyzed the assembler code generated for the failing destructor, which is contained in the above example in Derived::~Derived(). After doing the body of Derived::~Derived() itself and called Base::~Base() then some condition, which is always true, causes a call to abort(), hence the core is dumped. The condition, as far as I can decifer the code, simply says: if (this != 0) abort() ; and this will never be 0 within a destructor, so we'll always get an abort. Having looked at the virtual function table for the abstract base class Abstract, which contains abort instead of the destructor that doesn't exist, I assume that this illegal-destructor-call-check is expanded unconditionally into the destructor for the derived class. The idea of having abort in the virtual function table is a simple and ellegant runtime check, but it should never go into the destructor for the derived class. It seems to me that something has been forgotten here, another of those special cases (dealing with those destructors in a compiler, must be a pain in the ... hhrrmm, sorry ... output stream :^). Hope my explanation will help you on the right track to the error faster. Please resond, thanks in advance Jesper Jorgensen jj@idris.id.dk Research associate Department of Computer Science Technical University of Denmark DK-2800 Lyngby DENMARK PS: I should never have praised the new version of the compiler, now the BUGs seems to be queuing to bite (good old Murphy :^).