From patchwork Sun Nov 5 15:06:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: waffl3x X-Patchwork-Id: 161647 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:8f47:0:b0:403:3b70:6f57 with SMTP id j7csp2166394vqu; Sun, 5 Nov 2023 07:06:48 -0800 (PST) X-Google-Smtp-Source: AGHT+IH8AD8HUvr6o8dtFALQK89r3pnXauYRsJprrF3jIajSSxCorJfCPPNuDuT9LxHVPIaPr3rY X-Received: by 2002:a05:622a:52:b0:416:4cc5:2f51 with SMTP id y18-20020a05622a005200b004164cc52f51mr10479140qtw.1.1699196808019; Sun, 05 Nov 2023 07:06:48 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1699196808; cv=pass; d=google.com; s=arc-20160816; b=Rhv1t0c601Tg2JQF8nuipjtX4DaWrvckbxibPBJJTPZeOt+SclrHFN65y3GpokTfxj xnKx8U1rz8YHARLIOrzjiCus7RK1eYv4nNn/OKWi+L5GkageLfLFJGmovv9SJ8vat/Yb 0qoCcbQS6Fp+YxBcKsLzpDhWhhJ9EXRCVeMyI/Zv5DIIBNtYsGyDKPbIIw319DscxNQN Q/Gk/+MgSWCgY+z83u6NDitm3qCRAkioA8H3WM5MStiqUqkIGgmf51VJD7OxnXzXM57t Sw8I7gXDPgNbzC3stVUgceLazRXz6Lsv/H4H2OKvjhKgo7nexUblX3QjNb/KkiIkenO2 IxDw== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:mime-version:feedback-id :message-id:subject:cc:from:to:date:dkim-signature:arc-filter :dmarc-filter:delivered-to; bh=OD2Pq6jdW8U70+Z0IrfEGAha5qJ5Y9vZClCd4oR32GA=; fh=gNIGGMO26EMDdcN9T420kkj04Dd+kYGKSsUsy/rG/M8=; b=nPSpUMG6iNBMsFEWnZGeEe8/na1m+2wbDUeuZh/k5a8huC6XIdoNu3XS/tnazfPaqT StO6zMW8c7NBXZs+64pPTjk40z/fb3RYsDwDyh1BfcjbtsbDI1lI3koBT83AdapIBkcu qxkLgpdYvnwRs2aqNUXNBlO400dteFccR94gzOhf5/3fqdL+/Rsr8UCtZfTl5OZ22Bsc TO8a3qI49KDfuWmhUUMp89fJBwmu6ujX3ZdZXXpPeDNG4e8ZjMBt6UoaYt6R7f9ZcFqV 3tQ9B2jfKEWNJB79CjRR6TOsg5zbvOu+n7kHBkPZbdm0OGW+byaM1x4w2f98K8LK/ejf wiRw== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@protonmail.com header.s=protonmail3 header.b=ipXacX9v; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id v11-20020a05622a144b00b0041cbaa0003dsi4375013qtx.202.2023.11.05.07.06.47 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 05 Nov 2023 07:06:47 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@protonmail.com header.s=protonmail3 header.b=ipXacX9v; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BE8A73858296 for ; Sun, 5 Nov 2023 15:06:47 +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 A3E693858CDA for ; Sun, 5 Nov 2023 15:06:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org A3E693858CDA 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 A3E693858CDA 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=1699196783; cv=none; b=nCRoeEJU7ADvHNCmSM//SpUTuuN1r3CZeWC9cldT/ECnxFRyxo6B7jBljz2vxH5l/ivzWjgvQsxd5a57oTiLzi3ECwh9OkfNG7N3IvB1i5kArIZMpZFGZpY1hYn/UHSCJ9yC46Sivz/X18gdxnh34zbQeo9isnUn+vGex3E5UVw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1699196783; c=relaxed/simple; bh=OD2Pq6jdW8U70+Z0IrfEGAha5qJ5Y9vZClCd4oR32GA=; h=DKIM-Signature:Date:To:From:Subject:Message-ID:MIME-Version; b=sYhfoH4ZGwu9KUANjoTw6ApIJ94yMHCBmQZSVBt0WA1Rz7GvDYh2X5m3jz8rY41klG68H+BIZqqJ1F/K8xyODkt2EFzz1jY/cyXvuQiIgCtTXgR+2/hwMcVvmgO+GvmLvd2Ckdef5x5iJA/aDzNfT91HqIegGjEfDf3g9s0xmZQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1699196774; x=1699455974; bh=OD2Pq6jdW8U70+Z0IrfEGAha5qJ5Y9vZClCd4oR32GA=; h=Date:To:From:Cc:Subject:Message-ID:Feedback-ID:From:To:Cc:Date: Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=ipXacX9vrHLrXj/0WpweAPoCysVFVh/bx127ZNnDmYUS56ZcI7Mh0w47CMdHulJSO 3zvXFjRpK2X6TqXgM/iL8UsrXFTFGLC2skDJeNyi9vmUS6rCi4CcaxhQtTgzIzh7zh e2uXpHQlzQbu7yx4wapy6WSeTR3k1ownNqeH80RQeogBPVTd6hdARAbLAewO6I0xXG jLQUANwxYm9i80a8OC+emODHpSGmtAl367sTjwSEuNh/XllpmESjhwjUr85F5mDAud 0RiFKySmmYBzFUM200e/W3TaOAz2BZwv0IR5V8o0WMDnNjCRZ6eAlanMj+SbZ43HIi tv5HYo9jcl5mA== Date: Sun, 05 Nov 2023 15:06:09 +0000 To: waffl3x From: waffl3x Cc: Jason Merrill , "gcc-patches@gcc.gnu.org" Subject: [PATCH v4 1/2] c++: Initial support for P0847R7 (Deducing this) [PR102609] Message-ID: Feedback-ID: 14591686:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-9.0 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+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781736991827891125 X-GMAIL-MSGID: 1781736991827891125 Bootstrapped and tested on x86_64-linux with no regressions. I originally threw this e-mail together last night, but threw in the towel when I thought I saw tests failing and went to sleep. I did a proper bootstrap and comparison and whatnot and found that there were thankfully no regressions. Anyhow, the first patch feels ready for trunk, the second needs at least one review, I'll write more on that in the second e-mail though. I put quite a lot into the commit message, in hindsight I think I may have gone overboard, but that isn't something I'm going to rewrite at the moment. I really want to get these patches up for review so they can be finalized. I'm also including my usual musings on things that came up as I was polishing off the patches. I reckon some of them aren't all that important right now but I would rather toss them in here than forget about them. I'm starting to think that we should have a general macro that indicates whether an implicit object argument should be passed in the call. It might be more clear than what is currently present. I've also noticed that there's a fair amount of places where instead of using DECL_NONSTATIC_MEMBER_FUNCTION_P the code checks if tree_code of the type is a METHOD_TYPE, which is exactly what the aforementioned macro does. In build_min_non_dep_op_overload I reversed the branches of a condition because it made more sense with METHOD_TYPE first so it doesn't have to take xobj member functions into account on both branches. I am slightly concerned that flipping the branch around might have consequences, hence why I am mentioning it. Realistically I think it's probably fine though. I have a test prepared for diagnosing virtual specifiers on xobj member functions, but it's got some issues so I won't be including it with the following diagnostic patch. Diagnostics for virtual specifiers are still implemented, it's just the test that is having trouble. I mostly had a hard time working out edge cases, and the standard doesn't actually properly specify what the criteria for overriding a function is so I've been stumped on what behavior I want it to have. So for the time being, it only diagnoses uses of virtual on xobj member functions, while errors for final and override are handled by code that is already present. This can result in multiple errors, but again, I don't know how I want to handle it yet, especially since the standard doesn't specify this stuff very well. BTW let me know if there's anything you would prefer to be done differently in the changelog, I am still having trouble writing them and I'm usually uncertain if I'm writing them properly. Alex From e730dcba51503446cc362909fcab19361970b448 Mon Sep 17 00:00:00 2001 From: waffl3x Date: Sat, 4 Nov 2023 05:35:10 -0600 Subject: [PATCH 1/2] c++: Initial support for C++23 P0847R7 (Deducing this) [PR102609] This patch implements initial support for P0847R7 without diagnostics. My goal was to minimize changes to the existing code. To achieve this I chose to treat xobj member functions as static member functions, while opting into member function handling when necessary. This seemed to be the better choice since most of the time they are more like static member functions. This is achieved by inhibiting conversion of the declaration's type from FUNCTION_TYPE to METHOD_TYPE. Most if not everything seems to differentiate between member functions and static member functions by inspecting the FUNCTION_DECL's type, so forcing this is sufficient. An xobj member function is any member function that is declared with an xobj parameter as it's first parameter. This information is passed through the declarator's parameter list, stored in the purpose of the parameter's tree_list node. Normally this is used to store default arguments, but as default arguments are not allowed for xobj parameters it is fine for us to hijack it. By utilizing this we can pass this information from cp_parser_parameter_declaration over to grokdeclarator without adding anything new to the tree structs. We still need to differentiate this new function variety from static member functions and regular functions, and since this information needs to stick to the declaration we should select a more concrete place to store it. Unlike the previous hack for parameters, we instead add a flag to lang_decl_fn, the only modification this patch makes to any tree data-structures. We could probably try to stick the information in the decl's parameters somewhere, but I think a proper flag is justified. The new flag can be set and cleared through DECL_FUNCTION_XOBJ_FLAG, it is invalid to use this with anything other than FUNCTION_DECL nodes. For inspecting the value of this flag DECL_XOBJ_MEMBER_FUNC_P should be used, this macro is safe to use with any node type and will harmlessly evaluate to false for invalid node types. It needs to be noted that we can not add checking for xobj member functions to DECL_NONSTATIC_MEMBER_FUNCTION_P as it is used in cp-objcp-common.cc. While it most likely would be fine, it's possible it could have unintended effects. In light of this, we will most likely need to do some refactoring, possibly renaming and replacing it. In contrast, DECL_FUNCTION_MEMBER_P is not used outside of C++ code, so we can add checking for xobj member functions to it without any concerns. There are a few known issues still present in this patch. Most importantly, the implicit object argument fails to convert when passed to by-value xobj parameters. This occurs both for xobj parameters that match the argument type and xobj parameters that are unrelated to the object type, but have valid conversions available. This behavior can be observed in the explicit-obj-by-value[1-3].C tests. The implicit object argument appears to be simply reinterpreted instead of any conversion applied. This is elaborated on in the test cases. Despite this, calls where there is no valid conversion available are correctly rejected, which I find surprising. The explicit-obj-by-value4.C testcase demonstrates this odd but correct behavior. Other than this, lambdas are not yet supported, and there is some outstanding odd behavior where invalid calls to operators are improperly accepted. The test for this utilizes requires expressions to operate though so it's possible that the problems originate from there, but I have a hunch they aren't responsible. See explicit-obj-ops-requires-mem.C and explicit-obj-ops-requires-non-mem.C for those tests. One of the more invasive changes I chose to make is in build_min_non_dep_op_overload, I flipped the if branches because the conditions make significantly more sense when METHOD_TYPE is tested for first. It did not appear that the previous ordering was for any particular purpose so I felt it was better to flip it than make the conditions significantly more confusing. I did want to note this change in particular just in case. PR c++/102609 gcc/cp/ChangeLog: PR c++/102609 Initial support for C++23 P0847R7 - Deducing this. * call.cc (add_candidates): Check if fn is an xobj member function. (build_over_call): Ditto. * cp-tree.h (struct lang_decl_fn::xobj_func): New data member. (DECL_FUNCTION_XOBJ_FLAG): Define. (DECL_XOBJ_MEMBER_FUNC_P): Define. (DECL_FUNCTION_MEMBER_P): Add check for xobj member functions. (enum cp_decl_spec): Add ds_this. * decl.cc (grokfndecl): New param XOBJ_FUNC_P, for xobj member functions set DECL_FUNCTION_XOBJ_FLAG and don't set DECL_STATIC_FUNCTION_P. (grokdeclarator): Check for xobj param, clear it's purpose and set is_xobj_member_function if it is present. When flag set, don't change type to METHOD_TYPE, keep it as FUNCTION_TYPE. Adjust call to grokfndecl, pass is_xobj_member_function. (grok_op_properties): Treat xobj member functions as iobj member functions. * parser.cc (cp_parser_decl_specifier_seq): Handle "this" specifier. (cp_parser_parameter_declaration): When "this" decl spec present, set default_argument to this_identifier. (set_and_check_decl_spec_loc) : Add "this". * tree.cc (build_min_non_dep_op_overload): Flip if branches, treat xobj member functions as iobj member functions. * typeck.cc (cp_build_addr_expr_1): Handle xobj member functions without overloads. gcc/testsuite/ChangeLog: PR c++/102609 Initial support for C++23 P0847R7 - Deducing this. * g++.dg/cpp23/explicit-obj-basic1.C: New test. * g++.dg/cpp23/explicit-obj-basic2.C: New test. * g++.dg/cpp23/explicit-obj-by-value1.C: New test. * g++.dg/cpp23/explicit-obj-by-value2.C: New test. * g++.dg/cpp23/explicit-obj-by-value3.C: New test. * g++.dg/cpp23/explicit-obj-by-value4.C: New test. * g++.dg/cpp23/explicit-obj-lambda1.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-arrow.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-assignment.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-call.C: New test. * g++.dg/cpp23/explicit-obj-ops-mem-subscript.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C: New test. * g++.dg/cpp23/explicit-obj-ops-non-mem.h: New test. * g++.dg/cpp23/explicit-obj-ops-requires-mem.C: New test. * g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C: New test. Signed-off-by: waffl3x --- gcc/cp/call.cc | 9 +- gcc/cp/cp-tree.h | 25 +- gcc/cp/decl.cc | 44 +++- gcc/cp/parser.cc | 20 +- gcc/cp/tree.cc | 26 +- gcc/cp/typeck.cc | 6 + .../g++.dg/cpp23/explicit-obj-basic1.C | 113 +++++++++ .../g++.dg/cpp23/explicit-obj-basic2.C | 27 +++ .../g++.dg/cpp23/explicit-obj-by-value1.C | 49 ++++ .../g++.dg/cpp23/explicit-obj-by-value2.C | 59 +++++ .../g++.dg/cpp23/explicit-obj-by-value3.C | 42 ++++ .../g++.dg/cpp23/explicit-obj-by-value4.C | 19 ++ .../g++.dg/cpp23/explicit-obj-lambda1.C | 11 + .../g++.dg/cpp23/explicit-obj-ops-mem-arrow.C | 27 +++ .../cpp23/explicit-obj-ops-mem-assignment.C | 26 ++ .../g++.dg/cpp23/explicit-obj-ops-mem-call.C | 39 +++ .../cpp23/explicit-obj-ops-mem-subscript.C | 39 +++ .../cpp23/explicit-obj-ops-non-mem-dep.C | 57 +++++ .../cpp23/explicit-obj-ops-non-mem-non-dep.C | 56 +++++ .../g++.dg/cpp23/explicit-obj-ops-non-mem.h | 202 +++++++++++++++ .../cpp23/explicit-obj-ops-requires-mem.C | 172 +++++++++++++ .../cpp23/explicit-obj-ops-requires-non-mem.C | 229 ++++++++++++++++++ 22 files changed, 1266 insertions(+), 31 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 2eb54b5b6ed..c7ca32d1d34 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -6526,8 +6526,10 @@ add_candidates (tree fns, tree first_arg, const vec *args, tree fn_first_arg = NULL_TREE; const vec *fn_args = args; - - if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)) + /* We can't add xobj member functions to DECL_NONSTATIC_MEMBER_FUNCTION_P, + for now just check for them specifically. */ + if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn) + || DECL_XOBJ_MEMBER_FUNC_P (fn)) { /* Figure out where the object arg comes from. If this function is a non-static member and we didn't get an @@ -9949,7 +9951,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) } } /* Bypass access control for 'this' parameter. */ - else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE) + else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE + || DECL_XOBJ_MEMBER_FUNC_P (fn)) { tree arg = build_this (first_arg != NULL_TREE ? first_arg diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 98b29e9cf81..901fb1f4616 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2938,8 +2938,9 @@ struct GTY(()) lang_decl_fn { unsigned maybe_deleted : 1; unsigned coroutine_p : 1; unsigned implicit_constexpr : 1; + unsigned xobj_func : 1; - unsigned spare : 9; + unsigned spare : 8; /* 32-bits padding on 64-bit host. */ @@ -3338,14 +3339,25 @@ struct GTY(()) lang_decl { (LANG_DECL_FN_CHECK (NODE)->static_function) /* Nonzero for FUNCTION_DECL means that this decl is a non-static - member function. */ + member function, UNLESS it is an xobj member function. + This inconsistency will be fixed in the future. */ #define DECL_NONSTATIC_MEMBER_FUNCTION_P(NODE) \ (TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE) +/* Simple member access, only valid for FUNCTION_DECL nodes. */ +#define DECL_FUNCTION_XOBJ_FLAG(NODE) \ + (LANG_DECL_FN_CHECK (NODE)->xobj_func) +/* Nonzero if NODE is a FUNCTION_DECL that is an xobj member function, + safely evaluates to false for all non FUNCTION_DECL nodes. */ +#define DECL_XOBJ_MEMBER_FUNC_P(NODE) \ + (TREE_CODE (STRIP_TEMPLATE (NODE)) == FUNCTION_DECL \ + && DECL_FUNCTION_XOBJ_FLAG (NODE) == 1) + /* Nonzero for FUNCTION_DECL means that this decl is a member function (static or non-static). */ #define DECL_FUNCTION_MEMBER_P(NODE) \ - (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE)) + (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE) \ + || DECL_XOBJ_MEMBER_FUNC_P (NODE)) /* Nonzero for FUNCTION_DECL means that this member function has `this' as const X *const. */ @@ -6264,11 +6276,13 @@ enum cp_storage_class { /* An individual decl-specifier. This is used to index the array of locations for the declspecs in struct cp_decl_specifier_seq - below. */ + below. + A subset of these enums also corresponds to elements of + cp_parser_set_decl_spec_type:decl_spec_names in parser.cc. */ enum cp_decl_spec { ds_first, - ds_signed = ds_first, + ds_signed = ds_first, /* Index of first element of decl_spec_names. */ ds_unsigned, ds_short, ds_long, @@ -6285,6 +6299,7 @@ enum cp_decl_spec { ds_complex, ds_constinit, ds_consteval, + ds_this, /* Index of last element of decl_spec_names. */ ds_thread, ds_type_spec, ds_redefined_builtin_type_spec, diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 16af59de696..c02d78b8102 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10318,6 +10318,7 @@ grokfndecl (tree ctype, int publicp, int inlinep, bool deletedp, + bool xobj_func_p, special_function_kind sfk, bool funcdef_flag, bool late_return_type_p, @@ -10327,7 +10328,6 @@ grokfndecl (tree ctype, location_t location) { tree decl; - int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE; tree t; if (location == UNKNOWN_LOCATION) @@ -10525,12 +10525,9 @@ grokfndecl (tree ctype, (IDENTIFIER_POINTER (declarator)))))) SET_DECL_LANGUAGE (decl, lang_c); - /* Should probably propagate const out from type to decl I bet (mrs). */ - if (staticp) - { - DECL_STATIC_FUNCTION_P (decl) = 1; - DECL_CONTEXT (decl) = ctype; - } + DECL_STATIC_FUNCTION_P (decl) + = !xobj_func_p && ctype && TREE_CODE (type) == FUNCTION_TYPE; + DECL_FUNCTION_XOBJ_FLAG (decl) = xobj_func_p; if (deletedp) DECL_DELETED_FN (decl) = 1; @@ -12998,6 +12995,8 @@ grokdeclarator (const cp_declarator *declarator, if (attrlist) diagnose_misapplied_contracts (*attrlist); + /* Skip over build_memfn_type when a FUNCTION_DECL is an xobj memfn. */ + bool is_xobj_member_function = false; /* Determine the type of the entity declared by recurring on the declarator. */ for (; declarator; declarator = declarator->declarator) @@ -13113,6 +13112,25 @@ grokdeclarator (const cp_declarator *declarator, if (raises == error_mark_node) raises = NULL_TREE; + auto find_xobj_parm = [](tree parm_list) + { + /* There is no need to iterate over the list, + only the first parm can be a valid xobj parm. */ + if (!parm_list || parm_list == void_list_node) + return false; + if (TREE_PURPOSE (parm_list) != this_identifier) + return false; + /* If we make it here, we are looking at an xobj parm. + + Non-null 'purpose' usually means the parm has a default + argument, we don't want to violate this assumption. */ + TREE_PURPOSE (parm_list) = NULL_TREE; + return true; + }; + + is_xobj_member_function + = find_xobj_parm (declarator->u.function.parameters); + if (reqs) error_at (location_of (reqs), "requires-clause on return type"); reqs = declarator->u.function.requires_clause; @@ -14177,6 +14195,8 @@ grokdeclarator (const cp_declarator *declarator, } if (ctype && TREE_CODE (type) == FUNCTION_TYPE && staticp < 2 + /* Don't convert xobj member functions to METHOD_TYPE. */ + && !is_xobj_member_function && !(unqualified_id && identifier_p (unqualified_id) && IDENTIFIER_NEWDEL_OP_P (unqualified_id))) @@ -14398,7 +14418,8 @@ grokdeclarator (const cp_declarator *declarator, friendp ? -1 : 0, friendp, publicp, inlinep | (2 * constexpr_p) | (4 * concept_p) | (8 * consteval_p), - initialized == SD_DELETED, sfk, + initialized == SD_DELETED, + is_xobj_member_function, sfk, funcdef_flag, late_return_type_p, template_count, in_namespace, attrlist, id_loc); @@ -14733,8 +14754,8 @@ grokdeclarator (const cp_declarator *declarator, inlinep | (2 * constexpr_p) | (4 * concept_p) | (8 * consteval_p), initialized == SD_DELETED, - sfk, - funcdef_flag, + is_xobj_member_function, sfk, + funcdef_flag, late_return_type_p, template_count, in_namespace, attrlist, id_loc); @@ -15628,7 +15649,8 @@ grok_op_properties (tree decl, bool complain) /* An operator function must either be a non-static member function or have at least one parameter of a class, a reference to a class, an enumeration, or a reference to an enumeration. 13.4.0.6 */ - if (! methodp || DECL_STATIC_FUNCTION_P (decl)) + if ((!methodp && !DECL_XOBJ_MEMBER_FUNC_P (decl)) + || DECL_STATIC_FUNCTION_P (decl)) { if (operator_code == TYPE_EXPR || operator_code == COMPONENT_REF diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 20e18365906..4fbcc52c222 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -16091,6 +16091,16 @@ cp_parser_decl_specifier_seq (cp_parser* parser, decl_specs->locations[ds_attribute] = token->location; continue; } + /* Special case for "this" specifier, indicating a parm is an xobj parm. + The "this" specifier must be the first specifier in the declaration, + after any attributes. */ + if (token->keyword == RID_THIS) + { + cp_lexer_consume_token (parser->lexer); + set_and_check_decl_spec_loc (decl_specs, ds_this, token); + continue; + } + /* Assume we will find a decl-specifier keyword. */ found_decl_spec = true; /* If the next token is an appropriate keyword, we can simply @@ -25495,6 +25505,13 @@ cp_parser_parameter_declaration (cp_parser *parser, if (default_argument) STRIP_ANY_LOCATION_WRAPPER (default_argument); + if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this)) + { + /* Xobj parameters can not have default arguments, thus + we can reuse the default argument field to flag the param as such. */ + default_argument = this_identifier; + } + /* Generate a location for the parameter, ranging from the start of the initial token to the end of the final token (using input_location for the latter, set up by cp_lexer_set_source_position_from_token when @@ -33873,7 +33890,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs, "constexpr", "__complex", "constinit", - "consteval" + "consteval", + "this" }; gcc_rich_location richloc (location); richloc.add_fixit_remove (); diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 417c92ba76f..ab5fe79146c 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3660,6 +3660,7 @@ build_min_non_dep_op_overload (enum tree_code op, expected_nargs = cp_tree_code_length (op); if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE + || DECL_XOBJ_MEMBER_FUNC_P (overload) /* For ARRAY_REF, operator[] is either a non-static member or newly static member, never out of class and for the static member case if user uses single index the operator[] needs to have a single @@ -3677,24 +3678,26 @@ build_min_non_dep_op_overload (enum tree_code op, releasing_vec args; va_start (p, overload); - if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE) + if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE + || DECL_XOBJ_MEMBER_FUNC_P (overload)) { - fn = overload; - if (op == ARRAY_REF) - obj = va_arg (p, tree); + tree object = va_arg (p, tree); + tree binfo = TYPE_BINFO (TREE_TYPE (object)); + tree method = build_baselink (binfo, binfo, overload, NULL_TREE); + fn = build_min (COMPONENT_REF, TREE_TYPE (overload), + object, method, NULL_TREE); for (int i = 0; i < nargs; i++) { tree arg = va_arg (p, tree); vec_safe_push (args, arg); } } - else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE) + else if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE) { - tree object = va_arg (p, tree); - tree binfo = TYPE_BINFO (TREE_TYPE (object)); - tree method = build_baselink (binfo, binfo, overload, NULL_TREE); - fn = build_min (COMPONENT_REF, TREE_TYPE (overload), - object, method, NULL_TREE); + gcc_assert (!DECL_XOBJ_MEMBER_FUNC_P (overload)); + fn = overload; + if (op == ARRAY_REF) + obj = va_arg (p, tree); for (int i = 0; i < nargs; i++) { tree arg = va_arg (p, tree); @@ -3729,7 +3732,8 @@ build_min_non_dep_op_overload (tree non_dep, tree overload, tree object, unsigned int nargs = call_expr_nargs (non_dep); tree fn = overload; - if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE) + if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE + || DECL_XOBJ_MEMBER_FUNC_P (overload)) { tree binfo = TYPE_BINFO (TREE_TYPE (object)); tree method = build_baselink (binfo, binfo, overload, NULL_TREE); diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 49afbd8fb5e..00b999cec19 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -7164,6 +7164,12 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) && !mark_used (t, complain) && !(complain & tf_error)) return error_mark_node; + /* Exception for non-overloaded explicit object member function. + I am pretty sure this is not perfect, I think we aren't + handling some constexpr stuff, but I am leaving it for now. */ + if (TREE_CODE (TREE_TYPE (t)) == FUNCTION_TYPE) + return build_address (t); + type = build_ptrmem_type (context_for_name_lookup (t), TREE_TYPE (t)); t = make_ptrmem_cst (type, t); diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C new file mode 100644 index 00000000000..1e44c9123b7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C @@ -0,0 +1,113 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// basic use cases and calling + +// non-trailing return +// definitions +struct S0 { + void f0(this S0) {} + void f1(this S0&) {} + void f2(this S0&&) {} + void f3(this S0 const&) {} + void f4(this S0 const&&) {} + template + void d0(this Self&&) {} + void d1(this auto&&) {} +}; +// declarations +struct S1 { + void f0(this S1); + void f1(this S1&); + void f2(this S1&&); + void f3(this S1 const&); + void f4(this S1 const&&); + template + void d0(this Self&&); + void d1(this auto&&); +}; +// out of line definitions +void S1::f0(this S1) {} +void S1::f1(this S1&) {} +void S1::f2(this S1&&) {} +void S1::f3(this S1 const&) {} +void S1::f4(this S1 const&&) {} +template +void S1::d0(this Self&&) {} +void S1::d1(this auto&&) {} + +// trailing return +// definitions +struct S2 { + auto f0(this S2) -> void {} + auto f1(this S2&) -> void {} + auto f2(this S2&&) -> void {} + auto f3(this S2 const&) -> void {} + auto f4(this S2 const&&) -> void {} + template + auto d0(this Self&&) -> void {} + + auto d1(this auto&&) -> void {} +}; +// declarations +struct S3 { + auto f0(this S3) -> void; + auto f1(this S3&) -> void; + auto f2(this S3&&) -> void; + auto f3(this S3 const&) -> void; + auto f4(this S3 const&&) -> void; + template + auto d0(this Self&&) -> void; + auto d1(this auto&&) -> void; +}; +// out of line definitions +auto S3::f0(this S3) -> void {} +auto S3::f1(this S3&) -> void {} +auto S3::f2(this S3&&) -> void {} +auto S3::f3(this S3 const&) -> void {} +auto S3::f4(this S3 const&&) -> void {} +template +auto S3::d0(this Self&&) -> void {} +auto S3::d1(this auto&&) -> void {} + +template +void call_with_qualification() +{ + T obj{}; + // by value should take any qualification (f0) + T{}.f0(); + obj.f0(); + static_cast(obj).f0(); + static_cast(obj).f0(); + static_cast(obj).f0(); + // specific qualification (f1 - f4) + T{}.f2(); + T{}.f3(); + T{}.f4(); + obj.f1(); + obj.f3(); + static_cast(obj).f2(); + static_cast(obj).f3(); + static_cast(obj).f4(); + static_cast(obj).f3(); + static_cast(obj).f4(); + // deduced should (obviously) take any qualification (d0, d1) + T{}.d0(); + obj.d0(); + static_cast(obj).d0(); + static_cast(obj).d0(); + static_cast(obj).d0(); + T{}.d1(); + obj.d1(); + static_cast(obj).d1(); + static_cast(obj).d1(); + static_cast(obj).d1(); +} + +void perform_calls() +{ + call_with_qualification(); + call_with_qualification(); + call_with_qualification(); + call_with_qualification(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C new file mode 100644 index 00000000000..2c2b69ad362 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C @@ -0,0 +1,27 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// explicit object member function pointer type deduction, +// conversion to function pointer, +// and calling through pointer to function + +struct S { + int _n; + int f(this S& self) { return self._n; } +}; + +using f_type = int(*)(S&); + +static_assert (__is_same (f_type, decltype (&S::f))); + +int main() +{ + auto fp0 = &S::f; + f_type fp1 = &S::f; + static_assert (__is_same (decltype (fp0), decltype (fp1))); + S s{42}; + if (fp0 (s) != 42) + __builtin_abort (); + if (fp1 (s) != 42) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C new file mode 100644 index 00000000000..e85c9ab03b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C @@ -0,0 +1,49 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// conversion of the implicit object argument to an xobj parameter +// when calling by value xobj member functions + +// The initial implementation of xobj member functions incorrectly did not +// convert the implicit object argument when binding to the xobj +// parameter. In spite of this, it did correctly check to see if such a +// conversion would be valid, thus no diagnostic would be emitted when a +// conversion was valid, but instead of applying the conversion, the +// argument would silently be reinterpreted as the type of the parameter. + +// This is why we use uintptr_t for the value in S and compare the result +// of f to &s, we want to test for simple reinterpretation of the +// argument. To accurately test for this we make sure to use an object +// that has a different address than the value of our magic number. It's +// an impossibly improbable edge case but it's trivial to work around. We +// still compare against both the address of s and the magic number so we +// can additionally test for bugged conversions, while also +// differentiating that case from reinterpretation of the argument. + +// { dg-xfail-run-if "by value explicit object parameter is not supported yet" { *-*-* } } + +using uintptr_t = __UINTPTR_TYPE__; +inline constexpr uintptr_t magic = 42; + +struct S { + uintptr_t _v; + uintptr_t f(this S self) { + return self._v; + } +}; + +int main() +{ + S s0{magic}; + S s1{magic}; + // prevent (absurdly improbable) bogus failures + S& s = magic != (uintptr_t)(&s0) ? s0 : s1; + + uintptr_t const ret = s.f(); + // check for reinterpretation of the object argument + if (ret == (uintptr_t)(&s)) + __builtin_abort (); + // check for a bugged conversion + if (ret != magic) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C new file mode 100644 index 00000000000..051439bb1df --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C @@ -0,0 +1,59 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// conversion of the implicit object argument to an xobj parameter +// using a user defined conversion or converting constructor +// when calling by value xobj member functions + +// see explicit-obj-by-value1.C for details on this test + +// { dg-xfail-run-if "user defined conversions from an implicit object argument to an explicit object parameter are not supported yet" { *-*-* } } + +using uintptr_t = __UINTPTR_TYPE__; +inline constexpr uintptr_t magic = 42; + +struct S; + +struct FromS { + uintptr_t _v; + FromS(S); +}; + +struct S { + operator uintptr_t() const { + return magic; + } + uintptr_t f(this uintptr_t n) { + return n; + } + uintptr_t g(this FromS from_s) { + return from_s._v; + } +}; + +FromS::FromS(S) : _v(magic) {} + + +int main() +{ + S s0{}; + S s1{}; + // prevent (absurdly improbable) bogus failures + S& s = magic != (uintptr_t)(&s0) ? s0 : s1; + + uintptr_t const ret0 = s.f(); + // check for reinterpretation of the object argument + if (ret0 == (uintptr_t)(&s)) + __builtin_abort (); + // check for a bugged conversion + if (ret0 != magic) + __builtin_abort (); + + uintptr_t const ret1 = s.g(); + // check for reinterpretation of the object argument + if (ret1 == (uintptr_t)(&s)) + __builtin_abort (); + // check for a bugged conversion + if (ret1 != magic) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C new file mode 100644 index 00000000000..30e556bd6cb --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C @@ -0,0 +1,42 @@ +// P0847R7 +// { dg-do run { target c++23 } } + +// correct constructor selection when initializing a by value xobj parameter + +// see explicit-obj-by-value1.C for details on this test + +// { dg-xfail-run-if "by value explicit object parameter is not supported yet" { *-*-* } } + +using uintptr_t = __UINTPTR_TYPE__; +inline constexpr uintptr_t magic = 42; +inline constexpr uintptr_t copy_magic = 5; +inline constexpr uintptr_t move_magic = 10; + +struct S { + uintptr_t _v; + explicit S(uintptr_t v) : _v(v) {} + S(S const& other) : _v(other._v + copy_magic) {} + S(S&& other) : _v(other._v + move_magic) {} + uintptr_t f(this S self) { + return self._v; + } +}; + +int main() +{ + S s0{magic}; + S s1{magic}; + // prevent (absurdly improbable (^2)) bogus results + // it's virtually impossible for both to have a bogus result, + // but we can guarantee correct results from both easily, so why not? + S& s_copy_from = magic + copy_magic != (uintptr_t)(&s0) ? s0 : s1; + S& s_move_from = magic + move_magic != (uintptr_t)(&s0) ? s0 : s1; + uintptr_t const copy_ret = static_cast(s_copy_from).f(); + uintptr_t const move_ret = static_cast(s_move_from).f(); + // we test specifically for reinterpretation in other + // by value tests, it's unnecessary to do it again here + if (copy_ret != magic + copy_magic) + __builtin_abort (); + if (move_ret != magic + move_magic) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C new file mode 100644 index 00000000000..d3c5e393e7b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C @@ -0,0 +1,19 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// diagnosis of ill-formed calls to by-value xobj member functions +// due to an absence of valid conversion functions + +struct NotFromS {}; + +struct S { + void f(this int) {} + void g(this NotFromS) {} +}; + +void test() +{ + S s{}; + s.f(); // { dg-error {cannot convert 'S' to 'int'} } + s.g(); // { dg-error {cannot convert 'S' to 'NotFromS'} } +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C new file mode 100644 index 00000000000..913fb3ca5ce --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C @@ -0,0 +1,11 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// lambda declaration with xobj parameter + +// { dg-excess-errors "explicit object parameter with lambdas not implemented yet" { xfail *-*-* } } + +void test() +{ + (void)[](this auto&& self){}; +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C new file mode 100644 index 00000000000..c5b2c805a2f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C @@ -0,0 +1,27 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (arrow) + +struct S { + int _v; + S* operator->(this S& self) { return &self; } +}; + +void non_dep() +{ + S s{}; + (void)s->_v; +} + +template +void dependent() +{ + S s{}; + (void)s->_v; +} + +void call() +{ + dependent(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C new file mode 100644 index 00000000000..829c7137abc --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C @@ -0,0 +1,26 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (assignment) + +struct S { + void operator=(this S&, int) {} +}; + +void non_dep() +{ + S s{}; + s = 0; +} + +template +void dependent() +{ + S s{}; + s = 0; +} + +void call() +{ + dependent(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C new file mode 100644 index 00000000000..1dfe764de83 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C @@ -0,0 +1,39 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (call op) + +// execution paths for subscript with 1 argument and 0 and 2+ arguments are different +// just to be safe, also test 0 and 2 argument cases here too + +struct S { + void operator()(this S&) {} + void operator()(this S&, int) {} + void operator()(this S&, int, int) {} + template + void operator()(this S&, Args... args) {} +}; + +void non_dep() +{ + S s{}; + s(); + s(0); + s(0, 0); + s(0, 0, 0); +} + +template +void dependent() +{ + S s{}; + s(); + s(0); + s(0, 0); + s(0, 0, 0); +} + +void call() +{ + dependent(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C new file mode 100644 index 00000000000..cee5f6e135c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C @@ -0,0 +1,39 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// uses of member only operators (subscript) + +// execution paths for subscript with 1 argument and 0 and 2+ arguments are different +// therefore we should additionally test the 0 and 2 argument cases as well + +struct S { + void operator[](this S&) {} + void operator[](this S&, int) {} + void operator[](this S&, int, int) {} + template + void operator[](this S&, Args... args) {} +}; + +void non_dep() +{ + S s{}; + s[]; + s[0]; + s[0, 0]; + s[0, 0, 0]; +} + +template +void dependent() +{ + S s{}; + s[]; + s[0]; + s[0, 0]; + s[0, 0, 0]; +} + +void call() +{ + dependent(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C new file mode 100644 index 00000000000..3c9f93fbcc2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C @@ -0,0 +1,57 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// operators that are not required to be members +// called in a dependent context (as non dependent exprs) +// see header +#include "explicit-obj-ops-non-mem.h" + +// noop, indicates which versions are ill-formed +// I could not find a way to test the invalid cases +// without requires expressions +#define TEST_INVALID(X) + +template +void do_calls() +{ + Value value{}; + TEST_OPS(value) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + + LRef l_ref{}; + TEST_OPS(l_ref) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + + RRef r_ref{}; + TEST_INVALID(r_ref) + TEST_OPS(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + + ConstLRef const_l_ref{}; + TEST_OPS(const_l_ref) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + + ConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref) + TEST_INVALID(static_cast(const_r_ref)) + TEST_INVALID(static_cast(const_r_ref)) + TEST_OPS(static_cast(const_r_ref)) + + Deduced deduced{}; + TEST_OPS(deduced) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + + VALIDATE_RETURN_TYPES(deduced, Deduced&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced&&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&&) +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C new file mode 100644 index 00000000000..790a6ec379f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C @@ -0,0 +1,56 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// operators that are not required to be members +// called in a non-dependent context +// see header +#include "explicit-obj-ops-non-mem.h" + +// noop, indicates which versions are ill-formed +// I could not find a way to test the invalid cases +// without requires expressions +#define TEST_INVALID(X) + +void do_calls() +{ + Value value{}; + TEST_OPS(value) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + TEST_OPS(static_cast(value)) + + LRef l_ref{}; + TEST_OPS(l_ref) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + + RRef r_ref{}; + TEST_INVALID(r_ref) + TEST_OPS(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + + ConstLRef const_l_ref{}; + TEST_OPS(const_l_ref) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + TEST_OPS(static_cast(const_l_ref)) + + ConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref) + TEST_INVALID(static_cast(const_r_ref)) + TEST_INVALID(static_cast(const_r_ref)) + TEST_OPS(static_cast(const_r_ref)) + + Deduced deduced{}; + TEST_OPS(deduced) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + TEST_OPS(static_cast(deduced)) + + VALIDATE_RETURN_TYPES(deduced, Deduced&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced&&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&) + VALIDATE_RETURN_TYPES(static_cast(deduced), Deduced const&&) +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h new file mode 100644 index 00000000000..b94e56b1dd6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h @@ -0,0 +1,202 @@ +// missing test for three-way-compare (I don't know how to write it) +// missing test for ->* (I don't know how to write it) + +// tests for ops that must be member functions are seperate + +#define MAKE_STRUCT_OPS(TYPE) \ + TYPE operator+=(this TYPE self, int) { return self; } \ + TYPE operator-=(this TYPE self, int) { return self; } \ + TYPE operator*=(this TYPE self, int) { return self; } \ + TYPE operator/=(this TYPE self, int) { return self; } \ + TYPE operator%=(this TYPE self, int) { return self; } \ + TYPE operator&=(this TYPE self, int) { return self; } \ + TYPE operator|=(this TYPE self, int) { return self; } \ + TYPE operator^=(this TYPE self, int) { return self; } \ + TYPE operator<<=(this TYPE self, int) { return self; } \ + TYPE operator>>=(this TYPE self, int) { return self; } \ + TYPE operator++(this TYPE self) { return self; } \ + TYPE operator--(this TYPE self) { return self; } \ + TYPE operator++(this TYPE self, int) { return self; } \ + TYPE operator--(this TYPE self, int) { return self; } \ + TYPE operator+(this TYPE self) { return self; } \ + TYPE operator-(this TYPE self) { return self; } \ + TYPE operator+(this TYPE self, int) { return self; } \ + TYPE operator-(this TYPE self, int) { return self; } \ + TYPE operator*(this TYPE self, int) { return self; } \ + TYPE operator/(this TYPE self, int) { return self; } \ + TYPE operator%(this TYPE self, int) { return self; } \ + TYPE operator&(this TYPE self, int) { return self; } \ + TYPE operator|(this TYPE self, int) { return self; } \ + TYPE operator^(this TYPE self, int) { return self; } \ + TYPE operator<<(this TYPE self, int) { return self; } \ + TYPE operator>>(this TYPE self, int) { return self; } \ + TYPE operator!(this TYPE self) { return self; } \ + TYPE operator&&(this TYPE self, int const&) { return self; } \ + TYPE operator||(this TYPE self, int const&) { return self; } \ + TYPE operator==(this TYPE self, int) { return self; } \ + TYPE operator!=(this TYPE self, int) { return self; } \ + TYPE operator<(this TYPE self, int) { return self; } \ + TYPE operator>(this TYPE self, int) { return self; } \ + TYPE operator<=(this TYPE self, int) { return self; } \ + TYPE operator>=(this TYPE self, int) { return self; } \ + TYPE operator*(this TYPE self) { return self; } \ + TYPE operator&(this TYPE self) { return self; } \ + TYPE operator,(this TYPE self, int) { return self; } + +struct Value { + MAKE_STRUCT_OPS (Value) +}; + +struct LRef { + MAKE_STRUCT_OPS (LRef&) +}; + +struct RRef { + MAKE_STRUCT_OPS (RRef&&) +}; + +struct ConstLRef { + MAKE_STRUCT_OPS (ConstLRef const&) +}; + +struct ConstRRef { + MAKE_STRUCT_OPS (ConstRRef const&&) +}; + +#undef MAKE_STRUCT_OPS + +struct Deduced { + template Self&& operator+=(this Self&& self, int) { return static_cast(self); } + template Self&& operator-=(this Self&& self, int) { return static_cast(self); } + template Self&& operator*=(this Self&& self, int) { return static_cast(self); } + template Self&& operator/=(this Self&& self, int) { return static_cast(self); } + template Self&& operator%=(this Self&& self, int) { return static_cast(self); } + template Self&& operator&=(this Self&& self, int) { return static_cast(self); } + template Self&& operator|=(this Self&& self, int) { return static_cast(self); } + template Self&& operator^=(this Self&& self, int) { return static_cast(self); } + template Self&& operator<<=(this Self&& self, int) { return static_cast(self); } + template Self&& operator>>=(this Self&& self, int) { return static_cast(self); } + + template Self&& operator++(this Self&& self) { return static_cast(self); } + template Self&& operator--(this Self&& self) { return static_cast(self); } + template Self&& operator++(this Self&& self, int) { return static_cast(self); } + template Self&& operator--(this Self&& self, int) { return static_cast(self); } + + template Self&& operator+(this Self&& self) { return static_cast(self); } + template Self&& operator-(this Self&& self) { return static_cast(self); } + template Self&& operator+(this Self&& self, int) { return static_cast(self); } + template Self&& operator-(this Self&& self, int) { return static_cast(self); } + template Self&& operator*(this Self&& self, int) { return static_cast(self); } + template Self&& operator/(this Self&& self, int) { return static_cast(self); } + template Self&& operator%(this Self&& self, int) { return static_cast(self); } + template Self&& operator&(this Self&& self, int) { return static_cast(self); } + template Self&& operator|(this Self&& self, int) { return static_cast(self); } + template Self&& operator^(this Self&& self, int) { return static_cast(self); } + template Self&& operator<<(this Self&& self, int) { return static_cast(self); } + template Self&& operator>>(this Self&& self, int) { return static_cast(self); } + + template Self&& operator!(this Self&& self) { return static_cast(self); } + template Self&& operator&&(this Self&& self, int const&) { return static_cast(self); } + template Self&& operator||(this Self&& self, int const&) { return static_cast(self); } + + template Self&& operator==(this Self&& self, int) { return static_cast(self); } + template Self&& operator!=(this Self&& self, int) { return static_cast(self); } + template Self&& operator<(this Self&& self, int) { return static_cast(self); } + template Self&& operator>(this Self&& self, int) { return static_cast(self); } + template Self&& operator<=(this Self&& self, int) { return static_cast(self); } + template Self&& operator>=(this Self&& self, int) { return static_cast(self); } + + template Self&& operator*(this Self&& self) { return static_cast(self); } + template Self&& operator&(this Self&& self) { return static_cast(self); } + template Self&& operator,(this Self&& self, int) { return static_cast(self); } +}; + +#define TEST_OPS(OPERAND) \ + (OPERAND) += 0; \ + (OPERAND) -= 0; \ + (OPERAND) *= 0; \ + (OPERAND) /= 0; \ + (OPERAND) %= 0; \ + (OPERAND) &= 0; \ + (OPERAND) |= 0; \ + (OPERAND) ^= 0; \ + (OPERAND) <<= 0; \ + (OPERAND) >>= 0; \ + \ + ++(OPERAND); \ + --(OPERAND); \ + (OPERAND)++; \ + (OPERAND)--; \ + \ + +(OPERAND); \ + -(OPERAND); \ + (OPERAND) + 0; \ + (OPERAND) - 0; \ + (OPERAND) * 0; \ + (OPERAND) / 0; \ + (OPERAND) % 0; \ + (OPERAND) & 0; \ + (OPERAND) | 0; \ + (OPERAND) ^ 0; \ + (OPERAND) << 0; \ + (OPERAND) >> 0; \ + \ + !(OPERAND); \ + (OPERAND) && 0; \ + (OPERAND) || 0; \ + \ + (OPERAND) == 0; \ + (OPERAND) != 0; \ + (OPERAND) < 0; \ + (OPERAND) > 0; \ + (OPERAND) <= 0; \ + (OPERAND) >= 0; \ + \ + *(OPERAND); \ + &(OPERAND); \ + (OPERAND), 0; + +#define VALIDATE_RETURN_TYPES(OPERAND, CORRECT_TYPE) \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) += 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) -= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) *= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) /= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) %= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) &= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) |= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) ^= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) <<= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) >>= 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(++(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype(--(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND)++))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND)--))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(+(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype(-(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) + 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) - 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) * 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) / 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) % 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) & 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) | 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) ^ 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) << 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) >> 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(!(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) && 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) || 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) == 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) != 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) < 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) > 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) <= 0))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND) >= 0))); \ + \ + static_assert(__is_same(CORRECT_TYPE, decltype(*(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype(&(OPERAND)))); \ + static_assert(__is_same(CORRECT_TYPE, decltype((OPERAND), 0))); diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C new file mode 100644 index 00000000000..b5f88c38f32 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C @@ -0,0 +1,172 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// well-formed and ill-formed uses of member only operators in a requires expression + +// suppress the warning for Value's arrow operator +// { dg-options "-Wno-return-local-addr" } + +// { dg-excess-errors "Known issue with operators" { xfail *-*-* } } + +// It's very hard to test for incorrect successes without requires, and by extension a non dependent variable +// so for the time being, there are no non dependent tests invalid calls. + +struct Value { + int _v; + Value operator=(this Value self, int) { return self; } + Value operator()(this Value self) { return self; } + Value operator[](this Value self) { return self; } + Value* operator->(this Value self) { return &self; } +}; + +struct LRef { + int _v; + LRef& operator=(this LRef& self, int) { return self; } + LRef& operator()(this LRef& self) { return self; } + LRef& operator[](this LRef& self) { return self; } + LRef* operator->(this LRef& self) { return &self; } +}; + +struct RRef { + int _v; + RRef&& operator=(this RRef&& self, int) { return static_cast(self); } + RRef&& operator()(this RRef&& self) { return static_cast(self) } + RRef&& operator[](this RRef&& self) { return static_cast(self) } + RRef* operator->(this RRef&& self) { return &self; } +}; + +struct ConstLRef { + int _v; + ConstLRef const& operator=(this ConstLRef const& self, int) { return self; } + ConstLRef const& operator()(this ConstLRef const& self) { return self; } + ConstLRef const& operator[](this ConstLRef const& self) { return self; } + ConstLRef const* operator->(this ConstLRef const& self) { return &self; } +}; + +struct ConstRRef { + int _v; + ConstRRef const&& operator=(this ConstRRef const&& self, int) { return static_cast(self); } + ConstRRef const&& operator()(this ConstRRef const&& self) { return static_cast(self); } + ConstRRef const&& operator[](this ConstRRef const&& self) { return static_cast(self); } + ConstRRef const* operator->(this ConstRRef const&& self) { return &self; } +}; + +// needed to implement deduced operator-> +template struct remove_ref { using type = T; }; +template struct remove_ref { using type = T; }; +template struct remove_ref { using type = T; }; +template using remove_ref_t = typename remove_ref::type; + +struct Deduced { + int _v; + template + Self&& operator=(this Self&& self, int) { return static_cast(self); } + template + Self&& operator()(this Self&& self) { return static_cast(self); } + template + Self&& operator[](this Self&& self) { return static_cast(self); } + template + remove_ref_t* operator->(this Self&& self) { return &self; } +}; + +#define TEST_INVALID(OPERAND) \ + static_assert(!requires{ (OPERAND) = 0; }, "Unexpected success calling operator = with " #OPERAND); \ + static_assert(!requires{ (OPERAND)(); }, "Unexpected success calling operator () with " #OPERAND); \ + static_assert(!requires{ (OPERAND)[]; }, "Unexpected success calling operator [] with " #OPERAND); \ + static_assert(!requires{ (OPERAND)->_v; }, "Unexpected success calling operator -> with " #OPERAND); + +#define TEST_VALID(OPERAND) \ + static_assert(requires{ (OPERAND) = 0; }, "Unexpected failure calling operator = with " #OPERAND); \ + static_assert(requires{ (OPERAND)(); }, "Unexpected failure calling operator () with " #OPERAND); \ + static_assert(requires{ (OPERAND)[]; }, "Unexpected failure calling operator [] with " #OPERAND); \ + static_assert(requires{ (OPERAND)->_v; }, "Unexpected failure calling operator -> with " #OPERAND); + +template +concept same_as = __is_same(T, U); + +#define TEST_VALID_WITH_RETURN_TYPES(OPERAND, CORRECT_TYPE) \ + static_assert(requires{ {(OPERAND) = 0} -> same_as; },"Unexpected failure with return type check calling operator = with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \ + static_assert(requires{ {(OPERAND)()} -> same_as; }, "Unexpected failure with return type check calling operator () with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \ + static_assert(requires{ {(OPERAND)[]} -> same_as; }, "Unexpected failure with return type check calling operator [] with " #OPERAND " -> expected return type: " #CORRECT_TYPE); + + +template +void test_value() +{ + DepValue value{}; + TEST_VALID(value) + TEST_VALID(static_cast(value)) + TEST_VALID(static_cast(value)) + TEST_VALID(static_cast(value)) +} + +template +void test_l_ref() +{ + DepLRef l_ref{}; + TEST_VALID(l_ref) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) +} + +template +void test_r_ref() +{ + DepRRef r_ref{}; + TEST_INVALID(r_ref) + TEST_VALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) +} + +template +void test_const_l_ref() +{ + DepConstLRef const_l_ref{}; + TEST_VALID(const_l_ref) + TEST_VALID(static_cast(const_l_ref)) + TEST_VALID(static_cast(const_l_ref)) + TEST_VALID(static_cast(const_l_ref)) +} + +template +void test_const_r_ref() +{ + DepConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref) + TEST_INVALID(static_cast(const_r_ref)) + TEST_INVALID(static_cast(const_r_ref)) + TEST_VALID(static_cast(const_r_ref)) +} + +template +void test_deduced() +{ + DepDeduced deduced{}; + + TEST_VALID(deduced) + TEST_VALID(static_cast(deduced)) + TEST_VALID(static_cast(deduced)) + TEST_VALID(static_cast(deduced)) + + TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced&&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&&) + // arrow operator needs to be seperate to check the type of _v + static_assert(requires{ {(deduced->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with deduced->_v"); + static_assert(requires{ {(static_cast(deduced)->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with static_cast(deduced)->_v"); + static_assert(requires{ {(static_cast(deduced)->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with static_cast(deduced)->_v"); + static_assert(requires{ {(static_cast(deduced)->_v)} -> same_as; }, "Unexpected failure with return type check calling operator -> with static_cast(deduced)->_v"); +} + +void test() +{ + test_value(); + test_l_ref(); + test_r_ref(); + test_const_l_ref(); + test_const_r_ref(); + test_deduced(); +} diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C new file mode 100644 index 00000000000..981ab39b79d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C @@ -0,0 +1,229 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// well-formed and ill-formed uses of non-member capable operators in a requires expression + +// { dg-excess-errors "Known issue with operators" { xfail *-*-* } } + +#include "explicit-obj-ops-non-mem.h" + +// we only need the structs from the header +#undef TEST_OPS +#undef VALIDATE_RETURN_TYPES + +// It's very hard to test for incorrect successes without requires, and by extension a non dependent variable +// so for the time being, there are no non dependent tests invalid calls. + +#define TEST_INVALID(OPERAND) \ + static_assert(!requires{ (OPERAND) += 0; }, "Unexpected success calling operator += with " #OPERAND); \ + static_assert(!requires{ (OPERAND) -= 0; }, "Unexpected success calling operator -= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) *= 0; }, "Unexpected success calling operator *= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) /= 0; }, "Unexpected success calling operator /= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) %= 0; }, "Unexpected success calling operator %= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) &= 0; }, "Unexpected success calling operator &= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) |= 0; }, "Unexpected success calling operator |= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) ^= 0; }, "Unexpected success calling operator ^= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) <<= 0; }, "Unexpected success calling operator <<= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) >>= 0; }, "Unexpected success calling operator >>= with " #OPERAND); \ + \ + static_assert(!requires{ ++(OPERAND); }, "Unexpected success calling operator pre++ with " #OPERAND); \ + static_assert(!requires{ --(OPERAND); }, "Unexpected success calling operator pre-- with " #OPERAND); \ + static_assert(!requires{ (OPERAND)++; }, "Unexpected success calling operator post++ with " #OPERAND); \ + static_assert(!requires{ (OPERAND)--; }, "Unexpected success calling operator post-- with " #OPERAND); \ + \ + static_assert(!requires{ +(OPERAND); }, "Unexpected success calling operator unary+ with " #OPERAND); \ + static_assert(!requires{ -(OPERAND); }, "Unexpected success calling operator unary- with " #OPERAND); \ + static_assert(!requires{ (OPERAND) + 0; }, "Unexpected success calling operator binary+ with " #OPERAND); \ + static_assert(!requires{ (OPERAND) - 0; }, "Unexpected success calling operator binary- with " #OPERAND); \ + static_assert(!requires{ (OPERAND) * 0; }, "Unexpected success calling operator binary* with " #OPERAND); \ + static_assert(!requires{ (OPERAND) / 0; }, "Unexpected success calling operator / with " #OPERAND); \ + static_assert(!requires{ (OPERAND) % 0; }, "Unexpected success calling operator % with " #OPERAND); \ + static_assert(!requires{ (OPERAND) & 0; }, "Unexpected success calling operator binary& with " #OPERAND); \ + static_assert(!requires{ (OPERAND) | 0; }, "Unexpected success calling operator | with " #OPERAND); \ + static_assert(!requires{ (OPERAND) ^ 0; }, "Unexpected success calling operator ^ with " #OPERAND); \ + static_assert(!requires{ (OPERAND) << 0; }, "Unexpected success calling operator << with " #OPERAND); \ + static_assert(!requires{ (OPERAND) >> 0; }, "Unexpected success calling operator >> with " #OPERAND); \ + \ + static_assert(!requires{ !(OPERAND); }, "Unexpected success calling operator ! with " #OPERAND); \ + static_assert(!requires{ (OPERAND) && 0; }, "Unexpected success calling operator && with " #OPERAND); \ + static_assert(!requires{ (OPERAND) || 0; }, "Unexpected success calling operator || with " #OPERAND); \ + \ + static_assert(!requires{ (OPERAND) == 0; }, "Unexpected success calling operator == with " #OPERAND); \ + static_assert(!requires{ (OPERAND) != 0; }, "Unexpected success calling operator != with " #OPERAND); \ + static_assert(!requires{ (OPERAND) < 0; }, "Unexpected success calling operator < with " #OPERAND); \ + static_assert(!requires{ (OPERAND) > 0; }, "Unexpected success calling operator > with " #OPERAND); \ + static_assert(!requires{ (OPERAND) <= 0; }, "Unexpected success calling operator <= with " #OPERAND); \ + static_assert(!requires{ (OPERAND) >= 0; }, "Unexpected success calling operator >= with " #OPERAND); \ + \ + static_assert(!requires{ *(OPERAND); }, "Unexpected success calling operator unary* with " #OPERAND); \ + static_assert(!requires{ &(OPERAND); }, "Unexpected success calling operator unary& with " #OPERAND); \ + static_assert(!requires{ (OPERAND), 0; }, "Unexpected success calling operator , with " #OPERAND); + +#define TEST_VALID(OPERAND) \ + static_assert(requires{ (OPERAND) += 0; }, "Unexpected failure calling operator += with " #OPERAND); \ + static_assert(requires{ (OPERAND) -= 0; }, "Unexpected failure calling operator -= with " #OPERAND); \ + static_assert(requires{ (OPERAND) *= 0; }, "Unexpected failure calling operator *= with " #OPERAND); \ + static_assert(requires{ (OPERAND) /= 0; }, "Unexpected failure calling operator /= with " #OPERAND); \ + static_assert(requires{ (OPERAND) %= 0; }, "Unexpected failure calling operator %= with " #OPERAND); \ + static_assert(requires{ (OPERAND) &= 0; }, "Unexpected failure calling operator &= with " #OPERAND); \ + static_assert(requires{ (OPERAND) |= 0; }, "Unexpected failure calling operator |= with " #OPERAND); \ + static_assert(requires{ (OPERAND) ^= 0; }, "Unexpected failure calling operator ^= with " #OPERAND); \ + static_assert(requires{ (OPERAND) <<= 0; }, "Unexpected failure calling operator <<= with " #OPERAND); \ + static_assert(requires{ (OPERAND) >>= 0; }, "Unexpected failure calling operator >>= with " #OPERAND); \ + \ + static_assert(requires{ ++(OPERAND); }, "Unexpected failure calling operator pre++ with " #OPERAND); \ + static_assert(requires{ --(OPERAND); }, "Unexpected failure calling operator pre-- with " #OPERAND); \ + static_assert(requires{ (OPERAND)++; }, "Unexpected failure calling operator post++ with " #OPERAND); \ + static_assert(requires{ (OPERAND)--; }, "Unexpected failure calling operator post-- with " #OPERAND); \ + \ + static_assert(requires{ +(OPERAND); }, "Unexpected failure calling operator unary+ with " #OPERAND); \ + static_assert(requires{ -(OPERAND); }, "Unexpected failure calling operator unary- with " #OPERAND); \ + static_assert(requires{ (OPERAND) + 0; }, "Unexpected failure calling operator binary+ with " #OPERAND); \ + static_assert(requires{ (OPERAND) - 0; }, "Unexpected failure calling operator binary- with " #OPERAND); \ + static_assert(requires{ (OPERAND) * 0; }, "Unexpected failure calling operator binary* with " #OPERAND); \ + static_assert(requires{ (OPERAND) / 0; }, "Unexpected failure calling operator / with " #OPERAND); \ + static_assert(requires{ (OPERAND) % 0; }, "Unexpected failure calling operator % with " #OPERAND); \ + static_assert(requires{ (OPERAND) & 0; }, "Unexpected failure calling operator binary& with " #OPERAND); \ + static_assert(requires{ (OPERAND) | 0; }, "Unexpected failure calling operator | with " #OPERAND); \ + static_assert(requires{ (OPERAND) ^ 0; }, "Unexpected failure calling operator ^ with " #OPERAND); \ + static_assert(requires{ (OPERAND) << 0; }, "Unexpected failure calling operator << with " #OPERAND); \ + static_assert(requires{ (OPERAND) >> 0; }, "Unexpected failure calling operator >> with " #OPERAND); \ + \ + static_assert(requires{ !(OPERAND); }, "Unexpected failure calling operator ! with " #OPERAND); \ + static_assert(requires{ (OPERAND) && 0; }, "Unexpected failure calling operator && with " #OPERAND); \ + static_assert(requires{ (OPERAND) || 0; }, "Unexpected failure calling operator || with " #OPERAND); \ + \ + static_assert(requires{ (OPERAND) == 0; }, "Unexpected failure calling operator == with " #OPERAND); \ + static_assert(requires{ (OPERAND) != 0; }, "Unexpected failure calling operator != with " #OPERAND); \ + static_assert(requires{ (OPERAND) < 0; }, "Unexpected failure calling operator < with " #OPERAND); \ + static_assert(requires{ (OPERAND) > 0; }, "Unexpected failure calling operator > with " #OPERAND); \ + static_assert(requires{ (OPERAND) <= 0; }, "Unexpected failure calling operator <= with " #OPERAND); \ + static_assert(requires{ (OPERAND) >= 0; }, "Unexpected failure calling operator >= with " #OPERAND); \ + \ + static_assert(requires{ *(OPERAND); }, "Unexpected failure calling operator unary* with " #OPERAND); \ + static_assert(requires{ &(OPERAND); }, "Unexpected failure calling operator unary& with " #OPERAND); \ + static_assert(requires{ (OPERAND), 0; }, "Unexpected failure calling operator , with " #OPERAND); + +template +concept same_as = __is_same(T, U); + +#define TEST_VALID_WITH_RETURN_TYPES(OPERAND, CORRECT_TYPE) \ + static_assert(requires{ {(OPERAND) += 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) -= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) *= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) /= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) %= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) &= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) |= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) ^= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) <<= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) >>= 0} -> same_as; }); \ + \ + static_assert(requires{ {++(OPERAND)} -> same_as; }); \ + static_assert(requires{ {--(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND)++} -> same_as; }); \ + static_assert(requires{ {(OPERAND)--} -> same_as; }); \ + \ + static_assert(requires{ {+(OPERAND)} -> same_as; }); \ + static_assert(requires{ {-(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND) + 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) - 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) * 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) / 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) % 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) & 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) | 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) ^ 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) << 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) >> 0} -> same_as; }); \ + \ + static_assert(requires{ {!(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND) && 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) || 0} -> same_as; }); \ + \ + static_assert(requires{ {(OPERAND) == 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) != 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) < 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) > 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) <= 0} -> same_as; }); \ + static_assert(requires{ {(OPERAND) >= 0} -> same_as; }); \ + \ + static_assert(requires{ {*(OPERAND)} -> same_as; }); \ + static_assert(requires{ {&(OPERAND)} -> same_as; }); \ + static_assert(requires{ {(OPERAND), 0} -> same_as; }); + +template +void test_value() +{ + DepValue value{}; + TEST_VALID(value) + TEST_VALID(static_cast(value)) + TEST_VALID(static_cast(value)) + TEST_VALID(static_cast(value)) +} + +template +void test_l_ref() +{ + DepLRef l_ref{}; + TEST_VALID(l_ref) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) + TEST_INVALID(static_cast(l_ref)) +} + +template +void test_r_ref() +{ + DepRRef r_ref{}; + TEST_INVALID(r_ref) + TEST_VALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) + TEST_INVALID(static_cast(r_ref)) +} + +template +void test_const_l_ref() +{ + DepConstLRef const_l_ref{}; + TEST_VALID(const_l_ref) + TEST_VALID(static_cast(const_l_ref)) + TEST_VALID(static_cast(const_l_ref)) + TEST_VALID(static_cast(const_l_ref)) +} + +template +void test_const_r_ref() +{ + DepConstRRef const_r_ref{}; + TEST_INVALID(const_r_ref) + TEST_INVALID(static_cast(const_r_ref)) + TEST_INVALID(static_cast(const_r_ref)) + TEST_VALID(static_cast(const_r_ref)) +} + +template +void test_deduced() +{ + DepDeduced deduced{}; + + TEST_VALID(deduced) + TEST_VALID(static_cast(deduced)) + TEST_VALID(static_cast(deduced)) + TEST_VALID(static_cast(deduced)) + + TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced&&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&) + TEST_VALID_WITH_RETURN_TYPES(static_cast(deduced), DepDeduced const&&) +} + +void test() +{ + test_value(); + test_l_ref(); + test_r_ref(); + test_const_l_ref(); + test_const_r_ref(); + test_deduced(); +} -- 2.42.0 From patchwork Sun Nov 5 15:06:36 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: waffl3x X-Patchwork-Id: 161648 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:8f47:0:b0:403:3b70:6f57 with SMTP id j7csp2166758vqu; Sun, 5 Nov 2023 07:07:19 -0800 (PST) X-Google-Smtp-Source: AGHT+IFKECFUEr7k0GngFHDsoxaUIecThN7r3BysCHSyCw3e1jopnf1Z2FKGA+FyvVS5fw1W0ncX X-Received: by 2002:a05:622a:1742:b0:41c:c489:5737 with SMTP id l2-20020a05622a174200b0041cc4895737mr29369225qtk.52.1699196839010; Sun, 05 Nov 2023 07:07:19 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1699196838; cv=pass; d=google.com; s=arc-20160816; b=TWbdugNyxjni71NqAqGRJxjdTKDurBa51u8Y7WLf1iuRTrvz4rI9ba7BDLSearhXaY EL93Q++Ctz9wPEr3UB9FCsGnbBiJEA3kQ9DYycW6FFJBF4lS7ZFmMfZYF9SS6BH/2iW5 6FsGh1PWV9jS0d6HXcZ8fMZo9dEvbAVRG5CI0aZrhn1fKg0N+7/YgE/OhdqFDmhVr6Oi e15rb5QMlO3TQCeIjaI+BQoWA5qLXp9l6ws/+P71yupZNzWZvW+QiiY24ZS2R/vXH3Ok Lzxinp/9dazTYWZmEc/0pYTk1GKcWq7HQUZnseG9vhYl378q1ym6tGDZz5OyeVa0Gtsr Q+Tg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:mime-version:feedback-id :message-id:subject:cc:from:to:date:dkim-signature:arc-filter :dmarc-filter:delivered-to; bh=j4neNq5Wx4lMUo7b9hs58vXAH9bSstsZd4qrehKj83c=; fh=gNIGGMO26EMDdcN9T420kkj04Dd+kYGKSsUsy/rG/M8=; b=RP+yB9JuKXEZsFcDZrd9kGhQAFFtrqaXTKqXCvB8PumG3CU9v9qg7E1k7XSxUBeLcR a4gMnjgfPeY4Fa9V1ucTfgJ6mvzy1wnjr+HdTagMbNbxGHj+360Wc11KXSgZK4DSzZNQ cAWqvJPT2+yYi6UWXRWXTPmvro5bsSytm1xWSM1lZ9zbvjYnmrX0TQV+CYQNbk2+e0DV 4FaoJHqn6U7T8A/MzyR0U8G4Izpnr9Z1eXapBzfQbpioAMZRSb/EBJ9rVbsvC6la+ZMI tLeBHRnWBqQ7xN0HUcBlsAO5RwsMkJSH5mOk90+gtsDW/vLxSU9ge1Fyvh2dTMNIXFR8 A8GA== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@protonmail.com header.s=protonmail3 header.b=GANRV545; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id f12-20020ac87f0c000000b0041cc2ac0f45si4374260qtk.72.2023.11.05.07.07.18 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 05 Nov 2023 07:07:18 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@protonmail.com header.s=protonmail3 header.b=GANRV545; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=QUARANTINE sp=QUARANTINE dis=NONE) header.from=protonmail.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id C0A0538582A3 for ; Sun, 5 Nov 2023 15:07:18 +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 90D403858298 for ; Sun, 5 Nov 2023 15:06:49 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 90D403858298 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 90D403858298 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=1699196815; cv=none; b=ZBCxqmK3rSDu6Eqv7yppeZDJY0YFFOXAJm/gd/YESqJ494IIKbWq/rVJAbtV1SiYvc3ipYFoLMq6rZQEgk7UdcZxeUkpCdi73SgHSI5cYgMwOTV4pcfR8QizMnDg4Tw26v/Ujg53QPDIaT9RzshvibGzreZef88+IliTnummw44= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1699196815; c=relaxed/simple; bh=j4neNq5Wx4lMUo7b9hs58vXAH9bSstsZd4qrehKj83c=; h=DKIM-Signature:Date:To:From:Subject:Message-ID:MIME-Version; b=Xd097D/9h9cmSFBUoFA3ZnDSddVCJ7c+vWFZ9MCMcX5G4WTUx6ZDqNMXphKyiPkxAY6y9UFs5JgwIn9s28HSEyMc1RgeVZeVQ0rey23tLZswpiTORqmndq5OhAGcs13CEem7pwqMwrRAghJW5lcNkiA8qzl8HBbQuBWMaOOY9hE= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=protonmail.com; s=protonmail3; t=1699196807; x=1699456007; bh=j4neNq5Wx4lMUo7b9hs58vXAH9bSstsZd4qrehKj83c=; h=Date:To:From:Cc:Subject:Message-ID:Feedback-ID:From:To:Cc:Date: Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=GANRV545Y98FGlKKYWtOlhiuNANorRB+XxzHjlXMl2AOMKaJiDhzxi84PJYjcq3Kc T77wI99IFeO4YVs/o57ONRnCIqblMYqltXg6Oii+iMc9LNJOKaTw6O76Mgw1X9Q07e WaNUFjDpgmuv7dJSUjFKoitRxAytZpNU7/6E9w0YSaj7+iQ+00sVIMCYcAXIJGcToD cPyS4Ja0ImP20XSwzb1L+n9qfsAQ2QpxHozqpJQs7h///kP+h99ooM6LNnCRo2aacw lBibVEFvtYEO5gdSIaFOjp6I2AZLWHZSiFFuPhB/yYXf2hL9btaLg6xeW8R6l18TLR nC5n2fPJ/bpjw== Date: Sun, 05 Nov 2023 15:06:36 +0000 To: waffl3x From: waffl3x Cc: Jason Merrill , "gcc-patches@gcc.gnu.org" Subject: [PATCH v4 2/2] c++: Diagnostics for P0847R7 (Deducing this) [PR102609] Message-ID: Feedback-ID: 14591686:user:proton MIME-Version: 1.0 X-Spam-Status: No, score=-9.2 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+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1781737025040109408 X-GMAIL-MSGID: 1781737025040109408 Bootstrapped and tested on x86_64-linux with no regressions. Finally, the fabled diagnostics patch. I would like to note really quickly that there was never a v2 and v3 of this patch, only the first of these 2 had those versions. Originally I had planned to revise this patch alongside the first but it just didn't happen. Anyhow, I decided to match the version of this second patch to the current first patch to avoid any confusion. With that out of the way, I feel mostly okay about the code in this patch, but I have a feeling it will need a revision, especially with the large amounts of comments I left in. At the very least I expect to need to pull those out before the patch can be accepted. I had wanted to write about some of my frustrations with trying to write a test for virtual specifiers and errors/warnings for shadowing/overloading virtual functions, but I am a bit too tired at the moment and I don't want to delay getting this up for another night. In short, the standard does not properly specify the criteria for overriding functions, which leaves a lot of ambiguity in how exactly we should be handling these cases. The standard also really poorly specifies things related to the implicit object parameter and implicit object argument which also causes some trouble. Anyhow, for the time being I am not including my test for diagnostics related to a virtual specifier on xobj member functions. I can't get it to a point I am happy with it and I think there will need to be some discussion on how exactly we want to handle that. I was fairly lazy with the changelog and commit message in this patch as I expect to need to do another round on this patch before it can be accepted. One specific question I have is whether I should be listing out all the diagnostics that were added to a function. For the cases where there were only one diagnostic added I stated it, but for grokdeclarator which has the majority of the diagnostics I did not. I welcome input here, really I request it, because the changelogs are still fairly difficult for me to write. Hell, the commit messages are hard to write, I feel I went overboard on the first patch but I guess it's a fairly large patch so maybe it's alright? Again, I am looking for feedback here if anyone is willing to provide it. I've written more than I want here, so I'll wrap this e-mail up and go to bed. I am very happy to be getting close to a final product here. Hopefully if all goes well I'll be able to fit in the final missing features before feature lock hits. Alex From c8e8155a635fab7f326d0ad32326da352d7c323e Mon Sep 17 00:00:00 2001 From: waffl3x Date: Sun, 5 Nov 2023 05:17:18 -0700 Subject: [PATCH 2/2] c++: Diagnostics for C++23 P0847R7 (Deducing this) [PR102609] This patch adds diagnostics for various ill-formed code related to xobj member functions. Some of the code in here leaves something to be desired, but the majority of cases should be handled. I opted to add a new TFF flag despite only using it in a single place, other solutions seemed non ideal and there are plenty of bits left. Some of the diagnostics are more scattered around than I would like, perhaps this could be refactored in the future, especially those in grokfndecl. PR c++/102609 gcc/cp/ChangeLog: PR c++/102609 Diagnostics for C++23 P0847R7 - Deducing this. * cp-tree.h (TFF_XOBJ_FUNC): Define. * decl.cc (grokfndecl): Diagnose cvref-qualifiers on an xobj member function. (grokdeclarator): Diagnostics * error.cc (dump_function_decl): For xobj member function add TFF_XOBJ_FUNC bit to dump_parameters flags argument. (dump_parameters): When printing xobj member function's params add "this" to the first param. (function_category): Say so when in an xobj member function. * parser.cc (cp_parser_decl_specifier_seq): Diagnose incorrectly positioned "this" specifier. (cp_parser_parameter_declaration): Diagnose default argument on xobj params. * semantics.cc (finish_this_expr): Diagnose uses of "this" in body of xobj member function. gcc/testsuite/ChangeLog: PR c++/102609 Diagnostics for C++23 P0847R7 - Deducing this. * g++.dg/cpp23/explicit-obj-cxx-dialect-A.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-B.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-C.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-D.C: New test. * g++.dg/cpp23/explicit-obj-cxx-dialect-E.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics1.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics2.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics3.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics4.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics5.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics6.C: New test. * g++.dg/cpp23/explicit-obj-diagnostics7.C: New test. Signed-off-by: waffl3x --- gcc/cp/cp-tree.h | 5 +- gcc/cp/decl.cc | 133 ++++++++++++++--- gcc/cp/error.cc | 8 +- gcc/cp/parser.cc | 45 ++++++ gcc/cp/semantics.cc | 28 +++- .../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C | 6 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C | 7 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C | 7 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C | 7 + .../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C | 7 + .../g++.dg/cpp23/explicit-obj-diagnostics1.C | 138 ++++++++++++++++++ .../g++.dg/cpp23/explicit-obj-diagnostics2.C | 25 ++++ .../g++.dg/cpp23/explicit-obj-diagnostics3.C | 84 +++++++++++ .../g++.dg/cpp23/explicit-obj-diagnostics4.C | 19 +++ .../g++.dg/cpp23/explicit-obj-diagnostics5.C | 15 ++ .../g++.dg/cpp23/explicit-obj-diagnostics6.C | 22 +++ .../g++.dg/cpp23/explicit-obj-diagnostics7.C | 24 +++ 17 files changed, 560 insertions(+), 20 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 901fb1f4616..1d118fdeb7e 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6116,7 +6116,9 @@ enum auto_deduction_context identical to their defaults. TFF_NO_TEMPLATE_BINDINGS: do not print information about the template arguments for a function template specialization. - TFF_POINTER: we are printing a pointer type. */ + TFF_POINTER: we are printing a pointer type. + TFF_XOBJ_FUNC: we are printing an explicit object member function's + parameters. */ #define TFF_PLAIN_IDENTIFIER (0) #define TFF_SCOPE (1) @@ -6134,6 +6136,7 @@ enum auto_deduction_context #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS (1 << 12) #define TFF_NO_TEMPLATE_BINDINGS (1 << 13) #define TFF_POINTER (1 << 14) +#define TFF_XOBJ_FUNC (1 << 15) /* These constants can be used as bit flags to control strip_typedefs. diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index c02d78b8102..26acf96322c 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -10607,24 +10607,29 @@ grokfndecl (tree ctype, TREE_TYPE (decl) = apply_memfn_quals (TREE_TYPE (decl), TYPE_UNQUALIFIED, REF_QUAL_NONE); - if (quals) - { - error (ctype + error (!ctype + ? G_("non-member function %qD cannot have cv-qualifier") + : !xobj_func_p ? G_("static member function %qD cannot have cv-qualifier") - : G_("non-member function %qD cannot have cv-qualifier"), - decl); - quals = TYPE_UNQUALIFIED; - } - + : G_("explicit object member function " + "%qD cannot have cv-qualifier"), + decl); if (rqual) - { - error (ctype + error (!ctype + ? G_("non-member function %qD cannot have ref-qualifier") + : !xobj_func_p ? G_("static member function %qD cannot have ref-qualifier") - : G_("non-member function %qD cannot have ref-qualifier"), + : G_("explicit object member function " + "%qD cannot have ref-qualifier"), decl); - rqual = REF_QUAL_NONE; - } + + if (xobj_func_p && (quals || rqual)) + inform (DECL_SOURCE_LOCATION (DECL_ARGUMENTS (decl)), + "explicit object parameter declared here"); + quals = TYPE_UNQUALIFIED; + rqual = REF_QUAL_NONE; + } if (deduction_guide_p (decl)) @@ -13117,19 +13122,113 @@ grokdeclarator (const cp_declarator *declarator, /* There is no need to iterate over the list, only the first parm can be a valid xobj parm. */ if (!parm_list || parm_list == void_list_node) - return false; + return NULL_TREE; if (TREE_PURPOSE (parm_list) != this_identifier) - return false; + return NULL_TREE; /* If we make it here, we are looking at an xobj parm. Non-null 'purpose' usually means the parm has a default argument, we don't want to violate this assumption. */ TREE_PURPOSE (parm_list) = NULL_TREE; - return true; + return TREE_VALUE (parm_list); }; - is_xobj_member_function + tree xobj_parm = find_xobj_parm (declarator->u.function.parameters); + is_xobj_member_function = xobj_parm; + + if (xobj_parm && cxx_dialect < cxx23) + pedwarn(DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions, + "explicit object member function only available " + "with %<-std=c++23%> or %<-std=gnu++23%>"); + + /* Error handling for explicit object member functions. */ + if (!xobj_parm) + /* Early escape. */; + else if (decl_context == TYPENAME) + { + bool ptr_type = true; + /* If */ + if (!declarator->declarator) + { + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a function type cannot " + "have an explicit object parameter"); + ptr_type = false; + } + else if (declarator->declarator->kind == cdk_pointer) + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a function pointer type cannot " + "have an explicit object parameter"); + else if (declarator->declarator->kind == cdk_ptrmem) + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a member function pointer type " + "cannot have an explicit object parameter"); + else + gcc_unreachable (); + + if (ptr_type) + inform (DECL_SOURCE_LOCATION (xobj_parm), + "the type of a pointer to explicit object member " + "function is a regular pointer to function type"); + else + inform (DECL_SOURCE_LOCATION (xobj_parm), + "the type of an explicit object " + "member function is a regular function type"); + /* Ideally we should synthesize the correct syntax + for the user, perhaps this could be added later. */ + } + /* Free function case, + surely there is a better way to identify it? */ + else if (decl_context == NORMAL + && (in_namespace + || !declarator->declarator->u.id.qualifying_scope)) + error_at (DECL_SOURCE_LOCATION (xobj_parm), + "a free function cannot have " + "an explicit object parameter"); + else /* if (xobj_parm) */ + { + if (virtualp) + { + error_at (declspecs->locations[ds_virtual], + "an explicit object member function cannot be " + "%"); + inform (DECL_SOURCE_LOCATION (xobj_parm), + "explicit object parameter declared here"); + virtualp = false; + } + if (staticp >= 2) + { + error_at (declspecs->locations[ds_storage_class], + "an explicit object member function cannot be " + "%"); + inform (DECL_SOURCE_LOCATION (xobj_parm), + "explicit object parameter declared here"); + } + } + /* We don't need to skip over the first node even if it is an xobj + parm, find_xobj_parm clears the flag so it will be treated as a + normal parm, thus immediately going to the next node without + emitting an errant diagnosis. */ + bool bad_xobj_parm_encountered = false; + for (tree parm = declarator->u.function.parameters; + parm && parm != void_list_node; + parm = TREE_CHAIN (parm)) + { + if (TREE_PURPOSE (parm) != this_identifier) + continue; + bad_xobj_parm_encountered = true; + gcc_rich_location bad_xobj_parm + (DECL_SOURCE_LOCATION (TREE_VALUE (parm))); + /* I'm keeping it more basic for now. */ + error_at (&bad_xobj_parm, + "Only the first parameter of a member function " + "can be declared as an explicit object parameter"); + } + if (bad_xobj_parm_encountered && xobj_parm) + inform (DECL_SOURCE_LOCATION (xobj_parm), + "Valid explicit object parameter declared here"); + /* End of explicit object member function diagnostics. */ if (reqs) error_at (location_of (reqs), "requires-clause on return type"); diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index 0ed69bca6fc..c9870d45706 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -1831,7 +1831,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) if (!(flags & TFF_NO_FUNCTION_ARGUMENTS)) { - dump_parameters (pp, parmtypes, flags); + dump_parameters (pp, parmtypes, + DECL_XOBJ_MEMBER_FUNC_P (t) ? TFF_XOBJ_FUNC | flags + : flags); if (TREE_CODE (fntype) == METHOD_TYPE) { @@ -1910,6 +1912,8 @@ dump_parameters (cxx_pretty_printer *pp, tree parmtypes, int flags) for (first = 1; parmtypes != void_list_node; parmtypes = TREE_CHAIN (parmtypes)) { + if (first && flags & TFF_XOBJ_FUNC) + pp_string (pp, "this "); if (!first) pp_separate_with_comma (pp); first = 0; @@ -3685,6 +3689,8 @@ function_category (tree fn) return _("In destructor %qD"); else if (LAMBDA_FUNCTION_P (fn)) return _("In lambda function"); + else if (DECL_XOBJ_MEMBER_FUNC_P (fn)) + return _("In explicit object member function %qD"); else return _("In member function %qD"); } diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 4fbcc52c222..77e1f405c61 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -16019,6 +16019,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser, /* Assume no class or enumeration type is declared. */ *declares_class_or_enum = 0; + /* Keep a token that additionally will be used for diagnostics. */ + cp_token *first_specifier = NULL; /* Keep reading specifiers until there are no more to read. */ while (true) { @@ -16091,12 +16093,42 @@ cp_parser_decl_specifier_seq (cp_parser* parser, decl_specs->locations[ds_attribute] = token->location; continue; } + /* We know by this point that the token is not part of an attribute. */ + if (!first_specifier) + first_specifier = token; /* Special case for "this" specifier, indicating a parm is an xobj parm. The "this" specifier must be the first specifier in the declaration, after any attributes. */ if (token->keyword == RID_THIS) { cp_lexer_consume_token (parser->lexer); + if (token != first_specifier) + { + /* Don't emit diagnostics if we have already seen "this", + leave it for set_and_check_decl_spec_loc. */ + if (decl_specs->locations[ds_this] == 0) + { + gcc_rich_location richloc (token->location); + /* Ideally we synthesize a full rewrite, at the moment + there are issues with it though. + It rewrites "f(S this & s)" correctly, + but fails to rewrite "f(const this S s)" correctly. + It also does not handle "f(S& this s)" correctly at all. + + It's also possible we want to wait and see if the parm + could even be a valid xobj parm as it might be confusing + to the user to see an error, fix it, and then see another + error for something new. + + In short, this area needs improvement. */ + richloc.add_fixit_insert_before + (first_specifier->location, "this "); + richloc.add_fixit_remove (); + error_at (&richloc, + "% must be the first specifier " + "in a parameter declaration"); + } + } set_and_check_decl_spec_loc (decl_specs, ds_this, token); continue; } @@ -25507,6 +25539,19 @@ cp_parser_parameter_declaration (cp_parser *parser, if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this)) { + if (default_argument) + { + /* If default_argument is non-null token should always be the + the location of the `=' token, this is brittle code though + and should be rectified in the future. */ + location_t param_with_init_loc + = make_location (token->location, + decl_spec_token_start->location, + input_location); + error_at (param_with_init_loc, + "an explicit object parameter " + "may not have a default argument"); + } /* Xobj parameters can not have default arguments, thus we can reuse the default argument field to flag the param as such. */ default_argument = this_identifier; diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 52044be7af8..0588ed25d2c 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -3085,7 +3085,33 @@ finish_this_expr (void) return rvalue (result); tree fn = current_nonlambda_function (); - if (fn && DECL_STATIC_FUNCTION_P (fn)) + if (fn && DECL_XOBJ_MEMBER_FUNC_P (fn)) + { + /* I can imagine doing a fixit here, suggesting replacing + this / *this / this-> with &name / name / "name." but it would be + very difficult to get it perfect and I've been advised against + making imperfect fixits. + Perhaps it would be as simple as the replacements listed, + even if one is move'ing/forward'ing, if the replacement is just + done in the same place, it will be exactly what the user wants? + Even if this is true though, there's still a problem of getting the + context of the expression to find which tokens to replace. + I would really like for this to be possible though. + I will decide whether or not to persue this after review. */ + tree xobj_parm = FUNCTION_DECL_CHECK (fn)->function_decl.arguments; + tree parm_name = DECL_MINIMAL_CHECK (xobj_parm)->decl_minimal.name; + error ("% is unavailable for explicit object member " + "functions"); + if (parm_name) + inform (DECL_SOURCE_LOCATION (xobj_parm), + "use explicit object parameter %qD instead", + parm_name); + else + inform (DECL_SOURCE_LOCATION (xobj_parm), + "name and use the explicit object parameter instead"); + /* Maybe suggest self as a name here? */ + } + else if (fn && DECL_STATIC_FUNCTION_P (fn)) error ("% is unavailable for static member functions"); else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn)) error ("invalid use of % before it is valid"); diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C new file mode 100644 index 00000000000..033745d5784 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C @@ -0,0 +1,6 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +struct S { + void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C new file mode 100644 index 00000000000..a427506b61e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C @@ -0,0 +1,7 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } +// { dg-options "-pedantic-errors" } + +struct S { + void f(this S); // { dg-error {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C new file mode 100644 index 00000000000..ed6259b70cd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C @@ -0,0 +1,7 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } +// { dg-options "-Wno-error=pedantic" } + +struct S { + void f(this S); // { dg-warning {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C new file mode 100644 index 00000000000..dccb0cf07df --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C @@ -0,0 +1,7 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } +// { dg-options "-Wno-c++23-extensions" } + +struct S { + void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C new file mode 100644 index 00000000000..1924212fb23 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C @@ -0,0 +1,7 @@ +// P0847R7 +// { dg-do compile { target c++20_down } } +// { dg-options "-Wno-c++23-extensions -pedantic-errors" } + +struct S { + void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C new file mode 100644 index 00000000000..7b94f7e9c12 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C @@ -0,0 +1,138 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of xobj member functions that have member function qualifiers. + +struct S { + void f_value_0(this S) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_value_1(this S) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_value_2(this S) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_value_3(this S) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_value_4(this S) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_value_5(this S) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_6(this S) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_7(this S) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_8(this S) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_9(this S) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_value_A(this S) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_ref_0(this S&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_ref_1(this S&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_ref_2(this S&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_ref_3(this S&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_ref_4(this S&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_ref_5(this S&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_6(this S&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_7(this S&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_8(this S&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_9(this S&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_ref_A(this S&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_refref_0(this S&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_refref_1(this S&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_refref_2(this S&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_refref_3(this S&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_refref_4(this S&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_refref_5(this S&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_6(this S&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_7(this S&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_8(this S&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_9(this S&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_refref_A(this S&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_cref_0(this S const&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cref_1(this S const&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cref_2(this S const&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cref_3(this S const&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cref_4(this S const&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cref_5(this S const&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_6(this S const&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_7(this S const&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_8(this S const&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_9(this S const&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cref_A(this S const&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_crefref_0(this S const&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_crefref_1(this S const&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_crefref_2(this S const&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_crefref_3(this S const&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_crefref_4(this S const&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_crefref_5(this S const&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_6(this S const&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_7(this S const&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_8(this S const&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_9(this S const&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_crefref_A(this S const&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_vref_0(this S volatile&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vref_1(this S volatile&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vref_2(this S volatile&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vref_3(this S volatile&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vref_4(this S volatile&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vref_5(this S volatile&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_6(this S volatile&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_7(this S volatile&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_8(this S volatile&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_9(this S volatile&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vref_A(this S volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_vrefref_0(this S volatile&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vrefref_1(this S volatile&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vrefref_2(this S volatile&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_vrefref_3(this S volatile&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vrefref_4(this S volatile&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_vrefref_5(this S volatile&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_6(this S volatile&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_7(this S volatile&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_8(this S volatile&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_9(this S volatile&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_vrefref_A(this S volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_cvref_0(this S const volatile&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvref_1(this S const volatile&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvref_2(this S const volatile&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvref_3(this S const volatile&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvref_4(this S const volatile&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvref_5(this S const volatile&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_6(this S const volatile&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_7(this S const volatile&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_8(this S const volatile&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_9(this S const volatile&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvref_A(this S const volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void f_cvrefref_0(this S const volatile&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvrefref_1(this S const volatile&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvrefref_2(this S const volatile&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void f_cvrefref_3(this S const volatile&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvrefref_4(this S const volatile&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void f_cvrefref_5(this S const volatile&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_6(this S const volatile&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_7(this S const volatile&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_8(this S const volatile&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_9(this S const volatile&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void f_cvrefref_A(this S const volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + template void d_templ_0(this Self&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + template void d_templ_1(this Self&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + template void d_templ_2(this Self&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + template void d_templ_3(this Self&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + template void d_templ_4(this Self&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + template void d_templ_5(this Self&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_6(this Self&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_7(this Self&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_8(this Self&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_9(this Self&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + template void d_templ_A(this Self&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + + void d_auto_0(this auto&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void d_auto_1(this auto&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void d_auto_2(this auto&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" } + void d_auto_3(this auto&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void d_auto_4(this auto&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" } + void d_auto_5(this auto&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_6(this auto&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_7(this auto&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_8(this auto&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_9(this auto&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } + void d_auto_A(this auto&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C new file mode 100644 index 00000000000..f09f8dea5a8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C @@ -0,0 +1,25 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of incorrect uses of 'this' in declarations and definitions + +using func_type = void(this int); // { dg-line func_type_line } +// { dg-error "a function type cannot have an explicit object parameter" "" { target *-*-* } func_type_line } +// { dg-note "the type of an explicit object member function is a regular function type" "" { target *-*-* } func_type_line } + +using func_ptr_type = void(*)(this int); // { dg-line func_ptr_type_line } +// { dg-error "a function pointer type cannot have an explicit object parameter" "" { target *-*-* } func_ptr_type_line } +// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } func_ptr_type_line } + +struct S { + static void f(this S) {} // { dg-line static_member_func_line } +}; +// { dg-error "an explicit object member function cannot be 'static'" "" { target *-*-* } static_member_func_line } +// { dg-note "explicit object parameter declared here" "" { target *-*-* } static_member_func_line } + +using mem_func_type = void (S::*)(this S&); // { dg-line mem_func_type_line } +// { dg-error "a member function pointer type cannot have an explicit object parameter" "" { target *-*-* } mem_func_type_line } +// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } mem_func_type_line } + +void f(this int); // { dg-error "a free function cannot have an explicit object parameter" } +void f(this int) {} // { dg-error "a free function cannot have an explicit object parameter" } diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C new file mode 100644 index 00000000000..70c6a825f58 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C @@ -0,0 +1,84 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of equivalent redeclarations of member functions + +// xobj redeclared as iobj + +struct S0 { + void f0(this S0&); // { dg-note "previous declaration" } + void f0(); // { dg-error "cannot be overloaded with" } + + void f1(this S0 const&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } } + void f1() const; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } } + + void f2(this S0 volatile&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } } + void f2() volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } } + + void f3(this S0 const volatile&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } } + void f3() const volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } } + + void f4(this S0&); // { dg-note "previous declaration" } + void f4() &; // { dg-error "cannot be overloaded with" } + + void f5(this S0&&); // { dg-note "previous declaration" } + void f5() &&; // { dg-error "cannot be overloaded with" } + + void f6(this S0 const&); // { dg-note "previous declaration" } + void f6() const&; // { dg-error "cannot be overloaded with" } + + void f7(this S0 const&&); // { dg-note "previous declaration" } + void f7() const&&; // { dg-error "cannot be overloaded with" } + + void f8(this S0 volatile&); // { dg-note "previous declaration" } + void f8() volatile&; // { dg-error "cannot be overloaded with" } + + void f9(this S0 volatile&&); // { dg-note "previous declaration" } + void f9() volatile&&; // { dg-error "cannot be overloaded with" } + + void fA(this S0 const volatile&); // { dg-note "previous declaration" } + void fA() const volatile&; // { dg-error "cannot be overloaded with" } + + void fB(this S0 const volatile&&); // { dg-note "previous declaration" } + void fB() const volatile&&; // { dg-error "cannot be overloaded with" } +}; + +// iobj redeclared as xobj + +struct S1 { + void f0(); // { dg-note "previous declaration" } + void f0(this S1&); // { dg-error "cannot be overloaded with" } + + void f1() const; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } } + void f1(this S1 const&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } } + + void f2() volatile; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } } + void f2(this S1 volatile&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } } + + void f3() const volatile; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } } + void f3(this S1 const volatile&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } } + + void f4() &; // { dg-note "previous declaration" } + void f4(this S1&); // { dg-error "cannot be overloaded with" } + + void f5() &&; // { dg-note "previous declaration" } + void f5(this S1&&); // { dg-error "cannot be overloaded with" } + + void f6() const&; // { dg-note "previous declaration" } + void f6(this S1 const&); // { dg-error "cannot be overloaded with" } + + void f7() const&&; // { dg-note "previous declaration" } + void f7(this S1 const&&); // { dg-error "cannot be overloaded with" } + + void f8() volatile&; // { dg-note "previous declaration" } + void f8(this S1 volatile&); // { dg-error "cannot be overloaded with" } + + void f9() volatile&&; // { dg-note "previous declaration" } + void f9(this S1 volatile&&); // { dg-error "cannot be overloaded with" } + + void fA() const volatile&; // { dg-note "previous declaration" } + void fA(this S1 const volatile&); // { dg-error "cannot be overloaded with" } + + void fB() const volatile&&; // { dg-note "previous declaration" } + void fB(this S1 const volatile&&); // { dg-error "cannot be overloaded with" } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C new file mode 100644 index 00000000000..1f743a8509f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C @@ -0,0 +1,19 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of an xobj parameter declared with a default argument + +struct S { + void f0(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } + void f1(this S = {}); // { dg-error "an explicit object parameter may not have a default argument" } + void f2(this S); + void f10(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } + void f11(this S s = {}); // { dg-error "an explicit object parameter may not have a default argument" } + void f12(this S s); +}; + +void S::f1(this S) {} +void S::f2(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } + +void S::f11(this S s) {} +void S::f12(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" } diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C new file mode 100644 index 00000000000..65a5c63f20b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C @@ -0,0 +1,15 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// location diagnostic text when an error is emitted from an xobj member function +// this does not test for specific ill-formed code, just the additional diagnostic message + +// { dg-message "In explicit object member function" "" { target *-*-* } 0 } + +struct S { + void f(this S s) { + // The specific diagnosis issued here does not matter + // we just need to force an error to be emitted + +s; // { dg-error "" } + } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C new file mode 100644 index 00000000000..e56d6265ea1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C @@ -0,0 +1,22 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis of invalid uses of 'this' in body of xobj member functions + +// { dg-message "In explicit object member function" "" { target *-*-* } 0 } + +struct S0 { + int _n; + void f(this S0& s) { // { dg-note "use explicit object parameter 's' instead" } + this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" } + // suppress unused variable warning + static_cast(s); + } +}; + +struct S1 { + int _n; + void f(this S1&) { // { dg-note "name and use the explicit object parameter instead" } + this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" } + } +}; diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C new file mode 100644 index 00000000000..3c1d018cd68 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C @@ -0,0 +1,24 @@ +// P0847R7 +// { dg-do compile { target c++23 } } + +// rejection and diagnosis when taking address of an unqualified xobj member function + +struct S { + static void static_f(S&) {} + void iobj_member_f() {} + void xobj_member_f(this S&) {} + + void test() { + using func_ptr_type = void(*)(S&); + // using mem_func_ptr_type = void (S::*)(); + + // allowed (not testing for this) + // func_ptr_type static_f_ptr = &static_f; + + // not allowed (also not testing for this) + // mem_func_ptr_type iobj_mem_f_ptr = &iobj_member_f; + + // not allowed (this is what we are testing for) + func_ptr_type xobj_mem_f_ptr = &xobj_member_f; // { dg-error "undetermined error" "disallowing address of unqualified explicit object member function is not implemented yet" { xfail *-*-* } } + } +}; -- 2.42.0