Bug 45734 - [DR 1116] Devirtualization results in wrong-code
Summary: [DR 1116] Devirtualization results in wrong-code
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: tree-optimization (show other bugs)
Version: 4.6.0
: P3 normal
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2010-09-20 14:48 UTC by Richard Biener
Modified: 2021-08-10 19:03 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2010-09-21 13:37:32


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Richard Biener 2010-09-20 14:48:44 UTC
namespace std {
  typedef __SIZE_TYPE__ size_t;
}
inline void* operator new(std::size_t, void* __p) throw() {
  return __p;
}
extern "C" void abort (void);
class Foo {
public:
    virtual void test (void) { abort (); }
};
class Bar {
public:
    virtual void test (void) { }
};
int main()
{
  Foo f;
  Bar *b;
  b = new (&f) Bar();
  b->test();
  return 0;
}
Comment 1 H.J. Lu 2010-09-20 15:27:10 UTC
It is caused by revision 161655:

http://gcc.gnu.org/ml/gcc-cvs/2010-07/msg00006.html
Comment 2 Richard Biener 2010-09-20 15:46:33 UTC
Of course it is ;)  Before pointer-conversions became useless we didn't
propagate the invariant address into the OBJ_TYPE_REF expression.

We still have useful function-pointer conversions as well, because
dropping them would wreck CALL_EXPRs, too (we need to preserve the
original function type, similar to the alias-type on MEM_REFs).
I suppose we could do the same for OBJ_TYPE_REFs that I plan(ned) for
CALL_EXPRs - store the pointed-to type via a MEM_REF - thus a
dereferenced address.

You'd then have

  CALL_EXPR (MEM [fnptr], args ...)
  OBJ_TYPE_REF (MEM [fnptr], MEM [objptr], index)

where both TREE_TYPE of the function and the object are kept like the
FE specified them.
Comment 3 Richard Biener 2010-09-21 10:12:21 UTC
The following is miscompiled since ever (and also ICC miscompiles it), so
I'm not sure if it is valid to use 'f' to refer to the Bar object.

namespace std {
  typedef __SIZE_TYPE__ size_t;
}
inline void* operator new(std::size_t, void* __p) throw() {
  return __p;
}
extern "C" void abort (void);
class Foo {
public:
    virtual void test (void) { abort (); }
};
class Bar : public Foo {
public:
    virtual void test (void) { }
};
int main()
{
  Foo f;
  new (&f) Bar();
  f.test();
  return 0;
}

A variant with using a pointer of type Bar * like

int main()
{
  Foo f;
  static_cast<Foo *>(new (&f) Bar())->test();
  return 0;
}

only breaks on recent trunk.

int main()
{
  Foo f;
  new (&f) Bar();
  (&f)->test();
  return 0;
}

also breaks always.  While

int main()
{
  Foo f, *p;
  p = new (&f) Bar();
  p->test();
  return 0;
}

works.

int main()
{
  Foo f, *p;
  new (&f) Bar();
  ((Foo *)(void *)&f)->test();
  return 0;
}

also works.

Fun.
Comment 4 Jason Merrill 2010-09-21 13:37:32 UTC
Not a bug; all these testcases (should) have undefined behavior.  I'm working on clarifying the standard to that end.  This is related to

http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_active.html#1116

and on the C side,

http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_236.htm
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1409.htm
Comment 5 Jakub Jelinek 2011-03-25 19:53:20 UTC
GCC 4.6.0 is being released, adjusting target milestone.
Comment 6 Jason Merrill 2011-03-31 16:11:02 UTC
Suspending until the DR is resolved.  We didn't get to it in time for C++0x, but the committee seemed to agree that these should be undefined.
Comment 7 Andrew Pinski 2020-01-04 20:03:34 UTC
*** Bug 93153 has been marked as a duplicate of this bug. ***
Comment 8 Andrew Pinski 2020-01-04 20:06:09 UTC
The C++ defect has been resolved now:
http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#1116

So closing as invalid as the behavior was decided as undefined.
Comment 9 Alexander Cherepanov 2020-01-05 15:35:58 UTC
DR 1116 is said to be resolved by P0137R1[1]. By looking through it, I don't see how it covers testcases from this pr where "right" pointer is used (like the example in comment 0). And it even introduces std::launder for using "wrong" pointers.

Even better, the C++ standard contains a whole paragraph ([basic.life]p9 -- see [2]) describing finer details of killing non-dynamic variables. And the example there features exactly placement new over a local variable:

----------------------------------------------------------------------
class T { };
struct B {
   ~B();
};

void h() {
   B b;
   new (&b) T;
}                               // undefined behavior at block exit
----------------------------------------------------------------------

The examples in this pr don't have classes with non-trivial destructors so they don't hit such UB.

[1] http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0137r1.html
[2] https://eel.is/c++draft/basic.life#9