aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2020-05-13 15:52:42 -0400
committerMarek Polacek <polacek@redhat.com>2020-05-18 16:26:06 -0400
commitae8ed736addb2b005d54c0b3191ac599a04ec170 (patch)
tree3dce991ddf0a96e1a585b86ca63233d79f4296d6
parent295790712f1168d9d540c240446b167592b77aca (diff)
downloadgcc-ae8ed736addb2b005d54c0b3191ac599a04ec170.zip
gcc-ae8ed736addb2b005d54c0b3191ac599a04ec170.tar.gz
gcc-ae8ed736addb2b005d54c0b3191ac599a04ec170.tar.bz2
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) <case EQ_EXPR>: 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.
-rw-r--r--gcc/cp/ChangeLog12
-rw-r--r--gcc/cp/call.c74
-rw-r--r--gcc/cp/typeck.c61
-rw-r--r--gcc/testsuite/ChangeLog13
-rw-r--r--gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C41
-rw-r--r--gcc/testsuite/g++.dg/expr/composite-ptr-type.C72
-rw-r--r--gcc/testsuite/g++.dg/expr/ptr-comp1.C32
-rw-r--r--gcc/testsuite/g++.dg/expr/ptr-comp2.C14
-rw-r--r--gcc/testsuite/g++.dg/expr/ptr-comp3.C15
-rw-r--r--gcc/testsuite/g++.dg/overload/builtin4.C31
-rw-r--r--gcc/testsuite/g++.dg/warn/Wextra-3.C9
11 files changed, 287 insertions, 87 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 1c4d065..52422ce 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,15 @@
+2020-05-18 Marek Polacek <polacek@redhat.com>
+
+ DR 1512
+ PR c++/87699
+ * call.c (add_builtin_candidate) <case EQ_EXPR>: 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 <jason@redhat.com>
* call.c (build_over_call): Remove unnecessary
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 0b0eaa6..264f4a1 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 39f5187..768c622 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 0b5e1d3..facaf44 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,18 @@
2020-05-18 Marek Polacek <polacek@redhat.com>
+ 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 <polacek@redhat.com>
+
* g++.dg/overload/builtin5.C: New test.
2020-05-18 Doug Rupp <rupp@adacore.com>
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C
index b1d47cf..5224bb1 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 0000000..a7c301d
--- /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 <class T, class U> struct same;
+template <class T> struct same<T,T> { };
+
+template<typename T>
+T fn ()
+{
+}
+
+// Check that the composite pointer type of T and U is RES.
+template<typename T, typename U, typename RES>
+void test ()
+{
+ same<decltype(true ? fn<T>() : fn<U>()), 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<nullptr_t, nullptr_t, nullptr_t>();
+
+ // if either p1 or p2 is a null pointer constant -> T2 or T1.
+ test<nullptr_t, const char **, const char **>();
+ test<const char **, nullptr_t, const char **>();
+
+ // 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<const int *, volatile void *, const volatile void *>();
+ test<const void *, volatile int *, const volatile void *>();
+
+ test<int *, const int *, const int *>();
+ // Make sure that we propagate 'const' here as per [conv.qual]/3.3.
+ test<int **, const int **, const int *const *>();
+ test<int *volatile *, const int **, const int *const volatile *>();
+ test<int **, volatile int **, volatile int *const *>();
+
+ // 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<const A*, volatile B*, const volatile A *>();
+ test<const B*, volatile A*, const volatile A *>();
+
+ test<const int *A::*, volatile int *A::*, const volatile int *const A::*>();
+ // FIXME: This doesn't work if they're reference-related but not same.
+ //test<const int *A::*, volatile int *B::*, const volatile int *const B::*>();
+ //test<const int *B::*, volatile int *A::*, const volatile int *const B::*>();
+
+ // 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<int (*)() noexcept, int (*)(), int (*)()>();
+ test<int (*)(), int (*)() noexcept, int (*)()>();
+
+ // 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<int (A::*)() noexcept, int (A::*)(), int (A::*)()>();
+ test<int (A::*)(), int (A::*)() noexcept, int (A::*)()>();
+}
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 0000000..f2434cb
--- /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 0000000..da5b09a
--- /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<class T, decltype((((T*) 0) < nullptr), true) = false> // { 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 0000000..e1bc3c5
--- /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 0000000..567bb93
--- /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<typename T>
+struct S { operator T(); };
+
+void
+fn ()
+{
+ S<nullptr_t> 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<int *> 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 1bf2a6e..1b596b3 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" }