Revert "[Clang] Allow devirtualization involving array subscripts with constant indices when the pointee type is known [CWG1504] (#207540) (#209596)

It caused miscompiles, see discussion on the PR.

This reverts commit ee24ac00d8.
This commit is contained in:
Hans Wennborg 2026-07-14 22:10:27 +02:00 committed by GitHub
parent eecd874fba
commit 04aa50715c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 3 additions and 95 deletions

View file

@ -2600,41 +2600,6 @@ CXXMethodDecl *CXXMethodDecl::getDevirtualizedMethod(const Expr *Base,
}
}
// By CWG1504 / C++11 [expr.add]p6, pointer arithmetic on a base pointer into
// an array of derived objects is undefined behavior when the element type and
// pointee type are not similar. This means we can devirtualize calls on
// objects accessed through array subscripts or pointer arithmetic with
// non-zero offsets, since the dynamic type must match the static type.
//
// A single object is considered to be an array of one element, so p[0]
// could still be a derived object, but p[N] for N != 0 cannot.
const Expr *Inner = Base->IgnoreParenImpCasts();
if (const auto *UO = dyn_cast<UnaryOperator>(Inner))
if (UO->getOpcode() == UO_Deref)
Inner = UO->getSubExpr()->IgnoreParenImpCasts();
// Handle p[N].f() (dot syntax with array subscript).
if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(Inner)) {
Expr::EvalResult Result;
if (ASE->getIdx()->EvaluateAsInt(Result, getASTContext()) &&
!Result.Val.getInt().isZero())
return DevirtualizedMethod;
}
// Handle (p + N)->f() (arrow syntax with pointer arithmetic).
if (const auto *BO = dyn_cast<BinaryOperator>(Inner)) {
if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
// Identify the integer operand (the offset).
const Expr *IdxExpr = BO->getLHS()->getType()->isPointerType()
? BO->getRHS()
: BO->getLHS();
Expr::EvalResult Result;
if (IdxExpr->EvaluateAsInt(Result, getASTContext()) &&
!Result.Val.getInt().isZero())
return DevirtualizedMethod;
}
}
// We can't devirtualize the call.
return nullptr;
}

View file

@ -11,25 +11,6 @@
// cxx98-error@-1 {{variadic macros are a C99 feature}}
#endif
namespace cwg1504 { // cwg1504: 23
#if __cplusplus >= 201103L
// CWG1504: Pointer arithmetic after derived-base conversion
struct Base { int x; };
struct Derived : Base { int y; };
constexpr Derived arr[2] = {};
// Pointer arithmetic on a base pointer into a derived array is UB,
// and the constexpr evaluator must diagnose it.
constexpr int test(int n) {
return ((const Base*)arr)[n].x; // #cwg1504-x
}
constexpr int bad = test(1);
// since-cxx11-error@-1 {{constexpr variable 'bad' must be initialized by a constant expression}}
// since-cxx11-note@#cwg1504-x {{cannot access field of pointer past the end of object}}
// since-cxx11-note@-3 {{in call to 'test(1)'}}
#endif
} // namespace cwg1504
namespace cwg1512 { // cwg1512: 4
void f(char *p) {
if (p > 0) {}

View file

@ -92,48 +92,10 @@ void fd(D d, XD xd, D *p) {
// CHECK: call void %
p[0].f();
// We can devirtualize this, by CWG1504 / [expr.add]/6 (if the array
// FIXME: We can devirtualize this, by C++1z [expr.add]/6 (if the array
// element type and the pointee type are not similar, behavior is undefined).
// CHECK: call void @_ZN1A1fEv
// CHECK: call void %
p[1].f();
// Negative indices are also UB for the same reason.
// CHECK: call void @_ZN1A1fEv
p[-1].f();
// Pointer arithmetic with arrow syntax: (p + N)->f()
// CHECK: call void @_ZN1A1fEv
(p + 1)->f();
// Pointer subtraction with arrow syntax: (p - N)->f()
// CHECK: call void @_ZN1A1fEv
(p - 1)->f();
// Can't devirtualize with non-constant index; we can't prove N != 0.
int n = 1;
// CHECK: call void %
p[n].f();
// Zero through expression: p[1-1] evaluates to 0, can't devirtualize.
// CHECK: call void %
p[1-1].f();
// (p + 0)->f() also can't be devirtualized (same as *p).
// CHECK: call void %
(p + 0)->f();
// 1 + p is legal pointer arithmetic too.
// CHECK: call void @_ZN1A1fEv
(1 + p)->f();
// Constant variables are evaluated.
const int N = 1;
// CHECK: call void @_ZN1A1fEv
p[N].f();
// Pointer arithmetic with deref: *(p + 1)
// CHECK: call void @_ZN1A1fEv
(*(p + 1)).f();
}
struct B {

View file

@ -10309,7 +10309,7 @@
<td>[<a href="https://wg21.link/expr.add">expr.add</a>]</td>
<td>CD3</td>
<td>Pointer arithmetic after derived-base conversion</td>
<td class="full" align="center">Clang 23</td>
<td class="unknown" align="center">Unknown</td>
</tr>
<tr id="1505">
<td><a href="https://cplusplus.github.io/CWG/issues/1505.html">1505</a></td>