[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