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