SourceForge.net Logo

[Release b]
<t4m_bug_vt_dtor>
Mon Aug  2 17:42:19 WEST 2004

In the current release of tendra4minix (a), after fixing
<t4m_bug_pure_virtual>, the program vt_dtor.C makes TenDRA emit the definition
of the virtual table for class Alloc<Thread>, thus violating the ARM rule for
defining virtual tables (this is done in the module containing the definition
of the first non-implicit, non-inline, non-pure virtual function declared in
the class). This rule is not contained in the spec X3J16/96-0225 WG21/N1043
(as far as I know), but the compiler is supposed to follow it. It is a nasty
bug that causes duplicate definitions of virtual tables.

If we place another virtual function before the declaration of Alloc
destructor,

export template <class N>
class Alloc {
public:
  virtual N *get();
  virtual ~Alloc();
};

then the bug disappears. It only happens with destructors, never with ordinary
virtual member functions. This bug is clearly related to the (supposedly fixed)
<t4m_bug_vt>.

In fact, this bug is more or less the same bug as <t4m_bug_vt>, that we have
fixed only partially. The problem is again the function define_template
[instance.c], which marks a template instance Alloc<Thread>::~Alloc() as being
defined in the current compilation unit (dspec_defn flag), without knowing if
it is really defined. The function in charge of clearing this spurious flag is
copy_template [instance.c]. What is happening is that compile_virtual
[output/compile.c] is being called before the invocation of copy_template
[instance.c] takes place (this invocation is made because the compiler is
emitting the virtual table for class ThreadAlloc, and checks if ThreadAlloc has
any base class whose virtual table needs to be emitted, but all this is ok).
compile_virtual [output/compile.c] thinks that the destructor is defined, and
so it is the first non-implicit, non-inline, non-pure virtual function that is
declared and defined in this compilation unit, and this means it must emit the
virtual table for Alloc<Thread> in this compilation unit.

The bug disappears when we add the virtual function Alloc<N>::get because this
function is never used, and so define_template does not get called on it.

In summary, the problem is the stupid function define_template [instance.c].
Since my patience is exhausting, the only fix I can propose now is to
adapt the solution to bug <t4m_bug_vt> in this way: inside define_template,
call copy_template unconditionally in the case of functions, that is, we change

		/* Template functions */
		if ( !( ds & dspec_defn ) ) {
		    CONS_id ( id, pending_templates, pending_templates ) ;
		    COPY_dspec ( id_storage ( id ), ( ds | dspec_defn ) ) ;
		    COPY_loc ( id_loc ( id ), crt_loc ) ;
		    if ( all_templates_cleared ) {
			copy_template ( id, 1 ) ;
		    }
		}
for
		/* Template functions */
		if ( !( ds & dspec_defn ) ) {
		    CONS_id ( id, pending_templates, pending_templates ) ;
		    COPY_dspec ( id_storage ( id ), ( ds | dspec_defn ) ) ;
		    COPY_loc ( id_loc ( id ), crt_loc ) ;
		    copy_template ( id, all_templates_cleared ) ;
		}

This fixes the bug, but I warn that I am not sure about its consequences.

PS. As I suspected, this breaks the compilation of S-Lisp in several places,
so I have undone this wrong solution. I have also undone the solution to
<t4m_bug_vt>, and now I propose this simpler solution to both bugs: in the
function compile_virtual [output/compile.c], use the field id_function_etc_defn
instead of the dspec_defn flag to check whether a function is defined in the
current compilation unit, change line 1579

 			if ( ds & dspec_defn ) {

for

 			EXP e = DEREF_exp ( id_function_etc_defn ( fn ) ) ;
 			if ( !IS_NULL_exp ( e ) ) {

This seems to fix both bugs and, until now, it hasn't produced any other
consequence.