[gnu.g++.bug] execution-time BUGS in G++ 1.33.0

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);
	}
}