aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/method.c
diff options
context:
space:
mode:
authorThomas Koenig <tkoenig@gcc.gnu.org>2021-01-03 21:40:04 +0100
committerThomas Koenig <tkoenig@gcc.gnu.org>2021-01-03 21:40:04 +0100
commitafae4a55ccaa0de95ea11e5f634084db6ab2f444 (patch)
treed632cc867d10410ba9fb750523be790b86846ac4 /gcc/cp/method.c
parent9d9a82ec8478ff52c7a9d61f58cd2a7b6295b5f9 (diff)
parentd2eb616a0f7bea78164912aa438c29fe1ef5774a (diff)
downloadgcc-afae4a55ccaa0de95ea11e5f634084db6ab2f444.zip
gcc-afae4a55ccaa0de95ea11e5f634084db6ab2f444.tar.gz
gcc-afae4a55ccaa0de95ea11e5f634084db6ab2f444.tar.bz2
Merge branch 'master' into devel/coarray_native
Diffstat (limited to 'gcc/cp/method.c')
-rw-r--r--gcc/cp/method.c228
1 files changed, 195 insertions, 33 deletions
diff --git a/gcc/cp/method.c b/gcc/cp/method.c
index 6e4c5f7..dd35173 100644
--- a/gcc/cp/method.c
+++ b/gcc/cp/method.c
@@ -351,7 +351,7 @@ use_thunk (tree thunk_fndecl, bool emit_p)
resolve_unique_section (thunk_fndecl, 0, flag_function_sections);
/* Output the thunk into the same section as function. */
- set_decl_section_name (thunk_fndecl, DECL_SECTION_NAME (fn));
+ set_decl_section_name (thunk_fndecl, fn);
symtab_node::get (thunk_fndecl)->implicit_section
= symtab_node::get (fn)->implicit_section;
}
@@ -1063,43 +1063,60 @@ spaceship_type (tree optype, tsubst_flags_t complain)
return lookup_comparison_category (tag, complain);
}
-/* Turn <=> with type TYPE and operands OP0 and OP1 into GENERIC. */
+/* Turn <=> with type TYPE and operands OP0 and OP1 into GENERIC.
+ This is also used by build_comparison_op for fallback to op< and op==
+ in a defaulted op<=>. */
tree
-genericize_spaceship (tree type, tree op0, tree op1)
+genericize_spaceship (location_t loc, tree type, tree op0, tree op1)
{
/* ??? maybe optimize based on knowledge of representation? */
comp_cat_tag tag = cat_tag_for (type);
+
+ if (tag == cc_last && is_auto (type))
+ {
+ /* build_comparison_op is checking to see if we want to suggest changing
+ the op<=> return type from auto to a specific comparison category; any
+ category will do for now. */
+ tag = cc_strong_ordering;
+ type = lookup_comparison_category (tag, tf_none);
+ if (type == error_mark_node)
+ return error_mark_node;
+ }
+
gcc_checking_assert (tag < cc_last);
tree r;
- op0 = save_expr (op0);
- op1 = save_expr (op1);
+ if (SCALAR_TYPE_P (TREE_TYPE (op0)))
+ {
+ op0 = save_expr (op0);
+ op1 = save_expr (op1);
+ }
tree gt = lookup_comparison_result (tag, type, 1);
+ int flags = LOOKUP_NORMAL;
+ tsubst_flags_t complain = tf_none;
+
if (tag == cc_partial_ordering)
{
/* op0 == op1 ? equivalent : op0 < op1 ? less :
- op0 > op1 ? greater : unordered */
+ op1 < op0 ? greater : unordered */
tree uo = lookup_comparison_result (tag, type, 3);
- tree comp = fold_build2 (GT_EXPR, boolean_type_node, op0, op1);
- r = fold_build3 (COND_EXPR, type, comp, gt, uo);
+ tree comp = build_new_op (loc, LT_EXPR, flags, op1, op0, complain);
+ r = build_conditional_expr (loc, comp, gt, uo, complain);
}
else
/* op0 == op1 ? equal : op0 < op1 ? less : greater */
r = gt;
tree lt = lookup_comparison_result (tag, type, 2);
- tree comp = fold_build2 (LT_EXPR, boolean_type_node, op0, op1);
- r = fold_build3 (COND_EXPR, type, comp, lt, r);
+ tree comp = build_new_op (loc, LT_EXPR, flags, op0, op1, complain);
+ r = build_conditional_expr (loc, comp, lt, r, complain);
tree eq = lookup_comparison_result (tag, type, 0);
- comp = fold_build2 (EQ_EXPR, boolean_type_node, op0, op1);
- r = fold_build3 (COND_EXPR, type, comp, eq, r);
-
- /* Wrap the whole thing in a TARGET_EXPR like build_conditional_expr_1. */
- r = get_target_expr (r);
+ comp = build_new_op (loc, EQ_EXPR, flags, op0, op1, complain);
+ r = build_conditional_expr (loc, comp, eq, r, complain);
return r;
}
@@ -1213,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. */
@@ -1323,7 +1342,7 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain)
if (!info.defining && !(complain & tf_error) && !DECL_MAYBE_DELETED (fndecl))
return;
- int flags = LOOKUP_NORMAL | LOOKUP_NONVIRTUAL | LOOKUP_DEFAULTED;
+ int flags = LOOKUP_NORMAL;
const ovl_op_info_t *op = IDENTIFIER_OVL_OP_INFO (DECL_NAME (fndecl));
tree_code code = op->tree_code;
@@ -1364,6 +1383,10 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain)
if (code == EQ_EXPR || code == SPACESHIP_EXPR)
{
+ comp_cat_tag retcat = cc_last;
+ if (code == SPACESHIP_EXPR && !FNDECL_USED_AUTO (fndecl))
+ retcat = cat_tag_for (rettype);
+
bool bad = false;
auto_vec<tree> comps;
@@ -1375,13 +1398,15 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain)
{
tree expr_type = TREE_TYPE (field);
+ location_t field_loc = DECL_SOURCE_LOCATION (field);
+
/* A defaulted comparison operator function for class C is defined as
deleted if any non-static data member of C is of reference type or
C has variant members. */
if (TREE_CODE (expr_type) == REFERENCE_TYPE)
{
if (complain & tf_error)
- inform (DECL_SOURCE_LOCATION (field), "cannot default compare "
+ inform (field_loc, "cannot default compare "
"reference member %qD", field);
bad = true;
continue;
@@ -1390,35 +1415,128 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain)
&& next_initializable_field (TYPE_FIELDS (expr_type)))
{
if (complain & tf_error)
- inform (DECL_SOURCE_LOCATION (field), "cannot default compare "
+ inform (field_loc, "cannot default compare "
"anonymous union member");
bad = true;
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 comp = build_new_op (info.loc, code, flags, lhs_mem, rhs_mem,
- NULL_TREE, NULL, complain);
+ 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,
+ retcat != cc_last ? tf_none : complain);
if (comp == error_mark_node)
{
- bad = true;
- continue;
+ if (overload == NULL_TREE && code == SPACESHIP_EXPR
+ && (retcat != cc_last || complain))
+ {
+ tree comptype = (retcat != cc_last ? rettype
+ : DECL_SAVED_AUTO_RETURN_TYPE (fndecl));
+ /* No viable <=>, try using op< and op==. */
+ tree lteq = genericize_spaceship (field_loc, comptype,
+ lhs_mem, rhs_mem);
+ if (lteq != error_mark_node)
+ {
+ /* We found usable < and ==. */
+ if (retcat != cc_last)
+ /* Return type is a comparison category, use them. */
+ comp = lteq;
+ else if (complain & tf_error)
+ /* Return type is auto, suggest changing it. */
+ inform (info.loc, "changing the return type from %qs "
+ "to a comparison category type will allow the "
+ "comparison to use %qs and %qs", "auto",
+ "operator<", "operator==");
+ }
+ else if (retcat != cc_last && complain != tf_none)
+ /* No usable < and ==, give an error for op<=>. */
+ build_new_op (field_loc, code, flags, lhs_mem, rhs_mem,
+ complain);
+ }
+ if (comp == error_mark_node)
+ {
+ bad = true;
+ continue;
+ }
}
- if (code == SPACESHIP_EXPR
- && cat_tag_for (TREE_TYPE (comp)) == cc_last)
+ if (code != SPACESHIP_EXPR)
+ ;
+ else if (FNDECL_USED_AUTO (fndecl)
+ && cat_tag_for (TREE_TYPE (comp)) == cc_last)
{
/* The operator function is defined as deleted if ... Ri is not a
comparison category type. */
if (complain & tf_error)
- inform (DECL_SOURCE_LOCATION (field),
+ inform (field_loc,
"three-way comparison of %qD has type %qT, not a "
"comparison category type", field, TREE_TYPE (comp));
bad = true;
continue;
}
+ else if (!FNDECL_USED_AUTO (fndecl)
+ && !can_convert (rettype, TREE_TYPE (comp), complain))
+ {
+ if (complain & tf_error)
+ error_at (field_loc,
+ "three-way comparison of %qD has type %qT, which "
+ "does not convert to %qT",
+ field, TREE_TYPE (comp), rettype);
+ 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))
@@ -1435,8 +1553,38 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain)
{
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)
@@ -1475,6 +1623,9 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain)
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)
@@ -1924,15 +2075,26 @@ is_xible_helper (enum tree_code code, tree to, tree from, bool trivial)
bool
is_trivially_xible (enum tree_code code, tree to, tree from)
{
- tree expr;
- expr = is_xible_helper (code, to, from, /*trivial*/true);
-
+ tree expr = is_xible_helper (code, to, from, /*trivial*/true);
if (expr == NULL_TREE || expr == error_mark_node)
return false;
tree nt = cp_walk_tree_without_duplicates (&expr, check_nontriv, NULL);
return !nt;
}
+/* Returns true iff TO is nothrow assignable (if CODE is MODIFY_EXPR) or
+ constructible (otherwise) from FROM, which is a single type for
+ assignment or a list of types for construction. */
+
+bool
+is_nothrow_xible (enum tree_code code, tree to, tree from)
+{
+ tree expr = is_xible_helper (code, to, from, /*trivial*/false);
+ if (expr == NULL_TREE || expr == error_mark_node)
+ return false;
+ return expr_noexcept_p (expr, tf_none);
+}
+
/* Returns true iff TO is assignable (if CODE is MODIFY_EXPR) or
constructible (otherwise) from FROM, which is a single type for
assignment or a list of types for construction. */