[clang] [clang][bytecode] Diagnose more pointer comparisons (PR #201588)
Timm Baeder via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 10 22:51:02 PDT 2026
https://github.com/tbaederr updated https://github.com/llvm/llvm-project/pull/201588
>From 2a1e7afd9cbf0b1be39b7cb85f151116c1cd8235 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Thu, 4 Jun 2026 14:28:40 +0200
Subject: [PATCH] base cmp
---
clang/lib/AST/ByteCode/Interp.h | 23 +++++++++++++--
clang/lib/AST/ByteCode/Pointer.cpp | 7 ++++-
clang/test/AST/ByteCode/cxx11.cpp | 29 +++++++++++++++++++
.../SemaCXX/constant-expression-p2280r4.cpp | 4 +--
4 files changed, 57 insertions(+), 6 deletions(-)
diff --git a/clang/lib/AST/ByteCode/Interp.h b/clang/lib/AST/ByteCode/Interp.h
index fa77e19afce66..d131d11dd88dd 100644
--- a/clang/lib/AST/ByteCode/Interp.h
+++ b/clang/lib/AST/ByteCode/Interp.h
@@ -1228,6 +1228,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)
@@ -1236,13 +1241,25 @@ inline bool CmpHelper<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
return false;
}
- // Diagnose comparisons between fields with different access specifiers.
+ // Diagnose comparisons between fields with different 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_differing_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 96409faeb6929..af6e728f72a05 100644
--- a/clang/lib/AST/ByteCode/Pointer.cpp
+++ b/clang/lib/AST/ByteCode/Pointer.cpp
@@ -789,8 +789,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 different 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 different 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 8b768f42d1260..13d727f33e123 100644
--- a/clang/test/SemaCXX/constant-expression-p2280r4.cpp
+++ b/clang/test/SemaCXX/constant-expression-p2280r4.cpp
@@ -376,8 +376,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 different 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 different base classes has unspecified value}}
}
namespace InvalidConstexprFn {
More information about the cfe-commits
mailing list