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(InterpState &S, CodePtr OpPC, CompareFn Fn) { return false; } + if (LHS == RHS) { + S.Stk.push(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(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> 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 +}