From: west@jade.ab.CA (Darrin West)
Subject: Re:  g++ 2.2.2 runs constructor even if operator new() returns 0
Organization: Jade Simulations International, Inc.
Distribution: gnu
Date: Tue, 8 Sep 1992 19:55:52 GMT

In article <9209071313.AA03475@larsen.chorus.fr> ev@chorus.FR (Eric Valette) writes:
>>This behavior is under consideration by the ANSI C++ committee, they
>>are thinking about defining things such that new should never return
>>0.  It would throw an exception instead.  This has long been recognized
>>by g++ as the proper way to do things, and hence its past and current
>>behavior.
>>
>>I am inclided not to consider this a bug, until ANSI C++ decides it is.
>>
>>For now, I would recommend that you try and code your software in such
>>a way that it will work or be easy to change, either way ANSI decides.
>>
>>As a practical matter, you can do something like:
>>
>>  p = (void *) malloc (sz);
>>  if (p == 0)
>>    (*__new_handler) ();
>>  return p;
>
>
>Enough! I have seen this bug reported since G++ 1.37.2. I do not want
>to protect EACH constructor by code like:
>
>	if (this == 0)
>		return;
>

After deleting a bit of a flame...

It is not possible to work around this "bug" with a test of "this".  Consider
that base class constructors will be called before executing the most
derived constructor.  Those base class must also test "this".  Unfortunately
by that time, the address (NULL) may have been modified and there is
nothing that "this" can safely be compared against.  Consider:

class x{
	int a,b;
};

class y{
	int c;
	x d;
};

main()
{
  y *p =  new y;
}

If the call to new fails (imagine we have our own operator new, or maybe we
are out of swap space), then the constructor for class x (member d) gets a
this value of about 4, not NULL.  How do you test "this" then?

Sadly, our system does extensive memory management and MUST survive allocation
failures.  As a result I have had to modify the g++ compiler to keep this
problem from causing core dumps.  It is not a full solution (it doesnt
deal with allocated vectors) but it has worked for us for the past week
or so (i.e. buyer beware).  Could someone please consider this argument.

BTW I suppose we could get our system to work using exceptions (the operator
new could throw, and all the allocating code could catch).  This would
not be substantially different than the current implementation:

x = new thing;
if (x == NULL) return ERROR_FLAG;

I suppose the exception handler could just as easily handle the error, but
it sure seems like a large amount of work to set up an exception and catch
it when a simple test can work.

Anyway, I include the code change for those with access to the compiler
sources and a deep need to have this kind of thing not result in a core
dump:

>From the file cp-init.c at in the function build_new:

      /* We've figured out where the allocation is to go.
         If we're not eliding constructors, then if a constructor
         is defined, we must go through it.  */
      if ((rval == NULL_TREE || !flag_elide_constructors)
          && TYPE_HAS_CONSTRUCTOR (true_type) && init != void_type_node)
        {
          /* Constructors are never virtual.  */
          int flags = LOOKUP_NORMAL|LOOKUP_NONVIRTUAL;
>         tree s;

          if (rval && TYPE_USES_VIRTUAL_BASECLASSES (true_type))
            {
              init = tree_cons (NULL_TREE, integer_one_node, init);
              flags |= LOOKUP_HAS_IN_CHARGE;
            }

>         /* modified by Darrin West Sept 02/92 */
>         /* should really do the same thing below in the vector allocation */
>         /* could alternately use build_conditional_expr() with a zero second
>            argument (the first and second are the the same without
>            reevaluation) */
>         s = save_expr(rval);
>         rval = build (COND_EXPR, TREE_TYPE(rval),
>                       s,
>                       build_method_call (s, constructor_name (true_type),
>                                          init, NULL_TREE, flags),
>                       integer_zero_node);

          TREE_HAS_CONSTRUCTOR (rval) = 1;

          goto done;
        }
    }

Note that the solution is simply to test the return value of each allocation
and if NULL skip the constructor call.  I expect that this may not be
the most efficient solution, but I hope it is "right".

I expect I have raised a few people's hackles here.  Apologies.  We needed
a solution immediately and needed it to work for both cfront and g++.

Take it for what it is worth.

-- 
Darrin West, MSc.
  Jade Simulations International Corporation. west@jade.ab.ca

Newsgroups: gnu.g++.bug