SourceForge.net Logo

[Release b]
<t4m_bug_tok_recur>
Wed Apr 21 18:50:58 WEST 2004

In the current release of tendra4minix (a), the compilation of the file
tok_recur.C produces this error

"tok_recur.C", line 26: Error:
  [Token]: Token 'T' defined recursively.

This error is generated in the frontend [src/producers/common/construct/],
inside the function expand_type [token.c], in the case type_token_tag. The
perception of this error is correct (the compiler is trying to resolve the
equation T = C<T>), but this is a kind of internal temporary error condition
that should not be reported to the user. What is happening is a failure in
the viability of the candidate 'template <class T> operator<< (B &, const T &)'
during the overload resolution for the template application that is in its own
body: the template function is itself a candidate, but not a viable candidate,
since that would need the unification of T = C<T>, and this is imposible in
C++. This error should be reported to the calling function, and all the way up
to deduce_args [instance.c], instead of being reported as a compilation error.

To see better where the problem lies, let us trace the calls starting from the
function resolve_overload [overload.c] (note the call to deduce_param and its
subroutines, which returns 1, indicating that the types T and C<T> are equal or
unifiable),

resolve_overload [overload.c]
  plausible_candidate [overload.c],template<class T> operator<<(B&, const T&)
  plausible_candidate [overload.c],template<class T> A::operator<<(const C<T>&)
  viable_candidate [overload.c], template<class T> operator<<(B &, const T &)
    deduce_args [instance.c]
      depends_on [template.c], p = B &        [--> returns 0]
      depends_on [template.c], p = const T &  [--> returns 1]
      deduce_param [instance.c], t = const T, s = C<T> [--> returns eq = 1]
        eq_type_qual [chktype.c] [--> returns 1]
          eq_type_aux [chktype.c] [--> returns 0, and thus calls unify_types]
          unify_types [chktype.c] [--> returns 1]
            unify_type [chktype.c] [--> returns 1]
              define_type_token [tokdef.c], id = T, t = C<T> [--> returns 1]
                type_category [chktype.c] [--> returns CTYPE_CLASS]
      make_token_args [tokdef.c], template<class T> operator<<(B &, const T &)
        expand_sort [token.c], tok_type_tag
          expand_type [token.c], t = C<T>, type_compound_tag /* Tokenised... */
            expand_args [token.c]
              expand_sort [token.c], tok_type_tag
                expand_type, t = T, type_token_tag /* Expand token definition*/
                  expand_type, t = C<T>, type_compound_tag /* Tokenised... */
                    expand_args [token.c]
                      expand_sort [token.c], tok_type_tag
                        expand_type, t = T, type_token_tag /*...recursive...*/

For some reason, type_category [chktype.c] ignores that C<T> is a template
instance depending on T, the identifier that it is currently being bound, and
define_type_token [tokdef.c] concludes that it is ok to make the binding
[T := C<T>]. I do not know what is wrong with type_category or
define_type_token (they are very complex), so we are going to fix this problem
in the functions expand_sort/expand_type/expand_args [token.c], making this
error appear in the list of errors built by make_token_args [tokdef.c],
instead of being reported to the user.

Recursion in expand_type [token.c] is detected because the first time the
case type_token_tag is reached (before stepping into the conditional branch
that reinvokes expand_type to expand the token definition),

        IDENTIFIER id = DEREF_id ( type_token_tok ( t ) );
        ...
        TOKEN tok = DEREF_tok ( id_token_sort ( id ) );
        unsigned tag = TAG_tok ( tok );
        ...
        if (tag == tok_type_tag) {
        ...
        }

the dspec_temp flag is raised in the identifier id,

         COPY_dspec( id_storage ( id ), ( ds | dspec_temp ) );

and this flag is detected the second time the control flow reaches that point.

We will make the following modifications.

[ ] In src/producers/common/construct/token.c, we rename expand_sort ->
    expand_sort1, expand_type -> expand_type1, expand_args -> expand_args1, and
    add them an additional parameter 'ERROR *err'.

[ ] In the functions expand_sort1, expand_type1, expand_args1, we change all
    invocations to expand_sort, expand_type, expand_args for invocations to
    expand_sort1, expand_type1, expand_args1, respectively (adding, of course,
    the new parameter to all invocations).

