------- Comment #15 from mmitchel at gcc dot gnu dot org  2006-10-01 18:39 
-------
I cannot see any plausible way to argue that this is a library bug. 
Implementation of placement new should not need barriers or other compiler
intrinsics.  It's twisted to argue that the C++ standard itself would prevent
implementation of placement new without non-standard facilities, since the
people that wrote the standard all certainly expected the implementation to be
just:

  void *operator new(size_t, void *p) {
    return p;
  }

Variations on that definition also occur in various user-defined new operators,
including those for class-scoped operators.  I don't think we can reasonably
ask people to change this code, which works with every other C++ compiler. 
Therefore, the only possible conclusion is that either the compiler is buggy or
that the examples are invalid.  Unfortunately, I don't think Andrew's example
is invalid.

There are two possible approaches to fixing the compiler: either (a) the C++
front end should mark all new operators as special, in some way that the middle
end then uses to avoid this over-optimization, or (b) relax the aliasing rules
used by the middle end.

I think the right solution is (b).  Fundamentally, I'm not sure that the object
models in C and C++ are sufficiently well-defined to permit the kind 
of optimization that we are discussing.  I also think that people use functions
not named "operator new" to do the kinds of things being discussed (i.e., to
allocate blocks of memory) and that C and C++ are intended to support that
usage.  Certainly, in C, people can't use "operator new".  So, I'd probably
suggest that we relax the aliasing rules, at least for heap-allocated objects.

As an example of the kind of problems the standard poses, [basic.life] says:

"The  lifetime  of  an object is a runtime property of the object.  The
  lifetime of an object of type T begins when:

  --storage with the proper alignment and size for type T  is  obtained,
    and

  --if  T is a class type with a non-trivial constructor (_class.ctor_),
    the constructor call has completed.

  The lifetime of an object of type T ends when:

  --if T is a class type with a non-trivial  destructor  (_class.dtor_),
    the destructor call starts, or

  --the storage which the object occupies is reused or released."

So, by that definition, it might seem that:

  int i = 3;
  *((float *)(&i)) = 7.0;

is valid; we're reusing the "int" memory, as a "float", and it has the right
size and alignment (on most systems).  But, that would effectively nullify the
[basic.lval] language that suggests that you can't access an object through a
type other than its dynamic type -- or, at least, it would limit such access to
writes.  I don't think anyone would expect the dynamic type of "i" to be
"float"  in this example, even if someone had written:

  new (&i) float;

Surely, the dynamic type of something explicitly declared "int" must be "int". 
But, for something with dynamic storage duration, perhaps we should assume that
any write to the storage, with a type other than the one that it already has,
forces it to possibly alias things of both types.


-- 


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29286

Reply via email to