Index: clang/lib/AST/Interp/FunctionPointer.h =================================================================== --- clang/lib/AST/Interp/FunctionPointer.h +++ clang/lib/AST/Interp/FunctionPointer.h @@ -6,6 +6,7 @@ #include "Function.h" #include "Primitives.h" #include "clang/AST/APValue.h" +#include "clang/AST/ASTContext.h" namespace clang { namespace interp { @@ -38,6 +39,13 @@ OS << ")"; } + QualType getType(const ASTContext &Ctx) const { + if (!Func) + return Ctx.NullPtrTy; + + return Func->getDecl()->getType(); + } + ComparisonCategoryResult compare(const FunctionPointer &RHS) const { if (Func == RHS.Func) return ComparisonCategoryResult::Equal; Index: clang/lib/AST/Interp/Interp.h =================================================================== --- clang/lib/AST/Interp/Interp.h +++ clang/lib/AST/Interp/Interp.h @@ -637,6 +637,32 @@ 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(); + + std::string LS = + LHS.toAPValue().getAsString(S.getCtx(), LHS.getType(S.getCtx())); + std::string RS = + RHS.toAPValue().getAsString(S.getCtx(), RHS.getType(S.getCtx())); + const SourceInfo &Loc = S.Current->getSource(OpPC); + S.FFDiag(Loc, diag::note_constexpr_pointer_comparison_unspecified) + << LS << RS; + 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; Index: clang/lib/AST/Interp/Opcodes.td =================================================================== --- clang/lib/AST/Interp/Opcodes.td +++ 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 { Index: clang/test/AST/Interp/functions.cpp =================================================================== --- clang/test/AST/Interp/functions.cpp +++ 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 {