[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