
[Release b]
<t4m_bug_vt>
Mon Jul 12 18:03:27 WEST 2004
In the current release of tendra4minix (a), after fixing <t4m_bug_usage>,
the program vt.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.
The phenomenon is subtle, since it disappears if we drop a single function
in any of the template classes (constructor o destructor). It also disappears
when a declaration of another virtual function is placed before the declaration
of Alloc destructor,
export template <class T>
class Alloc {
public:
Alloc() {}
virtual T *get();
virtual ~Alloc();
};
This bug does not show up with non-template classes.
The function compile_virtual [../output/compile.c] decides whether to declare
or define (externally or locally) virtual tables in the current compilation
unit. And it finds that the virtual destructor 'Alloc<Thread>::~Alloc()' is
defined, so it generates an externally declared virtual table for class
'Alloc<Thread>'.
The origin of the problem is in an out-of-order invocation of function
define_template [instance.c]. This function is used to mark a template instance
for being instantiated later on during the calls to clear_templates
[instance.c]; this is done by inserting the template instance in a list named
'pending_templates', and the template instance is marked with the 'dspec_defn'
flag without checking whether it's really defined. The second action is
not a bug. The function copy_template [instance.c], which is in charge of
instantiating the template body, should discover that a template is not
defined in the current translation unit and then clear the 'dspec_defn' flag.
There is an spurious call to define_template in function reuse_id
[identifier.c], after all templates have been emitted,
compile_pending
...
compile_function, id = GCAlloc<Thread>::GCAlloc(), force = 1
make_tagdef, id = GCAlloc<Thread>::GCAlloc()
enc_func_defn, id = GCAlloc<Thread>::GCAlloc()
except_postlude, id = GCAlloc<Thread>::GCAlloc()
make_constr, fn = GCAlloc<Thread>::GCAlloc(), n = m = DEFAULT_DESTR
init_empty_base, gr = GCAlloc<Thread>::Alloc<Thread>
init_default, t = Alloc<Thread>
call_constr, id = Alloc<Thread>::~Alloc()
apply_func_id, id = Alloc<Thread>::~Alloc()
reuse_id, id = Alloc<Thread>::~Alloc(), suppress = 0
define_template, id = Alloc<Thread>::~Alloc(), expl = 0
define_template: returning
reuse_id: returning
apply_func_id: returning
call_constr: returning
init_default: returning
init_empty_base: returning
make_constr: returning
except_postlude: returning
enc_func_defn: returning
make_tagdef: returning
compile_function: returning
...
compile_pending: returning
Function compile_pending [compile.c] is invoked from process_files [main.c],
after the last call to clear_templates [instance.c] (that is, templ = 2) is
made, and just before the emission of the TDF capsule.
Possible solution: add a flag 'all_templates_cleared' to instance.c indicating
that clear_templates(2) has already been called; define_template calls
'copy_template (id, 1);' if all_templates_cleared is not zero. The flag, which
can be local to instance.c, gets activated just before returning from
clear_templates when templ = 2 (end of compilation unit),
/* this flag is for avoiding marking with dspec_defn template instances that
are not really defined, during late calls to function define_template */
static int all_templates_cleared = 0 ;
At the end of function clear_templates [instance.c],
if ( templ == 2 ) {
all_templates_cleared = 1 ;
}
return ;
In function define_template [instance.c], call immediately copy_template for
having either the instance generated or the dspec_defn flag cleared again,
/* 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 ) ;
}
}
Observe that calling copy_template here is supposed to be all right, since
function define_template already has a call to copy_templates a few lines
below.
PS. This solution has not been adopted because of the appearance of a related
bug (<t4m_bug_vt_dtor>). Instead, I have used a (possibly better) solution to
both bugs. See <t4m_bug_vt_dtor>.
Notes.
[ ] DECL_SPEC of Alloc<Thread>::~Alloc before spurious definition takes place,
33554432 = 0x2000000 = dspec_main
16777216 = 0x1000000 = dspec_instance
4194304 = 0x0400000 = dspec_ignore
2097152 = 0x0200000 = dspec_cpp
65536 = 0x0010000 = dspec_public
32768 = 0x0008000 = dspec_typedef
4096 = 0x0001000 = dspec_virtual
128 = 0x0000080 = dspec_extern
--------------------------------------
56725632 = 0x3619080