diff options
29 files changed, 574 insertions, 216 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index ede1d9e3a0..974f3bd02c 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -5536,6 +5536,8 @@ def ext_typecheck_ordered_comparison_of_pointer_integer : ExtWarn< "ordered comparison between pointer and integer (%0 and %1)">; def ext_typecheck_ordered_comparison_of_pointer_and_zero : Extension< "ordered comparison between pointer and zero (%0 and %1) is an extension">; +def err_typecheck_ordered_comparison_of_pointer_and_zero : Error< + "ordered comparison between pointer and zero (%0 and %1)">; def ext_typecheck_ordered_comparison_of_function_pointers : ExtWarn< "ordered comparison of function pointers (%0 and %1)">; def ext_typecheck_comparison_of_fptr_to_void : Extension< @@ -5556,9 +5558,6 @@ def err_cond_voidptr_arc : Error < "in ARC mode">; def err_typecheck_comparison_of_distinct_pointers : Error< "comparison of distinct pointer types%diff{ ($ and $)|}0,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">, InGroup<CompareDistinctPointerType>; def err_typecheck_op_on_nonoverlapping_address_space_pointers : Error< "%select{comparison between %diff{ ($ and $)|}0,1" "|arithmetic operation with operands of type %diff{ ($ and $)|}0,1" @@ -6837,9 +6836,6 @@ 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%diff{ ($ and $)|}0,1">; -def ext_typecheck_cond_incompatible_operands_nonstandard : ExtWarn< - "incompatible operand types%diff{ ($ and $)|}0,1 use non-standard composite " - "pointer type %2">; def err_cast_selector_expr : Error< "cannot type cast @selector expression">; def ext_typecheck_cond_incompatible_pointers : ExtWarn< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 08e6a29ca1..951d5c4e7d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -8954,15 +8954,13 @@ public: ExprResult &cond, ExprResult &lhs, ExprResult &rhs, ExprValueKind &VK, ExprObjectKind &OK, SourceLocation questionLoc); QualType FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2, - bool *NonStandardCompositeType = nullptr, bool ConvertArgs = true); QualType FindCompositePointerType(SourceLocation Loc, ExprResult &E1, ExprResult &E2, - bool *NonStandardCompositeType = nullptr, bool ConvertArgs = true) { Expr *E1Tmp = E1.get(), *E2Tmp = E2.get(); - QualType Composite = FindCompositePointerType( - Loc, E1Tmp, E2Tmp, NonStandardCompositeType, ConvertArgs); + QualType Composite = + FindCompositePointerType(Loc, E1Tmp, E2Tmp, ConvertArgs); E1 = E1Tmp; E2 = E2Tmp; return Composite; diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 15564918b3..eddc4207df 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/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) { diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 83474cb7f8..66705f88f9 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -5399,7 +5399,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, // exception specifications, if any. if (LTy->isFunctionPointerType() || LTy->isMemberFunctionPointerType()) { Qualifiers Qs = LTy.getQualifiers(); - LTy = FindCompositePointerType(QuestionLoc, LHS, RHS, nullptr, + LTy = FindCompositePointerType(QuestionLoc, LHS, RHS, /*ConvertArgs*/false); LTy = Context.getQualifiedType(LTy, Qs); @@ -5511,19 +5511,9 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, // 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. - bool NonStandardCompositeType = false; - QualType Composite = FindCompositePointerType(QuestionLoc, LHS, RHS, - isSFINAEContext() ? nullptr - : &NonStandardCompositeType); - if (!Composite.isNull()) { - if (NonStandardCompositeType) - Diag(QuestionLoc, - diag::ext_typecheck_cond_incompatible_operands_nonstandard) - << LTy << RTy << Composite - << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); - + QualType Composite = FindCompositePointerType(QuestionLoc, LHS, RHS); + if (!Composite.isNull()) return Composite; - } // Similarly, attempt to find composite type of two objective-c pointers. Composite = FindCompositeObjCPointerType(LHS, RHS, QuestionLoc); @@ -5622,19 +5612,10 @@ mergeExceptionSpecs(Sema &S, FunctionProtoType::ExceptionSpecInfo ESI1, /// \param Loc The location of the operator requiring these two expressions to /// be converted to the composite pointer type. /// -/// 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. -/// /// \param ConvertArgs If \c false, do not convert E1 and E2 to the target type. QualType Sema::FindCompositePointerType(SourceLocation Loc, Expr *&E1, Expr *&E2, - bool *NonStandardCompositeType, bool ConvertArgs) { - if (NonStandardCompositeType) - *NonStandardCompositeType = false; - assert(getLangOpts().CPlusPlus && "This function assumes C++"); // C++1z [expr]p14: @@ -5727,8 +5708,7 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // 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()) + if (Composite1.getCVRQualifiers() != Composite2.getCVRQualifiers()) NeedConstBefore = QualifierUnion.size(); QualifierUnion.push_back( @@ -5745,8 +5725,7 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, // 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()) + if (Composite1.getCVRQualifiers() != Composite2.getCVRQualifiers()) NeedConstBefore = QualifierUnion.size(); QualifierUnion.push_back( @@ -5797,16 +5776,13 @@ QualType Sema::FindCompositePointerType(SourceLocation Loc, } } - if (NeedConstBefore && NonStandardCompositeType) { + if (NeedConstBefore) { // 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) { + 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. diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 8cc592e21e..8d6c562dd0 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -7624,12 +7624,12 @@ public: } // C++ [over.match.oper]p16: - // For every pointer to member type T, there exist candidate operator - // functions of the form + // For every pointer to member type T or type std::nullptr_t, there + // exist candidate operator functions of the form // // bool operator==(T,T); // bool operator!=(T,T); - void addEqualEqualOrNotEqualMemberPointerOverloads() { + void addEqualEqualOrNotEqualMemberPointerOrNullptrOverloads() { /// Set of (canonical) types that we've already handled. llvm::SmallPtrSet<QualType, 8> AddedTypes; @@ -7646,13 +7646,22 @@ public: QualType ParamTypes[2] = { *MemPtr, *MemPtr }; S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, CandidateSet); } + + if (CandidateTypes[ArgIdx].hasNullPtrType()) { + CanQualType NullPtrTy = S.Context.getCanonicalType(S.Context.NullPtrTy); + if (AddedTypes.insert(NullPtrTy).second) { + QualType ParamTypes[2] = { NullPtrTy, NullPtrTy }; + S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, + CandidateSet); + } + } } } // C++ [over.built]p15: // - // For every T, where T is an enumeration type, a pointer type, or - // std::nullptr_t, there exist candidate operator functions of the form + // For every T, where T is an enumeration type or a pointer type, + // there exist candidate operator functions of the form // // bool operator<(T, T); // bool operator>(T, T); @@ -7737,17 +7746,6 @@ public: QualType ParamTypes[2] = { *Enum, *Enum }; S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, CandidateSet); } - - if (CandidateTypes[ArgIdx].hasNullPtrType()) { - CanQualType NullPtrTy = S.Context.getCanonicalType(S.Context.NullPtrTy); - if (AddedTypes.insert(NullPtrTy).second && - !UserDefinedBinaryOperators.count(std::make_pair(NullPtrTy, - NullPtrTy))) { - QualType ParamTypes[2] = { NullPtrTy, NullPtrTy }; - S.AddBuiltinCandidate(S.Context.BoolTy, ParamTypes, Args, - CandidateSet); - } - } } } @@ -8443,7 +8441,7 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op, case OO_EqualEqual: case OO_ExclaimEqual: - OpBuilder.addEqualEqualOrNotEqualMemberPointerOverloads(); + OpBuilder.addEqualEqualOrNotEqualMemberPointerOrNullptrOverloads(); // Fall through. case OO_Less: diff --git a/test/CXX/drs/dr15xx.cpp b/test/CXX/drs/dr15xx.cpp index 5f85a196fd..2b4c900c76 100644 --- a/test/CXX/drs/dr15xx.cpp +++ b/test/CXX/drs/dr15xx.cpp @@ -3,9 +3,137 @@ // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++1z -triple x86_64-unknown-unknown %s -verify -fexceptions -fcxx-exceptions -pedantic-errors -#if __cplusplus < 201103L -// expected-no-diagnostics +namespace dr1512 { // dr1512: 4.0 + void f(char *p) { + if (p > 0) {} // expected-error {{ordered comparison between pointer and zero}} +#if __cplusplus >= 201103L + if (p > nullptr) {} // expected-error {{invalid operands}} #endif + } + bool g(int **x, const int **y) { + return x < y; + } + + template<typename T> T val(); + + template<typename A, typename B, typename C> void composite_pointer_type_is_base() { + typedef __typeof(true ? val<A>() : val<B>()) type; + typedef C type; + + typedef __typeof(val<A>() == val<B>()) cmp; + typedef __typeof(val<A>() != val<B>()) cmp; + typedef bool cmp; + } + + template<typename A, typename B, typename C> void composite_pointer_type_is_ord() { + composite_pointer_type_is_base<A, B, C>(); + + typedef __typeof(val<A>() < val<B>()) cmp; + typedef __typeof(val<A>() <= val<B>()) cmp; + typedef __typeof(val<A>() > val<B>()) cmp; + typedef __typeof(val<A>() >= val<B>()) cmp; + typedef bool cmp; + } + + template <typename A, typename B, typename C> + void composite_pointer_type_is_unord(int = 0) { + composite_pointer_type_is_base<A, B, C>(); + } + template <typename A, typename B, typename C> + void composite_pointer_type_is_unord(__typeof(val<A>() < val<B>()) * = 0); + template <typename A, typename B, typename C> + void composite_pointer_type_is_unord(__typeof(val<A>() <= val<B>()) * = 0); + template <typename A, typename B, typename C> + void composite_pointer_type_is_unord(__typeof(val<A>() > val<B>()) * = 0); + template <typename A, typename B, typename C> + void composite_pointer_type_is_unord(__typeof(val<A>() >= val<B>()) * = 0); + + // A call to this is ambiguous if a composite pointer type exists. + template<typename A, typename B> + void no_composite_pointer_type(__typeof((true ? val<A>() : val<B>()), void()) * = 0); + template<typename A, typename B> void no_composite_pointer_type(int = 0); + + struct A {}; + struct B : A {}; + struct C {}; + + void test() { +#if __cplusplus >= 201103L + using nullptr_t = decltype(nullptr); + composite_pointer_type_is_unord<nullptr_t, nullptr_t, nullptr_t>(); + no_composite_pointer_type<nullptr_t, int>(); + + composite_pointer_type_is_unord<nullptr_t, const char**, const char**>(); + composite_pointer_type_is_unord<const char**, nullptr_t, const char**>(); +#endif + + composite_pointer_type_is_ord<const int *, volatile void *, const volatile void*>(); + composite_pointer_type_is_ord<const void *, volatile int *, const volatile void*>(); + + composite_pointer_type_is_ord<const A*, volatile B*, const volatile A*>(); + composite_pointer_type_is_ord<const B*, volatile A*, const volatile A*>(); + + composite_pointer_type_is_unord<const int *A::*, volatile int *B::*, const volatile int *const B::*>(); + composite_pointer_type_is_unord<const int *B::*, volatile int *A::*, const volatile int *const B::*>(); + no_composite_pointer_type<int (A::*)(), int (C::*)()>(); + no_composite_pointer_type<const int (A::*)(), volatile int (C::*)()>(); + +#if __cplusplus > 201402 + composite_pointer_type_is_ord<int (*)() noexcept, int (*)(), int (*)()>(); + composite_pointer_type_is_ord<int (*)(), int (*)() noexcept, int (*)()>(); + composite_pointer_type_is_unord<int (A::*)() noexcept, int (A::*)(), int (A::*)()>(); + composite_pointer_type_is_unord<int (A::*)(), int (A::*)() noexcept, int (A::*)()>(); + // FIXME: This looks like a standard defect; these should probably all have type 'int (B::*)()'. + composite_pointer_type_is_unord<int (B::*)(), int (A::*)() noexcept, int (B::*)()>(); + composite_pointer_type_is_unord<int (A::*)() noexcept, int (B::*)(), int (B::*)()>(); + composite_pointer_type_is_unord<int (B::*)() noexcept, int (A::*)(), int (B::*)()>(); + composite_pointer_type_is_unord<int (A::*)(), int (B::*)() noexcept, int (B::*)()>(); + + // FIXME: It would be reasonable to permit these, with a common type of 'int (*const *)()'. + no_composite_pointer_type<int (**)() noexcept, int (**)()>(); + no_composite_pointer_type<int (**)(), int (**)() noexcept>(); + + // FIXME: It would be reasonable to permit these, with a common type of 'int (A::*)()'. + no_composite_pointer_type<int (A::*)() const, int (A::*)()>(); + no_composite_pointer_type<int (A::*)(), int (A::*)() const>(); + + // FIXME: It would be reasonable to permit these, with a common type of + // 'int (A::*)() &' and 'int (A::*)() &&', respectively. + no_composite_pointer_type<int (A::*)() &, int (A::*)()>(); + no_composite_pointer_type<int (A::*)(), int (A::*)() &>(); + no_composite_pointer_type<int (A::*)() &&, int (A::*)()>(); + no_composite_pointer_type<int (A::*)(), int (A::*)() &&>(); + + no_composite_pointer_type<int (A::*)() &&, int (A::*)() &>(); + no_composite_pointer_type<int (A::*)() &, int (A::*)() &&>(); + + no_composite_pointer_type<int (C::*)(), int (A::*)() noexcept>(); + no_composite_pointer_type<int (A::*)() noexcept, int (C::*)()>(); +#endif + } + + template<typename T> struct Wrap { operator T(); }; + void test_overload() { +#if __cplusplus >= 201103L + using nullptr_t = decltype(nullptr); + void(Wrap<nullptr_t>() == Wrap<nullptr_t>()); + void(Wrap<nullptr_t>() != Wrap<nullptr_t>()); + void(Wrap<nullptr_t>() < Wrap<nullptr_t>()); // expected-error {{invalid operands}} + void(Wrap<nullptr_t>() > Wrap<nullptr_t>()); // expected-error {{invalid operands}} + void(Wrap<nullptr_t>() <= Wrap<nullptr_t>()); // expected-error {{invalid operands}} + void(Wrap<nullptr_t>() >= Wrap<nullptr_t>()); // expected-error {{invalid operands}} + + // The wording change fails to actually disallow this. This is valid + // via the builtin operator<(int*, int*) etc. + void(Wrap<nullptr_t>() == Wrap<int*>()); + void(Wrap<nullptr_t>() != Wrap<int*>()); + void(Wrap<nullptr_t>() < Wrap<int*>()); + void(Wrap<nullptr_t>() > Wrap<int*>()); + void(Wrap<nullptr_t>() <= Wrap<int*>()); + void(Wrap<nullptr_t>() >= Wrap<int*>()); +#endif + } +} namespace dr1550 { // dr1550: yes int f(bool b, int n) { diff --git a/test/CXX/drs/dr5xx.cpp b/test/CXX/drs/dr5xx.cpp index 6461712cf0..9bac700579 100644 --- a/test/CXX/drs/dr5xx.cpp +++ b/test/CXX/drs/dr5xx.cpp @@ -863,14 +863,13 @@ namespace dr580 { // dr580: partial // dr582: na -namespace dr583 { // dr583: no +namespace dr583 { // dr583: 4.0 // see n3624 int *p; - // FIXME: These are all ill-formed. - bool b1 = p < 0; - bool b2 = p > 0; - bool b3 = p <= 0; - bool b4 = p >= 0; + bool b1 = p < 0; // expected-error {{ordered comparison between pointer and zero}} + bool b2 = p > 0; // expected-error {{ordered comparison between pointer and zero}} + bool b3 = p <= 0; // expected-error {{ordered comparison between pointer and zero}} + bool b4 = p >= 0; // expected-error {{ordered comparison between pointer and zero}} } // dr584: na diff --git a/test/CXX/expr/expr.const/p2-0x.cpp b/test/CXX/expr/expr.const/p2-0x.cpp index fd15960647..b9927e49c7 100644 --- a/test/CXX/expr/expr.const/p2-0x.cpp +++ b/test/CXX/expr/expr.const/p2-0x.cpp @@ -461,14 +461,14 @@ namespace UnspecifiedRelations { constexpr bool u2 = p > q; // expected-error {{constant expression}} constexpr bool u3 = p <= q; // expected-error {{constant expression}} constexpr bool u4 = p >= q; // expected-error {{constant expression}} - constexpr bool u5 = p < 0; // expected-error {{constant expression}} - constexpr bool u6 = p <= 0; // expected-error {{constant expression}} - constexpr bool u7 = p > 0; // expected-error {{constant expression}} - constexpr bool u8 = p >= 0; // expected-error {{constant expression}} - constexpr bool u9 = 0 < q; // expected-error {{constant expression}} - constexpr bool u10 = 0 <= q; // expected-error {{constant expression}} - constexpr bool u11 = 0 > q; // expected-error {{constant expression}} - constexpr bool u12 = 0 >= q; // expected-error {{constant expression}} + constexpr bool u5 = p < (int*)0; // expected-error {{constant expression}} + constexpr bool u6 = p <= (int*)0; // expected-error {{constant expression}} + constexpr bool u7 = p > (int*)0; // expected-error {{constant expression}} + constexpr bool u8 = p >= (int*)0; // expected-error {{constant expression}} + constexpr bool u9 = (int*)0 < q; // expected-error {{constant expression}} + constexpr bool u10 = (int*)0 <= q; // expected-error {{constant expression}} + constexpr bool u11 = (int*)0 > q; // expected-error {{constant expression}} + constexpr bool u12 = (int*)0 >= q; // expected-error {{constant expression}} void f(), g(); constexpr void (*pf)() = &f, (*pg)() = &g; @@ -522,7 +522,7 @@ namespace UnspecifiedRelations { constexpr void *null = 0; constexpr void *pv = (void*)&s.a; constexpr void *qv = (void*)&s.b; - constexpr bool v1 = null < 0; + constexpr bool v1 = null < (int*)0; constexpr bool v2 = null < pv; // expected-error {{constant expression}} constexpr bool v3 = null == pv; // ok constexpr bool v4 = qv == pv; // ok diff --git a/test/CXX/over/over.built/p15.cpp b/test/CXX/over/over.built/p15.cpp new file mode 100644 index 0000000000..64ed3e7b83 --- /dev/null +++ b/test/CXX/over/over.built/p15.cpp @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s -Wno-tautological-compare + +struct A { operator decltype(nullptr)(); }; +struct B { operator const int *(); }; +void f(A a, B b, volatile int *pi) { + (void)(a == a); + (void)(a != a); + (void)(a < a); // expected-error {{invalid operands}} + (void)(a > a); // expected-error {{invalid operands}} + (void)(a <= a); // expected-error {{invalid operands}} + (void)(a >= a); // expected-error {{invalid operands}} + + (void)(a == b); + (void)(a != b); + // FIXME: These cases were intended to be made ill-formed by N3624, but it + // fails to actually achieve this goal. + (void)(a < b); + (void)(a > b); + (void)(a <= b); + (void)(a >= b); + + (void)(b == a); + (void)(b != a); + // FIXME: These cases were intended to be made ill-formed by N3624, but it + // fails to actually achieve this goal. + (void)(b < a); + (void)(b > a); + (void)(b <= a); + (void)(b >= a); + + (void)(a == pi); + (void)(a != pi); + // FIXME: These cases were intended to be made ill-formed by N3624, but it + // fails to actually achieve this goal. + (void)(a < pi); + (void)(a > pi); + (void)(a <= pi); + (void)(a >= pi); + + (void)(pi == a); + (void)(pi != a); + // FIXME: These cases were intended to be made ill-formed by N3624, but it + // fails to actually achieve this goal. + (void)(pi < a); + (void)(pi > a); + (void)(pi <= a); + (void)(pi >= a); + + (void)(b == pi); + (void)(b != pi); + (void)(b < pi); + (void)(b > pi); + (void)(b <= pi); + (void)(b >= pi); + + (void)(pi == b); + (void)(pi != b); + (void)(pi < b); + (void)(pi > b); + (void)(pi <= b); + (void)(pi >= b); + + (void)(b == b); + (void)(b != b); + (void)(b < b); + (void)(b > b); + (void)(b <= b); + (void)(b >= b); + + (void)(pi == pi); + (void)(pi != pi); + (void)(pi < pi); + (void)(pi > pi); + (void)(pi <= pi); + (void)(pi >= pi); +} + +// FIXME: This is wrong: the type T = 'const volatile int * const * const *' +// would work here, and there exists a builtin candidate for that type. +struct C { operator const int ***(); }; +void g(C c, volatile int ***p) { + (void)(c < p); // expected-error {{invalid operands}} +} diff --git a/test/CXX/over/over.built/p16.cpp b/test/CXX/over/over.built/p16.cpp new file mode 100644 index 0000000000..139e864475 --- /dev/null +++ b/test/CXX/over/over.built/p16.cpp @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -std=c++11 -verify %s -Wno-tautological-compare + +struct A { operator decltype(nullptr)(); }; +struct B { operator int A::*(); }; +void f(A a, B b, int A::*pi) { + (void)(a == a); + (void)(a != a); + (void)(a < a); // expected-error {{invalid operands}} + (void)(a > a); // expected-error {{invalid operands}} + (void)(a <= a); // expected-error {{invalid operands}} + (void)(a >= a); // expected-error {{invalid operands}} + + (void)(a == b); + (void)(a != b); + (void)(a < b); // expected-error {{invalid operands}} + (void)(a > b); // expected-error {{invalid operands}} + (void)(a <= b); // expected-error {{invalid operands}} + (void)(a >= b); // expected-error {{invalid operands}} + + (void)(b == a); + (void)(b != a); + (void)(b < a); // expected-error {{invalid operands}} + (void)(b > a); // expected-error {{invalid operands}} + (void)(b <= a); // expected-error {{invalid operands}} + (void)(b >= a); // expected-error {{invalid operands}} + + (void)(a == pi); + (void)(a != pi); + (void)(a < pi); // expected-error {{invalid operands}} + (void)(a > pi); // expected-error {{invalid operands}} + (void)(a <= pi); // expected-error {{invalid operands}} + (void)(a >= pi); // expected-error {{invalid operands}} + + (void)(pi == a); + (void)(pi != a); + (void)(pi < a); // expected-error {{invalid operands}} + (void)(pi > a); // expected-error {{invalid operands}} + (void)(pi <= a); // expected-error {{invalid operands}} + (void)(pi >= a); // expected-error {{invalid operands}} + + (void)(b == pi); + (void)(b != pi); + (void)(b < pi); // expected-error {{invalid operands}} + (void)(b > pi); // expected-error {{invalid operands}} + (void)(b <= pi); // expected-error {{invalid operands}} + (void)(b >= pi); // expected-error {{invalid operands}} + + (void)(pi == b); + (void)(pi != b); + (void)(pi < b); // expected-error {{invalid operands}} + (void)(pi > b); // expected-error {{invalid operands}} + (void)(pi <= b); // expected-error {{invalid operands}} + (void)(pi >= b); // expected-error {{invalid operands}} + + (void)(b == b); + (void)(b != b); + (void)(b < b); // expected-error {{invalid operands}} + (void)(b > b); // expected-error {{invalid operands}} + (void)(b <= b); // expected-error {{invalid operands}} + (void)(b >= b); // expected-error {{invalid operands}} + + (void)(pi == pi); + (void)(pi != pi); + (void)(pi < pi); // expected-error {{invalid operands}} + (void)(pi > pi); // expected-error {{invalid operands}} + (void)(pi <= pi); // expected-error {{invalid operands}} + (void)(pi >= pi); // expected-error {{invalid operands}} +} + +// FIXME: This is wrong: type T = 'const volatile int * const A::* const B::*' +// would work here, and there exists a builtin candidate for that type. +struct C { operator const int *A::*B::*(); }; +void g(C c, volatile int *A::*B::*p) { + (void)(c == p); // expected-error {{invalid operands}} +} diff --git a/test/Misc/warning-flags.c b/test/Misc/warning-flags.c index 69e820542a..18222986e6 100644 --- a/test/Misc/warning-flags.c +++ b/test/Misc/warning-flags.c @@ -18,7 +18,7 @@ This test serves two purposes: The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (84): +CHECK: Warnings without flags (83): CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list @@ -31,7 +31,6 @@ CHECK-NEXT: ext_plain_complex CHECK-NEXT: ext_template_arg_extra_parens CHECK-NEXT: ext_typecheck_comparison_of_pointer_integer CHECK-NEXT: ext_typecheck_cond_incompatible_operands -CHECK-NEXT: ext_typecheck_cond_incompatible_operands_nonstandard CHECK-NEXT: ext_typecheck_ordered_comparison_of_function_pointers CHECK-NEXT: ext_typecheck_ordered_comparison_of_pointer_integer CHECK-NEXT: ext_using_undefined_std diff --git a/test/OpenMP/distribute_parallel_for_simd_aligned_messages.cpp b/test/OpenMP/distribute_parallel_for_simd_aligned_messages.cpp index 9c9f3dda28..f865e0be42 100644 --- a/test/OpenMP/distribute_parallel_for_simd_aligned_messages.cpp +++ b/test/OpenMP/distribute_parallel_for_simd_aligned_messages.cpp @@ -166,7 +166,8 @@ template<class I, class C> int foomain(I argc, C **argv) { #pragma omp target #pragma omp teams -#pragma omp distribute parallel for simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? +#pragma omp distribute parallel for simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp target diff --git a/test/OpenMP/distribute_simd_aligned_messages.cpp b/test/OpenMP/distribute_simd_aligned_messages.cpp index 59e5be271d..10beb7198a 100644 --- a/test/OpenMP/distribute_simd_aligned_messages.cpp +++ b/test/OpenMP/distribute_simd_aligned_messages.cpp @@ -166,7 +166,8 @@ template<class I, class C> int foomain(I argc, C **argv) { #pragma omp target #pragma omp teams -#pragma omp distribute simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? +#pragma omp distribute simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp target diff --git a/test/OpenMP/for_simd_aligned_messages.cpp b/test/OpenMP/for_simd_aligned_messages.cpp index cef83c30e2..1d0918e449 100644 --- a/test/OpenMP/for_simd_aligned_messages.cpp +++ b/test/OpenMP/for_simd_aligned_messages.cpp @@ -121,7 +121,8 @@ template<class I, class C> int foomain(I argc, C **argv) { for (I k = 0; k < argc; ++k) ++k; #pragma omp for simd aligned (argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} for (I k = 0; k < argc; ++k) ++k; - #pragma omp for simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? + #pragma omp for simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp for simd aligned (argc : 5) // expected-warning {{aligned clause will be ignored because the requested alignment is not a power of 2}} for (I k = 0; k < argc; ++k) ++k; diff --git a/test/OpenMP/parallel_for_simd_aligned_messages.cpp b/test/OpenMP/parallel_for_simd_aligned_messages.cpp index 2ccdf06976..fc0f88cc38 100644 --- a/test/OpenMP/parallel_for_simd_aligned_messages.cpp +++ b/test/OpenMP/parallel_for_simd_aligned_messages.cpp @@ -121,7 +121,8 @@ template<class I, class C> int foomain(I argc, C **argv) { for (I k = 0; k < argc; ++k) ++k; #pragma omp parallel for simd aligned (argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} for (I k = 0; k < argc; ++k) ++k; - #pragma omp parallel for simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? + #pragma omp parallel for simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp parallel for simd aligned (argc : 5) // expected-warning {{aligned clause will be ignored because the requested alignment is not a power of 2}} for (I k = 0; k < argc; ++k) ++k; diff --git a/test/OpenMP/simd_aligned_messages.cpp b/test/OpenMP/simd_aligned_messages.cpp index 9515a0bca6..81aec960f2 100644 --- a/test/OpenMP/simd_aligned_messages.cpp +++ b/test/OpenMP/simd_aligned_messages.cpp @@ -121,7 +121,8 @@ template<class I, class C> int foomain(I argc, C **argv) { for (I k = 0; k < argc; ++k) ++k; #pragma omp simd aligned (argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} for (I k = 0; k < argc; ++k) ++k; - #pragma omp simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? + #pragma omp simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp simd aligned (argc : 5) // expected-warning {{aligned clause will be ignored because the requested alignment is not a power of 2}} for (I k = 0; k < argc; ++k) ++k; diff --git a/test/OpenMP/target_parallel_for_simd_aligned_messages.cpp b/test/OpenMP/target_parallel_for_simd_aligned_messages.cpp index 669cafeae1..538d65b82b 100644 --- a/test/OpenMP/target_parallel_for_simd_aligned_messages.cpp +++ b/test/OpenMP/target_parallel_for_simd_aligned_messages.cpp @@ -121,7 +121,8 @@ template<class I, class C> int foomain(I argc, C **argv) { for (I k = 0; k < argc; ++k) ++k; #pragma omp target parallel for simd aligned (argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} for (I k = 0; k < argc; ++k) ++k; - #pragma omp target parallel for simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? + #pragma omp target parallel for simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp target parallel for simd aligned (argc : 5) // expected-warning {{aligned clause will be ignored because the requested alignment is not a power of 2}} for (I k = 0; k < argc; ++k) ++k; diff --git a/test/OpenMP/target_simd_aligned_messages.cpp b/test/OpenMP/target_simd_aligned_messages.cpp index 547f3b41fb..ae2859d5b6 100644 --- a/test/OpenMP/target_simd_aligned_messages.cpp +++ b/test/OpenMP/target_simd_aligned_messages.cpp @@ -121,7 +121,8 @@ template<class I, class C> int foomain(I argc, C **argv) { for (I k = 0; k < argc; ++k) ++k; #pragma omp target simd aligned (argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} for (I k = 0; k < argc; ++k) ++k; - #pragma omp target simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? + #pragma omp target simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp target simd aligned (argc : 5) // expected-warning {{aligned clause will be ignored because the requested alignment is not a power of 2}} for (I k = 0; k < argc; ++k) ++k; diff --git a/test/OpenMP/taskloop_simd_aligned_messages.cpp b/test/OpenMP/taskloop_simd_aligned_messages.cpp index b45f44fe1c..6085660b70 100644 --- a/test/OpenMP/taskloop_simd_aligned_messages.cpp +++ b/test/OpenMP/taskloop_simd_aligned_messages.cpp @@ -121,7 +121,8 @@ template<class I, class C> int foomain(I argc, C **argv) { for (I k = 0; k < argc; ++k) ++k; #pragma omp taskloop simd aligned (argc, // expected-error {{expected expression}} expected-error {{expected ')'}} expected-note {{to match this '('}} for (I k = 0; k < argc; ++k) ++k; - #pragma omp taskloop simd aligned (argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} +// FIXME: Should argc really be a pointer? + #pragma omp taskloop simd aligned (*argc > 0 ? argv[1] : argv[2]) // expected-error {{expected variable name}} for (I k = 0; k < argc; ++k) ++k; #pragma omp taskloop simd aligned (argc : 5) // expected-warning {{aligned clause will be ignored because the requested alignment is not a power of 2}} for (I k = 0; k < argc; ++k) ++k; diff --git a/test/SemaCXX/compare.cpp b/test/SemaCXX/compare.cpp index ef0a524f92..0528b044fb 100644 --- a/test/SemaCXX/compare.cpp +++ b/test/SemaCXX/compare.cpp @@ -201,9 +201,10 @@ int test1(int i) { enum E { e }; void test2(int i, void *vp) { + if (&i == vp) { } // ok if (test1 == vp) { } // expected-warning{{equality comparison between function pointer and void pointer}} if (test1 == e) { } // expected-error{{comparison between pointer and integer}} - if (vp < 0) { } + if (vp < 0) { } // expected-error {{comparison between pointer and zero}} if (test1 < e) { } // expected-error{{comparison between pointer and integer}} } diff --git a/test/SemaCXX/composite-pointer-type.cpp b/test/SemaCXX/composite-pointer-type.cpp index 06fc8f4385..dddf424caa 100644 --- a/test/SemaCXX/composite-pointer-type.cpp +++ b/test/SemaCXX/composite-pointer-type.cpp @@ -53,8 +53,8 @@ bool f(Matrix4 m1, const Matrix4 m2) { // PR6346 bool f1(bool b, void **p, const void **q) { - if (p == q) // expected-warning{{comparison of distinct pointer types ('void **' and 'const void **') uses non-standard composite pointer type 'const void *const *'}} + if (p == q) return false; - return b? p : q; // expected-warning{{incompatible operand types ('void **' and 'const void **') use non-standard composite pointer type 'const void *const *'}} + return b? p : q; } diff --git a/test/SemaCXX/constant-expression-cxx11.cpp b/test/SemaCXX/constant-expression-cxx11.cpp index e2b3f091f7..0b36f3b6cb 100644 --- a/test/SemaCXX/constant-expression-cxx11.cpp +++ b/test/SemaCXX/constant-expression-cxx11.cpp @@ -279,17 +279,17 @@ static_assert(&s.x > &s.y, "false"); // expected-error {{false}} static_assert(0 == &y, "false"); // expected-error {{false}} static_assert(0 != &y, ""); -constexpr bool n3 = 0 <= &y; // expected-error {{must be initialized by a constant expression}} -constexpr bool n4 = 0 >= &y; // expected-error {{must be initialized by a constant expression}} -constexpr bool n5 = 0 < &y; // expected-error {{must be initialized by a constant expression}} -constexpr bool n6 = 0 > &y; // expected-error {{must be initialized by a constant expression}} +constexpr bool n3 = (int*)0 <= &y; // expected-error {{must be initialized by a constant expression}} +constexpr bool n4 = (int*)0 >= &y; // expected-error {{must be initialized by a constant expression}} +constexpr bool n5 = (int*)0 < &y; // expected-error {{must be initialized by a constant expression}} +constexpr bool n6 = (int*)0 > &y; // expected-error {{must be initialized by a constant expression}} static_assert(&x == 0, "false"); // expected-error {{false}} static_assert(&x != 0, ""); -constexpr bool n9 = &x <= 0; // expected-error {{must be initialized by a constant expression}} -constexpr bool n10 = &x >= 0; // expected-error {{must be initialized by a constant expression}} -constexpr bool n11 = &x < 0; // expected-error {{must be initialized by a constant expression}} -constexpr bool n12 = &x > 0; // expected-error {{must be initialized by a constant expression}} +constexpr bool n9 = &x <= (int*)0; // expected-error {{must be initialized by a constant expression}} +constexpr bool n10 = &x >= (int*)0; // expected-error {{must be initialized by a constant expression}} +constexpr bool n11 = &x < (int*)0; // expected-error {{must be initialized by a constant expression}} +constexpr bool n12 = &x > (int*)0; // expected-error {{must be initialized by a constant expression}} static_assert(&x == &x, ""); static_assert(&x != &x, "false"); // expected-error {{false}} diff --git a/test/SemaCXX/libstdcxx_libcxx_less_hack.cpp b/test/SemaCXX/libstdcxx_libcxx_less_hack.cpp new file mode 100644 index 0000000000..53b6a3b2c4 --- /dev/null +++ b/test/SemaCXX/libstdcxx_libcxx_less_hack.cpp @@ -0,0 +1,67 @@ +// This is a test for a hack in Clang that works around a problem introduced by +// DR583: it's no longer possible to compare a pointer against nullptr_t, but +// we still want to permit those comparisons within less<> and friends. + +// RUN: %clang_cc1 -verify %s -std=c++14 + +namespace std { + template<typename T = void> struct less {}; + template<typename T = void> struct less_equal {}; + template<typename T = void> struct greater {}; + template<typename T = void> struct greater_equal {}; + + template<> struct less<> { + template <class T1, class T2> + auto operator()(T1 &&t, T2 &&u) const noexcept(noexcept(t < u)) + -> decltype(t < u) { + return t < u; + } + }; + + template<> struct less_equal<> { + template <class T1, class T2> + auto operator()(T1 &&t, T2 &&u) const noexcept(noexcept(t <= u)) + -> decltype(t <= u) { + return t <= u; + } + }; + + template<> struct greater<> { + template <class T1, class T2> + auto operator()(T1 &&t, T2 &&u) const noexcept(noexcept(t > u)) + -> decltype(t > u) { + return t > u; + } + }; + + template<> struct greater_equal<> { + template <class T1, class T2> + auto operator()(T1 &&t, T2 &&u) const noexcept(noexcept(t >= u)) + -> decltype(t >= u) { + return t >= u; + } + }; + + template<typename = void> struct unrelated; + template<> struct unrelated<> { + template <class T1, class T2> + auto operator()(T1 &&t, T2 &&u) const noexcept(noexcept(t < u)) // expected-note {{substitution failure}} + -> decltype(t < u) { + return t < u; + } + }; +}; + +void test(int *p) { + using namespace std; + less<>()(p, nullptr); + less<>()(nullptr, p); + less_equal<>()(p, nullptr); + less_equal<>()(nullptr, p); + greater<>()(p, nullptr); + greater<>()(nullptr, p); + greater_equal<>()(p, nullptr); + greater_equal<>()(nullptr, p); + + unrelated<>()(p, nullptr); // expected-error {{no matching function}} +} diff --git a/test/SemaCXX/null_in_arithmetic_ops.cpp b/test/SemaCXX/null_in_arithmetic_ops.cpp index 3b42ab44fe..ee695ff242 100644 --- a/test/SemaCXX/null_in_arithmetic_ops.cpp +++ b/test/SemaCXX/null_in_arithmetic_ops.cpp @@ -71,8 +71,8 @@ void f() { b = a == NULL || a != NULL; // expected-warning 2{{comparison between NULL and non-pointer ('int' and NULL)}} b = NULL == a || NULL != a; // expected-warning 2{{comparison between NULL and non-pointer (NULL and 'int')}} - b = &a < NULL || NULL < &a || &a > NULL || NULL > &a; - b = &a <= NULL || NULL <= &a || &a >= NULL || NULL >= &a; + b = &a < NULL || NULL < &a || &a > NULL || NULL > &a; // expected-error 4{{ordered comparison between pointer and zero}} + b = &a <= NULL || NULL <= &a || &a >= NULL || NULL >= &a; // expected-error 4{{ordered comparison between pointer and zero}} b = &a == NULL || NULL == &a || &a != NULL || NULL != &a; b = 0 == a; diff --git a/test/SemaCXX/nullptr.cpp b/test/SemaCXX/nullptr.cpp index 7d765b482c..9a092910b6 100644 --- a/test/SemaCXX/nullptr.cpp +++ b/test/SemaCXX/nullptr.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 -ffreestanding -Wno-null-conversion %s +// RUN: %clang_cc1 -fcxx-exceptions -fexceptions -fsyntax-only -verify -std=c++11 -ffreestanding -Wno-null-conversion -Wno-tautological-compare %s #include <stdint.h> typedef decltype(nullptr) nullptr_t; @@ -32,17 +32,17 @@ nullptr_t f(nullptr_t null) // Operators (void)(null == nullptr); - (void)(null <= nullptr); + (void)(null <= nullptr); // expected-error {{invalid operands to binary expression}} (void)(null == 0); (void)(null == (void*)0); (void)((void*)0 == nullptr); - (void)(null <= 0); - (void)(null <= (void*)0); - (void)((void*)0 <= nullptr); + (void)(null <= 0); // expected-error {{invalid operands to binary expression}} + (void)(null <= (void*)0); // expected-error {{invalid operands to binary expression}} + (void)((void*)0 <= nullptr); // expected-error {{invalid operands to binary expression}} (void)(0 == nullptr); (void)(nullptr == 0); - (void)(nullptr <= 0); - (void)(0 <= nullptr); + (void)(nullptr <= 0); // expected-error {{invalid operands to binary expression}} + (void)(0 <= nullptr); // expected-error {{invalid operands to binary expression}} (void)(1 > nullptr); // expected-error {{invalid operands to binary expression}} (void)(1 != nullptr); // expected-error {{invalid operands to binary expression}} (void)(1 + nullptr); // expected-error {{invalid operands to binary expression}} @@ -118,24 +118,24 @@ static_assert(__is_scalar(nullptr_t), ""); static_assert(__is_pod(nullptr_t), ""); static_assert(sizeof(nullptr_t) == sizeof(void*), ""); -static_assert(!(nullptr < nullptr), ""); -static_assert(!(nullptr > nullptr), ""); -static_assert( nullptr <= nullptr, ""); -static_assert( nullptr >= nullptr, ""); +static_assert(!(nullptr < nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert(!(nullptr > nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr <= nullptr, ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr >= nullptr, ""); // expected-error {{invalid operands to binary expression}} static_assert( nullptr == nullptr, ""); static_assert(!(nullptr != nullptr), ""); -static_assert(!(0 < nullptr), ""); -static_assert(!(0 > nullptr), ""); -static_assert( 0 <= nullptr, ""); -static_assert( 0 >= nullptr, ""); +static_assert(!(0 < nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert(!(0 > nullptr), ""); // expected-error {{invalid operands to binary expression}} +static_assert( 0 <= nullptr, ""); // expected-error {{invalid operands to binary expression}} +static_assert( 0 >= nullptr, ""); // expected-error {{invalid operands to binary expression}} static_assert( 0 == nullptr, ""); static_assert(!(0 != nullptr), ""); -static_assert(!(nullptr < 0), ""); -static_assert(!(nullptr > 0), ""); -static_assert( nullptr <= 0, ""); -static_assert( nullptr >= 0, ""); +static_assert(!(nullptr < 0), ""); // expected-error {{invalid operands to binary expression}} +static_assert(!(nullptr > 0), ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr <= 0, ""); // expected-error {{invalid operands to binary expression}} +static_assert( nullptr >= 0, ""); // expected-error {{invalid operands to binary expression}} static_assert( nullptr == 0, ""); static_assert(!(nullptr != 0), ""); @@ -154,10 +154,10 @@ namespace overloading { void test_conversion(ConvertsToNullPtr ctn) { (void)(ctn == ctn); (void)(ctn != ctn); - (void)(ctn <= ctn); - (void)(ctn >= ctn); - (void)(ctn < ctn); - (void)(ctn > ctn); + (void)(ctn <= ctn); // expected-error {{invalid operands to binary expression}} + (void)(ctn >= ctn); // expected-error {{invalid operands to binary expression}} + (void)(ctn < ctn); // expected-error {{invalid operands to binary expression}} + (void)(ctn > ctn); // expected-error {{invalid operands to binary expression}} } } diff --git a/test/SemaCXX/nullptr_in_arithmetic_ops.cpp b/test/SemaCXX/nullptr_in_arithmetic_ops.cpp index 60b4670b3a..6273d9c42e 100644 --- a/test/SemaCXX/nullptr_in_arithmetic_ops.cpp +++ b/test/SemaCXX/nullptr_in_arithmetic_ops.cpp @@ -45,12 +45,12 @@ void foo() { b = a == nullptr || nullptr == a; // expected-error 2{{invalid operands to binary expression}} b = a != nullptr || nullptr != a; // expected-error 2{{invalid operands to binary expression}} - b = &a < nullptr || nullptr < &a || &a > nullptr || nullptr > &a; - b = &a <= nullptr || nullptr <= &a || &a >= nullptr || nullptr >= &a; + b = &a < nullptr || nullptr < &a || &a > nullptr || nullptr > &a; // expected-error 4{{invalid operands}} + b = &a <= nullptr || nullptr <= &a || &a >= nullptr || nullptr >= &a; // expected-error 4{{invalid operands}} b = &a == nullptr || nullptr == &a || &a != nullptr || nullptr != &a; - b = nullptr < nullptr || nullptr > nullptr; - b = nullptr <= nullptr || nullptr >= nullptr; + b = nullptr < nullptr || nullptr > nullptr; // expected-error 2{{invalid operands to binary expression}} + b = nullptr <= nullptr || nullptr >= nullptr; // expected-error 2{{invalid operands to binary expression}} b = nullptr == nullptr || nullptr != nullptr; b = ((nullptr)) != a; // expected-error{{invalid operands to binary expression}} diff --git a/test/SemaCXX/warn-memsize-comparison.cpp b/test/SemaCXX/warn-memsize-comparison.cpp index 54c410e3dc..b5c7a9d696 100644 --- a/test/SemaCXX/warn-memsize-comparison.cpp +++ b/test/SemaCXX/warn-memsize-comparison.cpp @@ -5,7 +5,7 @@ typedef __SIZE_TYPE__ size_t; extern "C" void *memset(void *, int, size_t); extern "C" void *memmove(void *s1, const void *s2, size_t n); extern "C" void *memcpy(void *s1, const void *s2, size_t n); -extern "C" void *memcmp(void *s1, const void *s2, size_t n); +extern "C" int memcmp(void *s1, const void *s2, size_t n); extern "C" int strncmp(const char *s1, const char *s2, size_t n); extern "C" int strncasecmp(const char *s1, const char *s2, size_t n); extern "C" char *strncpy(char *dst, const char *src, size_t n); @@ -28,11 +28,12 @@ void f() { expected-note {{explicitly cast the argument}} if (memmove(b1, b2, sizeof(b1)) == 0) {} + // FIXME: This fixit is bogus. if (memcpy(b1, b2, sizeof(b1) < 0)) {} // \ expected-warning{{size argument in 'memcpy' call is a comparison}} \ expected-note {{did you mean to compare}} \ expected-note {{explicitly cast the argument}} - if (memcpy(b1, b2, sizeof(b1)) < 0) {} + if (memcpy(b1, b2, sizeof(b1)) < 0) {} // expected-error {{ordered comparison between pointer and zero}} if (memcmp(b1, b2, sizeof(b1) <= 0)) {} // \ expected-warning{{size argument in 'memcmp' call is a comparison}} \ @@ -58,11 +59,12 @@ void f() { expected-note {{explicitly cast the argument}} if (strncpy(b1, b2, sizeof(b1)) == 0 || true) {} + // FIXME: This fixit is bogus. if (strncat(b1, b2, sizeof(b1) - 1 >= 0 && true)) {} // \ expected-warning{{size argument in 'strncat' call is a comparison}} \ expected-note {{did you mean to compare}} \ expected-note {{explicitly cast the argument}} - if (strncat(b1, b2, sizeof(b1) - 1) >= 0 && true) {} + if (strncat(b1, b2, sizeof(b1) - 1) >= 0 && true) {} // expected-error {{ordered comparison between pointer and zero}} if (strndup(b1, sizeof(b1) != 0)) {} // \ expected-warning{{size argument in 'strndup' call is a comparison}} \ diff --git a/test/SemaObjCXX/null_objc_pointer.mm b/test/SemaObjCXX/null_objc_pointer.mm index e0232bf3c8..2be397e31e 100644 --- a/test/SemaObjCXX/null_objc_pointer.mm +++ b/test/SemaObjCXX/null_objc_pointer.mm @@ -1,5 +1,4 @@ // RUN: %clang_cc1 -fsyntax-only -verify -Wnull-arithmetic %s -// expected-no-diagnostics #define NULL __null @interface X @@ -8,7 +7,7 @@ void f() { bool b; X *d; - b = d < NULL || NULL < d || d > NULL || NULL > d; - b = d <= NULL || NULL <= d || d >= NULL || NULL >= d; + b = d < NULL || NULL < d || d > NULL || NULL > d; // expected-error 4{{ordered comparison between pointer and zero}} + b = d <= NULL || NULL <= d || d >= NULL || NULL >= d; // expected-error 4{{ordered comparison between pointer and zero}} b = d == NULL || NULL == d || d != NULL || NULL != d; } diff --git a/www/cxx_dr_status.html b/www/cxx_dr_status.html index 73238b4d53..1385ec3398 100644 --- a/www/cxx_dr_status.html +++ b/www/cxx_dr_status.html @@ -3541,7 +3541,7 @@ and <I>POD class</I></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#583">583</a></td> <td>CD3</td> <td>Relational pointer comparisons against the null pointer constant</td> - <td class="none" align="center">No</td> + <td class="svn" align="center">SVN</td> </tr> <tr id="584"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#584">584</a></td> @@ -8887,7 +8887,7 @@ and <I>POD class</I></td> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1512">1512</a></td> <td>CD3</td> <td>Pointer comparison vs qualification conversions</td> - <td class="none" align="center">Unknown</td> + <td class="svn" align="center">SVN</td> </tr> <tr class="open" id="1513"> <td><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1513">1513</a></td> |