c++: Implement DR 1512, Pointer comparison vs qual convs [PR87699]
authorMarek Polacek <polacek@redhat.com>
Wed, 13 May 2020 19:52:42 +0000 (15:52 -0400)
committerMarek Polacek <polacek@redhat.com>
Mon, 18 May 2020 20:26:06 +0000 (16:26 -0400)
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.

gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/typeck.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/constexpr-array-ptr10.C
gcc/testsuite/g++.dg/expr/composite-ptr-type.C [new file with mode: 0644]
gcc/testsuite/g++.dg/expr/ptr-comp1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/expr/ptr-comp2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/expr/ptr-comp3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/overload/builtin4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/warn/Wextra-3.C

index 1c4d0657c36a9f738acb4ded9b9c8c1f69da7b66..52422cef10f732d3079b50a6b0fb18a2e8ea27e1 100644 (file)
@@ -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
index 0b0eaa65ad86b6b3b6c50bc248a368e2cccd4be1..264f4a126e6f167426a2b028d4fe9abcb01c8477 100644 (file)
@@ -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;
index 39f5187a7f4d9b65ef32d7a960a3f8c02c60df44..768c62281f486ee2b34e3e2ce252056cb88434f5 100644 (file)
@@ -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.  */
index 0b5e1d31fa62990fff0290dd5bf757bd0f90d77c..facaf4436655d3b7758b67ef6edf1089a3ac077f 100644 (file)
@@ -1,3 +1,16 @@
+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.
index b1d47cf2cbd2d630629971b377fd8627ff4a583e..5224bb142349f8aa4b58971389fd2b2129a06e5e 100644 (file)
@@ -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 (file)
index 0000000..a7c301d
--- /dev/null
@@ -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 (file)
index 0000000..f2434cb
--- /dev/null
@@ -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 (file)
index 0000000..da5b09a
--- /dev/null
@@ -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 (file)
index 0000000..e1bc3c5
--- /dev/null
@@ -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 (file)
index 0000000..567bb93
--- /dev/null
@@ -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" }
+}
index 1bf2a6e49777024999e08c3e04d7668dce7845f5..1b596b3f3e24a82f06eb1862e204568a0e9972d6 100644 (file)
@@ -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" }