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 { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
