[Release b] <t4m_bug_nonexport> Thu May 13 10:44:17 WEST 2004 In the current release of tendra4minix (a), after fixing <t4m_bug_copy_func_args>, the compilation of the file nonexport.C produces this error "nonexport.C", line 15: Error: [ISO 14]: The non-exported template 'int VectorClosure < double >::exec ( Vector < double > & ) const' has not been defined. It is the error ERR_temp_decl_undef, which is being reported inside the function copy_template [instance.c]. It disappears when the function 'VectorClosure<double>::exec' is not pure (see nonexport_e.C). Before tackling this problem, let's do a small experiment. Note that in the function copy_template, the error message is preceded by the conditional 'if ( !( ds & dspec_inline ) )'. What would happen if the function 'exec' had the flag 'inline' (see nonexport_i.C)? Although unusual, the compiler should accept it without complaints. The error message above is not produced, but instead it generates the message "nonexport_i.C", line 15: Error: [ISO 3.2]: The 'inline' function 'int VectorClosure < double >::exec ( Vector < double > & ) const' has been used but not defined. This is the error ERR_basic_odr_inline and it is being reported in the function check_usage [variable.c]. The compiler should ignore the flag 'inline' in a pure virtual function, so this is another bug that must be fixed, but since both problems are closely related, I am not going to classify it as a different bug. I have been making a detailed call trace of the instantiation of the templates, and I have seen two things: - [nonexport.C] The function rescan_member [copy.c] inserts the pure virtual function 'VectorClosure<double>::exec' into the list pending_templates (a list of template instances to be generated). This is done by calling define_template [instance.c]. Later on, the function copy_template [instance.c] finds that the definition of 'exec' is NULL, as it should be, and after a couple of tries the error is reported. - [nonexport_i.C] The function rescan_member [copy.c] marks the pure virtual function 'VectorClosure<double>::exec' with the dspec_used flag, which indicates that the function has been used in the current compilation unit. The function check_usage [variable.c] assumes that a virtual function can not be marked as having been used, and produces this spurious error. The things that rescan_member [copy.c] does are contradictory to the way virtual functions must be processed, according to the behaviour of the function use_func_id [identifier.c], and also according to the comment at the beginning of the function reuse_id [identifier.c]: "Note that use_func_id does not mark virtual functions as having been used because the actual function called can only be determined at run-time." This is also the reason why all virtual functions in a class template instance need to be defined: they are necessary to fill the virtual table; individual virtual functions are not considered. In summary, virtual functions are never marked as used, and pure virtual functions don't get inserted into the pending_templates list. To fix these problems, we have to make these changes to the function rescan_member [copy.c], - [nonexport.C] Avoid calling define_template [instance.c] on an instance of a member function whenever its identifier has the dspec_virtual and dspec_pure flags activated. Change this conditional lid = find_copied ( pid, id, 1 ) ; if ( !EQ_id ( lid, id ) ) { define_template ( lid, 0 ) ; id = lid ; ds = DEREF_dspec ( id_storage ( id ) ) ; } for lid = find_copied ( pid, id, 1 ) ; if ( !EQ_id ( lid, id ) ) { id = lid ; if ( !( ds & dspec_virtual && ds & dspec_pure ) ) { define_template ( id, 0 ) ; } ds = DEREF_dspec ( id_storage ( id ) ) ; } Optionally, we can also do this in the case of template (member) functions. They cannot be virtual, but I am not sure member function instances don't reach this part of the code. In any case it won't hurt. Change id = apply_template ( tid, args, 0, 0 ) ; define_template ( id, 0 ) ; ds = DEREF_dspec ( id_storage ( id ) ) ; ns = NULL_nspace ; for id = apply_template ( tid, args, 0, 0 ) ; if ( !( ds & dspec_virtual && ds & dspec_pure ) ) { define_template ( id, 0 ) ; } ds = DEREF_dspec ( id_storage ( id ) ) ; ns = NULL_nspace ; - [nonexport_i.C] Avoid marking as used an identifier whenever it has the dspec_virtual flag activated. Change /* Mark as used */ ds |= dspec_used ; COPY_dspec ( id_storage ( id ), ds ) ; lid = DEREF_id ( id_alias ( id ) ) ; if ( !EQ_id ( lid, id ) ) { ds = DEREF_dspec ( id_storage ( lid ) ) ; ds |= dspec_used ; COPY_dspec ( id_storage ( lid ), ds ) ; } for /* Mark as used */ if ( !( ds & dspec_virtual ) ) ds |= dspec_used ; COPY_dspec ( id_storage ( id ), ds ) ; lid = DEREF_id ( id_alias ( id ) ) ; if ( !EQ_id ( lid, id ) ) { ds = DEREF_dspec ( id_storage ( lid ) ) ; if ( !( ds & dspec_virtual ) ) ds |= dspec_used ; COPY_dspec ( id_storage ( lid ), ds ) ; } PS. See also <t4m_bug_usage> for more changes to rescan_member concerning suppress_usage [identifier.c]. Notes. [ ] What happens to use_func_id [identifier.c]? It has this piece of code, /* Define template function */ if ( !( ds & dspec_virtual ) ) { define_template ( id, 0 ) ; ds = DEREF_dspec ( id_storage ( id ) ) ; } ¿Should we use this condition instead of '!( ds & dspec_virtual && ds & dspec_pure )'? The function use_func_id is used mostly inside template declarations (see <t4m_bug_func_id>). During instantiation, it is rescan_member [copy.c] that is used for defining template functions.