From b2cb1cbd727469e1567a6f2535895e6b64e12c35 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Thu, 25 Feb 2010 22:29:57 +0000 Subject: [PATCH] When computing the composite pointer type for relational comparisons, equality comparisons, and conditional operators, produce a composite pointer type with the appropriate additional "const" qualifiers if the pointer types would otherwise be incompatible. This is a small extension (also present in GCC and EDG in a slightly different form) that permits code like: void** i; void const** j; i == j; with the following extwarn: t.cpp:5:5: warning: comparison of distinct pointer types ('void **' and 'void const **') uses non-standard composite pointer type 'void const *const *' [-pedantic] i == j; ~ ^ ~ Fixes PR6346, and I'll be filing a core issue about this with the C++ committee. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@97177 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 6 +++ lib/Sema/Sema.h | 3 +- lib/Sema/SemaExpr.cpp | 20 +++++++-- lib/Sema/SemaExprCXX.cpp | 51 ++++++++++++++++++++-- test/SemaCXX/composite-pointer-type.cpp | 8 ++++ 5 files changed, 81 insertions(+), 7 deletions(-) diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index de7624756f..da4713340e 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1828,6 +1828,9 @@ def ext_typecheck_cond_incompatible_operands : ExtWarn< "incompatible operand types (%0 and %1)">; def err_typecheck_comparison_of_distinct_pointers : Error< "comparison of distinct pointer types (%0 and %1)">; +def ext_typecheck_comparison_of_distinct_pointers_nonstandard : ExtWarn< + "comparison of distinct pointer types (%0 and %1) uses non-standard " + "composite pointer type %2">; def err_typecheck_vector_comparison : Error< "comparison of vector types (%0 and %1) not supported yet">; def err_typecheck_assign_const : Error<"read-only variable is not assignable">; @@ -2252,6 +2255,9 @@ def err_typecheck_expect_scalar_operand : Error< "operand of type %0 where arithmetic or pointer type is required">; def err_typecheck_cond_incompatible_operands : Error< "incompatible operand types (%0 and %1)">; +def ext_typecheck_cond_incompatible_operands_nonstandard : ExtWarn< + "incompatible operand types (%0 and %1) use non-standard composite pointer " + "type %2">; def err_cast_selector_expr : Error< "cannot type cast @selector expression">; def warn_typecheck_cond_incompatible_pointers : ExtWarn< diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 2ed4bf6297..42f9838785 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -3947,7 +3947,8 @@ public: Expr *&cond, Expr *&lhs, Expr *&rhs, SourceLocation questionLoc); QualType CXXCheckConditionalOperands( // C++ 5.16 Expr *&cond, Expr *&lhs, Expr *&rhs, SourceLocation questionLoc); - QualType FindCompositePointerType(Expr *&E1, Expr *&E2); // C++ 5.9 + QualType FindCompositePointerType(Expr *&E1, Expr *&E2, + bool *NonStandardCompositeType = 0); QualType FindCompositeObjCPointerType(Expr *&LHS, Expr *&RHS, SourceLocation questionLoc); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b074381b51..fc89b15c04 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -5324,11 +5324,18 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, // // C++ [expr.eq]p1 uses the same notion for (in)equality // comparisons of pointers. - QualType T = FindCompositePointerType(lex, rex); + bool NonStandardCompositeType = false; + QualType T = FindCompositePointerType(lex, rex, + isSFINAEContext()? 0 : &NonStandardCompositeType); if (T.isNull()) { Diag(Loc, diag::err_typecheck_comparison_of_distinct_pointers) << lType << rType << lex->getSourceRange() << rex->getSourceRange(); return QualType(); + } else if (NonStandardCompositeType) { + Diag(Loc, + diag::ext_typecheck_comparison_of_distinct_pointers_nonstandard) + << lType << rType << T + << lex->getSourceRange() << rex->getSourceRange(); } ImpCastExprToType(lex, T, CastExpr::CK_BitCast); @@ -5390,11 +5397,18 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, // 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); + bool NonStandardCompositeType = false; + QualType T = FindCompositePointerType(lex, rex, + isSFINAEContext()? 0 : &NonStandardCompositeType); if (T.isNull()) { Diag(Loc, diag::err_typecheck_comparison_of_distinct_pointers) - << lType << rType << lex->getSourceRange() << rex->getSourceRange(); + << lType << rType << lex->getSourceRange() << rex->getSourceRange(); return QualType(); + } else if (NonStandardCompositeType) { + Diag(Loc, + diag::ext_typecheck_comparison_of_distinct_pointers_nonstandard) + << lType << rType << T + << lex->getSourceRange() << rex->getSourceRange(); } ImpCastExprToType(lex, T, CastExpr::CK_BitCast); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 1a6fdd7e02..df9442cde5 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -2080,9 +2080,18 @@ QualType Sema::CXXCheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, // performed to bring them to a common type, whose cv-qualification // shall match the cv-qualification of either the second or the third // operand. The result is of the common type. - QualType Composite = FindCompositePointerType(LHS, RHS); - if (!Composite.isNull()) + bool NonStandardCompositeType = false; + QualType Composite = FindCompositePointerType(LHS, RHS, + isSFINAEContext()? 0 : &NonStandardCompositeType); + if (!Composite.isNull()) { + if (NonStandardCompositeType) + Diag(QuestionLoc, + diag::ext_typecheck_cond_incompatible_operands_nonstandard) + << LTy << RTy << Composite + << LHS->getSourceRange() << RHS->getSourceRange(); + return Composite; + } // Similarly, attempt to find composite type of twp objective-c pointers. Composite = FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); @@ -2101,7 +2110,16 @@ QualType Sema::CXXCheckConditionalOperands(Expr *&Cond, Expr *&LHS, Expr *&RHS, /// 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) { +/// +/// If \p NonStandardCompositeType is non-NULL, then we are permitted to find +/// a non-standard (but still sane) composite type to which both expressions +/// can be converted. When such a type is chosen, \c *NonStandardCompositeType +/// will be set true. +QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2, + bool *NonStandardCompositeType) { + if (NonStandardCompositeType) + *NonStandardCompositeType = false; + assert(getLangOptions().CPlusPlus && "This function assumes C++"); QualType T1 = E1->getType(), T2 = E2->getType(); @@ -2152,12 +2170,20 @@ QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2) { ContainingClassVector MemberOfClass; QualType Composite1 = Context.getCanonicalType(T1), Composite2 = Context.getCanonicalType(T2); + unsigned NeedConstBefore = 0; do { const PointerType *Ptr1, *Ptr2; if ((Ptr1 = Composite1->getAs()) && (Ptr2 = Composite2->getAs())) { Composite1 = Ptr1->getPointeeType(); Composite2 = Ptr2->getPointeeType(); + + // If we're allowed to create a non-standard composite type, keep track + // of where we need to fill in additional 'const' qualifiers. + if (NonStandardCompositeType && + Composite1.getCVRQualifiers() != Composite2.getCVRQualifiers()) + NeedConstBefore = QualifierUnion.size(); + QualifierUnion.push_back( Composite1.getCVRQualifiers() | Composite2.getCVRQualifiers()); MemberOfClass.push_back(std::make_pair((const Type *)0, (const Type *)0)); @@ -2169,6 +2195,13 @@ QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2) { (MemPtr2 = Composite2->getAs())) { Composite1 = MemPtr1->getPointeeType(); Composite2 = MemPtr2->getPointeeType(); + + // If we're allowed to create a non-standard composite type, keep track + // of where we need to fill in additional 'const' qualifiers. + if (NonStandardCompositeType && + Composite1.getCVRQualifiers() != Composite2.getCVRQualifiers()) + NeedConstBefore = QualifierUnion.size(); + QualifierUnion.push_back( Composite1.getCVRQualifiers() | Composite2.getCVRQualifiers()); MemberOfClass.push_back(std::make_pair(MemPtr1->getClass(), @@ -2182,6 +2215,18 @@ QualType Sema::FindCompositePointerType(Expr *&E1, Expr *&E2) { break; } while (true); + if (NeedConstBefore && NonStandardCompositeType) { + // Extension: Add 'const' to qualifiers that come before the first qualifier + // mismatch, so that our (non-standard!) composite type meets the + // requirements of C++ [conv.qual]p4 bullet 3. + for (unsigned I = 0; I != NeedConstBefore; ++I) { + if ((QualifierUnion[I] & Qualifiers::Const) == 0) { + QualifierUnion[I] = QualifierUnion[I] | Qualifiers::Const; + *NonStandardCompositeType = true; + } + } + } + // Rewrap the composites as pointers or member pointers with the union CVRs. ContainingClassVector::reverse_iterator MOC = MemberOfClass.rbegin(); diff --git a/test/SemaCXX/composite-pointer-type.cpp b/test/SemaCXX/composite-pointer-type.cpp index fdf838ffa0..e8b09204d1 100644 --- a/test/SemaCXX/composite-pointer-type.cpp +++ b/test/SemaCXX/composite-pointer-type.cpp @@ -50,3 +50,11 @@ typedef double Matrix4[4][4]; bool f(Matrix4 m1, const Matrix4 m2) { return m1 != m2; } + +// PR6346 +bool f1(bool b, void **p, const void **q) { + if (p == q) // expected-warning{{comparison of distinct pointer types ('void **' and 'void const **') uses non-standard composite pointer type 'void const *const *'}} + return false; + + return b? p : q; // expected-warning{{incompatible operand types ('void **' and 'void const **') use non-standard composite pointer type 'void const *const *'}} +} -- 2.40.0