SourceForge.net Logo

[Release b]
<t4m_bug_usage>
Wed Jun 23 16:39:23 WEST 2004

In the current release of tendra4minix (a), after fixing <t4m_bug_dtor_const>,
the program usage.C produces this error,

"usage.C", line 21: Error:
  [ISO 3.2]: The 'inline' function 'void Stack < int >::push ()' has been used but not defined.

This is ERR_basic_odr_inline reported in function check_usage [variable.c].
The error disappears when the declarations of the overloaded functions 'push'
are swapped.

The problem is as follows. While instantiating the expression 'stack.push(0)'
in function Iterator::advance, the function copy_offset [copy.c], called by
copy_exp [copy.c] due to an exp_add_ptr_tag, calls rescan_id [copy.c] for
some reason (this is probably intended to be useful for data members). Function
rescan_id happens to return 'Stack<int>::push()', just because it is the first
one of the overloaded 'push' member functions. If we swap declarations then
rescan_id returns 'Stack<int>::push(const int &)', which is the function really
used in the program, and no error is reported.

But this is not the real problem, since rescan_id is always used in contexts
where overload resolution is performed later on (see notes below). The problem
lies in the functions rescan_member [copy.c] and rescan_func_id [copy.c]. The
first one unconditionally marks an identifier as having been used, and the
second one calls rescan_member on identifiers which are not going to be
used/defined later. In this case, the identifier 'Stack<int>::push()' obtained
above is passed to rescan_func_id (called from make_func_exp [function.c] <-
apply_nary [operator.c] <- copy_exp), which calls rescan_member before overload
resolution takes place, and the wrong function gets marked as having been used.

The fact is that rescan_func_id shouldn't mark identifiers as used. This is not
needed at the three places where rescan_func_id is called (see notes below).
On the other hand, rescan_member is somewhat buggy since it does not obey the
global variable suppress_usage [identifier.c], which is used as a flag for not
marking as used identifiers in certain expressions.

The proposed solution to this problem (after the behaviour of use_id and
use_func_id [identifier.c]) is:

[ ] In function rescan_member [copy.c], wrap the code that defines the
    template in a conditional statement that checks suppress_usage, that is,
    change

		    id = apply_template ( tid, args, 0, 0 ) ;
		    if ( !( ds & dspec_virtual && ds & dspec_pure ) ) {
			define_template ( id, 0 ) ;
		    }
		    ds = DEREF_dspec ( id_storage ( id ) ) ;
		    ns = NULL_nspace ;

    for

		    id = apply_template ( tid, args, 0, 0 ) ;
		    if ( !( ds & dspec_virtual && ds & dspec_pure )
						&& !suppress_usage ) {
			define_template ( id, 0 ) ;
		    }
		    ds = DEREF_dspec ( id_storage ( id ) ) ;
		    ns = NULL_nspace ;

    And change

	    lid = find_copied ( pid, id, 1 ) ;
	    if ( !EQ_id ( lid, id ) ) {
		id = lid ;
		if ( !( ds & dspec_virtual && ds & dspec_pure ) ) {
		    define_template ( id, 0 ) ;
		}
		ds = DEREF_dspec ( id_storage ( id ) ) ;
	    }

    for

	    lid = find_copied ( pid, id, 1 ) ;
	    if ( !EQ_id ( lid, id ) ) {
		id = lid ;
		if ( !( ds & dspec_virtual && ds & dspec_pure )
						&& !suppress_usage ) {
		    define_template ( id, 0 ) ;
		}
		ds = DEREF_dspec ( id_storage ( id ) ) ;
	    }

[ ] In function rescan_member [copy.c], wrap the code that marks usage in a
    conditional statement that checks suppress_usage, that is, change

    /* Mark as used */
    if ( !( ds & dspec_virtual ) ) ds |= dspec_used ;
    COPY_dspec ( id_storage ( id ), ds ) ;
    lid = DEREF_id ( id_alias ( id ) ) ;
    if ( !EQ_id ( lid, id ) ) {
	ds = DEREF_dspec ( id_storage ( lid ) ) ;
	if ( !( ds & dspec_virtual ) ) ds |= dspec_used ;
	COPY_dspec ( id_storage ( lid ), ds ) ;
    }

for

    /* Mark as used */
    if ( !( ds & dspec_virtual ) && !suppress_usage ) ds |= dspec_used ;
    COPY_dspec ( id_storage ( id ), ds ) ;
    lid = DEREF_id ( id_alias ( id ) ) ;
    if ( !EQ_id ( lid, id ) ) {
	ds = DEREF_dspec ( id_storage ( lid ) ) ;
	if ( !( ds & dspec_virtual ) && !suppress_usage ) ds |= dspec_used ;
	COPY_dspec ( id_storage ( lid ), ds ) ;
    }

[ ] At the beginning of rescan_func_id [copy.c], activate suppress_usage
    before calling rescan_member, and deactivate it afterwards; change

    IDENTIFIER pid = rescan_id ( id, qual, 0 ) ;
    IDENTIFIER qid = rescan_member ( id ) ;
    NAMESPACE qns = DEREF_nspace ( id_parent ( qid ) ) ;

    for

    IDENTIFIER pid, qid;
    NAMESPACE qns;
    pid = rescan_id ( id, qual, 0 ) ;
    suppress_usage++; qid = rescan_member ( id ) ; suppress_usage--;
    qns = DEREF_nspace ( id_parent ( qid ) ) ;


Notes.

[ ] calls to rescan_id

common/construct/member.c:make_field_exp
common/construct/copy.c:copy_offset, off_member_tag
common/construct/copy.c:copy_exp, exp_undeclared_tag
common/construct/copy.c:rescan_id
common/construct/copy.c:rescan_id
common/construct/copy.c:rescan_func_id

[ ] calls to rescan_func_id

common/construct/function.c:make_func_exp, exp_identifier/member_tag
common/construct/function.c:make_func_exp, exp_call_tag
common/construct/overload.c:resolve_cast

[ ] calls to rescan_member

common/construct/class.c:copy_member
common/construct/copy.c:copy_exp, exp_identifier/member_tag
common/construct/copy.c:copy_exp, exp_func_id_tag
common/construct/copy.c:rescan_func_id
common/construct/copy.c:rescan_member