From 0c6db9417dceeb082296c4e097be5de3ee1c5eb7 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 4 May 2009 06:07:12 +0000 Subject: [PATCH] Implement support for comparing pointers with <, >, <=, >=, ==, and != in C++, taking into account conversions to the "composite pointer type" so that we can compare, e.g., a pointer to a derived class to a pointer to a base class. Also, upgrade the "comparing distinct pointer types" from a warning to an error for C++, since this is clearly an error. Turns out that we hadn't gone through and audited this code for C++, ever. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@70829 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticSemaKinds.td | 2 + lib/Sema/Sema.h | 2 + lib/Sema/SemaExpr.cpp | 27 +++++++- lib/Sema/SemaExprCXX.cpp | 71 ++++++++++++++++++++++ test/SemaCXX/composite-pointer-type.cpp | 27 ++++++++ test/SemaCXX/elaborated-type-specifier.cpp | 3 +- 6 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 test/SemaCXX/composite-pointer-type.cpp diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 1ea237eef5..334b6badca 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -1039,6 +1039,8 @@ def ext_typecheck_comparison_of_pointer_integer : Warning< "comparison between pointer and integer (%0 and %1)">; def ext_typecheck_comparison_of_distinct_pointers : Warning< "comparison of distinct pointer types (%0 and %1)">; +def err_typecheck_comparison_of_distinct_pointers : Error< + "comparison of distinct pointer types (%0 and %1)">; 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">; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 2bfc6b7857..1c652114e3 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -2424,6 +2424,8 @@ public: QualType rhsType); bool IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType); + QualType CompositePointerType(Expr *LHS, Expr *RHS, + bool LHSIsNull, bool RHSIsNull); bool PerformImplicitConversion(Expr *&From, QualType ToType, const char *Flavor, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 36ec9c65c1..a4eeb9f01c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -3653,7 +3653,7 @@ QualType Sema::CheckShiftOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, return LHSTy; } -// C99 6.5.8 +// C99 6.5.8, C++ [expr.rel] QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, unsigned OpaqueOpc, bool isRelational) { BinaryOperator::Opcode Opc = (BinaryOperator::Opcode)OpaqueOpc; @@ -3758,6 +3758,31 @@ QualType Sema::CheckCompareOperands(Expr *&lex, Expr *&rex, SourceLocation Loc, QualType RCanPointeeTy = Context.getCanonicalType(rType->getAsPointerType()->getPointeeType()); + // Simple check: if the pointee types are identical, we're done. + if (LCanPointeeTy == RCanPointeeTy) + return ResultTy; + + if (getLangOptions().CPlusPlus) { + // C++ [expr.rel]p2: + // [...] Pointer conversions (4.10) and qualification + // conversions (4.4) are performed on pointer operands (or on + // 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 + // comparisons of pointers. + QualType T = CompositePointerType(lex, rex, LHSIsNull, RHSIsNull); + 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; + } + if (!LHSIsNull && !RHSIsNull && // C99 6.5.9p2 !LCanPointeeTy->isVoidType() && !RCanPointeeTy->isVoidType() && !Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(), diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 4c3c85bbf5..d407f77c28 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -768,6 +768,77 @@ Sema::IsStringLiteralToNonConstPointerConversion(Expr *From, QualType ToType) { return false; } +/// \brief Determine the composite pointer type (C++ [expr.rel]p2) +/// given the left- and right-hand expressions in a relational +/// operation. +/// +/// While the notion of a composite pointer type is only described for +/// relational operators (<, >, <=, >=), the same computation is used +/// to determine the "common type" used for the equality operators +/// (==, !=) when comparing pointers. +/// +/// \param LHS the left-hand operand. +/// \param RHS the right-hand operand. +/// \param LHSIsNull whether \p LHS is the NULL pointer constant +/// \param RHSIsNull whether \p RHS is the NULL pointer constant +/// +/// \returns the composite pointer type, if any, or the null type if +/// no such type exists. It is the caller's responsibility to emit +/// diagnostic. +QualType Sema::CompositePointerType(Expr *LHS, Expr *RHS, + bool LHSIsNull, bool RHSIsNull) { + // First, determine whether LHS and RHS have pointer types, and what + // types they point to. + QualType LHSPointee; + QualType RHSPointee; + if (const PointerType *LHSPtr = LHS->getType()->getAsPointerType()) + LHSPointee = LHSPtr->getPointeeType(); + if (const PointerType *RHSPtr = RHS->getType()->getAsPointerType()) + RHSPointee = RHSPtr->getPointeeType(); + + // C++ [expr.rel]p2: + // [...] If one operand is a null pointer constant, the composite + // pointer type is the type of the other operand. + if (LHSIsNull && !RHSPointee.isNull()) + return RHS->getType(); + if (RHSIsNull && !LHSPointee.isNull()) + return LHS->getType(); + + // If neither LHS nor RHS has pointer type, we're done. + if (LHSPointee.isNull() && RHSPointee.isNull()) + return QualType(); + + // [...] Otherwise, if one of the operands has type "pointer to cv1 + // void", then the other has type "pointer to cv2 T" and the + // composite pointer type is "pointer to cv12 void", where cv12 is + // the union of cv1 and cv2. + QualType LHSPointeeCanon = Context.getCanonicalType(LHSPointee); + QualType RHSPointeeCanon = Context.getCanonicalType(RHSPointee); + unsigned CVQuals = + (LHSPointeeCanon.getCVRQualifiers() | RHSPointeeCanon.getCVRQualifiers()); + if (LHSPointeeCanon->isVoidType() || RHSPointeeCanon->isVoidType()) + return Context.getPointerType(Context.VoidTy.getQualifiedType(CVQuals)); + + // [...] Otherwise, the composite pointer type is a pointer 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 FullyQualifiedLHSType + = Context.getPointerType(LHSPointee.getQualifiedType(CVQuals)); + QualType CompositePointerType; + bool IncompatibleObjC = false; + if (IsPointerConversion(RHS, RHS->getType(), FullyQualifiedLHSType, + CompositePointerType, IncompatibleObjC)) + return CompositePointerType; + QualType FullyQualifiedRHSType + = Context.getPointerType(RHSPointee.getQualifiedType(CVQuals)); + if (IsPointerConversion(LHS, LHS->getType(), FullyQualifiedRHSType, + CompositePointerType, IncompatibleObjC)) + return CompositePointerType; + + return QualType(); +} + /// PerformImplicitConversion - Perform an implicit conversion of the /// expression From to the type ToType. Returns true if there was an /// error, false otherwise. The expression From is replaced with the diff --git a/test/SemaCXX/composite-pointer-type.cpp b/test/SemaCXX/composite-pointer-type.cpp new file mode 100644 index 0000000000..b4a5c884f7 --- /dev/null +++ b/test/SemaCXX/composite-pointer-type.cpp @@ -0,0 +1,27 @@ +// RUN: clang-cc -fsyntax-only -verify %s + +class Base { }; +class Derived1 : public Base { }; +class Derived2 : public Base { }; + +void f0(volatile Base *b, Derived1 *d1, const Derived2 *d2) { + if (b > d1) + return; + if (d1 <= b) + return; + if (b > d2) + return; + if (d1 >= d2) // expected-error{{comparison of distinct}} + return; +} + +void f1(volatile Base *b, Derived1 *d1, const Derived2 *d2) { + if (b == d1) + return; + if (d1 == b) + return; + if (b != d2) + return; + if (d1 == d2) // expected-error{{comparison of distinct}} + return; +} diff --git a/test/SemaCXX/elaborated-type-specifier.cpp b/test/SemaCXX/elaborated-type-specifier.cpp index c7bf46a5b1..70478e0f32 100644 --- a/test/SemaCXX/elaborated-type-specifier.cpp +++ b/test/SemaCXX/elaborated-type-specifier.cpp @@ -38,10 +38,9 @@ void test_S5_scope() { S4 *s4; // expected-error{{use of undeclared identifier 'S4'}} } -// FIXME: the warning below should be an error! int test_funcparam_scope(struct S5 * s5) { struct S5 { int y; } *s5_2 = 0; - if (s5 == s5_2) return 1; // expected-warning {{comparison of distinct pointer types ('struct S5 *' and 'struct S5 *')}} + if (s5 == s5_2) return 1; // expected-error {{comparison of distinct pointer types ('struct S5 *' and 'struct S5 *')}} return 0; } -- 2.40.0