diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index 70ce1c3750bbf..de02418076503 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -143,6 +143,7 @@ bool ByteCodeExprGen::VisitCastExpr(const CastExpr *CE) { case CK_NonAtomicToAtomic: case CK_NoOp: case CK_UserDefinedConversion: + case CK_BitCast: return this->visit(SubExpr); case CK_IntegralToBoolean: diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index ee3b953bb69cc..9eae79ba5d1e7 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -701,7 +701,9 @@ inline bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { 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(); diff --git a/clang/lib/AST/Interp/Pointer.cpp b/clang/lib/AST/Interp/Pointer.cpp index 00943dc846d3b..e0ea25d8f560b 100644 --- a/clang/lib/AST/Interp/Pointer.cpp +++ b/clang/lib/AST/Interp/Pointer.cpp @@ -145,6 +145,13 @@ APValue Pointer::toAPValue() const { 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"); const Descriptor *Desc = getFieldDesc(); diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index 10e0330daecbd..d2b5f4a5a1901 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -122,6 +122,35 @@ namespace PointerToBool { 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);