From: Douglas Gregor Date: Mon, 24 Aug 2009 17:42:35 +0000 (+0000) Subject: Implement support for equality comparisons (!=, ==) of member X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=20b3e9918cf7d7845c920baabc4fb2f1ac0ee1d2;p=clang Implement support for equality comparisons (!=, ==) of member pointers, by extending the "composite pointer type" logic to include member pointer types. Introduce test cases for member pointer comparisons, including those that involve the builtin operator candidates implemented earlier. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@79925 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 2f1438c2c3..59502c4edb 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -4247,7 +4247,7 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, // a pointer operand and a null pointer constant) to bring // them to their composite pointer type. [...] // - // C++ [expr.eq]p2 uses the same notion for (in)equality + // C++ [expr.eq]p1 uses the same notion for (in)equality // comparisons of pointers. QualType T = FindCompositePointerType(lex, rex); if (T.isNull()) { @@ -4285,20 +4285,53 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, ImpCastExprToType(rex, lType); // promote the pointer to pointer return ResultTy; } - // C++ allows comparison of pointers with null pointer constants. + if (getLangOptions().CPlusPlus) { - if (lType->isPointerType() && RHSIsNull) { + // Comparison of pointers with null pointer constants and equality + // comparisons of member pointers to null pointer constants. + if (RHSIsNull && + (lType->isPointerType() || + (!isRelational && lType->isMemberPointerType()))) { ImpCastExprToType(rex, lType); return ResultTy; } - if (rType->isPointerType() && LHSIsNull) { + if (LHSIsNull && + (rType->isPointerType() || + (!isRelational && rType->isMemberPointerType()))) { ImpCastExprToType(lex, rType); return ResultTy; } - // And comparison of nullptr_t with itself. + + // Comparison of member pointers. + if (!isRelational && + lType->isMemberPointerType() && rType->isMemberPointerType()) { + // C++ [expr.eq]p2: + // In addition, pointers to members can be compared, or a pointer to + // member and a null pointer constant. Pointer to member conversions + // (4.11) and qualification conversions (4.4) are performed to bring + // them to a common type. If one operand is a null pointer constant, + // the common type is the type of the other operand. Otherwise, the + // common type is a pointer to member type similar (4.4) to the type + // of one of the operands, with a cv-qualification signature (4.4) + // that is the union of the cv-qualification signatures of the operand + // types. + QualType T = FindCompositePointerType(lex, rex); + if (T.isNull()) { + Diag(Loc, diag::err_typecheck_comparison_of_distinct_pointers) + << lType << rType << lex->getSourceRange() << rex->getSourceRange(); + return QualType(); + } + + ImpCastExprToType(lex, T); + ImpCastExprToType(rex, T); + return ResultTy; + } + + // Comparison of nullptr_t with itself. if (lType->isNullPtrType() && rType->isNullPtrType()) return ResultTy; } + // Handle block pointer types. if (!isRelational && lType->isBlockPointerType() && rType->isBlockPointerType()) { QualType lpointee = lType->getAs()->getPointeeType(); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 0434f304d8..d89a0909aa 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -1510,15 +1510,20 @@ QualType Sema::CXXCheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, /// \brief Find a merged pointer type and convert the two expressions to it. /// -/// This finds the composite pointer type for @p E1 and @p E2 according to -/// C++0x 5.9p2. It converts both expressions to this type and returns it. +/// This finds the composite pointer type (or member pointer type) for @p E1 +/// and @p E2 according to C++0x 5.9p2. It converts both expressions to this +/// type and returns it. /// It does not emit diagnostics. QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2) { assert(getLangOptions().CPlusPlus && "This function assumes C++"); QualType T1 = E1->getType(), T2 = E2->getType(); - if(!T1->isAnyPointerType() && !T2->isAnyPointerType()) - return QualType(); + + if (!T1->isPointerType() && !T1->isMemberPointerType() && + !T2->isPointerType() && !T2->isMemberPointerType()) + return QualType(); + // FIXME: Do we need to work on the canonical types? + // C++0x 5.9p2 // Pointer conversions and qualification conversions are performed on // pointer operands to bring them to their composite pointer type. If @@ -1532,8 +1537,10 @@ QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2) { ImpCastExprToType(E2, T1); return T1; } - // Now both have to be pointers. - if(!T1->isPointerType() || !T2->isPointerType()) + + // Now both have to be pointers or member pointers. + if (!T1->isPointerType() && !T1->isMemberPointerType() && + !T2->isPointerType() && !T2->isMemberPointerType()) return QualType(); // Otherwise, of one of the operands has type "pointer to cv1 void," then @@ -1547,20 +1554,56 @@ QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2) { // conversions in both directions. If only one works, or if the two composite // types are the same, we have succeeded. llvm::SmallVector QualifierUnion; + llvm::SmallVector, 4> MemberOfClass; QualType Composite1 = T1, Composite2 = T2; - const PointerType *Ptr1, *Ptr2; - while ((Ptr1 = Composite1->getAs()) && - (Ptr2 = Composite2->getAs())) { - Composite1 = Ptr1->getPointeeType(); - Composite2 = Ptr2->getPointeeType(); - QualifierUnion.push_back( - Composite1.getCVRQualifiers() | Composite2.getCVRQualifiers()); - } - // Rewrap the composites as pointers with the union CVRs. - for (llvm::SmallVector::iterator I = QualifierUnion.begin(), - E = QualifierUnion.end(); I != E; ++I) { - Composite1 = Context.getPointerType(Composite1.getQualifiedType(*I)); - Composite2 = Context.getPointerType(Composite2.getQualifiedType(*I)); + do { + const PointerType *Ptr1, *Ptr2; + if ((Ptr1 = Composite1->getAs()) && + (Ptr2 = Composite2->getAs())) { + Composite1 = Ptr1->getPointeeType(); + Composite2 = Ptr2->getPointeeType(); + QualifierUnion.push_back( + Composite1.getCVRQualifiers() | Composite2.getCVRQualifiers()); + MemberOfClass.push_back(std::make_pair((const Type *)0, (const Type *)0)); + continue; + } + + const MemberPointerType *MemPtr1, *MemPtr2; + if ((MemPtr1 = Composite1->getAs()) && + (MemPtr2 = Composite2->getAs())) { + Composite1 = MemPtr1->getPointeeType(); + Composite2 = MemPtr2->getPointeeType(); + QualifierUnion.push_back( + Composite1.getCVRQualifiers() | Composite2.getCVRQualifiers()); + MemberOfClass.push_back(std::make_pair(MemPtr1->getClass(), + MemPtr2->getClass())); + continue; + } + + // FIXME: block pointer types? + + // Cannot unwrap any more types. + break; + } while (true); + + // Rewrap the composites as pointers or member pointers with the union CVRs. + llvm::SmallVector, 4>::iterator MOC + = MemberOfClass.begin(); + for (llvm::SmallVector::iterator + I = QualifierUnion.begin(), + E = QualifierUnion.end(); + I != E; (void)++I, ++MOC) { + if (MOC->first && MOC->second) { + // Rebuild member pointer type + Composite1 = Context.getMemberPointerType(Composite1.getQualifiedType(*I), + MOC->first); + Composite2 = Context.getMemberPointerType(Composite2.getQualifiedType(*I), + MOC->second); + } else { + // Rebuild pointer type + Composite1 = Context.getPointerType(Composite1.getQualifiedType(*I)); + Composite2 = Context.getPointerType(Composite2.getQualifiedType(*I)); + } } ImplicitConversionSequence E1ToC1 = TryImplicitConversion(E1, Composite1); diff --git a/test/SemaCXX/composite-pointer-type.cpp b/test/SemaCXX/composite-pointer-type.cpp index b4a5c884f7..ebc40c14b7 100644 --- a/test/SemaCXX/composite-pointer-type.cpp +++ b/test/SemaCXX/composite-pointer-type.cpp @@ -25,3 +25,11 @@ void f1(volatile Base *b, Derived1 *d1, const Derived2 *d2) { if (d1 == d2) // expected-error{{comparison of distinct}} return; } + +// PR4691 +int ptrcmp1(void *a, int *b) { + return a < b; +} +int ptrcmp2(long *a, int *b) { + return a < b; // expected-error{{distinct}} +} \ No newline at end of file diff --git a/test/SemaCXX/conditional-expr.cpp b/test/SemaCXX/conditional-expr.cpp index 3f4d7159a0..65fbd83e6d 100644 --- a/test/SemaCXX/conditional-expr.cpp +++ b/test/SemaCXX/conditional-expr.cpp @@ -170,7 +170,7 @@ void test() i1 ? &MixedFields::ci : &MixedFieldsDerived::i; const volatile int (MixedFields::*mp2) = i1 ? &MixedFields::ci : &MixedFields::cvi; - i1 ? &MixedFields::ci : &MixedFields::vi; // expected-error {{incompatible operand types}} + (void)(i1 ? &MixedFields::ci : &MixedFields::vi); // Conversion of primitives does not result in an lvalue. &(i1 ? i1 : d1); // expected-error {{address expression must be an lvalue or a function designator}} diff --git a/test/SemaCXX/member-pointer.cpp b/test/SemaCXX/member-pointer.cpp index 3b106d5576..9e407a184c 100644 --- a/test/SemaCXX/member-pointer.cpp +++ b/test/SemaCXX/member-pointer.cpp @@ -40,6 +40,14 @@ void f() { // Conversion to member of base. pdi1 = pdid; // expected-error {{incompatible type assigning 'int struct D::*', expected 'int struct A::*'}} + + // Comparisons + int (A::*pf2)(int, int); + int (D::*pf3)(int, int) = 0; + bool b1 = (pf == pf2); (void)b1; + bool b2 = (pf != pf2); (void)b2; + bool b3 = (pf == pf3); (void)b3; + bool b4 = (pf != 0); (void)b4; } struct TheBase diff --git a/test/SemaCXX/overloaded-builtin-operators.cpp b/test/SemaCXX/overloaded-builtin-operators.cpp index 2a6c24a677..a8c94f1822 100644 --- a/test/SemaCXX/overloaded-builtin-operators.cpp +++ b/test/SemaCXX/overloaded-builtin-operators.cpp @@ -20,11 +20,21 @@ struct Enum2 { operator E2(); }; + +struct X { + void f(); +}; + +typedef void (X::*pmf)(); +struct Xpmf { + operator pmf(); +}; + yes& islong(long); yes& islong(unsigned long); // FIXME: shouldn't be needed no& islong(int); -void f(Short s, Long l, Enum1 e1, Enum2 e2) { +void f(Short s, Long l, Enum1 e1, Enum2 e2, Xpmf pmf) { // C++ [over.built]p8 int i1 = +e1; int i2 = -e2; @@ -37,6 +47,10 @@ void f(Short s, Long l, Enum1 e1, Enum2 e2) { (void)static_cast(islong(s + l)); (void)static_cast(islong(s + s)); + // C++ [over.built]p16 + (void)(pmf == &X::f); + (void)(pmf == 0); + // C++ [over.built]p17 (void)static_cast(islong(s % l)); (void)static_cast(islong(l << s)); @@ -53,7 +67,15 @@ struct LongRef { operator volatile long&(); }; -void g(ShortRef sr, LongRef lr) { +struct XpmfRef { + operator pmf&(); +}; + +struct E2Ref { + operator E2&(); +}; + +void g(ShortRef sr, LongRef lr, E2Ref e2_ref, XpmfRef pmf_ref) { // C++ [over.built]p3 short s1 = sr++; @@ -64,6 +86,14 @@ void g(ShortRef sr, LongRef lr) { short& sr1 = (sr *= lr); volatile long& lr1 = (lr *= sr); + // C++ [over.built]p20: + E2 e2r2; + e2r2 = e2_ref; + + pmf &pmr = (pmf_ref = &X::f); // expected-error{{no viable overloaded '='}} + pmf pmr2; + pmr2 = pmf_ref; + // C++ [over.built]p22 short& sr2 = (sr %= lr); volatile long& lr2 = (lr <<= sr); diff --git a/www/cxx_status.html b/www/cxx_status.html index 05f3a0d00c..d01c5a7752 100644 --- a/www/cxx_status.html +++ b/www/cxx_status.html @@ -1764,7 +1764,7 @@ welcome!

N/A - Missing pointer-to-member versions (p11, p16) and support for + Missing support for the ->* operator (p11, p16) and support for the ternary operator (p24, p25).