SourceForge.net Logo

[Release b]
<t4m_bug_pure_virtual>
Thu Jul  8 11:15:05 WEST 2004

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

"pure_virtual.C", line 23: Error:
  [ISO 10.4]: 'void Abstract::f ()' is a pure virtual function.
  [ISO 10.4]: The class 'Confluence' is abstract.
  [ISO 10.4]: The object 'c' can't have abstract type.

The error disappears when we drop virtual inheritance, and also when there is
no multiple inheritance.

The errors are ERR_class_abstract_pure, ERR_class_abstract_class, and
ERR_class_abstract_decl, respectively, and they are generated at class_info
[class.c], and check_obj_decl [declare.c]; the sequence of calls is

check_obj_decl [declare.c]
  check_abstract [chktype.c]
    class_info [class.c]
 
These functions just check the flag cinfo_abstract in field ctype_info. The
problem is somewhere before that check.

What is happening is that a virt_link (see src/producers/common/c_class.alg)
is being confused with a pure virtual function (Abstract::f) inherited from
virtual base class Abstract. This virt_link is being created in function
inherit_virtual [virtual.c], in the part that post-processes virtual functions
inherited from virtual base classes (beginning with the conditional 'if
(ci & cinfo_virtual_base )'). When the same virtual function is inherited
twice from a virtual base class, one of the two copies is selected by calling
function inherit_duplicate [virtual.c], and then inherit_virtual returns a
virtual link to the function chosen. Although it points to the selected
function, the virtual link has the name (field virt_func) of the pure virtual
function Abstract::f in the virtual base class.

It is not clear what's the use of these virtual links. They are ignored in
almost every subroutine of the compiler, with the only exceptions of:

1.- inherit_virtual [virtual.c] (they are dereferenced);
2.- end_virtual [virtual.c] (they are not dereferenced, so the function is
    taken as pure virtual, and this is the cause of the bug we are seeing);
3.- compile_virtual [../output/compile.c] (they are dereferenced);
4.- enc_vtable_defn [../output/struct.c] (they are dereferenced);

To fix this bug we just change line

	    if ( ds & dspec_pure ) ci |= cinfo_abstract ;

in function end_virtual [virtual.c] for

	    if ( ds & dspec_pure && !IS_virt_link ( vf ) )
		ci |= cinfo_abstract ;

(optionally, we can also do an analogous change in function find_pure_function
[virtual.c], we change

	    if ( ds & dspec_pure ) return ( id ) ;
for
	    if ( ds & dspec_pure && !IS_virt_link ( vf ) )
		return ( id ) ;

but this is not important).

There is another bug that has not shown up yet. In function inherit_virtual
[virtual.c], after calling inherit_duplicate, the index for the function
selected is not correctly set; it goes

			unsigned long n = DEREF_ulong ( virt_no ( vr ) ) ;
			vp = inherit_duplicate ( vr, vp ) ;
			COPY_ulong ( virt_no ( vr ), n ) ;

while it should clearly say

			unsigned long n = DEREF_ulong ( virt_no ( vr ) ) ;
			vp = inherit_duplicate ( vr, vp ) ;
			COPY_ulong ( virt_no ( vp ), n ) ;

('vp' instead of 'vr' in the second virt_no); observe that this happens in both
cases, virt_inherit_tag and virt_complex_tag.

Finally, a remark on points 3 and 4 above. Because of the existence of these
virtual links, when a virtual function is inherited from a virtual base class,
the resulting virtual table has several copies of the same final overrider
corresponding to that virtual function. In our example, the virtual table for
class Confluence has two identical entries filled with pointers to function
Confluence::f. This is intended to be so, according to the documentation (see
the first paragraph of section 2.6.13 of the C++ Producer Guide), but I have
the impression that this duplication is useless (though I have no proof of this
statement). In case it is necessary to avoid it, one can make inherit_virtual
return NULL_virt instead of a virtual link, changing

			unsigned long n = DEREF_ulong ( virt_no ( vr ) ) ;
			vp = inherit_duplicate ( vr, vp ) ;
			COPY_ulong ( virt_no ( vp ), n ) ;
			COPY_virt ( HEAD_list ( p ), vp ) ;
			MAKE_virt_link ( bn, n, gr, HEAD_list ( p ), vp ) ;
			return ( vp ) ;

for

			unsigned long n = DEREF_ulong ( virt_no ( vr ) ) ;
			vp = inherit_duplicate ( vr, vp ) ;
			COPY_ulong ( virt_no ( vp ), n ) ;
			COPY_virt ( HEAD_list ( p ), vp ) ;
			return ( NULL_virt ) ;

in both cases, virt_inherit_tag and virt_complex_tag. I have made some simple
tests and no error arise, but it is difficult to be sure about this change.