rfg@MCC.COM (Ron Guilmette) (02/23/89)
The following three short g++ programs all print a FAILED message when compiled with g++ 1.33.0 on a Sun 3 and executed on a Sun3. These bugs were also reported for g++ 1.32.0. The third bug below indicates (I believe) a fundamental flaw in the way that "new" allocation of object space is done. The allocation of space does not appear to take place at the point of a "new" operation (as one would naturally assume). Rather, it seems that g++ cheats by moving the call to __builtin_new into the constructor! This call only happens if "this" is zero upon entry to the constructor. It is obvious that, in a distributed system, this "trick" may be useful, but I don't like it for several reasons. First, as the third example file below demonstrates, this "trick" is not foolproof. Second, It sometimes causes unexpected results which can totally throw you for a loop if you are trying to debug something. It is just too "tricky" of a trick... the result being that the average user is left amazed and confused. Third, I don't want a language which does what it thinks I wanted it to do! I just want a language which only does what I tell it to do. Now I didn't expect the compiler to put this unnecessary overhead into all my constructors (even the ones that get no benefit from it) and I sure as heck didn't want that effect. If I want the compiler to generate a call to the memory allocator, I will write "new" and expect to pay the price for that AT THAT POINT. If on the other hand I want a compiler that thinks that it knows better than I do what I really want (or what I really should have) I'll go off and start using Logo or something. So the long and the short of my beef is this. Can't "new" be handled in the straightforward, simple, intuitive, and obvious way (i.e. by generating the calls to __builtin_new where the new's actually are)? I don't want to get overly metaphysical, but if there is a God, (no, no... I don't mean Bjarne) isn't this what He would want? :-) Does anybody out there agree with me? Am I a tree falling in the forrest and making no sound? Will Nixon run again? Inquiring minds want to know! Please send all hate mail and death threats to: // Ron Guilmette - MCC - Experimental (parallel) Systems Kit Project // 3500 West Balcones Center Drive, Austin, TX 78759 - (512)338-3740 // ARPA: rfg@mcc.com // UUCP: {rutgers,uunet,gatech,ames,pyramid}!cs.utexas.edu!pp!rfg /*---------------------- execution-errors/x11.cc -------------------------- */ /* Description - check that overloaded method call operators are properly called even when their results are subsequently re-cast and then assigned to a reference variable. Note: this fails on 1.32.0.5 when the -fall-virtual flag is used. */ struct future { int operator int(); }; struct futref { futref() {}; futref(int*); int operator int(); future operator future(); }; struct add_stuff; //typedef int (add_stuff::*MF)(int); typedef int MF; struct add_stuff { int add_stuff_function(int); futref operator->()(MF , int , ...); }; add_stuff* add_stuff_pointer_object; void test() { future& v = add_stuff_pointer_object->add_stuff_function(17); } int method_call_op_called = 0; main () { test (); if (method_call_op_called != 2) printf ("FAILED - method call op only invoked %d times\n", method_call_op_called); else printf ("FAILED\n"); exit (0); } int future::operator int () { printf ("future::operator int() called\n"); return 0; } future future_dummy; futref futref_dummy; futref::futref(int* i) { printf ("futref::futref(int*) called\n"); } int futref::operator int() { printf ("futref::operator int() called\n"); return futref_dummy; } future futref::operator future() { // printf ("futref::operator future() called\n"); return future_dummy; } int add_stuff::add_stuff_function(int) { printf ("add_stuff::add_stuff_function(int) called\n"); return 0; } futref add_stuff::operator->()(MF member_function, int arg_count , ...) { method_call_op_called++; return futref_dummy; } /* ----------------------- execution-errors/x15.cc ------------------------ */ /* Description - check that the number of bytes worth of arguments is correct when the overloaded method call operator is invoked. In particular, check that the number of bytes of arguments is computed based on the actual number of bytes passed (taking into account implicit default type conversions which happen to actual arguments) and *not* just based on the total sizes of all of the declared formal parameter types. */ int last_argument_byte_count = 0; int number_of_calls = 0; class c1 { public: operator->() (int method_vtable_index, int argument_bytes, ...) { number_of_calls++; if (last_argument_byte_count) { if (argument_bytes != last_argument_byte_count) { printf ("FAILED - inaccurate argument byte count\n"); exit (1); } } else last_argument_byte_count = argument_bytes; } virtual void float_method (float f); virtual void double_method (double d); }; test() { c1 *c1_object_ptr = new c1; float f = 1.2345; double d = 1.2345; c1_object_ptr->float_method(f); c1_object_ptr->double_method(d); } main () { test(); if (number_of_calls != 2) printf ("FAILED - method call operator not invoked once for each method call\n"); else printf ("PASSED\n"); exit (0); } void c1::float_method (float f) { } void c1::double_method (double d) { } /* ---------------------- execution-errors/x16.cc -------------------------- */ /* Description - check that the "this" pointer is zero when a method is called for an object pointed to by a null pointer. */ class my_class { int i; public: my_class (); }; my_class *my_pointer_1 = 0; my_class *my_pointer_2 = 0; main () { //my_pointer_2 = new my_class(); my_pointer_1->my_class (); printf ("PASSED\n"); exit (0); } my_class::my_class () { if ((int) this != 0) { printf ("FAILED - this pointer is non-zero\n"); exit (1); } }