
[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.