diff options
author | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-10-21 22:00:42 +0000 |
---|---|---|
committer | Richard Smith <richard-llvm@metafoo.co.uk> | 2016-10-21 22:00:42 +0000 |
commit | 5e9746f520e6bbeafc36b8a23585b2e6117e1a7a (patch) | |
tree | 19d45b99d8cfaddc7598d6986cdd522463951cad /clang/lib/Sema/SemaExpr.cpp | |
parent | 00c03c5de7fd930da8fe1f9561037c4bc3987685 (diff) | |
download | bcm5719-llvm-5e9746f520e6bbeafc36b8a23585b2e6117e1a7a.tar.gz bcm5719-llvm-5e9746f520e6bbeafc36b8a23585b2e6117e1a7a.zip |
DR583, DR1512: Implement a rewrite to C++'s 'composite pointer type' rules.
This has two significant effects:
1) Direct relational comparisons between null pointer constants (0 and nullopt)
and pointers are now ill-formed. This was always the case for C, and it
appears that C++ only ever permitted by accident. For instance, cases like
nullptr < &a
are now rejected.
2) Comparisons and conditional operators between differently-cv-qualified
pointer types now work, and produce a composite type that both source
pointer types can convert to (when possible). For instance, comparison
between 'int **' and 'const int **' is now valid, and uses an intermediate
type of 'const int *const *'.
Clang previously supported #2 as an extension.
We do not accept the cases in #1 as an extension. I've tested a fair amount of
code to check that this doesn't break it, but if it turns out that someone is
relying on this, we can easily add it back as an extension.
This is a re-commit of r284800.
llvm-svn: 284890
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) { |