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