diff options
author | Thomas Koenig <tkoenig@gcc.gnu.org> | 2021-01-03 21:40:04 +0100 |
---|---|---|
committer | Thomas Koenig <tkoenig@gcc.gnu.org> | 2021-01-03 21:40:04 +0100 |
commit | afae4a55ccaa0de95ea11e5f634084db6ab2f444 (patch) | |
tree | d632cc867d10410ba9fb750523be790b86846ac4 /gcc/cp/method.c | |
parent | 9d9a82ec8478ff52c7a9d61f58cd2a7b6295b5f9 (diff) | |
parent | d2eb616a0f7bea78164912aa438c29fe1ef5774a (diff) | |
download | gcc-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.c | 228 |
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. */ |