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. */
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,
bad = true;
continue;
}
+ /* Most of the time, comp is the expression that should be evaluated
+ to compare the two members. If the expression needs to be
+ evaluated more than once in a loop, it will be a TREE_LIST
+ instead, whose TREE_VALUE is the expression for one array element,
+ TREE_PURPOSE is innermost iterator temporary and if the array
+ is multidimensional, TREE_CHAIN will contain another TREE_LIST
+ with second innermost iterator in its TREE_PURPOSE and so on. */
+ if (loop_indexes)
+ {
+ TREE_VALUE (loop_indexes) = comp;
+ comp = loop_indexes;
+ }
comps.safe_push (comp);
}
if (code == SPACESHIP_EXPR && is_auto (rettype))
{
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);
+ /* Store in TREE_VALUE the for_stmt tree, so that we can
+ later on call finish_for_stmt on it (in the reverse
+ order). */
+ 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)
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)
--- /dev/null
+// { 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 ();
+}