2020-12-21 Jakub Jelinek <ja...@redhat.com>
PR c++/93480
* method.c (common_comparison_type): If comps[i] is a TREE_LIST,
use its TREE_VALUE instead.
(build_comparison_op): Handle array members.
* g++.dg/cpp2a/spaceship-synth10.C: New test.
* g++.dg/cpp2a/spaceship-synth-neg5.C: New test.
--- gcc/cp/method.c.jj 2020-12-09 00:00:27.047344706 +0100
+++ gcc/cp/method.c 2020-12-20 15:14:03.048393226 +0100
@@ -1230,6 +1230,8 @@ common_comparison_type (vec<tree> &comps
for (unsigned i = 0; i < comps.length(); ++i)
{
tree comp = comps[i];
+ if (TREE_CODE (comp) == TREE_LIST)
+ comp = TREE_VALUE (comp);
tree ctype = TREE_TYPE (comp);
comp_cat_tag tag = cat_tag_for (ctype);
/* build_comparison_op already checked this. */
@@ -1419,10 +1421,47 @@ build_comparison_op (tree fndecl, tsubst
continue;
}
- tree lhs_mem = build3 (COMPONENT_REF, expr_type, lhs, field,
- NULL_TREE);
- tree rhs_mem = build3 (COMPONENT_REF, expr_type, rhs, field,
- NULL_TREE);
+ tree lhs_mem = build3_loc (field_loc, COMPONENT_REF, expr_type, lhs,
+ field, NULL_TREE);
+ tree rhs_mem = build3_loc (field_loc, COMPONENT_REF, expr_type, rhs,
+ field, NULL_TREE);
+ tree loop_indexes = NULL_TREE;
+ while (TREE_CODE (expr_type) == ARRAY_TYPE)
+ {
+ /* Flexible array member. */
+ if (TYPE_DOMAIN (expr_type) == NULL_TREE
+ || TYPE_MAX_VALUE (TYPE_DOMAIN (expr_type)) == NULL_TREE)
+ {
+ if (complain & tf_error)
+ inform (field_loc, "cannot default compare "
+ "flexible array member");
+ bad = true;
+ break;
+ }
+ tree maxval = TYPE_MAX_VALUE (TYPE_DOMAIN (expr_type));
+ /* [0] array. No subobjects to compare, just skip it. */
+ if (integer_all_onesp (maxval))
+ break;
+ tree idx;
+ /* [1] array, no loop needed, just add [0] ARRAY_REF.
+ Similarly if !info.defining. */
+ if (integer_zerop (maxval) || !info.defining)
+ idx = size_zero_node;
+ /* Some other array, will need runtime loop. */
+ else
+ {
+ idx = force_target_expr (sizetype, maxval, complain);
+ loop_indexes = tree_cons (idx, NULL_TREE, loop_indexes);
+ }
+ expr_type = TREE_TYPE (expr_type);
+ lhs_mem = build4_loc (field_loc, ARRAY_REF, expr_type, lhs_mem,
+ idx, NULL_TREE, NULL_TREE);
+ rhs_mem = build4_loc (field_loc, ARRAY_REF, expr_type, rhs_mem,
+ idx, NULL_TREE, NULL_TREE);
+ }
+ if (TREE_CODE (expr_type) == ARRAY_TYPE)
+ continue;
+
tree overload = NULL_TREE;
tree comp = build_new_op (field_loc, code, flags, lhs_mem, rhs_mem,
NULL_TREE, &overload,
@@ -1486,6 +1525,11 @@ build_comparison_op (tree fndecl, tsubst
bad = true;
continue;
}
+ if (loop_indexes)
+ {
+ TREE_VALUE (loop_indexes) = comp;
+ comp = loop_indexes;
+ }
comps.safe_push (comp);
}
if (code == SPACESHIP_EXPR && is_auto (rettype))
@@ -1502,8 +1546,35 @@ build_comparison_op (tree fndecl, tsubst
{
tree comp = comps[i];
tree eq, retval = NULL_TREE, if_ = NULL_TREE;
+ tree loop_indexes = NULL_TREE;
if (info.defining)
- if_ = begin_if_stmt ();
+ {
+ if (TREE_CODE (comp) == TREE_LIST)
+ {
+ loop_indexes = comp;
+ comp = TREE_VALUE (comp);
+ loop_indexes = nreverse (loop_indexes);
+ for (tree loop_index = loop_indexes; loop_index;
+ loop_index = TREE_CHAIN (loop_index))
+ {
+ tree for_stmt = begin_for_stmt (NULL_TREE, NULL_TREE);
+ tree idx = TREE_PURPOSE (loop_index);
+ tree maxval = TARGET_EXPR_INITIAL (idx);
+ TARGET_EXPR_INITIAL (idx) = size_zero_node;
+ add_stmt (idx);
+ finish_init_stmt (for_stmt);
+ finish_for_cond (build2 (LE_EXPR, boolean_type_node, idx,
+ maxval), for_stmt, false, 0);
+ finish_for_expr (cp_build_unary_op (PREINCREMENT_EXPR,
+ TARGET_EXPR_SLOT
(idx),
+ false, complain),
+ for_stmt);
+ TREE_VALUE (loop_index) = for_stmt;
+ }
+ loop_indexes = nreverse (loop_indexes);
+ }
+ if_ = begin_if_stmt ();
+ }
/* Spaceship is specified to use !=, but for the comparison category
types, != is equivalent to !(==), so let's use == directly. */
if (code == EQ_EXPR)
@@ -1542,6 +1613,9 @@ build_comparison_op (tree fndecl, tsubst
finish_return_stmt (retval);
finish_else_clause (if_);
finish_if_stmt (if_);
+ for (tree loop_index = loop_indexes; loop_index;
+ loop_index = TREE_CHAIN (loop_index))
+ finish_for_stmt (TREE_VALUE (loop_index));
}
}
if (info.defining)
--- gcc/testsuite/g++.dg/cpp2a/spaceship-synth10.C.jj 2020-12-20
15:03:01.770871487 +0100
+++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth10.C 2020-12-20
15:15:26.311452033 +0100
@@ -0,0 +1,57 @@
+// { dg-do run { target c++20 } }
+// { dg-options "" }
+
+#include <compare>
+
+struct C {
+ int y;
+ int x[4];
+ auto operator<=>(C const&) const = default;
+};
+
+struct D {
+ int y;
+ int x[1];
+ auto operator<=>(D const&) const = default;
+};
+
+struct E {
+ int y;
+ int x[0];
+ auto operator<=>(E const&) const = default;
+};
+
+int
+main ()
+{
+ constexpr C c1 = { 1, { 2, 3, 4, 5 } };
+ constexpr C c2 = { 1, { 2, 3, 5, 4 } };
+ constexpr C c3 = { 1, { 2, 2, 6, 7 } };
+ static_assert (c1 < c2);
+ static_assert (c3 < c1);
+ constexpr D d1 = { 1, { 2 } };
+ constexpr D d2 = { 1, { 3 } };
+ constexpr D d3 = { 1, { 1 } };
+ static_assert (d1 < d2);
+ static_assert (d3 < d1);
+ constexpr E e1 = { 1, {} };
+ constexpr E e2 = { 2, {} };
+ constexpr E e3 = { 1, {} };
+ static_assert (e1 < e2);
+ static_assert (e1 == e3);
+ C c4 = { 1, { 2, 3, 4, 5 } };
+ C c5 = { 1, { 2, 3, 5, 4 } };
+ C c6 = { 1, { 2, 2, 6, 7 } };
+ if (c4 >= c5 || c6 >= c4)
+ __builtin_abort ();
+ D d4 = { 1, { 2 } };
+ D d5 = { 1, { 3 } };
+ D d6 = { 1, { 1 } };
+ if (d4 >= d5 || d6 >= d4)
+ __builtin_abort ();
+ E e4 = { 1, {} };
+ E e5 = { 2, {} };
+ E e6 = { 1, {} };
+ if (e4 >= e5 || e4 != e6)
+ __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg5.C.jj 2020-12-20
15:05:36.713119245 +0100
+++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg5.C 2020-12-20
15:17:00.203390689 +0100
@@ -0,0 +1,16 @@
+// { dg-do compile { target c++20 } }
+// { dg-options "" }
+
+#include <compare>
+
+struct C {
+ int y;
+ int x[]; // { dg-message "cannot default
compare flexible array member" }
+ auto operator<=>(C const&) const = default; // { dg-message "is implicitly
deleted because the default definition would be ill-formed" }
+};
+
+bool
+foo (C &c1, C &c2)
+{
+ return c1 > c2; // { dg-error "use of deleted function" }
+}
Jakub