From 9e2256dcd481ffe3a8c79b65eba19fbb14b7ff8d Mon Sep 17 00:00:00 2001 From: Ville Voutilainen Date: Mon, 26 Oct 2020 15:36:24 +0200 Subject: c++: Implement __is_nothrow_constructible and __is_nothrow_assignable gcc/c-family/ChangeLog: * c-common.c (__is_nothrow_assignable): New. (__is_nothrow_constructible): Likewise. * c-common.h (RID_IS_NOTHROW_ASSIGNABLE): New. (RID_IS_NOTHROW_CONSTRUCTIBLE): Likewise. gcc/cp/ChangeLog: * cp-tree.h (CPTK_IS_NOTHROW_ASSIGNABLE): New. (CPTK_IS_NOTHROW_CONSTRUCTIBLE): Likewise. (is_nothrow_xible): Likewise. * method.c (is_nothrow_xible): New. (is_trivially_xible): Tweak. * parser.c (cp_parser_primary_expression): Handle the new RID_*. (cp_parser_trait_expr): Likewise. * semantics.c (trait_expr_value): Handle the new RID_*. (finish_trait_expr): Likewise. libstdc++-v3/ChangeLog: * include/std/type_traits (__is_nt_constructible_impl): Remove. (__is_nothrow_constructible_impl): Adjust. (is_nothrow_default_constructible): Likewise. (__is_nt_assignable_impl): Remove. (__is_nothrow_assignable_impl): Adjust. --- gcc/cp/method.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'gcc/cp/method.c') diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 6e4c5f7..16e7635 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1924,15 +1924,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. */ -- cgit v1.1 From 8b9a92f794b8ad8011e6beb11a609efa635c4600 Mon Sep 17 00:00:00 2001 From: Strager Neds Date: Tue, 10 Nov 2020 11:42:01 -0700 Subject: Refactor copying decl section names gcc/ * cgraph.h (symtab_node::get_section): Constify. (symtab_node::set_section): Declare new overload. * symtab.c (symtab_node::set_section): Define new overload. (symtab_node::copy_visibility_from): Use new overload of symtab_node::set_section. (symtab_node::resolve_alias): Same. * tree.h (set_decl_section_name): Declare new overload. * tree.c (set_decl_section_name): Define new overload. * tree-emutls.c (get_emutls_init_templ_addr): Same. * cgraphclones.c (cgraph_node::create_virtual_clone): Use new overload of symtab_node::set_section. (cgraph_node::create_version_clone_with_body): Same. * trans-mem.c (ipa_tm_create_version): Same. gcc/c * c-decl.c (merge_decls): Use new overload of set_decl_section_name. gcc/cp * decl.c (duplicate_decls): Use new overload of set_decl_section_name. * method.c (use_thunk): Same. * optimize.c (maybe_clone_body): Same. * coroutines.cc (act_des_fn): Same. gcc/d * decl.cc (finish_thunk): Use new overload of set_decl_section_name --- gcc/cp/method.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'gcc/cp/method.c') diff --git a/gcc/cp/method.c b/gcc/cp/method.c index 16e7635..4de192f 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; } -- cgit v1.1 From 4ed1dc1275bba89af92bfc7d97c21b376e4c29c3 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Fri, 4 Dec 2020 21:48:43 -0500 Subject: c++: Fix defaulted <=> fallback to < and == [PR96299] I thought I had implemented P1186R3, but apparently I didn't read it closely enough to understand the point of the paper, namely that for a defaulted operator<=>, if a member type doesn't have a viable operator<=>, we will use its operator< and operator== if the defaulted operator has an specific comparison category as its return type; the compiler can't guess if it should be strong_ordering or something else, but the user can make that choice explicit. The libstdc++ test change was necessary because of the change in genericize_spaceship from op0 > op1 to op1 < op0; this should be equivalent, but isn't because of PR88173. gcc/cp/ChangeLog: PR c++/96299 * cp-tree.h (build_new_op): Add overload that omits some parms. (genericize_spaceship): Add location_t parm. * constexpr.c (cxx_eval_binary_expression): Pass it. * cp-gimplify.c (genericize_spaceship): Pass it. * method.c (genericize_spaceship): Handle class-type arguments. (build_comparison_op): Fall back to op 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; } @@ -1323,7 +1340,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 +1381,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 comps; @@ -1375,13 +1396,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,7 +1413,7 @@ 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; @@ -1400,25 +1423,69 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain) 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 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; + } comps.safe_push (comp); } if (code == SPACESHIP_EXPR && is_auto (rettype)) -- cgit v1.1 From ffd454b92ba6ff5499cf57f82a2b0f4cee59978c Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Tue, 22 Dec 2020 20:18:10 +0100 Subject: c++: Handle array members in build_comparison_op [PR93480] http://eel.is/c++draft/class.compare.default#6 says for the expanded list of subobjects: "In that list, any subobject of array type is recursively expanded to the sequence of its elements, in the order of increasing subscript." but build_comparison_op just tried to compare the whole arrays, which failed and therefore the defaulted comparison was deleted. The following patch instead compares the array elements, and if info.defining, adds runtime loops around it so that it iterates over increasing subscripts. For flexible array members it punts, we don't know how large those will be, for zero sized arrays it doesn't even try to compare the elements, because if there are no elements, there is nothing to compare, and for [1] arrays it will not emit a loop because it is enough to use [0] array ref to cover everything. 2020-12-21 Jakub Jelinek 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 | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 5 deletions(-) (limited to 'gcc/cp/method.c') diff --git a/gcc/cp/method.c b/gcc/cp/method.c index da580a8..dd35173 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -1230,6 +1230,8 @@ common_comparison_type (vec &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_flags_t complain) 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,18 @@ build_comparison_op (tree fndecl, tsubst_flags_t complain) 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)) @@ -1502,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) @@ -1542,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) -- cgit v1.1