SourceForge.net Logo

[Release b]
<t4m_bug_applyfunctempl>
Thu May 13 21:03:13 WEST 2004

In the current release of tendra4minix (a), after fixing <t4m_bug_bfprintf>,
the program lshiftover.C (using Vector-0.0.1) produces this output,

      6.366e-314     5.88557e-310     4.24399e-314 
      6.366e-314     5.89066e-310     4.24399e-314 
      6.366e-314     6.12408e-310      4.44892e-86 

while it should give

               1                1                1 
               0                1                1 
               0                0                1 

After a detailed trace of the execution of the compiler, I have found that the
problem is in function apply_func_templ [template.c]. This function is used
for applying a function template to a list of template arguments; it's called
from apply_template [template.c], this one is called from rescan_member
[copy.c], called from copy_exp [copy.c], and this one called from copy_object
[copy.c], the function used for instantiating templates.

The error happens when instantiating 'template <class T> Matrix<T>::write'
with [T := double] at line 16 of lshiftover.C. This template function contains
a call to operator<< which has been correctly resolved to 'operator<< <T>
(STIOform &, const Vector<T> &)' during the first pass over the template
definition. Now, rescan_member is being called to instantiate the identifier of
the function call, that is, to get 'operator<< <double> (STIOform &, const
Vector<double> &)'.

In TenDRA, identifiers representing overloaded functions are kept linked
together through the 'id_function_etc_over' field of union IDENTIFIER (see
src/producers/common/c_class.alg), in reverse order as they appear in the
program source. If id = 'template <class T> operator<< (STIOform &, const
Vector<T> &)', then we have that id_function_etc_over(id) == 'template <class
Real> operator<< (STIOform &, const Punto3D<Real> &)', and then
id_function_etc_over(id_function_etc_over(id)) == 'NULL_id'. The function
apply_func_templ instantiates every identifier in this list beginning with
the given identifier, but while doing this, it also reverses the list of
instances generated, and finally it returns the head of this list, which
happens to be 'operator<< <double> (STIOform &, const Punto3D<double> &)'. The
wrong numbers in the program output above are just the three coordinates of a
nonexistent Punto3D<double> object.

To fix this, we write a recursive subfunction of apply_func_templ, named
apply_func_templ_over, that let us build the list of instances in the same
order as they appear in the list of overloaded template functions.

static IDENTIFIER apply_func_templ_over
    PROTO_N ( ( fid, args, force, def ) )
    PROTO_T ( IDENTIFIER fid X LIST ( TOKEN ) args X int force X int def )
{
    if ( !IS_NULL_id ( fid ) ) {
	TYPE t = DEREF_type ( id_function_etc_type ( fid ) ) ;
	IDENTIFIER sid = DEREF_id ( id_function_etc_over ( fid ) ) ;
	sid = apply_func_templ_over ( sid, args, force, def ) ;
	if ( IS_type_templ ( t ) ) {
	    TOKEN sort = DEREF_tok ( type_templ_sort ( t ) ) ;
	    if ( force || match_template_args ( sort, args ) ) {
		/* Argument sorts match */
		int td = in_template_decl ;
		args = check_templ_args ( sort, args, fid ) ;
		fid = instance_func ( fid, args, 0, def ) ;
		in_template_decl = td ;
		if ( !IS_NULL_id ( fid ) ) {
		    COPY_id ( id_function_etc_over ( fid ), sid ) ;
		    return ( fid ) ;
		}
	    }
	}
	fid = sid ;
    }
    return ( fid ) ;
}

An then we rewrite the function apply_func_templ, so that it simply calls
apply_func_templ_over instead of going into the while loop,

static IDENTIFIER apply_func_templ
    PROTO_N ( ( id, args, def ) )
    PROTO_T ( IDENTIFIER id X LIST ( TOKEN ) args X int def )
{
    int force = 0 ;
    IDENTIFIER tid = NULL_id ;
    do {
	/* Build up result */
	tid = apply_func_templ_over ( id, args, force, def ) ;
	if ( force ) {
	    /* Should have bound arguments by now */
	    if ( IS_NULL_id ( tid ) ) tid = id ;
	} else {
	    /* Try again allowing for mismatches */
	    force = 1 ;
	}
    } while ( IS_NULL_id ( tid ) ) ;
    return ( tid ) ;
}

An important note: if you look carefully at the original apply_func_templ,
you can see that its semantics has changed slightly. The original function
feeds the template arguments obtained from check_templ_args [template.c] during
the current instantiation into the next instantiation,

		    args = check_templ_args ( sort, args, fid ) ;

Since the overloaded template functions are totally unrelated, there is no
reason why declaration order may affect template instantiation, no matter what
check_templ_args is doing. So I have cut that feedback and reused the same list
of template arguments for every function on the list.