From patchwork Sun Jan 7 23:10:00 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: waffl3x X-Patchwork-Id: 83489 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id A867A3858C2C for ; Sun, 7 Jan 2024 23:10:32 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-4322.protonmail.ch (mail-4322.protonmail.ch [185.70.43.22]) by sourceware.org (Postfix) with ESMTPS id 80E793858CD1 for ; Sun, 7 Jan 2024 23:10:06 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 80E793858CD1 Authentication-Results: sourceware.org; dmarc=pass (p=quarantine dis=none) header.from=protonmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=protonmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 80E793858CD1 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=185.70.43.22 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704669009; cv=none; b=fu5riLLIHvOyzsFCLG/TvJitgIERAGkSsNv6MqbUMrSrJt6eiJgpt9qoTOM0zNLonyLN1KURzv2FgwlTk9nLJeFy71oLfkiipdJBVFVWgq4537kGQaLfset4WiJ5WmDkjNevXhAViS242pysYEgVmm0Gv6v+1PJX42ZaRe+Qp8c= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1704669009; c=relaxed/simple; bh=z7gJR3sfZ7POjoxtlNQz+TTcFY+niK5kPBYiwQfUmFo=; h=DKIM-Signature:Date:To:From:Subject:Message-ID:MIME-Version; b=OS+ZOn74uS6a3qInNDZAPddpJWbroqO4jzHB/o7hEQ/FTd/SP1ROtcrf1KaLdHBaTC9qbBRQHHgN+OtujlJ3IBA1SfCk6O6nOzDua9R+YwZ5llkr4SMJPE0udMvopuC/09G9xWcyJoeP+0E3FGaiWD+Af9agr6x6geD8C2BqCT8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1704669005; x=1704928205; bh=z7gJR3sfZ7POjoxtlNQz+TTcFY+niK5kPBYiwQfUmFo=; h=Date:To:From:Cc:Subject:Message-ID:Feedback-ID:From:To:Cc:Date: Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=DHRHK8Ut3Ogtb+1G/fYF2MoRFfO6L8Q/TD9oN1Ir5Fkmss4Oq18Jd3NFcI2cKx19c 6BDLjHCF4plntVwwaAbDZqVXo6C093LvGj7wMPqzuPu+gfoJodh3pgxH4rgp79/8RH o2BXJWS8fNeN5IWaB97wIifxNuzvqAOltN2nTrEr/8FEbBOFr3xGnK1/ctr2eHy7yd 4E4VSHdin1469WuXVFA7PQ+EsGu3VhFfx7y8hVVCYRyKcArDzGgSelr9UXSfoDkDto tngYFZh5B2c3cMmcLmA6v48MnSSDVtv/B9Fms8yeKrRzwO8YzoTksj4NxcnVZVX1fL c1AGK9/5O6bzg== Date: Sun, 07 Jan 2024 23:10:00 +0000 To: Jakub Jelinek From: waffl3x Cc: Jason Merrill , "gcc-patches@gcc.gnu.org" Subject: [PATCH v8 5/4] c++: P0847R7 (deducing this) - CWG2586. [PR102609] Message-ID: Feedback-ID: 14591686:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-9.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_PASS, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+patchwork=sourceware.org@gcc.gnu.org Bootstrapped and tested on x86_64-linux with no regressions. Not as hard as I thought it would be! As noted in the commit message, I believe this makes explicit object member functions feature complete. From a5f947d411b5e19ce7efbb4d766a2792b02c9626 Mon Sep 17 00:00:00 2001 From: Waffl3x Date: Sun, 7 Jan 2024 15:02:57 -0700 Subject: [PATCH] C++23 P0847R7 (deducing this) - CWG2586. [PR102609] This adds support for defaulted comparison operators and copy/move assignment operators, as well as allowing user defined xobj copy/move assignment operators. It turns out defaulted comparison operators already worked though, so this just adds a test for them. Defaulted comparison operators were not so nice and required a bit of a hack. Should work fine though! The diagnostics leave something to be desired, and there are some things that could be improved with more extensive design changes. There are a few notes left indicating where I think we could make improvements. Aside from some small bugs, with this commit xobj member functions should be feature complete. PR c++/102609 gcc/cp/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - CWG2586. * decl.cc (copy_fn_p): Accept xobj copy assignment functions. (move_signature_fn_p): Accept xobj move assignment functions. * method.cc (do_build_copy_assign): Handle defaulted xobj member functions. (defaulted_late_check): Comment. (defaultable_fn_check): Comment. gcc/testsuite/ChangeLog: PR c++/102609 C++23 P0847R7 (deducing this) - CWG2586. * g++.dg/cpp23/explicit-obj-basic6.C: New test. * g++.dg/cpp23/explicit-obj-default1.C: New test. * g++.dg/cpp23/explicit-obj-default2.C: New test. Signed-off-by: Waffl3x --- gcc/cp/decl.cc | 28 +++++++- gcc/cp/method.cc | 55 ++++++++++++++-- .../g++.dg/cpp23/explicit-obj-basic6.C | 51 +++++++++++++++ .../g++.dg/cpp23/explicit-obj-default1.C | 57 ++++++++++++++++ .../g++.dg/cpp23/explicit-obj-default2.C | 65 +++++++++++++++++++ 5 files changed, 248 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 7f267055c29..b10a72a87bf 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -15663,7 +15663,19 @@ copy_fn_p (const_tree d) && DECL_NAME (d) != assign_op_identifier) return 0; - args = FUNCTION_FIRST_USER_PARMTYPE (d); + if (DECL_XOBJ_MEMBER_FUNCTION_P (d)) + { + tree object_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (d))); + if (!TYPE_REF_P (object_param) + || TYPE_REF_IS_RVALUE (object_param) + /* Reject unrelated object parameters. */ + || TYPE_MAIN_VARIANT (TREE_TYPE (object_param)) != DECL_CONTEXT (d) + || CP_TYPE_CONST_P (TREE_TYPE (object_param))) + return 0; + args = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (d))); + } + else + args = FUNCTION_FIRST_USER_PARMTYPE (d); if (!args) return 0; @@ -15738,7 +15750,19 @@ move_signature_fn_p (const_tree d) && DECL_NAME (d) != assign_op_identifier) return 0; - args = FUNCTION_FIRST_USER_PARMTYPE (d); + if (DECL_XOBJ_MEMBER_FUNCTION_P (d)) + { + tree object_param = TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (d))); + if (!TYPE_REF_P (object_param) + || TYPE_REF_IS_RVALUE (object_param) + /* Reject unrelated object parameters. */ + || TYPE_MAIN_VARIANT (TREE_TYPE (object_param)) != DECL_CONTEXT (d) + || CP_TYPE_CONST_P (TREE_TYPE (object_param))) + return 0; + args = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (d))); + } + else + args = FUNCTION_FIRST_USER_PARMTYPE (d); if (!args) return 0; diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index aa5a044883e..da6a08a0304 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -795,13 +795,19 @@ do_build_copy_assign (tree fndecl) compound_stmt = begin_compound_stmt (0); parm = convert_from_reference (parm); + /* If we are building a defaulted xobj copy/move assignment operator then + current_class_ref will not have been set up. + Kind of an icky hack, but what can ya do? */ + tree const class_ref = DECL_XOBJ_MEMBER_FUNCTION_P (fndecl) + ? cp_build_fold_indirect_ref (DECL_ARGUMENTS (fndecl)) : current_class_ref; + if (trivial && is_empty_class (current_class_type)) /* Don't copy the padding byte; it might not have been allocated if *this is a base subobject. */; else if (trivial) { - tree t = build2 (MODIFY_EXPR, void_type_node, current_class_ref, parm); + tree t = build2 (MODIFY_EXPR, void_type_node, class_ref, parm); finish_expr_stmt (t); } else @@ -826,7 +832,7 @@ do_build_copy_assign (tree fndecl) /* Call the base class assignment operator. */ releasing_vec parmvec (make_tree_vector_single (converted_parm)); finish_expr_stmt - (build_special_member_call (current_class_ref, + (build_special_member_call (class_ref, assign_op_identifier, &parmvec, base_binfo, @@ -839,7 +845,7 @@ do_build_copy_assign (tree fndecl) fields; fields = DECL_CHAIN (fields)) { - tree comp = current_class_ref; + tree comp = class_ref; tree init = parm; tree field = fields; tree expr_type; @@ -898,7 +904,7 @@ do_build_copy_assign (tree fndecl) finish_expr_stmt (init); } } - finish_return_stmt (current_class_ref); + finish_return_stmt (class_ref); finish_compound_stmt (compound_stmt); } @@ -3380,13 +3386,46 @@ defaulted_late_check (tree fn) NULL, NULL); tree eh_spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (implicit_fn)); + /* Includes special handling for a default xobj operator. */ + auto compare_fn_params = [](tree fn, tree implicit_fn){ + tree fn_parms = TYPE_ARG_TYPES (TREE_TYPE (fn)); + tree implicit_fn_parms = TYPE_ARG_TYPES (TREE_TYPE (implicit_fn)); + + if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) + { + tree fn_obj_ref_type = TREE_VALUE (fn_parms); + /* We can't default xobj operators with an xobj parameter that is not + an lvalue reference. */ + if (!TYPE_REF_P (fn_obj_ref_type) + || TYPE_REF_IS_RVALUE (fn_obj_ref_type)) + return false; + /* If implicit_fn's object parameter is not a pointer, something is not + right. (Or we have finally changed the type of the iobj parameter + in iobj member functions.) */ + gcc_assert (TYPE_PTR_P (TREE_VALUE (implicit_fn_parms))); + /* Strip the reference/pointer off each object parameter before + comparing them. */ + if (!same_type_p (TREE_TYPE (fn_obj_ref_type), + TREE_TYPE (TREE_VALUE (implicit_fn_parms)))) + return false; + /* We just compared the object parameters, skip over them before + passing to compparms. */ + fn_parms = TREE_CHAIN (fn_parms); + implicit_fn_parms = TREE_CHAIN (implicit_fn_parms); + } + return compparms(fn_parms, implicit_fn_parms); + }; + if (!same_type_p (TREE_TYPE (TREE_TYPE (fn)), TREE_TYPE (TREE_TYPE (implicit_fn))) - || !compparms (TYPE_ARG_TYPES (TREE_TYPE (fn)), - TYPE_ARG_TYPES (TREE_TYPE (implicit_fn)))) + || !compare_fn_params (fn, implicit_fn)) { error ("defaulted declaration %q+D does not match the " "expected signature", fn); + /* FIXME: If the user is defaulting an xobj member function we should + emit an xobj member function for a signature. When we do this, maybe + we can just synthesize implicit_fn as an xobj member function and + avoid the dance in compare_fn_parms. */ inform (DECL_SOURCE_LOCATION (fn), "expected signature: %qD", implicit_fn); } @@ -3473,6 +3512,10 @@ defaultable_fn_check (tree fn) return false; } + /* FIXME: We need to check for xobj member functions here to give better + diagnostics for weird cases where unrelated xobj parameters are given. + We just want to do better than 'cannot be defaulted'. */ + if (kind == sfk_none) { error ("%qD cannot be defaulted", fn); diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C new file mode 100644 index 00000000000..6b8881ed07e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic6.C @@ -0,0 +1,51 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// user defined copy/move assignment operators + +inline constexpr int add_when_copy = 5; +inline constexpr int add_when_move = 10; +inline constexpr int poison = -1; + +struct S { + int _v; + S& operator=(this S& self, S const& rhs) { + self._v = rhs._v + add_when_copy; + return self; + }; + S& operator=(this S& self, S&& rhs) { + self._v = rhs._v + add_when_move; + rhs._v = poison; + return self; + } +}; + +inline constexpr int init_val = 5; + +int main() +{ + S s0{0}; + S s1{init_val}; + + // Sanity check. + if (s0._v != 0 + || s1._v != init_val) + __builtin_abort (); + + s0 = s1; + if (s0._v != init_val + add_when_copy) + __builtin_abort (); + if (s1._v != init_val) + __builtin_abort (); + + s0 = S{init_val}; + if (s0._v != init_val + add_when_move) + __builtin_abort (); + + S s2{init_val}; + s0 = static_cast(s2); + if (s0._v != init_val + add_when_move) + __builtin_abort (); + if (s2._v != poison) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C new file mode 100644 index 00000000000..fac3c28904c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default1.C @@ -0,0 +1,57 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// defaulted comparison operators + +#include + +struct S { + int _v; + bool operator==(this S const&, S const&) = default; + auto operator<=>(this S const&, S const&) = default; +}; + +int main() +{ + S const a_10{10}; + S const b_10{10}; + S const c_20{20}; + S const d_5{5}; + + if (a_10 != b_10) + __builtin_abort (); + if (c_20 == a_10) + __builtin_abort (); + if (!(a_10 == b_10)) + __builtin_abort (); + if (!(c_20 != a_10)) + __builtin_abort (); + + if (a_10 < b_10) + __builtin_abort (); + if (a_10 > b_10) + __builtin_abort (); + if (!(a_10 <= b_10)) + __builtin_abort (); + if (!(a_10 >= b_10)) + __builtin_abort (); + + if (!(a_10 < c_20)) + __builtin_abort (); + if (a_10 > c_20) + __builtin_abort (); + if (!(a_10 <= c_20)) + __builtin_abort (); + if (a_10 >= c_20) + __builtin_abort (); + + if (a_10 < d_5) + __builtin_abort (); + if (!(a_10 > d_5)) + __builtin_abort (); + if (a_10 <= d_5) + __builtin_abort (); + if (!(a_10 >= d_5)) + __builtin_abort (); +} + diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C new file mode 100644 index 00000000000..786f0c21c80 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-default2.C @@ -0,0 +1,65 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// defaulted copy/move assignment operators + +inline constexpr int add_when_copy = 5; +inline constexpr int add_when_move = 10; +inline constexpr int poison = -1; + +struct A { + int _v; + A(int v) : _v(v) {} + A& operator=(A const& rhs) { + if (&rhs == this) + return *this; + _v = rhs._v + add_when_copy; + return *this; + } + A& operator=(A&& rhs) { + if (&rhs == this) + return *this; + _v = rhs._v + add_when_move; + rhs._v = poison; + return *this; + } +}; + +struct S { + A _a; + S& operator=(this S&, S const&) = default; + S& operator=(this S&, S&&) = default; + + int v() const { return _a._v; } +}; + +inline constexpr int init_val = 5; + +int main() +{ + S s0{0}; + S s1{init_val}; + + // Sanity check. + if (s0.v () != 0 + || s1.v () != init_val) + __builtin_abort (); + + s0 = s1; + if (s0.v () != init_val + add_when_copy) + __builtin_abort (); + if (s1.v () != init_val) + __builtin_abort (); + + s0 = S{init_val}; + if (s0.v () != init_val + add_when_move) + __builtin_abort (); + + S s2{init_val}; + s0 = static_cast(s2); + if (s0.v () != init_val + add_when_move) + __builtin_abort (); + if (s2.v () != poison) + __builtin_abort (); +} + -- 2.43.0