SourceForge.net Logo

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