diff options
Diffstat (limited to 'clang/lib/Sema/SemaExpr.cpp')
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 193 |
1 files changed, 111 insertions, 82 deletions
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 15564918b37..eddc4207dfc 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8929,35 +8929,21 @@ static bool convertPointersToCompositeType(Sema &S, SourceLocation Loc, // C++ [expr.eq]p1 uses the same notion for (in)equality // comparisons of pointers. - // 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 LHSType = LHS.get()->getType(); QualType RHSType = RHS.get()->getType(); - assert((LHSType->isPointerType() && RHSType->isPointerType()) || - (LHSType->isMemberPointerType() && RHSType->isMemberPointerType())); + assert(LHSType->isPointerType() || RHSType->isPointerType() || + LHSType->isMemberPointerType() || RHSType->isMemberPointerType()); - bool NonStandardCompositeType = false; - bool *BoolPtr = S.isSFINAEContext() ? nullptr : &NonStandardCompositeType; - QualType T = S.FindCompositePointerType(Loc, LHS, RHS, BoolPtr); + QualType T = S.FindCompositePointerType(Loc, LHS, RHS); if (T.isNull()) { - diagnoseDistinctPointerComparison(S, Loc, LHS, RHS, /*isError*/true); + if ((LHSType->isPointerType() || LHSType->isMemberPointerType()) && + (RHSType->isPointerType() || RHSType->isMemberPointerType())) + diagnoseDistinctPointerComparison(S, Loc, LHS, RHS, /*isError*/true); + else + S.InvalidOperands(Loc, LHS, RHS); return true; } - if (NonStandardCompositeType) - S.Diag(Loc, diag::ext_typecheck_comparison_of_distinct_pointers_nonstandard) - << LHSType << RHSType << T << LHS.get()->getSourceRange() - << RHS.get()->getSourceRange(); - LHS = S.ImpCastExprToType(LHS.get(), T, CK_BitCast); RHS = S.ImpCastExprToType(RHS.get(), T, CK_BitCast); return false; @@ -9314,41 +9300,53 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, LHS.get()->getSourceRange()); } - // All of the following pointer-related warnings are GCC extensions, except - // when handling null pointer constants. - if (LHSType->isPointerType() && RHSType->isPointerType()) { // C99 6.5.8p2 - QualType LCanPointeeTy = - LHSType->castAs<PointerType>()->getPointeeType().getCanonicalType(); - QualType RCanPointeeTy = - RHSType->castAs<PointerType>()->getPointeeType().getCanonicalType(); - - if (getLangOpts().CPlusPlus) { - if (LCanPointeeTy == RCanPointeeTy) - return ResultTy; - if (!IsRelational && - (LCanPointeeTy->isVoidType() || RCanPointeeTy->isVoidType())) { - // Valid unless comparison between non-null pointer and function pointer - // This is a gcc extension compatibility comparison. - // In a SFINAE context, we treat this as a hard error to maintain - // conformance with the C++ standard. - if ((LCanPointeeTy->isFunctionType() || RCanPointeeTy->isFunctionType()) - && !LHSIsNull && !RHSIsNull) { - diagnoseFunctionPointerToVoidComparison( - *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext()); - - if (isSFINAEContext()) - return QualType(); - - RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return ResultTy; - } - } + if ((LHSType->isIntegerType() && !LHSIsNull) || + (RHSType->isIntegerType() && !RHSIsNull)) { + // Skip normal pointer conversion checks in this case; we have better + // diagnostics for this below. + } else if (getLangOpts().CPlusPlus) { + // Equality comparison of a function pointer to a void pointer is invalid, + // but we allow it as an extension. + // FIXME: If we really want to allow this, should it be part of composite + // pointer type computation so it works in conditionals too? + if (!IsRelational && + ((LHSType->isFunctionPointerType() && RHSType->isVoidPointerType()) || + (RHSType->isFunctionPointerType() && LHSType->isVoidPointerType()))) { + // This is a gcc extension compatibility comparison. + // In a SFINAE context, we treat this as a hard error to maintain + // conformance with the C++ standard. + diagnoseFunctionPointerToVoidComparison( + *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext()); + + if (isSFINAEContext()) + return QualType(); + + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); + return ResultTy; + } + // C++ [expr.eq]p2: + // If at least one operand is a pointer [...] bring them to their + // composite pointer type. + // C++ [expr.rel]p2: + // If both operands are pointers, [...] bring them to their composite + // pointer type. + if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >= + (IsRelational ? 2 : 1)) { if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) return QualType(); else return ResultTy; } + } else if (LHSType->isPointerType() && + RHSType->isPointerType()) { // C99 6.5.8p2 + // All of the following pointer-related warnings are GCC extensions, except + // when handling null pointer constants. + QualType LCanPointeeTy = + LHSType->castAs<PointerType>()->getPointeeType().getCanonicalType(); + QualType RCanPointeeTy = + RHSType->castAs<PointerType>()->getPointeeType().getCanonicalType(); + // C99 6.5.9p2 and C99 6.5.8p2 if (Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(), RCanPointeeTy.getUnqualifiedType())) { @@ -9393,36 +9391,63 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, } if (getLangOpts().CPlusPlus) { - // Comparison of nullptr_t with itself. - if (LHSType->isNullPtrType() && RHSType->isNullPtrType()) - return ResultTy; - - // Comparison of pointers with null pointer constants and equality - // comparisons of member pointers to null pointer constants. - if (RHSIsNull && - ((LHSType->isAnyPointerType() || LHSType->isNullPtrType()) || - (!IsRelational && - (LHSType->isMemberPointerType() || LHSType->isBlockPointerType())))) { - RHS = ImpCastExprToType(RHS.get(), LHSType, - LHSType->isMemberPointerType() - ? CK_NullToMemberPointer - : CK_NullToPointer); + // C++ [expr.eq]p4: + // Two operands of type std::nullptr_t or one operand of type + // std::nullptr_t and the other a null pointer constant compare equal. + if (!IsRelational && LHSIsNull && RHSIsNull) { + if (LHSType->isNullPtrType()) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + return ResultTy; + } + if (RHSType->isNullPtrType()) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return ResultTy; + } + } + + // Comparison of Objective-C pointers and block pointers against nullptr_t. + // These aren't covered by the composite pointer type rules. + if (!IsRelational && RHSType->isNullPtrType() && + (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); return ResultTy; } - if (LHSIsNull && - ((RHSType->isAnyPointerType() || RHSType->isNullPtrType()) || - (!IsRelational && - (RHSType->isMemberPointerType() || RHSType->isBlockPointerType())))) { - LHS = ImpCastExprToType(LHS.get(), RHSType, - RHSType->isMemberPointerType() - ? CK_NullToMemberPointer - : CK_NullToPointer); + if (!IsRelational && LHSType->isNullPtrType() && + (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); return ResultTy; } - // Comparison of member pointers. + if (IsRelational && + ((LHSType->isNullPtrType() && RHSType->isPointerType()) || + (RHSType->isNullPtrType() && LHSType->isPointerType()))) { + // HACK: Relational comparison of nullptr_t against a pointer type is + // invalid per DR583, but we allow it within std::less<> and friends, + // since otherwise common uses of it break. + // FIXME: Consider removing this hack once LWG fixes std::less<> and + // friends to have std::nullptr_t overload candidates. + DeclContext *DC = CurContext; + if (isa<FunctionDecl>(DC)) + DC = DC->getParent(); + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(DC)) { + if (CTSD->isInStdNamespace() && + llvm::StringSwitch<bool>(CTSD->getName()) + .Cases("less", "less_equal", "greater", "greater_equal", true) + .Default(false)) { + if (RHSType->isNullPtrType()) + RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); + else + LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); + return ResultTy; + } + } + } + + // C++ [expr.eq]p2: + // If at least one operand is a pointer to member, [...] bring them to + // their composite pointer type. if (!IsRelational && - LHSType->isMemberPointerType() && RHSType->isMemberPointerType()) { + (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) { if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) return QualType(); else @@ -9531,15 +9556,19 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, // Under a debugger, allow the comparison of pointers to integers, // since users tend to want to compare addresses. } else if ((LHSIsNull && LHSType->isIntegerType()) || - (RHSIsNull && RHSType->isIntegerType())) { - if (IsRelational && !getLangOpts().CPlusPlus) - DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_and_zero; - } else if (IsRelational && !getLangOpts().CPlusPlus) - DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_integer; - else if (getLangOpts().CPlusPlus) { + (RHSIsNull && RHSType->isIntegerType())) { + if (IsRelational) { + isError = getLangOpts().CPlusPlus; + DiagID = + isError ? diag::err_typecheck_ordered_comparison_of_pointer_and_zero + : diag::ext_typecheck_ordered_comparison_of_pointer_and_zero; + } + } else if (getLangOpts().CPlusPlus) { DiagID = diag::err_typecheck_comparison_of_pointer_integer; isError = true; - } else + } else if (IsRelational) + DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_integer; + else DiagID = diag::ext_typecheck_comparison_of_pointer_integer; if (DiagID) { |