From b02e4629f78a0c0c0adf9d66b644e5932a781c7e Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 1 Feb 2012 01:42:44 +0000 Subject: [PATCH] constexpr: add support for comparisons of pointer-to-members. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@149463 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticASTKinds.td | 2 + lib/AST/ExprConstant.cpp | 54 ++++++++++++++++++++-- test/SemaCXX/constant-expression-cxx11.cpp | 23 +++++++++ 3 files changed, 76 insertions(+), 3 deletions(-) diff --git a/include/clang/Basic/DiagnosticASTKinds.td b/include/clang/Basic/DiagnosticASTKinds.td index 1cdbcb962e..0901263624 100644 --- a/include/clang/Basic/DiagnosticASTKinds.td +++ b/include/clang/Basic/DiagnosticASTKinds.td @@ -39,6 +39,8 @@ def note_constexpr_array_index : Note<"cannot refer to element %0 of " def note_constexpr_pointer_arithmetic : Note< "cannot refer to element %0 of non-array object in a constant " "expression">; +def note_constexpr_compare_virtual_mem_ptr : Note< + "comparison of pointer to virtual member function %0 has unspecified value">; def note_constexpr_past_end : Note< "dereferenced pointer past the end of %select{|subobject of }0" "%select{temporary|%2}1 is not a constant expression">; diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 15ab15ce3d..8897667592 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -813,6 +813,15 @@ namespace { } }; + /// Compare two member pointers, which are assumed to be of the same type. + static bool operator==(const MemberPtr &LHS, const MemberPtr &RHS) { + if (!LHS.getDecl() || !RHS.getDecl()) + return !LHS.getDecl() && !RHS.getDecl(); + if (LHS.getDecl()->getCanonicalDecl() != RHS.getDecl()->getCanonicalDecl()) + return false; + return LHS.Path == RHS.Path; + } + /// Kinds of constant expression checking, for diagnostics. enum CheckConstantExpressionKind { CCEK_Constant, ///< A normal constant. @@ -4295,9 +4304,9 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { (!RHSValue.Base && !RHSValue.Offset.isZero())) return Error(E); // It's implementation-defined whether distinct literals will have - // distinct addresses. In clang, we do not guarantee the addresses are - // distinct. However, we do know that the address of a literal will be - // non-null. + // distinct addresses. In clang, the result of such a comparison is + // unspecified, so it is not a constant expression. However, we do know + // that the address of a literal will be non-null. if ((IsLiteralLValue(LHSValue) || IsLiteralLValue(RHSValue)) && LHSValue.Base && RHSValue.Base) return Error(E); @@ -4354,6 +4363,45 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { } } } + + if (LHSTy->isMemberPointerType()) { + assert(E->isEqualityOp() && "unexpected member pointer operation"); + assert(RHSTy->isMemberPointerType() && "invalid comparison"); + + MemberPtr LHSValue, RHSValue; + + bool LHSOK = EvaluateMemberPointer(E->getLHS(), LHSValue, Info); + if (!LHSOK && Info.keepEvaluatingAfterFailure()) + return false; + + if (!EvaluateMemberPointer(E->getRHS(), RHSValue, Info) || !LHSOK) + return false; + + // C++11 [expr.eq]p2: + // If both operands are null, they compare equal. Otherwise if only one is + // null, they compare unequal. + if (!LHSValue.getDecl() || !RHSValue.getDecl()) { + bool Equal = !LHSValue.getDecl() && !RHSValue.getDecl(); + return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E); + } + + // Otherwise if either is a pointer to a virtual member function, the + // result is unspecified. + if (const CXXMethodDecl *MD = dyn_cast(LHSValue.getDecl())) + if (MD->isVirtual()) + CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD; + if (const CXXMethodDecl *MD = dyn_cast(RHSValue.getDecl())) + if (MD->isVirtual()) + CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD; + + // Otherwise they compare equal if and only if they would refer to the + // same member of the same most derived object or the same subobject if + // they were dereferenced with a hypothetical object of the associated + // class type. + bool Equal = LHSValue == RHSValue; + return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E); + } + if (!LHSTy->isIntegralOrEnumerationType() || !RHSTy->isIntegralOrEnumerationType()) { // We can't continue from here for non-integral types. diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index 5b837e39d7..5a05cf3c78 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -827,6 +827,18 @@ namespace MemberPointer { static_assert((s.*&S::f)() == 2, ""); static_assert((s.*s.pf)() == 2, ""); + static_assert(pf == &S::f, ""); + static_assert(pf == s.*&S::pf, ""); + static_assert(pm == &S::m, ""); + static_assert(pm != pn, ""); + static_assert(s.pn != pn, ""); + static_assert(s.pn == pm, ""); + static_assert(pg != nullptr, ""); + static_assert(pf != nullptr, ""); + static_assert((int S::*)nullptr == nullptr, ""); + static_assert(pg == pg, ""); // expected-error {{constant expression}} expected-note {{comparison of pointer to virtual member function 'g' has unspecified value}} + static_assert(pf != pg, ""); // expected-error {{constant expression}} expected-note {{comparison of pointer to virtual member function 'g' has unspecified value}} + template struct T : T {}; template<> struct T<0> { int n; }; template<> struct T<30> : T<29> { int m; }; @@ -836,10 +848,13 @@ namespace MemberPointer { constexpr int (T<10>::*deepn) = &T<0>::n; static_assert(&(t17.*deepn) == &t17.n, ""); + static_assert(deepn == &T<2>::n, ""); constexpr int (T<15>::*deepm) = (int(T<10>::*))&T<30>::m; constexpr int *pbad = &(t17.*deepm); // expected-error {{constant expression}} static_assert(&(t30.*deepm) == &t30.m, ""); + static_assert(deepm == &T<50>::m, ""); + static_assert(deepm != deepn, ""); constexpr T<5> *p17_5 = &t17; constexpr T<13> *p17_13 = (T<13>*)p17_5; @@ -857,6 +872,14 @@ namespace MemberPointer { static_assert(&(p30_5->*(int(T<2>::*))deepm) == &t30.m, ""); static_assert(&(((T<17>*)p30_13)->*deepm) == &t30.m, ""); static_assert(&(p30_23->*deepm) == &t30.m, ""); + + struct Base { int n; }; + template struct Mid : Base {}; + struct Derived : Mid<0>, Mid<1> {}; + static_assert(&Mid<0>::n == &Mid<1>::n, ""); + static_assert((int Derived::*)(int Mid<0>::*)&Mid<0>::n != + (int Derived::*)(int Mid<1>::*)&Mid<1>::n, ""); + static_assert(&Mid<0>::n == (int Mid<0>::*)&Base::n, ""); } namespace ArrayBaseDerived { -- 2.40.0