rfg@paris.ics.uci.edu (Ronald Guilmette) (05/29/90)
The following is a list of some bugs I have recently found and isolated in cfront 2.0. I actually know of many more bugs than this, but I have not had time to properly write all of these up. :-( One important class of bugs that I have found (but have yet to write-up) are bugs in which cfront 2.0 allows the operands of various operators to be of types which should probably not be allowed (for the given operator). For instance, arithmetic operators (including ++ and --) should not be legal for enums or for function pointers, however cfront 2.0 often allows such invalid combinations of (built-in) operators and operands. I know of many more examples in this category. In one case I don't know if I have found a bug or not. Ask yourself if ordered comparison operators should or should not be legal for pointers to data members and/or pointer to function members. It is really unclear. Keep in mind that the compiler can (and must) rearrange the order of appearance of data members within a class/struct so that they appear in batches based on their accessability (i.e. public/private/protected). As before, I will distribute my entire collection of cfront & g++ bug reports to those persons & organizations who have a legitimate need for them. I do not consider simple curiosity a legitimate need. If you want a copy of all of my posted bugs reports, and if you have Internet FTP access, send me a short mail message describing why you want the reports and I'll tell you how to obtain them. By the way, I'm looking for a new contract, so if anybody out there needs a good late-model C++/compiler programmer/tester please contact me immediately at 714-434-7666. After Wednesday, leave a message at 408-249-7702. // Ron Guilmette (rfg@ics.uci.edu) // C++ Entomologist // Motto: If it sticks, force it. If it breaks, it needed replacing anyway. =========================================================================== // cfront 2.0 bug 900511_03 // The following erroneous code causes cfront to abort. // keywords: abort, typedef names, shadowing, scope, formal parameter list class class0; struct struct1 { int class0; void member_function (class0 *); // ERROR }; void class1::member_function (class0 *p) { // ERROR } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900514_01 // In Section 12.3.2 it says "Defining conversion by both a constructor and // a conversion function can lead to ambiguities." // The following erroneous code causes cfront 2.0 to segfault. // Note that if the ordering of the functions `test_0_func0' and // `test_0_func1' is reversed, the segfault does not occur. // keywords: segfault, type conversion struct test_0_st_0; struct test_0_st_1 { test_0_st_1 (test_0_st_0&); }; struct test_0_st_0 { operator test_0_st_1 (); }; test_0_st_0 test_0_st_0_obj0; void test_0_func0 () { test_0_st_1 test_0_st_1_obj0 = test_0_st_0_obj0; // ERROR } void test_0_func1 () { test_0_st_1 test_0_st_1_obj2; test_0_st_1_obj2 = (test_0_st_1) test_0_st_0_obj0; // OK } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900514_02 // cfront aborts when compiling the following erroneous code. // g++ 1.37.1 passes this test. // keywords: abort, user-defined type conversion operator, return type, type definition enum etype { e_value } operator etype (int) { // ERROR return (etype) 0; } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900515_01 // The following legal code causes cfront to generate invalid C code. // keywords: C code, user-defined type conversion operator, derived types struct struct_1 { }; struct struct_2 : public struct_1 { }; struct struct_3 { // Uncommenting the following line makes the C errors go away //operator struct_1 (); operator struct_2 (); }; struct_2 struct_2_obj; struct_3::operator struct_2 () { return struct_2_obj; } struct_1 struct_1_obj; struct_3 struct_3_obj; void test () { struct_1_obj = struct_3_obj; // causes invalid C code } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900515_01 // cfront generates errors for the following legal code because it fails to // recongize that an object of one class (i.e. "struct_2") may be implicitly // converted to an object of one of its own public base classes (i.e. // "struct_1"). // keywords: user-defined type conversion operator, derived types struct struct_3; struct struct_1 { }; struct struct_2 : public struct_1 { struct_2 (); struct_2 (struct_3&); }; struct_2::struct_2 () { } struct_2::struct_2 (struct_3&) { } struct struct_3 { }; struct_2 struct_2_obj; struct_1 struct_1_obj; struct_3 struct_3_obj; void test () { struct_2_obj = struct_3_obj; // OK struct_1_obj = struct_3_obj; // gets bogus error } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900517_01 // cfront segfaults when it sees a call to a function which was previously // declared with invalid parameter type specifications. // g++ 1.37.1 passes this test. // keywords: segfault, formal parameter type, function call void foobar (undeclared); // ERROR char *cp; void test () { foobar (cp); } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900517_02 // cfront generates an unnecessary temporary (which is later destructed) // for the following code. // g++ 1.37.1 passes this test. // keywords: implicit operator=, explicit cast, type conversion, temporary int dtor_call_count = 0; struct base { int member; //base (const base&); // Implicitly generated //base& operator= (const base&); // Implicitly generated //~base (); }; struct derived : public base { //derived (const derived&); // Implicitly generated //derived& operator= (const derived&); // Implicitly generated ~derived (); }; //base::~base () { } derived::~derived () { dtor_call_count++; } base base_object; derived derived_object; void test_0 () { base_object = derived_object; // OK base_object = (derived) derived_object; // OK base_object = (const derived) derived_object; // OK base_object = (derived&) derived_object; // OK base_object = (const derived&) derived_object; // generates temp which // is later destructed base_object = (base) derived_object; // OK base_object = (const base) derived_object; // OK base_object = (base&) derived_object; // OK base_object = (const base&) derived_object; // OK } int test () { test_0 (); return (dtor_call_count != 0); } int main () { return test (); } =========================================================================== // cfront 2.0 bug 900518_01 // cfront generates an error for the following legal code. // This code is derived from a similar example in section 3.5.7 of the // ANSI C standard. // g++ 1.37.1 passes this test. // keywords: initialization, braced initializer list struct { int a[3]; } w[] = { { 1 }, 2 }; // gets bogus error int main () { return 0; } =========================================================================== // cfront 2.0 bug 900518_02 // cfront generates invalid C code for the following legal code. // This code is identical to an example given in section 3.5.7 of the // ANSI C standard. // The problem is that (in the generated C code) cfront tries (but fails) // to accurately pre-compute the actual length of the array being initialized. // In this example the number of elements in the array "w" should be 2, but // cfront computes it as 1. // g++ 1.37.1 passes this test. // keywords: initialization, braced initializer list struct { int a[3], b; } w[] = { { 1 }, 2 }; // causes invalid C code int main () { return 0; } =========================================================================== // cfront 2.0 bug 900518_03 // The ANSI C standard (in section 3.5.7) says "The initializer for a scalar // shall be a single expression, optionally enclosed in braces." // It also says "If the aggregate contains members that are aggregates or // unions, of if the first member of a union is an aggregate or union, the // rules apply recursively to the subaggregates or contained unions." // On the basis of these rules, the following braced initializations are // completely legal, however cfront generates bogus errors for them. // These initializations are accepted as legal by pcc, gcc, lcc, and g++. // g++ 1.37.1 passes this test. // keywords: braced initializer list, aggregate initialization struct struct_0 { int a; int b; }; typedef struct struct_0 S; int i = { 7 }; // OK - accepted S array_0[] = { { { 1 } } }; // gets bogus error S array_1[] = { { { 1 }, 2 }, 3 }; // gets bogus error S array_2[] = { 1, { 2 } }; // gets bogus error int main () { return 0; } =========================================================================== // cfront 2.0 bug 900519_02 // The C++ Reference Manual says (in section 8.4.3) "A reference to a plain // T can only be initialized with a plain T" however cfront allows the // initialization of plain references with qualified objects in many cases. // keywords: references, initialization, type qualifiers extern const int cint_obj = 9; void take_int_ref (int& arg) { } int& global_int_ref = cint_obj; // ERROR - caught const int& cint_ref = cint_obj; void test_0 () { int& local_int_ref = cint_obj; // ERROR take_int_ref (cint_obj); // ERROR take_int_ref (cint_ref); // ERROR } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900519_06 // cfront allows the type given in an invocation of operator new to be a // reference type. // Since pointers to reference types are illegal, the required return type // from such an invocation (of operator new) is illegal, and thus (it seems) // the entire call to new should be treated as being illegal. typedef int& int_ref; void test (int n) { new int&; // ERROR - missed new int_ref; // ERROR - missed new int&[n]; // ERROR new int_ref[n]; // ERROR - missed new int&[3]; // ERROR new int_ref[3]; // ERROR - missed } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900519_07 // It is illegal to specify or to use array-of-reference types, yet cfront // allows them to be specified in typedef statements. // keywords: reference types, array types, typedef statements int i; int j; typedef int& int_ref; typedef int_ref int_ref_array_type[2]; // ERROR - missed int& int_ref_array_obj0[2] = { i, j }; // ERROR - caught int_ref int_ref_array_obj1[2] = { i, j }; // ERROR - caught =========================================================================== // cfront 2.0 bug 900519_08 // The following erroneous code causes cfront to segfault. // g++ 1.37.1 passes this test. // keywords: segfault, void type, reference type, initialization void func () { void& local_void_ref_obj = func(); // ERROR } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900519_09 // cfront allows the allocation of const objects via operator new even when // these uses of operator new do not include initializations. // This is inconsistant within the restrictions placed on the construction // of class, struct, and union types which have constant members. // Since there is no completely valid way of initializing such objects // after the invocation of new, these cases should all be illegal. // keywords: operator new, initialization, const qualifier struct struct_0 { int member; }; typedef const int const_int; typedef const struct struct_0 const_struct_0; void test () { new const int; // ERROR new const_int; // ERROR new const struct_0; // ERROR new const_struct_0; // ERROR new struct_1; // ERROR } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900519_10 // cfront does not implement harmless labels within blocks within which // objects of class types (which require destruction) may be created. // g++ 1.37.1 passes this test. // keywords: unimplemented, labels, blocks, destructors int i; struct struct_0 { struct_0 (); ~struct_0 (); }; struct_0::struct_0 () { } struct_0::~struct_0 () { } void test () { struct_0 struct_0_object; label: // gets bogus error if (i) goto label; } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900520_01 // According to the syntax given in the ANSI C standard (section A.1.2.4) // a translation-unit consists of one or more external-declarations. // This implies that each C language input file must (after preprocessing) // contain at least one file-scope declaration. // Input files which do not contain at least one file-scope declaration // (after preprocessing) are not ANSI C conformant. // Although this rule is not explicitly specifief as being applicable to // C++ also, one msut assume to C++ will follow ANSI C in this respect. // cfront fails to diagnose either errors or warnings for input files which // contain no file-scope declarations. // keywords: syntax, translation unit, file scope, declarations // ERROR =========================================================================== // cfront 2.0 bug 900520_02 // The latest C++ Reference Manual does not specify what the compatability // rules are when it comes to pointer-to-array types. // In particular, it is not specified whether or not a pointer to an array // type with an explicit bound is compatible with a pointer to an array // type without an explicit bound (i.e. an incomplete array type). // Since this issue is not clarified by the C++ Reference Manual, one must // assume that C++ will follow ANSI C in this regard, and will allow pointers // to complete array types to be assigned to pointers to incomplete array // types (and vise versa). // As the following code indicates, cfront fails to allow such assignments // between pointer types. // g++ 1.37.1 passes this test. // keywords: array types, pointer types, incomplete types, array bounds typedef int b_array[3]; typedef int u_array[]; typedef b_array *b_array_ptr; typedef u_array *u_array_ptr; void take_b_array_ptr (b_array_ptr arg) { } void take_u_array_ptr (u_array_ptr arg) { } extern b_array b_array_gbl_obj; extern u_array u_array_gbl_obj; const b_array_ptr b_array_ptr_gbl_obj0 = & b_array_gbl_obj; const u_array_ptr u_array_ptr_gbl_obj0 = & u_array_gbl_obj; b_array_ptr b_array_ptr_gbl_obj1 = & u_array_gbl_obj; u_array_ptr u_array_ptr_gbl_obj1 = & b_array_gbl_obj; b_array_ptr b_array_ptr_gbl_obj2 = b_array_ptr_gbl_obj0; u_array_ptr u_array_ptr_gbl_obj2 = u_array_ptr_gbl_obj0; b_array_ptr b_array_ptr_gbl_obj3 = u_array_ptr_gbl_obj0; u_array_ptr u_array_ptr_gbl_obj3 = b_array_ptr_gbl_obj0; void test_local_initialization () { b_array_ptr b_array_ptr_lcl_obj0 = & b_array_gbl_obj; u_array_ptr u_array_ptr_lcl_obj0 = & u_array_gbl_obj; b_array_ptr b_array_ptr_lcl_obj1 = & u_array_gbl_obj; u_array_ptr u_array_ptr_lcl_obj1 = & b_array_gbl_obj; b_array_ptr b_array_ptr_lcl_obj2 = b_array_ptr_gbl_obj0; u_array_ptr u_array_ptr_lcl_obj2 = u_array_ptr_gbl_obj0; b_array_ptr b_array_ptr_lcl_obj3 = u_array_ptr_gbl_obj0; u_array_ptr u_array_ptr_lcl_obj3 = b_array_ptr_gbl_obj0; } void test_local_assignment () { b_array_ptr b_array_ptr_lcl_obj0; u_array_ptr u_array_ptr_lcl_obj0; b_array_ptr b_array_ptr_lcl_obj1; u_array_ptr u_array_ptr_lcl_obj1; b_array_ptr b_array_ptr_lcl_obj2; u_array_ptr u_array_ptr_lcl_obj2; b_array_ptr b_array_ptr_lcl_obj3; u_array_ptr u_array_ptr_lcl_obj3; b_array_ptr_lcl_obj0 = & b_array_gbl_obj; u_array_ptr_lcl_obj0 = & u_array_gbl_obj; b_array_ptr_lcl_obj1 = & u_array_gbl_obj; u_array_ptr_lcl_obj1 = & b_array_gbl_obj; b_array_ptr_lcl_obj2 = b_array_ptr_gbl_obj0; u_array_ptr_lcl_obj2 = u_array_ptr_gbl_obj0; b_array_ptr_lcl_obj3 = u_array_ptr_gbl_obj0; u_array_ptr_lcl_obj3 = b_array_ptr_gbl_obj0; } void test_passing () { take_b_array_ptr (& b_array_gbl_obj); take_u_array_ptr (& u_array_gbl_obj); take_b_array_ptr (& u_array_gbl_obj); take_u_array_ptr (& b_array_gbl_obj); take_b_array_ptr (b_array_ptr_gbl_obj0); take_u_array_ptr (u_array_ptr_gbl_obj0); take_b_array_ptr (u_array_ptr_gbl_obj0); take_u_array_ptr (b_array_ptr_gbl_obj0); } b_array b_array_gbl_obj; b_array u_array_gbl_obj; int main () { return 0; } =========================================================================== // cfront 2.0 bug 900520_03 // The C++ Reference Manual says (in section 8.2.4): // When an identifier of array type appears in an expression, except // as the operand of sizeof or & or used to initialize a reference, // it is converted into a pointer to the first member of the array. // One must assume from the verbage, that when the name of a non-const array // object appears in one of the exempted contexts mentioned in this passage, // that it is *not* automatically converted into a pointer value, but rather // that it remains as an array type value, and that it may therefore also // still be an lvalue. // As the following code demonstrates, cfront does in fact treat the names // of non-const array objects as valid initializers for reference-to-array // type object in some (but not all) contexts. // The exception is that file-scope reference initializers are not allowed // to be the names of array objects in some cases. // Interestingly, the only case where this is disallowed by cfront is where // the file scope reference object being initialized is explicitly declared // to be "const". This should make no difference whatsoever (but it apparently // does). // This is a bug in cfront. // g++ 1.37.1 passes this test. // keywords: reference types, array types, initialization, file scope typedef int b_array[3]; typedef int u_array[]; typedef b_array &b_array_ref; typedef u_array &u_array_ref; void take_b_array_ref (const b_array_ref arg) { } void take_u_array_ref (const u_array_ref arg) { } extern b_array b_array_gbl_obj; extern u_array u_array_gbl_obj; b_array_ref b_array_ref_gbl0 = b_array_gbl_obj; // OK u_array_ref u_array_ref_gbl0 = u_array_gbl_obj; // OK const b_array_ref b_array_ref_gbl1 = b_array_gbl_obj; // gets bogus error const u_array_ref u_array_ref_gbl1 = u_array_gbl_obj; // gets bogus error void test_local_initialization () { const b_array_ref b_array_ref_lcl = b_array_gbl_obj; // OK const u_array_ref u_array_ref_lcl = u_array_gbl_obj; // OK } void test_passing () { take_b_array_ref (b_array_gbl_obj); // OK //take_u_array_ref (u_array_gbl_obj); // OK - but causes trouble for g++ } b_array b_array_gbl_obj; b_array u_array_gbl_obj; int main () { return 0; } =========================================================================== // cfront 2.0 bug 900520_04 // cfront does not yet support the initialization of scalar type objects // (including built-in arithmetic types, enum types, and pointer types) // via constructor initialization syntax except within a call to operator // new. // keywords: unimplemented, syntax, initialization, scalar types enum e_type { e_value }; char *cp; int global_i (1); // gets bogus error double global_d (9.9); // gets bogus error char * global_cp0 (cp); // gets bogus error char * global_cp1 (0); // gets bogus error enum e_type global_e (e_value); // gets bogus error void func0 () { int local_i (1); // gets bogus error double local_d (9.9); // gets bogus error char * local_cp0 (cp); // gets bogus error char * local_cp1 (0); // gets bogus error enum e_type local_e (e_value); // gets bogus error } void func1 () { int* ip = new int (1); // OK double* dp = new double (9.9); // OK char** cpp0 = new char* (cp); // OK char** cpp1 = new char* (0); // OK enum e_type* ep = new e_type (e_value); // OK } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900520_05 // The following legal code causes cfront to abort. // keywords: abort, unimplemented, syntax, initialization, scalar types void func0 () { int local_i; double local_d (9.9); // causes abort } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900520_05 // The following legal code causes cfront to segfault. // keywords: segfault, unimplemented, syntax, initialization, scalar types enum e_type { e_value }; void func0 () { int local_i; enum e_type local_e (e_value); // causes segfault } int main () { return 0; } =========================================================================== // cfront 2.0 bug 900521_01 // When an object of a base class is initialized with the value of an object // which is (publically) derived from the given base class (via an implicit // compiler-created copy constructor) the virtual function table pointer // for the object of the base class is incorrectly initialized with the value // of the virtual function table pointer from the derived class object. // This subsequently makes the base class object (incorrectly) behaive like // an object of the derived class (as far a call to virtual functions are // concerned). // Thus, the following program exits with a non-zero exit status. // keywords: inheritance, initialization, virtual functions int base_member_called = 0; int derived_member_called = 0; struct struct_0 { virtual void virtual_member (); }; void struct_0::virtual_member () { base_member_called++; } struct struct_1 : public struct_0 { virtual void virtual_member (); }; void struct_1::virtual_member () { derived_member_called++; } void take_struct_0 (struct_0 arg) { arg.virtual_member (); } int test () { struct_1 struct_1_obj; struct_0 struct_0_obj0 = struct_1_obj; // initialize base with derived struct_0 struct_0_obj1 (struct_1_obj); // initialize base with derived struct_0* b0p; struct_0* b1p; b0p = &struct_0_obj0; b1p = &struct_0_obj1; b0p->virtual_member (); b1p->virtual_member (); take_struct_0 (struct_1_obj); // initialize base with derived return (base_member_called != 3); } int main () { return test (); }