From ae8ed736addb2b005d54c0b3191ac599a04ec170 Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Wed, 13 May 2020 15:52:42 -0400 Subject: [PATCH] c++: Implement DR 1512, Pointer comparison vs qual convs [PR87699] This patch resolves DR 1512 (and, by turn, DR 583). This entails: 1) Relational pointer comparisons against null pointer constants have been made ill-formed: void f(char *p) { if (p > 0) // ... } was always invalid in C but was -- accidentally -- allowed in C++. 2) This was ill-formed: bool foo(int** x, const int** y) { return x < y; } because 'int**' couldn't be converted to 'const int**'. This was fixed by re-defining a generic composite pointer type. The composite type of these two pointers will be 'const int *const *', to which both pointers can be converted. 3) The overload descriptions for built-in operators were adjusted, because objects of type std::nullptr_t cannot be used with relational operators any more. I fixed 1) by adjusting cp_build_binary_op; we already had a warning for it so made it a hard error now. Then 2) required tweaking composite_pointer_type_r. [expr.type] defines the composite pointer type by using the "cv-combined type." We didn't implement the [conv.qual]/3.3 part; previously the composite type of 'int**' and 'const int**' was 'const int**', so this didn't compile: void f(const int **p, int **q) { true ? p : q; } I wrote a more extensive test for this which uses decltype and some template magic to check the composite type, see composite-ptr-type.C. We still don't handle everything that [expr.type] requires us to, but it's pretty close. And finally 3) was handled in add_builtin_candidate. Turned out we weren't creating built-in operator candidates when the type was std::nullptr_t at all. We should, for == and !=. Tested in builtin4.C. In passing, I'm fixing some of the comments too. DR 1512 PR c++/87699 * call.c (add_builtin_candidate) : Create candidate operator functions when type is std::nullptr_t for ==/!=. * typeck.c (composite_pointer_type_r): Add bool a * parameter. Use it to maybe add "const" to the pointer type. (composite_pointer_type): Update the call to composite_pointer_type_r. (cp_build_binary_op): Turn two warning_at into error_at. Print the types. * g++.dg/cpp0x/constexpr-array-ptr10.C: Change dg-warning to dg-error and adjust the expected messages in dg-error. * g++.dg/expr/composite-ptr-type.C: New test. * g++.dg/expr/ptr-comp1.C: New test. * g++.dg/expr/ptr-comp2.C: New test. * g++.dg/expr/ptr-comp3.C: New test. * g++.dg/overload/builtin4.C: New test. * g++.dg/warn/Wextra-3.C: Change dg-warning to dg-error. --- gcc/cp/ChangeLog | 12 +++ gcc/cp/call.c | 74 ++++++++++--------- gcc/cp/typeck.c | 61 ++++++++------- gcc/testsuite/ChangeLog | 13 ++++ .../g++.dg/cpp0x/constexpr-array-ptr10.C | 41 +++++----- .../g++.dg/expr/composite-ptr-type.C | 72 ++++++++++++++++++ gcc/testsuite/g++.dg/expr/ptr-comp1.C | 32 ++++++++ gcc/testsuite/g++.dg/expr/ptr-comp2.C | 14 ++++ gcc/testsuite/g++.dg/expr/ptr-comp3.C | 15 ++++ gcc/testsuite/g++.dg/overload/builtin4.C | 31 ++++++++ gcc/testsuite/g++.dg/warn/Wextra-3.C | 9 +-- 11 files changed, 287 insertions(+), 87 deletions(-) create mode 100644 gcc/testsuite/g++.dg/expr/composite-ptr-type.C create mode 100644 gcc/testsuite/g++.dg/expr/ptr-comp1.C create mode 100644 gcc/testsuite/g++.dg/expr/ptr-comp2.C create mode 100644 gcc/testsuite/g++.dg/expr/ptr-comp3.C create mode 100644 gcc/testsuite/g++.dg/overload/builtin4.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 1c4d0657c36..52422cef10f 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,15 @@ +2020-05-18 Marek Polacek + + DR 1512 + PR c++/87699 + * call.c (add_builtin_candidate) : Create candidate + operator functions when type is std::nullptr_t for ==/!=. + * typeck.c (composite_pointer_type_r): Add a bool * parameter. Use it + to maybe add "const" to the pointer type. + (composite_pointer_type): Update the call to composite_pointer_type_r. + (cp_build_binary_op): Turn two warning_at into error_at. Print the + types. + 2020-05-18 Jason Merrill * call.c (build_over_call): Remove unnecessary diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 0b0eaa65ad8..264f4a126e6 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -2713,8 +2713,9 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, T& operator*(T*); - 8 For every function type T, there exist candidate operator functions of - the form + + 8 For every function type T that does not have cv-qualifiers or + a ref-qualifier, there exist candidate operator functions of the form T& operator*(T*); */ case INDIRECT_REF: @@ -2727,8 +2728,8 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, /* 9 For every type T, there exist candidate operator functions of the form T* operator+(T*); - 10For every promoted arithmetic type T, there exist candidate operator - functions of the form + 10 For every floating-point or promoted integral type T, there exist + candidate operator functions of the form T operator+(T); T operator-(T); */ @@ -2741,8 +2742,8 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, break; return; -/* 11For every promoted integral type T, there exist candidate operator - functions of the form +/* 11 For every promoted integral type T, there exist candidate operator + functions of the form T operator~(T); */ case BIT_NOT_EXPR: @@ -2750,10 +2751,10 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, break; return; -/* 12For every quintuple C1, C2, T, CV1, CV2), where C2 is a class type, C1 - is the same type as C2 or is a derived class of C2, T is a complete - object type or a function type, and CV1 and CV2 are cv-qualifier-seqs, - there exist candidate operator functions of the form +/* 12 For every quintuple (C1, C2, T, CV1, CV2), where C2 is a class type, C1 + is the same type as C2 or is a derived class of C2, and T is an object + type or a function type there exist candidate operator functions of the + form CV12 T& operator->*(CV1 C1*, CV2 T C2::*); where CV12 is the union of CV1 and CV2. */ @@ -2770,8 +2771,9 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, } return; -/* 13For every pair of promoted arithmetic types L and R, there exist can- - didate operator functions of the form +/* 13 For every pair of types L and R, where each of L and R is a floating-point + or promoted integral type, there exist candidate operator functions of the + form LR operator*(L, R); LR operator/(L, R); LR operator+(L, R); @@ -2782,34 +2784,33 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, bool operator>=(L, R); bool operator==(L, R); bool operator!=(L, R); - where LR is the result of the usual arithmetic conversions between - types L and R. + where LR is the result of the usual arithmetic conversions between + types L and R. - For every integral type T there exists a candidate operator function of - the form + 14 For every integral type T there exists a candidate operator function of + the form std::strong_ordering operator<=>(T, T); - For every pair of floating-point types L and R, there exists a candidate - operator function of the form + 15 For every pair of floating-point types L and R, there exists a candidate + operator function of the form std::partial_ordering operator<=>(L, R); - 14For every pair of types T and I, where T is a cv-qualified or cv- - unqualified complete object type and I is a promoted integral type, - there exist candidate operator functions of the form - T* operator+(T*, I); - T& operator[](T*, I); - T* operator-(T*, I); - T* operator+(I, T*); - T& operator[](I, T*); + 16 For every cv-qualified or cv-unqualified object type T there exist + candidate operator functions of the form + T* operator+(T*, std::ptrdiff_t); + T& operator[](T*, std::ptrdiff_t); + T* operator-(T*, std::ptrdiff_t); + T* operator+(std::ptrdiff_t, T*); + T& operator[](std::ptrdiff_t, T*); - 15For every T, where T is a pointer to complete object type, there exist - candidate operator functions of the form112) - ptrdiff_t operator-(T, T); + 17 For every T, where T is a pointer to object type, there exist candidate + operator functions of the form + std::ptrdiff_t operator-(T, T); - 16For every pointer or enumeration type T, there exist candidate operator - functions of the form + 18 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); bool operator<=(T, T); @@ -2818,13 +2819,12 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, bool operator!=(T, T); R operator<=>(T, T); - where R is the result type specified in [expr.spaceship]. + where R is the result type specified in [expr.spaceship]. - 17For every pointer to member type T, there exist candidate operator - functions of the form + 19 For every T, where T is a pointer-to-member type or std::nullptr_t, + there exist candidate operator functions of the form bool operator==(T, T); - bool operator!=(T, T); - std::strong_equality operator<=>(T, T); */ + bool operator!=(T, T); */ case MINUS_EXPR: if (TYPE_PTROB_P (type1) && TYPE_PTROB_P (type2)) @@ -2852,6 +2852,8 @@ add_builtin_candidate (struct z_candidate **candidates, enum tree_code code, if ((TYPE_PTRMEMFUNC_P (type1) && TYPE_PTRMEMFUNC_P (type2)) || (TYPE_PTRDATAMEM_P (type1) && TYPE_PTRDATAMEM_P (type2))) break; + if (NULLPTR_TYPE_P (type1) && NULLPTR_TYPE_P (type2)) + break; if (TYPE_PTRMEM_P (type1) && null_ptr_cst_p (args[1])) { type2 = type1; diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 39f5187a7f4..768c62281f4 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -478,11 +478,12 @@ composite_pointer_error (const op_location_t &location, } /* Subroutine of composite_pointer_type to implement the recursive - case. See that function for documentation of the parameters. */ + case. See that function for documentation of the parameters. And ADD_CONST + is used to track adding "const" where needed. */ static tree composite_pointer_type_r (const op_location_t &location, - tree t1, tree t2, + tree t1, tree t2, bool *add_const, composite_pointer_operation operation, tsubst_flags_t complain) { @@ -503,20 +504,17 @@ composite_pointer_type_r (const op_location_t &location, pointee2 = TYPE_PTRMEM_POINTED_TO_TYPE (t2); } - /* [expr.rel] + /* [expr.type] - Otherwise, the composite pointer type is a pointer type - similar (_conv.qual_) to the type of one of the operands, - with a cv-qualification signature (_conv.qual_) that is the - union of the cv-qualification signatures of the operand - types. */ + If T1 and T2 are similar types, the result is the cv-combined type of + T1 and T2. */ if (same_type_ignoring_top_level_qualifiers_p (pointee1, pointee2)) result_type = pointee1; else if ((TYPE_PTR_P (pointee1) && TYPE_PTR_P (pointee2)) || (TYPE_PTRMEM_P (pointee1) && TYPE_PTRMEM_P (pointee2))) { result_type = composite_pointer_type_r (location, pointee1, pointee2, - operation, complain); + add_const, operation, complain); if (result_type == error_mark_node) return error_mark_node; } @@ -529,9 +527,18 @@ composite_pointer_type_r (const op_location_t &location, return error_mark_node; result_type = void_type_node; } + const int q1 = cp_type_quals (pointee1); + const int q2 = cp_type_quals (pointee2); + const int quals = q1 | q2; result_type = cp_build_qualified_type (result_type, - (cp_type_quals (pointee1) - | cp_type_quals (pointee2))); + (quals | (*add_const + ? TYPE_QUAL_CONST + : TYPE_UNQUALIFIED))); + /* The cv-combined type can add "const" as per [conv.qual]/3.3 (except for + the TLQ). The reason is that both T1 and T2 can then be converted to the + cv-combined type of T1 and T2. */ + if (quals != q1 || quals != q2) + *add_const = true; /* If the original types were pointers to members, so is the result. */ if (TYPE_PTRMEM_P (t1)) @@ -556,7 +563,7 @@ composite_pointer_type_r (const op_location_t &location, return build_type_attribute_variant (result_type, attributes); } -/* Return the composite pointer type (see [expr.rel]) for T1 and T2. +/* Return the composite pointer type (see [expr.type]) for T1 and T2. ARG1 and ARG2 are the values with those types. The OPERATION is to describe the operation between the pointer types, in case an error occurs. @@ -573,7 +580,7 @@ composite_pointer_type (const op_location_t &location, tree class1; tree class2; - /* [expr.rel] + /* [expr.type] If one operand is a null pointer constant, the composite pointer type is the type of the other operand. */ @@ -584,10 +591,10 @@ composite_pointer_type (const op_location_t &location, /* We have: - [expr.rel] + [expr.type] - If one of the operands has type "pointer to cv1 void*", then - the other has type "pointer to cv2T", and the composite pointer + If one of the operands has type "pointer to cv1 void", then + the other has type "pointer to cv2 T", and the composite pointer type is "pointer to cv12 void", where cv12 is the union of cv1 and cv2. @@ -719,7 +726,9 @@ composite_pointer_type (const op_location_t &location, } } - return composite_pointer_type_r (location, t1, t2, operation, complain); + bool add_const = false; + return composite_pointer_type_r (location, t1, t2, &add_const, operation, + complain); } /* Return the merged type of two types. @@ -5316,17 +5325,19 @@ cp_build_binary_op (const op_location_t &location, CPO_COMPARISON, complain); else if (code0 == POINTER_TYPE && null_ptr_cst_p (orig_op1)) { - result_type = type0; - if (extra_warnings && (complain & tf_warning)) - warning_at (location, OPT_Wextra, - "ordered comparison of pointer with integer zero"); + /* Core Issue 1512 made this ill-formed. */ + if (complain & tf_error) + error_at (location, "ordered comparison of pointer with " + "integer zero (%qT and %qT)", type0, type1); + return error_mark_node; } else if (code1 == POINTER_TYPE && null_ptr_cst_p (orig_op0)) { - result_type = type1; - if (extra_warnings && (complain & tf_warning)) - warning_at (location, OPT_Wextra, - "ordered comparison of pointer with integer zero"); + /* Core Issue 1512 made this ill-formed. */ + if (complain & tf_error) + error_at (location, "ordered comparison of pointer with " + "integer zero (%qT and %qT)", type0, type1); + return error_mark_node; } else if (null_ptr_cst_p (orig_op0) && null_ptr_cst_p (orig_op1)) /* One of the operands must be of nullptr_t type. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 0b5e1d31fa6..facaf443665 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,16 @@ +2020-05-18 Marek Polacek + + DR 1512 + PR c++/87699 + * g++.dg/cpp0x/constexpr-array-ptr10.C: Change dg-warning to dg-error + and adjust the expected messages in dg-error. + * g++.dg/expr/composite-ptr-type.C: New test. + * g++.dg/expr/ptr-comp1.C: New test. + * g++.dg/expr/ptr-comp2.C: New test. + * g++.dg/expr/ptr-comp3.C: New test. + * g++.dg/overload/builtin4.C: New test. + * g++.dg/warn/Wextra-3.C: Change dg-warning to dg-error. + 2020-05-18 Marek Polacek * g++.dg/overload/builtin5.C: New test. diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C index b1d47cf2cbd..5224bb14234 100644 --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C @@ -19,18 +19,18 @@ constexpr int *p0 = &i; constexpr bool b0 = p0; // { dg-warning "address of .A::i." } constexpr bool b1 = p0 == 0; // { dg-warning "address of .A::i." } constexpr bool b2 = p0 != 0; // { dg-warning "address of .A::i." } -constexpr bool b3 = p0 < 0; // { dg-warning "25:ordered comparison" } -constexpr bool b4 = p0 <= 0; // { dg-warning "25:ordered comparison" } -constexpr bool b5 = p0 > 0; // { dg-warning "25:ordered comparison" } -constexpr bool b6 = p0 >= 0; // { dg-warning "25:ordered comparison" } +constexpr bool b3 = p0 < 0; // { dg-error "25:ordered comparison" } +constexpr bool b4 = p0 <= 0; // { dg-error "25:ordered comparison" } +constexpr bool b5 = p0 > 0; // { dg-error "25:ordered comparison" } +constexpr bool b6 = p0 >= 0; // { dg-error "25:ordered comparison" } constexpr bool b7 = !p0; // { dg-warning "address of .A::i." } constexpr bool b8 = 0 == p0; // { dg-warning "address of .A::i." } constexpr bool b9 = 0 != p0; // { dg-warning "address of .A::i." } -constexpr bool b10 = 0 < p0; // { dg-warning "24:ordered comparison" } -constexpr bool b11 = 0 <= p0; // { dg-warning "24:ordered comparison" } -constexpr bool b12 = 0 > p0; // { dg-warning "24:ordered comparison" } -constexpr bool b13 = 0 >= p0; // { dg-warning "24:ordered comparison" } +constexpr bool b10 = 0 < p0; // { dg-error "24:ordered comparison" } +constexpr bool b11 = 0 <= p0; // { dg-error "24:ordered comparison" } +constexpr bool b12 = 0 > p0; // { dg-error "24:ordered comparison" } +constexpr bool b13 = 0 >= p0; // { dg-error "24:ordered comparison" } } @@ -60,20 +60,19 @@ constexpr int *p0 = &i; constexpr bool b0 = p0; // { dg-error "not a constant expression" } constexpr bool b1 = p0 == 0; // { dg-error "not a constant expression" } constexpr bool b2 = p0 != 0; // { dg-error "not a constant expression" } -constexpr bool b4 = p0 <= 0; // { dg-error "not a constant expression" } -constexpr bool b5 = p0 > 0; // { dg-error "not a constant expression" } +constexpr bool b4 = p0 <= 0; // { dg-error "ordered comparison" } +constexpr bool b5 = p0 > 0; // { dg-error "ordered comparison" } constexpr bool b7 = !p0; // { dg-error "not a constant expression" } constexpr bool b8 = 0 == p0; // { dg-error "not a constant expression" } constexpr bool b9 = 0 != p0; // { dg-error "not a constant expression" } -constexpr bool b10 = 0 < p0; // { dg-error "not a constant expression" } -constexpr bool b13 = 0 >= p0; // { dg-error "not a constant expression" } +constexpr bool b10 = 0 < p0; // { dg-error "ordered comparison" } +constexpr bool b13 = 0 >= p0; // { dg-error "ordered comparison" } -// The following are accepted as constant expressions due to bug c++/70196. -constexpr bool b3 = p0 < 0; -constexpr bool b6 = p0 >= 0; -constexpr bool b11 = 0 <= p0; -constexpr bool b12 = 0 > p0; +constexpr bool b3 = p0 < 0; // { dg-error "ordered comparison" } +constexpr bool b6 = p0 >= 0; // { dg-error "ordered comparison" } +constexpr bool b11 = 0 <= p0; // { dg-error "ordered comparison" } +constexpr bool b12 = 0 > p0; // { dg-error "ordered comparison" } #pragma GCC diagnostic pop @@ -92,14 +91,14 @@ constexpr int *p1 = &i + 1; constexpr bool b0 = p1; // { dg-error "not a constant expression" } constexpr bool b1 = p1 == 0; // { dg-error "not a constant expression" } constexpr bool b2 = p1 != 0; // { dg-error "not a constant expression" } -constexpr bool b4 = p1 <= 0; // { dg-error "not a constant expression" } -constexpr bool b5 = p1 > 0; // { dg-error "not a constant expression" } +constexpr bool b4 = p1 <= 0; // { dg-error "ordered comparison" } +constexpr bool b5 = p1 > 0; // { dg-error "ordered comparison" } constexpr bool b7 = !p1; // { dg-error "not a constant expression" } constexpr bool b8 = 0 == p1; // { dg-error "not a constant expression" } constexpr bool b9 = 0 != p1; // { dg-error "not a constant expression" } -constexpr bool b10 = 0 < p1; // { dg-error "not a constant expression" } -constexpr bool b13 = 0 >= p1; // { dg-error "not a constant expression" } +constexpr bool b10 = 0 < p1; // { dg-error "ordered comparison" } +constexpr bool b13 = 0 >= p1; // { dg-error "ordered comparison" } // The following are accepted as constant expressions due to bug c++/70196. // constexpr bool b3 = p1 < 0; diff --git a/gcc/testsuite/g++.dg/expr/composite-ptr-type.C b/gcc/testsuite/g++.dg/expr/composite-ptr-type.C new file mode 100644 index 00000000000..a7c301d7cb0 --- /dev/null +++ b/gcc/testsuite/g++.dg/expr/composite-ptr-type.C @@ -0,0 +1,72 @@ +// DR 1512 +// Test the composite pointer type of two operands. +// { dg-do compile { target c++11 } } + +using nullptr_t = decltype(nullptr); + +template struct same; +template struct same { }; + +template +T fn () +{ +} + +// Check that the composite pointer type of T and U is RES. +template +void test () +{ + same() : fn()), RES> s; +} + +struct A { }; +struct B : A { }; + +// Test [expr.type]/3. +void +foo () +{ + // if both p1 and p2 are null pointer constants -> std::nullptr_­t. + test(); + + // if either p1 or p2 is a null pointer constant -> T2 or T1. + test(); + test(); + + // if T1 or T2 is 'pointer to cv1 void' and the other type is 'pointer + // to cv2 T', where T is an object type or void -> 'pointer to cv12 void', + // where cv12 is the union of cv1 and cv2. + test(); + test(); + + test(); + // Make sure that we propagate 'const' here as per [conv.qual]/3.3. + test(); + test(); + test(); + + // if T1 is 'pointer to cv1 C1' and T2 is 'pointer to cv2 C2', where C1 is + // reference-related to C2 or C2 is reference-related to C1 -> the cv-combined + // type of T1 and T2 or the cv-combined type of T2 and T1, respectively. + test(); + test(); + + test(); + // FIXME: This doesn't work if they're reference-related but not same. + //test(); + //test(); + + // if T1 or T2 is 'pointer to noexcept function' and the other type is + // 'pointer to function', where the function types are otherwise the same + // -> 'pointer to function'. + test(); + test(); + + // if T1 or T2 is 'pointer to member of C1 of type function', the other type + // is 'pointer to member of C2 of type noexcept function', and C1 is + // reference-related to C2 or C2 is reference-related to C1, where the + // function types are otherwise the same -> 'pointer to member of C2 of type + // function' or 'pointer to member of C1 of type function', respectively. + test(); + test(); +} diff --git a/gcc/testsuite/g++.dg/expr/ptr-comp1.C b/gcc/testsuite/g++.dg/expr/ptr-comp1.C new file mode 100644 index 00000000000..f2434cbc87e --- /dev/null +++ b/gcc/testsuite/g++.dg/expr/ptr-comp1.C @@ -0,0 +1,32 @@ +// DR 1512 +// PR c++/87699 +// { dg-do compile { target c++11 } } + +/* Relational comparisons between null pointer constants and pointers are now + ill-formed. */ + +void +f (char *p) +{ + if (p > 0) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (p >= 0) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (p < 0) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (p <= 0) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (p > nullptr) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (p >= nullptr) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (p < nullptr) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (p <= nullptr) { } // { dg-error "ordered comparison of pointer with integer zero" } +} + +void +f2 (char *p) +{ + if (0 > p) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (0 >= p) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (0 < p) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (0 <= p) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (nullptr > p) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (nullptr >= p) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (nullptr < p) { } // { dg-error "ordered comparison of pointer with integer zero" } + if (nullptr <= p) { } // { dg-error "ordered comparison of pointer with integer zero" } +} diff --git a/gcc/testsuite/g++.dg/expr/ptr-comp2.C b/gcc/testsuite/g++.dg/expr/ptr-comp2.C new file mode 100644 index 00000000000..da5b09af8ad --- /dev/null +++ b/gcc/testsuite/g++.dg/expr/ptr-comp2.C @@ -0,0 +1,14 @@ +// DR 1512 +// PR c++/87699 +// { dg-do compile { target c++11 } } + +template // { dg-error "ordered comparison" } +bool test(T*) +{ + return true; +} + +int main() +{ + test((int*)(nullptr)); // { dg-error "no matching function" } +} diff --git a/gcc/testsuite/g++.dg/expr/ptr-comp3.C b/gcc/testsuite/g++.dg/expr/ptr-comp3.C new file mode 100644 index 00000000000..e1bc3c56d4d --- /dev/null +++ b/gcc/testsuite/g++.dg/expr/ptr-comp3.C @@ -0,0 +1,15 @@ +// DR 1512 +// PR c++/87699 +// { dg-do compile { target c++11 } } +// { dg-options "-Wall -Wextra -pedantic-errors" } + +/* Comparisons between pointer types with different cv-quals are now OK. */ + +void +f (int **p1, const int **p2) +{ + if (p1 == p2) { } + if (p1 != p2) { } + if (p2 == p1) { } + if (p2 != p1) { } +} diff --git a/gcc/testsuite/g++.dg/overload/builtin4.C b/gcc/testsuite/g++.dg/overload/builtin4.C new file mode 100644 index 00000000000..567bb933fde --- /dev/null +++ b/gcc/testsuite/g++.dg/overload/builtin4.C @@ -0,0 +1,31 @@ +// DR 1512 +// PR c++/87699 +// { dg-do compile { target c++11 } } + +using nullptr_t = decltype(nullptr); + +template +struct S { operator T(); }; + +void +fn () +{ + S s; + // Make sure we create a builtin operator overload candidate for == and !=. + if (s == s) { } + if (s != s) { } + + // But not for these. + if (s > s) { } // { dg-error "no match for" } + if (s < s) { } // { dg-error "no match for" } + if (s <= s) { } // { dg-error "no match for" } + if (s >= s) { } // { dg-error "no match for" } + + S r; + if (s == r) { } // { dg-error "no match for" } + if (s != r) { } // { dg-error "no match for" } + if (s > r) { } // { dg-error "no match for" } + if (s < r) { } // { dg-error "no match for" } + if (s >= r) { } // { dg-error "no match for" } + if (s <= r) { } // { dg-error "no match for" } +} diff --git a/gcc/testsuite/g++.dg/warn/Wextra-3.C b/gcc/testsuite/g++.dg/warn/Wextra-3.C index 1bf2a6e4977..1b596b3f3e2 100644 --- a/gcc/testsuite/g++.dg/warn/Wextra-3.C +++ b/gcc/testsuite/g++.dg/warn/Wextra-3.C @@ -1,9 +1,8 @@ // PR c++/45278 -// { dg-options "-Wextra" } extern void* p; -int f1() { return ( p < 0 ? 1 : 0 ); } // { dg-warning "23:ordered comparison" } -int f2() { return ( p <= 0 ? 1 : 0 ); } // { dg-warning "23:ordered comparison" } -int f3() { return ( p > 0 ? 1 : 0 ); } // { dg-warning "23:ordered comparison" } -int f4() { return ( p >= 0 ? 1 : 0 ); } // { dg-warning "23:ordered comparison" } +int f1() { return ( p < 0 ? 1 : 0 ); } // { dg-error "23:ordered comparison" } +int f2() { return ( p <= 0 ? 1 : 0 ); } // { dg-error "23:ordered comparison" } +int f3() { return ( p > 0 ? 1 : 0 ); } // { dg-error "23:ordered comparison" } +int f4() { return ( p >= 0 ? 1 : 0 ); } // { dg-error "23:ordered comparison" } -- 2.30.2