summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaExpr.cpp
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2016-10-21 02:36:37 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2016-10-21 02:36:37 +0000
commit0c1c53e3fad7fadb1c182b3e26e38378d2abe7f8 (patch)
treefb6ebc4ea1222b72b3768ce00b702fe8cc9df890 /clang/lib/Sema/SemaExpr.cpp
parent14699cf1c601d2e8f0b6bf6b8e651a14a5a1e42d (diff)
downloadbcm5719-llvm-0c1c53e3fad7fadb1c182b3e26e38378d2abe7f8.tar.gz
bcm5719-llvm-0c1c53e3fad7fadb1c182b3e26e38378d2abe7f8.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. llvm-svn: 284800
Diffstat (limited to 'clang/lib/Sema/SemaExpr.cpp')
-rw-r--r--clang/lib/Sema/SemaExpr.cpp193
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) {
OpenPOWER on IntegriCloud