[ ] In the functions expand_sort1, expand_type1, expand_args1, we change all
    invocations to report [../utility/error.h] for invocations to add_error
    [../utility/error.c] using the new parameter 'err' added before.
    For example, in the case of the error "Token 'T' defined recursively",
    the code
		    report ( crt_loc, ERR_token_recursive ( id ) ) ;
    becomes
		    add_error ( err, ERR_token_recursive ( id ) ) ;

    Note: the line

		if ( !IS_NULL_err ( err ) ) report ( crt_loc, err ) ;

    in expand_args1 disappears, since the error report is done by the caller,
    whichever it is (see below).

[ ] In the functions expand_sort1, expand_type1, expand_args1, delete all local
    variable declarations 'ERROR err', and use the new parameter 'ERROR *err'
    instead. There is one declaration in expand_sort1 and two in expand_type1.
    This affects the calls to expand_nat, which has a parameter of type
    'ERROR *'; we must change '&err' for 'err' in the calls to expand_nat.
    We must also change 'err' for '*err' in the calls to IS_NULL_err in lines
    1469 and 1495.

[ ] We add these prototypes to token.c, before the definition of expand_sort1,
    expand_type1, expand_args1,

extern TOKEN expand_sort1 PROTO_S ( ( TOKEN, int, int, ERROR * ) ) ;
extern LIST ( TOKEN ) expand_args1 PROTO_S ( ( LIST ( TOKEN ), int, int, ERROR * ) ) ;
extern TYPE expand_type1 PROTO_S ( ( TYPE, int, ERROR * ) ) ;

[ ] In token.c, just after the above-mentioned prototypes, we write the
    new functions expand_sort, expand_type, expand_args,

TOKEN expand_sort
    PROTO_N ( ( tok, rec, force ) )
    PROTO_T ( TOKEN tok X int rec X int force )
{
    ERROR err = NULL_err ;
    TOKEN s = expand_sort1 ( tok, rec, force, &err ) ;
    report ( crt_loc, err ) ;
    return ( s ) ;
}

LIST ( TOKEN ) expand_args
    PROTO_N ( ( p, rec, force ) )
    PROTO_T ( LIST ( TOKEN ) p X int rec X int force )
{
    ERROR err = NULL_err ;
    LIST ( TOKEN ) s = expand_args1 ( p, rec, force, &err ) ;
    report ( crt_loc, err ) ;
    return ( s ) ;
}

TYPE expand_type
    PROTO_N ( ( t, rec ) )
    PROTO_T ( TYPE t X int rec )
{
    ERROR err = NULL_err ;
    TYPE s = expand_type1 ( t, rec, &err ) ;
    report ( crt_loc, err ) ;
    return ( s ) ;
}

    Note: report (which is in fact print_error [../utility/error.c]) invokes
    destroy_error [../utility/error.c] after printing the errors.

[ ] In token.c, we need two modifications to the function expand_type1 to stop
    expansion when it encounters an error. The first is made in the line 1517,
    we change

			if ( !IS_NULL_list ( p ) ) {

    for

			if ( !IS_NULL_list ( p ) && IS_NULL_err ( *err ) ) {

    The second modification is made in lines 1613-1618, enclosing the block

			if ( ds & dspec_auto ) {
			    COPY_type ( tok_type_value ( tok ), t ) ;
			}
			changed = 1 ;

    inside another conditional,

			if ( IS_NULL_err ( *err ) ) {
			  if ( ds & dspec_auto ) {
			      COPY_type ( tok_type_value ( tok ), t ) ;
			  }
			  changed = 1 ;
			}

[ ] The function deduce_args [instance.c] examines the list of errors passed to
    make_token_args [tokdef.c] and returns NULL_id when this list is not empty,
    so deduce_args needn't be changed. The last modification we must make is
    located inside the function make_token_args [tokdef.c], where we just
    change the call

	    tok = expand_sort ( tok, 2, 1 ) ;
    for

	    tok = expand_sort1 ( tok, 2, 1, err ) ;

    Also, we add the prototype for this call,

extern TOKEN expand_sort1 PROTO_S ( ( TOKEN, int, int, ERROR * ) ) ;

This solution works, and it also works with a variant that caused the same
problem, tok_recur_f.C, where the template function A::operator<< is no longer
a member function.