+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
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:
/* 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); */
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:
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. */
}
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);
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);
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))
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;
}
/* 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)
{
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;
}
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))
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.
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. */
/* 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.
}
}
- 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.
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. */
+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.
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" }
}
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
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;
--- /dev/null
+// 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::*)()>();
+}
--- /dev/null
+// 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" }
+}
--- /dev/null
+// 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" }
+}
--- /dev/null
+// 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) { }
+}
--- /dev/null
+// 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" }
+}
// 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" }