diff --git a/clang/lib/AST/Interp/FunctionPointer.h b/clang/lib/AST/Interp/FunctionPointer.h --- a/clang/lib/AST/Interp/FunctionPointer.h +++ b/clang/lib/AST/Interp/FunctionPointer.h @@ -8,6 +8,7 @@ #include "clang/AST/APValue.h" namespace clang { +class ASTContext; namespace interp { class FunctionPointer final { @@ -38,6 +39,13 @@ OS << ")"; } + std::string toDiagnosticString(const ASTContext &Ctx) const { + if (!Func) + return "nullptr"; + + return toAPValue().getAsString(Ctx, Func->getDecl()->getType()); + } + ComparisonCategoryResult compare(const FunctionPointer &RHS) const { if (Func == RHS.Func) return ComparisonCategoryResult::Equal; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -574,6 +574,29 @@ return CmpHelper(S, OpPC, Fn); } +/// Function pointers cannot be compared in an ordered way. +template <> +inline bool CmpHelper(InterpState &S, CodePtr OpPC, + CompareFn Fn) { + const auto &RHS = S.Stk.pop(); + const auto &LHS = S.Stk.pop(); + + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified) + << LHS.toDiagnosticString(S.getCtx()) + << RHS.toDiagnosticString(S.getCtx()); + return false; +} + +template <> +inline bool CmpHelperEQ(InterpState &S, CodePtr OpPC, + CompareFn Fn) { + const auto &RHS = S.Stk.pop(); + const auto &LHS = S.Stk.pop(); + S.Stk.push(Boolean::from(Fn(LHS.compare(RHS)))); + return true; +} + template <> inline bool CmpHelper(InterpState &S, CodePtr OpPC, CompareFn Fn) { using BoolT = PrimConv::T; diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td --- a/clang/lib/AST/Interp/Opcodes.td +++ b/clang/lib/AST/Interp/Opcodes.td @@ -94,7 +94,7 @@ } def ComparableTypeClass : TypeClass { - let Types = !listconcat(AluTypeClass.Types, [Ptr], [Float]); + let Types = !listconcat(AluTypeClass.Types, [Ptr], [Float], [FnPtr]); } class SingletonTypeClass : TypeClass { diff --git a/clang/test/AST/Interp/functions.cpp b/clang/test/AST/Interp/functions.cpp --- a/clang/test/AST/Interp/functions.cpp +++ b/clang/test/AST/Interp/functions.cpp @@ -178,6 +178,31 @@ static_assert(s.fp == nullptr, ""); // zero-initialized function pointer. } +namespace Comparison { + void f(), g(); + constexpr void (*pf)() = &f, (*pg)() = &g; + + constexpr bool u13 = pf < pg; // ref-warning {{ordered comparison of function pointers}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{comparison between '&f' and '&g' has unspecified value}} \ + // expected-warning {{ordered comparison of function pointers}} \ + // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{comparison between '&f' and '&g' has unspecified value}} + + constexpr bool u14 = pf < (void(*)())nullptr; // ref-warning {{ordered comparison of function pointers}} \ + // ref-error {{must be initialized by a constant expression}} \ + // ref-note {{comparison between '&f' and 'nullptr' has unspecified value}} \ + // expected-warning {{ordered comparison of function pointers}} \ + // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{comparison between '&f' and 'nullptr' has unspecified value}} + + + + static_assert(pf != pg, ""); + static_assert(pf == &f, ""); + static_assert(pg == &g, ""); +} + } struct F {