On Wed, Jul 5, 2017 at 12:14 PM, Jonathan Wakely <jwakely....@gmail.com> wrote: > On 5 July 2017 at 10:13, Oleg Endo wrote: >> Hi, >> >> On Wed, 2017-07-05 at 02:02 +0200, Geza Herman wrote: >>> >>> Here's what happens: in callInitA(), an Object put onto the stack (which >>> has a const member variable, initialized to 0). Then somefunction called >>> (which is intentionally not defined). Then ~Object() is called, which >>> has an "if", which has a not-immediately-obvious, but always false >>> condition. Compiling with -03, everything gets inlined. >>> >>> My question is about the inlined ~Object(). As m_initType is always 0, >>> why does not optimize the destructor GCC away? GCC inserts code that >>> checks the value of m_initType. >>> >>> Is it because such construct is rare in practice? Or is it hard to do an >>> optimization like that? >> >> It's not safe to optimize it away because the compiler does not know >> what "somefunction" does. Theoretically, it could cast the "Object&" >> to some subclass and overwrite the const variable. "const" does not >> mean that the memory location is read-only in some way. > > No, that would be undefined behaviour. The data member is defined as > const, so it's not possible to write to that member without undefined > behaviour. A variable defined with a const type is not the same as a > variable accessed through a pointer/reference to a const type. > > Furthermore, casting the Object to a derived class would also be > undefined, because the dynamic type is Object, not some derived type.
There's a related PR about this (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67886). basic.lifetime allows to devirtualize second call to foo in Base *p = new Derived; p->foo(); p->foo(); to Base *p = new Derived; Derived::foo(p); Derived::foo(p); because first call is not allowed to changed dynamic type of p. Clang optimizes this but GCC does not. > I think the reason it's not optimized away is for this case: > > void somefunction(const Object& object); > { > void* p = &object; > object.~Object(); > new(p) Object(); > } > > This means that after calling someFunction there could be a different > object at the same location (with a possibly different value for that > member). > > However, for this specific case that's also not possible, because > there are no constructors that could initialize the member to anything > except zero. But that requires more than just control-flow analysis, > it also requires analysing all the available constructors to check > there isn't one that does: > > Object::Object(int) : m_initType(1) { }