Commit c9cd5c56 by Jason Merrill Committed by Jason Merrill

Implement P1946R0, Allow defaulting comparisons by value.

	* method.c (early_check_defaulted_comparison): Accept by-value,
	reject mixed by-value and by-reference parms.
	* decl.c (grokdeclarator): Set funcdef_flag for defaulted friend.
	* decl2.c (grokfield): Don't SET_DECL_FRIEND_CONTEXT.

From-SVN: r278078
parent 6e9a85d5
2019-11-11 Jason Merrill <jason@redhat.com>
Implement P1946R0, Allow defaulting comparisons by value.
* method.c (early_check_defaulted_comparison): Accept by-value,
reject mixed by-value and by-reference parms.
* decl.c (grokdeclarator): Set funcdef_flag for defaulted friend.
* decl2.c (grokfield): Don't SET_DECL_FRIEND_CONTEXT.
* typeck.c (cp_build_binary_op): Sorry about <=> on VECTOR_TYPE.
2019-11-11 Jakub Jelinek <jakub@redhat.com>
......
......@@ -13202,6 +13202,11 @@ grokdeclarator (const cp_declarator *declarator,
; /* We already issued a permerror. */
else if (decl && DECL_NAME (decl))
{
if (initialized)
/* Kludge: We need funcdef_flag to be true in do_friend for
in-class defaulted functions, but that breaks grokfndecl.
So set it here. */
funcdef_flag = true;
if (template_class_depth (current_class_type) == 0)
{
decl = check_explicit_specialization
......
......@@ -927,10 +927,6 @@ grokfield (const cp_declarator *declarator,
}
else if (init == ridpointers[(int)RID_DEFAULT])
{
if (friendp)
/* ??? do_friend doesn't set this because funcdef_flag is false
for in-class defaulted functions. So set it here. */
SET_DECL_FRIEND_CONTEXT (value, current_class_type);
if (defaultable_fn_check (value))
{
DECL_DEFAULTED_FN (value) = 1;
......
......@@ -1094,38 +1094,54 @@ early_check_defaulted_comparison (tree fn)
if (!DECL_OVERLOADED_OPERATOR_IS (fn, SPACESHIP_EXPR)
&& !same_type_p (TREE_TYPE (TREE_TYPE (fn)), boolean_type_node))
{
error_at (loc, "defaulted %qD must return %<bool%>", fn);
ok = false;
diagnostic_t kind = DK_UNSPECIFIED;
int opt = 0;
if (is_auto (TREE_TYPE (fn)))
kind = DK_PEDWARN;
else
kind = DK_ERROR;
emit_diagnostic (kind, loc, opt,
"defaulted %qD must return %<bool%>", fn);
if (kind == DK_ERROR)
ok = false;
}
int i = DECL_NONSTATIC_MEMBER_FUNCTION_P (fn);
if (i && type_memfn_quals (TREE_TYPE (fn)) != TYPE_QUAL_CONST)
bool mem = DECL_NONSTATIC_MEMBER_FUNCTION_P (fn);
if (mem && type_memfn_quals (TREE_TYPE (fn)) != TYPE_QUAL_CONST)
{
error_at (loc, "defaulted %qD must be %<const%>", fn);
ok = false;
}
tree parmnode = FUNCTION_FIRST_USER_PARMTYPE (fn);
bool saw_byval = false;
bool saw_byref = mem;
bool saw_bad = false;
for (; parmnode != void_list_node; parmnode = TREE_CHAIN (parmnode))
{
++i;
tree parmtype = TREE_VALUE (parmnode);
diagnostic_t kind = DK_UNSPECIFIED;
int opt = 0;
if (same_type_p (parmtype, ctx))
/* The draft specifies const reference, but let's also allow by-value
unless -Wpedantic, hopefully it will be added soon. */
kind = DK_PEDWARN,
opt = OPT_Wpedantic;
saw_byval = true;
else if (TREE_CODE (parmtype) != REFERENCE_TYPE
|| TYPE_QUALS (TREE_TYPE (parmtype)) != TYPE_QUAL_CONST
|| !(same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (parmtype), ctx)))
kind = DK_ERROR;
if (kind)
emit_diagnostic (kind, loc, opt, "defaulted %qD must have "
"parameter type %<const %T&%>", fn, ctx);
if (kind == DK_ERROR)
ok = false;
saw_bad = true;
else
saw_byref = true;
}
if (saw_bad || (saw_byval && saw_byref))
{
if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
error_at (loc, "defaulted member %qD must have parameter type "
"%<const %T&%>", fn, ctx);
else if (saw_bad)
error_at (loc, "defaulted %qD must have parameters of either type "
"%<const %T&%> or %qT", fn, ctx, ctx);
else
error_at (loc, "defaulted %qD must have parameters of either type "
"%<const %T&%> or %qT, not both", fn, ctx, ctx);
ok = false;
}
/* We still need to deduce deleted/constexpr/noexcept and maybe return. */
......
// { dg-do compile { target c++2a } }
#include <compare>
template <class T>
struct D
{
T i;
auto operator<=>(D) const = default; // { dg-error "defaulted member" }
bool operator==(D) const = default; // { dg-error "defaulted member" }
bool operator!=(D) const = default; // { dg-error "defaulted member" }
bool operator<(D) const = default; // { dg-error "defaulted member" }
bool operator<=(D) const = default; // { dg-error "defaulted member" }
bool operator>(D) const = default; // { dg-error "defaulted member" }
bool operator>=(D) const = default; // { dg-error "defaulted member" }
};
template <class T>
struct E
{
T i;
friend auto operator<=>(const E&, E) = default; // { dg-error "not both" }
friend bool operator==(const E&, E) = default; // { dg-error "not both" }
friend bool operator!=(const E&, E) = default; // { dg-error "not both" }
friend bool operator<(E, const E&) = default; // { dg-error "not both" }
friend bool operator<=(E, const E&) = default; // { dg-error "not both" }
friend bool operator>(E, const E&) = default; // { dg-error "not both" }
friend bool operator>=(E, const E&) = default; // { dg-error "not both" }
};
// Test with all operators explicitly defaulted.
// { dg-do run { target c++2a } }
#include <compare>
struct D
{
int i;
friend auto operator<=>(const D& x, const D& y) = default;
friend bool operator==(const D& x, const D& y) = default;
friend bool operator!=(const D& x, const D& y) = default;
friend bool operator<(const D& x, const D& y) = default;
friend bool operator<=(const D& x, const D& y) = default;
friend bool operator>(const D& x, const D& y) = default;
friend bool operator>=(const D& x, const D& y) = default;
};
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
int main()
{
D d{42};
D d2{24};
assert (is_eq (d <=> d));
assert (is_lteq (d <=> d));
assert (is_gteq (d <=> d));
assert (is_lt (d2 <=> d));
assert (is_lteq (d2 <=> d));
assert (is_gt (d <=> d2));
assert (is_gteq (d <=> d2));
assert (d == d);
assert (!(d2 == d));
assert (!(d == d2));
assert (d != d2);
assert (!(d2 != d2));
assert (d2 < d);
assert (d2 <= d);
assert (d > d2);
assert (d >= d2);
}
// Test with all operators explicitly defaulted.
// { dg-do run { target c++2a } }
#include <compare>
struct D
{
int i;
friend auto operator<=>(D x, D y) = default;
friend bool operator==(D x, D y) = default;
friend bool operator!=(D x, D y) = default;
friend bool operator<(D x, D y) = default;
friend bool operator<=(D x, D y) = default;
friend bool operator>(D x, D y) = default;
friend bool operator>=(const D x, const D y) = default;
};
#define assert(X) do { if (!(X)) __builtin_abort(); } while (0)
int main()
{
D d{42};
D d2{24};
assert (is_eq (d <=> d));
assert (is_lteq (d <=> d));
assert (is_gteq (d <=> d));
assert (is_lt (d2 <=> d));
assert (is_lteq (d2 <=> d));
assert (is_gt (d <=> d2));
assert (is_gteq (d <=> d2));
assert (d == d);
assert (!(d2 == d));
assert (!(d == d2));
assert (d != d2);
assert (!(d2 != d2));
assert (d2 < d);
assert (d2 <= d);
assert (d > d2);
assert (d >= d2);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment