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