Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -671,7 +671,9 @@ if (!Pointer::hasSameBase(LHS, RHS)) { const SourceInfo &Loc = S.Current->getSource(OpPC); - S.FFDiag(Loc, diag::note_invalid_subexpr_in_const_expr); + S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified) + << LHS.toDiagnosticString(S.getCtx()) + << RHS.toDiagnosticString(S.getCtx()); return false; } else { unsigned VL = LHS.getByteOffset(); Index: clang/lib/AST/Interp/Pointer.h =================================================================== --- clang/lib/AST/Interp/Pointer.h +++ clang/lib/AST/Interp/Pointer.h @@ -79,6 +79,9 @@ /// Converts the pointer to an APValue. APValue toAPValue() const; + /// Converts the pointer to a string usable in diagnostics. + std::string toDiagnosticString(const ASTContext &Ctx) const; + /// Converts the pointer to an APValue that is an rvalue. APValue toRValue(const Context &Ctx) const; Index: clang/lib/AST/Interp/Pointer.cpp =================================================================== --- clang/lib/AST/Interp/Pointer.cpp +++ clang/lib/AST/Interp/Pointer.cpp @@ -150,6 +150,13 @@ return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr); } +std::string Pointer::toDiagnosticString(const ASTContext &Ctx) const { + if (!Pointee) + return "nullptr"; + + return toAPValue().getAsString(Ctx, getType()); +} + bool Pointer::isInitialized() const { assert(Pointee && "Cannot check if null pointer was initialized"); Descriptor *Desc = getFieldDesc(); Index: clang/test/AST/Interp/literals.cpp =================================================================== --- clang/test/AST/Interp/literals.cpp +++ clang/test/AST/Interp/literals.cpp @@ -125,6 +125,35 @@ static_assert(!!FP, ""); } +namespace PointerComparison { + + struct S { int a, b; } s; + constexpr void *null = 0; + constexpr void *pv = (void*)&s.a; + constexpr void *qv = (void*)&s.b; + constexpr bool v1 = null < (int*)0; + constexpr bool v2 = null < pv; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{comparison between 'nullptr' and '&s.a' has unspecified value}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{comparison between 'nullptr' and '&s.a' has unspecified value}} \ + + constexpr bool v3 = null == pv; // ok + constexpr bool v4 = qv == pv; // ok + + /// FIXME: These two are rejected by the current interpreter, but + /// accepted by GCC. + constexpr bool v5 = qv >= pv; // ref-error {{constant expression}} \ + // ref-note {{unequal pointers to void}} + constexpr bool v8 = qv > (void*)&s.a; // ref-error {{constant expression}} \ + // ref-note {{unequal pointers to void}} + constexpr bool v6 = qv > null; // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{comparison between '&s.b' and 'nullptr' has unspecified value}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{comparison between '&s.b' and 'nullptr' has unspecified value}} + + constexpr bool v7 = qv <= (void*)&s.b; // ok +} + namespace SizeOf { constexpr int soint = sizeof(int); constexpr int souint = sizeof(unsigned int);