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