------- 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