[clang] 907b5e9 - [clang][bytecode] Diagnose more pointer comparisons (#201588)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 10 23:49:00 PDT 2026
Author: Timm Baeder
Date: 2026-06-11T08:48:56+02:00
New Revision: 907b5e92e1ddbbcc93e7bac84fcfa5810f442997
URL: https://github.com/llvm/llvm-project/commit/907b5e92e1ddbbcc93e7bac84fcfa5810f442997
DIFF: https://github.com/llvm/llvm-project/commit/907b5e92e1ddbbcc93e7bac84fcfa5810f442997.diff
LOG: [clang][bytecode] Diagnose more pointer comparisons (#201588)
Diagnose comparisons between base classes as well as base classes and
fields. Also add some test cases for things that currently fail because
we compute the wrong offset.
Added:
Modified:
clang/lib/AST/ByteCode/Interp.h
clang/lib/AST/ByteCode/Pointer.cpp
clang/test/AST/ByteCode/cxx11.cpp
clang/test/SemaCXX/constant-expression-p2280r4.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index 9d3d3b449bea3..3f082f15ffacc 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1234,6 +1234,11 @@ inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
return false;
}
+ if (LHS == RHS) {
+ S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal)));
+ return true;
+ }
+
if (!Pointer::hasSameBase(LHS, RHS)) {
const SourceInfo &Loc = S.Current->getSource(OpPC);
S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified)
@@ -1242,13 +1247,25 @@ inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
return false;
}
- // Diagnose comparisons between fields with
diff erent access specifiers.
+ // Diagnose comparisons between fields with
diff erent access specifiers,
+ // comparisons between bases and bases+fields.
if (std::optional<std::pair<Pointer, Pointer>> Split =
Pointer::computeSplitPoint(LHS, RHS)) {
const FieldDecl *LF = Split->first.getField();
const FieldDecl *RF = Split->second.getField();
- if (LF && RF && !LF->getParent()->isUnion() &&
- LF->getAccess() != RF->getAccess()) {
+ if (!LF && !RF)
+ S.CCEDiag(S.Current->getSource(OpPC),
+ diag::note_constexpr_pointer_comparison_base_classes);
+ else if (!LF)
+ S.CCEDiag(S.Current->getSource(OpPC),
+ diag::note_constexpr_pointer_comparison_base_field)
+ << Split->first.getRecord()->getDecl() << RF->getParent() << RF;
+ else if (!RF)
+ S.CCEDiag(S.Current->getSource(OpPC),
+ diag::note_constexpr_pointer_comparison_base_field)
+ << Split->second.getRecord()->getDecl() << LF->getParent() << LF;
+ else if (!LF->getParent()->isUnion() &&
+ LF->getAccess() != RF->getAccess()) {
S.CCEDiag(S.Current->getSource(OpPC),
diag::note_constexpr_pointer_comparison_
diff ering_access)
<< LF << LF->getAccess() << RF << RF->getAccess() << LF->getParent();
diff --git a/clang/lib/AST/ByteCode/Pointer.cpp b/clang/lib/AST/ByteCode/Pointer.cpp
index 1b15c59c9fbff..0bf10cc524338 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -787,8 +787,13 @@ Pointer::computeSplitPoint(const Pointer &A, const Pointer &B) {
IterB = getBase(IterB);
}
- if (IterA == IterB)
+ if (IterA == IterB) {
+ // If the Iter is an array, CurA and CurB are both elements of the same
+ // array. That is fine, so return nullopt.
+ if (IterA.getFieldDesc()->isArray())
+ return std::nullopt;
return std::make_pair(CurA, CurB);
+ }
if (IterA.isRoot() && IterB.isRoot())
return std::nullopt;
diff --git a/clang/test/AST/ByteCode/cxx11.cpp b/clang/test/AST/ByteCode/cxx11.cpp
index b58088096e377..5d217f0bdd6cc 100644
--- a/clang/test/AST/ByteCode/cxx11.cpp
+++ b/clang/test/AST/ByteCode/cxx11.cpp
@@ -445,3 +445,32 @@ namespace AddSubMulNonNumber {
a:b:return;
}
}
+
+namespace SubobjectCompare {
+ struct S {
+ int i;
+ };
+ constexpr S s[2] = {};
+ static_assert(&s[0].i < &s[1].i, "");
+ static_assert(&s[0].i != &s[1].i, "");
+ static_assert(!(&s[0] < &s[0]), "");
+
+ class A { public: int a; };
+ class B : public A { public: int b; };
+ class C : public B { };
+ constexpr C c{};
+ static_assert(&c.a < &c.b, ""); // both-error {{not an integral constant expression}} \
+ // both-note {{comparison of address of base class subobject 'A' of class 'B' to field 'b' has unspecified value}}
+ static_assert(&c.a != &c.b, "");
+
+ class X { public: int x; };
+ class Y { public: int y; };
+ class Z : public X, public Y {};
+ constexpr Z z{};
+ static_assert(&z.x < &z.y, ""); // both-error {{not an integral constant expression}} \
+ // both-note {{comparison of addresses of subobjects of
diff erent base classes has unspecified value}}
+ static_assert(&z.x != &z.y, ""); // expected-error {{failed}} FIXME
+ static_assert((void*)(X*)&z < (void*)(Y*)&z, ""); // both-error {{not an integral constant expression}} \
+ // both-note {{comparison of addresses of subobjects of
diff erent base classes has unspecified value}}
+ static_assert((void*)(X*)&z != (void*)(Y*)&z, ""); // expected-error {{failed}} FIXME
+}
diff --git a/clang/test/SemaCXX/constant-expression-p2280r4.cpp b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
index 57b6f6fb3a3dd..e282688ed2248 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -375,8 +375,8 @@ namespace GH150015 {
struct Y {};
struct Z : X, Y {};
extern Z &z;
- constexpr int bases = (void*)(X*)&z <= (Y*)&z; // nointerpreter-error {{constexpr variable 'bases' must be initialized by a constant expression}} \
- // nointerpreter-note {{comparison of addresses of subobjects of
diff erent base classes has unspecified value}}
+ constexpr int bases = (void*)(X*)&z <= (Y*)&z; // expected-error {{constexpr variable 'bases' must be initialized by a constant expression}} \
+ // expected-note {{comparison of addresses of subobjects of
diff erent base classes has unspecified value}}
}
namespace InvalidConstexprFn {
More information about the cfe-commits
mailing list