From: waffl3x <waffl3x@protonmail.com>
To: waffl3x <waffl3x@protonmail.com>
Cc: Jason Merrill <jason@redhat.com>,
"gcc-patches@gcc.gnu.org" <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH v7 1/1] c++: Initial support for P0847R7 (Deducing This) [PR102609]
Date: Wed, 06 Dec 2023 07:33:21 +0000 [thread overview]
Message-ID: <bW3WA9EOXNz9A2y4HtlYLFCozq89_esrjpM9pFr_AiOL8YzxxeR_UgieAV8MjDVKnge2GoA1PLu6p8Bvr6czxA7wYHow6IBxaUaYeD51RO8=@protonmail.com> (raw)
In-Reply-To: <61NBWPmN34lD25i-dND5emZReJ7UbgkAcSiy1eYvjwWO9ALSTArJ4NWvOqN5UDOvbmr_dTNdRFNBCkpuu5eL41dHdZfNcqH4UZru7JK2k50=@protonmail.com>
[-- Attachment #1: Type: text/plain, Size: 808 bytes --]
Here is the next version, it feels very close to finished. As before, I
haven't ran a bootstrap or the full testsuite yet but I did run the
explicit-obj tests which completed as expected.
There's a few test cases that still need to be written but more tests
can always be added. The behavior added by CWG2789 works in at least
one case, but I have not added tests for it yet. The test cases for
dependent lambda expressions need to be fleshed out more, but a few
temporary ones are included to demonstrate that they do work and that
the crash is fixed. Explicit object conversion functions work, but I
need to add fleshed out tests for them, explicit-obj-basic5.C has that
test.
I'll start the tests now and report back if anything fails, I'm
confident everything will be fine though.
Alex
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-P0847R7-Deducing-This-PR102609.patch --]
[-- Type: text/x-patch; name=0001-P0847R7-Deducing-This-PR102609.patch, Size: 277839 bytes --]
From 937e12c57145bfd878a0bc4cd9735c2d3c4fcf22 Mon Sep 17 00:00:00 2001
From: Waffl3x <waffl3x@protonmail.com>
Date: Tue, 5 Dec 2023 23:16:01 -0700
Subject: [PATCH] P0847R7 (Deducing This) [PR102609] Another quick and dirty
patch for review, hopefully the last. gcc/cp/ChangeLog:
* call.cc (build_this_conversion):
(add_function_candidate):
(add_template_candidate_real):
(add_candidates):
(build_over_call):
(cand_parms_match):
* class.cc (add_method):
(resolve_address_of_overloaded_function):
* cp-tree.h (struct lang_decl_fn):
(DECL_IOBJ_MEMBER_FUNCTION_P):
(DECL_FUNCTION_XOBJ_FLAG):
(DECL_XOBJ_MEMBER_FUNCTION_P):
(DECL_OBJECT_MEMBER_FUNCTION_P):
(DECL_FUNCTION_MEMBER_P):
(enum auto_deduction_context):
(TFF_XOBJ_FUNC):
(enum cp_decl_spec):
* decl.cc (grokfndecl):
(grokdeclarator):
(grok_special_member_properties):
(grok_op_properties):
(start_preparsed_function):
* error.cc (dump_aggr_type):
(dump_lambda_function):
(dump_function_decl):
(dump_parameters):
(function_category):
* lambda.cc (build_capture_proxy):
(lambda_expr_this_capture):
(maybe_generic_this_capture):
* mangle.cc (write_nested_name):
* method.cc (skip_artificial_parms_for):
* module.cc (trees_out::lang_decl_bools):
(trees_in::lang_decl_bools):
* parser.cc (cp_parser_lambda_declarator_opt):
(cp_parser_decl_specifier_seq):
(cp_parser_parameter_declaration):
(set_and_check_decl_spec_loc):
* pt.cc (tsubst_function_decl):
(tsubst_lambda_expr):
(more_specialized_fn):
* search.cc (protected_accessible_p):
(look_for_overrides_here):
(look_for_overrides_r):
* semantics.cc (finish_this_expr):
(capture_decltype):
* tree.cc (build_min_non_dep_op_overload):
* typeck.cc (cxx_alignas_expr):
(invalid_nonstatic_memfn_p):
(cp_build_addr_expr_1):
gcc/testsuite/ChangeLog:
* g++.dg/cpp23/explicit-obj-basic1.C: New test.
* g++.dg/cpp23/explicit-obj-basic2.C: New test.
* g++.dg/cpp23/explicit-obj-basic3.C: New test.
* g++.dg/cpp23/explicit-obj-basic4.C: New test.
* g++.dg/cpp23/explicit-obj-basic5.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-constraints.C: New test.
* 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-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.
* g++.dg/cpp23/explicit-obj-lambda-temp1.C: New test.
* g++.dg/cpp23/explicit-obj-lambda-temp2.C: New test.
* g++.dg/cpp23/explicit-obj-lambda1.C: New test.
* g++.dg/cpp23/explicit-obj-lambda2.C: New test.
* g++.dg/cpp23/explicit-obj-lambda3.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX0.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX20.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX21.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX24.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX25.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX4.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX40.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX5.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX6.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX60.C: New test.
* g++.dg/cpp23/explicit-obj-lambdaX7.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.
* g++.dg/cpp23/explicit-obj-redecl-constraints.C: New test.
* g++.dg/cpp23/explicit-obj-redecl.C: New test.
* g++.dg/cpp23/explicit-obj-redecl2.C: New test.
* g++.dg/cpp23/explicit-obj-redecl3.C: New test.
* g++.dg/cpp23/explicit-obj-virtual.C: New test.
Signed-off-by: Waffl3x <waffl3x@protonmail.com>
---
gcc/cp/call.cc | 187 ++++---
gcc/cp/class.cc | 202 ++++++-
gcc/cp/cp-tree.h | 39 +-
gcc/cp/decl.cc | 184 ++++++-
gcc/cp/error.cc | 24 +-
gcc/cp/lambda.cc | 9 +-
gcc/cp/mangle.cc | 4 +-
gcc/cp/method.cc | 2 +-
gcc/cp/module.cc | 2 +
gcc/cp/parser.cc | 147 +++++-
gcc/cp/pt.cc | 78 ++-
gcc/cp/search.cc | 16 +-
gcc/cp/semantics.cc | 35 +-
gcc/cp/tree.cc | 10 +-
gcc/cp/typeck.cc | 36 +-
.../g++.dg/cpp23/explicit-obj-basic1.C | 114 ++++
.../g++.dg/cpp23/explicit-obj-basic2.C | 28 +
.../g++.dg/cpp23/explicit-obj-basic3.C | 496 ++++++++++++++++++
.../g++.dg/cpp23/explicit-obj-basic4.C | 113 ++++
.../g++.dg/cpp23/explicit-obj-basic5.C | 31 ++
.../g++.dg/cpp23/explicit-obj-by-value1.C | 48 ++
.../g++.dg/cpp23/explicit-obj-by-value2.C | 58 ++
.../g++.dg/cpp23/explicit-obj-by-value3.C | 41 ++
.../g++.dg/cpp23/explicit-obj-by-value4.C | 20 +
.../g++.dg/cpp23/explicit-obj-constraints.C | 470 +++++++++++++++++
.../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C | 7 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C | 7 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C | 9 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C | 8 +
.../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C | 8 +
.../g++.dg/cpp23/explicit-obj-diagnostics1.C | 139 +++++
.../g++.dg/cpp23/explicit-obj-diagnostics2.C | 26 +
.../g++.dg/cpp23/explicit-obj-diagnostics4.C | 20 +
.../g++.dg/cpp23/explicit-obj-diagnostics5.C | 16 +
.../g++.dg/cpp23/explicit-obj-diagnostics6.C | 23 +
.../g++.dg/cpp23/explicit-obj-diagnostics7.C | 17 +
.../g++.dg/cpp23/explicit-obj-lambda-temp1.C | 73 +++
.../g++.dg/cpp23/explicit-obj-lambda-temp2.C | 38 ++
.../g++.dg/cpp23/explicit-obj-lambda1.C | 25 +
.../g++.dg/cpp23/explicit-obj-lambda2.C | 23 +
.../g++.dg/cpp23/explicit-obj-lambda3.C | 64 +++
.../g++.dg/cpp23/explicit-obj-lambdaX0.C | 68 +++
.../g++.dg/cpp23/explicit-obj-lambdaX20.C | 46 ++
.../g++.dg/cpp23/explicit-obj-lambdaX21.C | 39 ++
.../g++.dg/cpp23/explicit-obj-lambdaX24.C | 98 ++++
.../g++.dg/cpp23/explicit-obj-lambdaX25.C | 101 ++++
.../g++.dg/cpp23/explicit-obj-lambdaX4.C | 23 +
.../g++.dg/cpp23/explicit-obj-lambdaX40.C | 20 +
.../g++.dg/cpp23/explicit-obj-lambdaX5.C | 21 +
.../g++.dg/cpp23/explicit-obj-lambdaX6.C | 48 ++
.../g++.dg/cpp23/explicit-obj-lambdaX60.C | 279 ++++++++++
.../g++.dg/cpp23/explicit-obj-lambdaX7.C | 87 +++
.../g++.dg/cpp23/explicit-obj-ops-mem-arrow.C | 28 +
.../cpp23/explicit-obj-ops-mem-assignment.C | 27 +
.../g++.dg/cpp23/explicit-obj-ops-mem-call.C | 40 ++
.../cpp23/explicit-obj-ops-mem-subscript.C | 40 ++
.../cpp23/explicit-obj-ops-non-mem-dep.C | 58 ++
.../cpp23/explicit-obj-ops-non-mem-non-dep.C | 57 ++
.../g++.dg/cpp23/explicit-obj-ops-non-mem.h | 210 ++++++++
.../cpp23/explicit-obj-ops-requires-mem.C | 171 ++++++
.../cpp23/explicit-obj-ops-requires-non-mem.C | 237 +++++++++
.../cpp23/explicit-obj-redecl-constraints.C | 115 ++++
.../g++.dg/cpp23/explicit-obj-redecl.C | 246 +++++++++
.../g++.dg/cpp23/explicit-obj-redecl2.C | 161 ++++++
.../g++.dg/cpp23/explicit-obj-redecl3.C | 263 ++++++++++
.../g++.dg/cpp23/explicit-obj-virtual.C | 95 ++++
66 files changed, 5314 insertions(+), 161 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-basic3.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.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-constraints.C
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-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
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp1.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp2.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX0.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX20.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX21.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX24.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX25.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX4.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX40.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX5.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX6.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX60.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX7.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
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl-constraints.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C
create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-virtual.C
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index ae0decd87f1..fa772344ea9 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -2277,7 +2277,7 @@ build_this_conversion (tree fn, tree ctype,
tree& parmtype, tree& argtype, tree& arg,
int flags, tsubst_flags_t complain)
{
- gcc_assert (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
+ gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn)
&& !DECL_CONSTRUCTOR_P (fn));
/* The type of the implicit object parameter ('this') for
@@ -2490,7 +2490,7 @@ add_function_candidate (struct z_candidate **candidates,
{
tree parmtype = TREE_VALUE (parmnode);
if (i == 0
- && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
+ && DECL_IOBJ_MEMBER_FUNCTION_P (fn)
&& !DECL_CONSTRUCTOR_P (fn))
t = build_this_conversion (fn, ctype, parmtype, argtype, arg,
flags, complain);
@@ -3447,7 +3447,7 @@ add_template_candidate_real (struct z_candidate **candidates, tree tmpl,
/* We don't do deduction on the in-charge parameter, the VTT
parameter or 'this'. */
- if (DECL_NONSTATIC_MEMBER_FUNCTION_P (tmpl))
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (tmpl))
{
if (first_arg_without_in_chrg != NULL_TREE)
first_arg_without_in_chrg = NULL_TREE;
@@ -3557,7 +3557,7 @@ add_template_candidate_real (struct z_candidate **candidates, tree tmpl,
convs = alloc_conversions (nargs);
if (shortcut_bad_convs
- && DECL_NONSTATIC_MEMBER_FUNCTION_P (tmpl)
+ && DECL_IOBJ_MEMBER_FUNCTION_P (tmpl)
&& !DECL_CONSTRUCTOR_P (tmpl))
{
/* Check the 'this' conversion before proceeding with deduction.
@@ -6544,7 +6544,7 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
tree fn_first_arg = NULL_TREE;
const vec<tree, va_gc> *fn_args = args;
- if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
+ if (DECL_OBJECT_MEMBER_FUNCTION_P (fn))
{
/* Figure out where the object arg comes from. If this
function is a non-static member and we didn't get an
@@ -6586,6 +6586,17 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
/* ??? This kludge excludes inline namespace members for the H
test in spaceship-eq15.C, but I don't see why we would want
that behavior. Asked Core 2022-11-04. Disabling for now. */;
+ /* FIXME: I believe this will be bugged for xobj member functions,
+ leaving this comment here to make sure we look into it
+ at some point.
+ Seeing this makes me want correspondence checking to be unified
+ in one place though, not sure if this one needs to be different
+ from other ones though.
+ This function is only used here, but maybe we can use it in add
+ method and move some of the logic out of there?
+
+ Side note: CWG2586 might be relevant for this area in
+ particular, perhaps we wait to see if it gets accepted first? */
else if (fns_correspond (fn, *ne))
{
found = true;
@@ -9762,14 +9773,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
const vec<tree, va_gc> *args = cand->args;
tree first_arg = cand->first_arg;
conversion **convs = cand->convs;
- conversion *conv;
tree parm = TYPE_ARG_TYPES (TREE_TYPE (fn));
int parmlen;
tree val;
- int i = 0;
- int j = 0;
- unsigned int arg_index = 0;
- int is_method = 0;
int nargs;
tree *argarray;
bool already_used = false;
@@ -9955,45 +9961,46 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
if (immediate_invocation_p (STRIP_TEMPLATE (fn)))
in_consteval_if_p = true;
+ int argarray_size = 0;
+ unsigned int arg_index = 0;
+ int conv_index = 0;
+ int param_index = 0;
+
+ auto consume_object_arg = [&arg_index, &first_arg, args]()
+ {
+ if (!first_arg)
+ return (*args)[arg_index++];
+ tree object_arg = first_arg;
+ first_arg = NULL_TREE;
+ return object_arg;
+ };
+
/* The implicit parameters to a constructor are not considered by overload
resolution, and must be of the proper type. */
if (DECL_CONSTRUCTOR_P (fn))
{
- tree object_arg;
- if (first_arg != NULL_TREE)
- {
- object_arg = first_arg;
- first_arg = NULL_TREE;
- }
- else
- {
- object_arg = (*args)[arg_index];
- ++arg_index;
- }
- argarray[j++] = build_this (object_arg);
+ tree object_arg = consume_object_arg ();
+ argarray[argarray_size++] = build_this (object_arg);
parm = TREE_CHAIN (parm);
/* We should never try to call the abstract constructor. */
gcc_assert (!DECL_HAS_IN_CHARGE_PARM_P (fn));
if (DECL_HAS_VTT_PARM_P (fn))
{
- argarray[j++] = (*args)[arg_index];
+ argarray[argarray_size++] = (*args)[arg_index];
++arg_index;
parm = TREE_CHAIN (parm);
}
}
/* Bypass access control for 'this' parameter. */
- else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
+ else if (DECL_IOBJ_MEMBER_FUNCTION_P (fn))
{
- tree arg = build_this (first_arg != NULL_TREE
- ? first_arg
- : (*args)[arg_index]);
+ tree arg = build_this (consume_object_arg ());
tree argtype = TREE_TYPE (arg);
if (arg == error_mark_node)
return error_mark_node;
-
- if (convs[i]->bad_p)
+ if (convs[conv_index++]->bad_p)
{
if (complain & tf_error)
{
@@ -10068,25 +10075,57 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
tree converted_arg = build_base_path (PLUS_EXPR, arg,
base_binfo, 1, complain);
- argarray[j++] = converted_arg;
+ argarray[argarray_size++] = converted_arg;
parm = TREE_CHAIN (parm);
- if (first_arg != NULL_TREE)
- first_arg = NULL_TREE;
+ }
+
+ auto handle_arg = [fn, flags, complain](tree type,
+ tree arg,
+ int const param_index,
+ conversion *conv,
+ bool const conversion_warning)
+ {
+ /* Set user_conv_p on the argument conversions, so rvalue/base handling
+ knows not to allow any more UDCs. This needs to happen after we
+ process cand->warnings. */
+ if (flags & LOOKUP_NO_CONVERSION)
+ conv->user_conv_p = true;
+
+ tsubst_flags_t const arg_complain
+ = conversion_warning ? complain : complain & ~tf_warning;
+
+ if (arg_complain & tf_warning)
+ maybe_warn_pessimizing_move (arg, type, /*return_p=*/false);
+
+ tree val = convert_like_with_context (conv, arg, fn,
+ param_index, arg_complain);
+ val = convert_for_arg_passing (type, val, arg_complain);
+ return val;
+ };
+
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ {
+ gcc_assert (cand->num_convs > 0);
+ static constexpr bool conversion_warning = true;
+ tree object_arg = consume_object_arg ();
+ val = handle_arg (TREE_VALUE (parm),
+ object_arg,
+ param_index++,
+ convs[conv_index++],
+ conversion_warning);
+
+ if (val == error_mark_node)
+ return error_mark_node;
else
- ++arg_index;
- ++i;
- is_method = 1;
+ argarray[argarray_size++] = val;
+ parm = TREE_CHAIN (parm);
}
gcc_assert (first_arg == NULL_TREE);
for (; arg_index < vec_safe_length (args) && parm;
- parm = TREE_CHAIN (parm), ++arg_index, ++i)
+ parm = TREE_CHAIN (parm), ++arg_index, ++param_index, ++conv_index)
{
- tree type = TREE_VALUE (parm);
- tree arg = (*args)[arg_index];
- bool conversion_warning = true;
-
- conv = convs[i];
+ tree current_arg = (*args)[arg_index];
/* If the argument is NULL and used to (implicitly) instantiate a
template function (and bind one of the template arguments to
@@ -10108,47 +10147,35 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
func(NULL);
}
*/
- if (null_node_p (arg)
- && DECL_TEMPLATE_INFO (fn)
- && cand->template_decl
- && !cand->explicit_targs)
- conversion_warning = false;
+ bool const conversion_warning = !(null_node_p (current_arg)
+ && DECL_TEMPLATE_INFO (fn)
+ && cand->template_decl
+ && !cand->explicit_targs);
- /* Set user_conv_p on the argument conversions, so rvalue/base handling
- knows not to allow any more UDCs. This needs to happen after we
- process cand->warnings. */
- if (flags & LOOKUP_NO_CONVERSION)
- conv->user_conv_p = true;
-
- tsubst_flags_t arg_complain = complain;
- if (!conversion_warning)
- arg_complain &= ~tf_warning;
-
- if (arg_complain & tf_warning)
- maybe_warn_pessimizing_move (arg, type, /*return_p*/false);
-
- val = convert_like_with_context (conv, arg, fn, i - is_method,
- arg_complain);
- val = convert_for_arg_passing (type, val, arg_complain);
+ val = handle_arg (TREE_VALUE (parm),
+ current_arg,
+ param_index,
+ convs[conv_index],
+ conversion_warning);
if (val == error_mark_node)
- return error_mark_node;
+ return error_mark_node;
else
- argarray[j++] = val;
+ argarray[argarray_size++] = val;
}
/* Default arguments */
- for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), i++)
+ for (; parm && parm != void_list_node; parm = TREE_CHAIN (parm), param_index++)
{
if (TREE_VALUE (parm) == error_mark_node)
return error_mark_node;
val = convert_default_arg (TREE_VALUE (parm),
TREE_PURPOSE (parm),
- fn, i - is_method,
+ fn, param_index,
complain);
if (val == error_mark_node)
- return error_mark_node;
- argarray[j++] = val;
+ return error_mark_node;
+ argarray[argarray_size++] = val;
}
/* Ellipsis */
@@ -10185,11 +10212,11 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
a = convert_arg_to_ellipsis (a, complain);
if (a == error_mark_node)
return error_mark_node;
- argarray[j++] = a;
+ argarray[argarray_size++] = a;
}
- gcc_assert (j <= nargs);
- nargs = j;
+ gcc_assert (argarray_size <= nargs);
+ nargs = argarray_size;
icip.reset ();
/* Avoid performing argument transformation if warnings are disabled.
@@ -10205,7 +10232,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
{
tree *fargs = (!nargs ? argarray
: (tree *) alloca (nargs * sizeof (tree)));
- for (j = 0; j < nargs; j++)
+ for (int j = 0; j < nargs; j++)
{
/* For -Wformat undo the implicit passing by hidden reference
done by convert_arg_to_ellipsis. */
@@ -12574,17 +12601,25 @@ cand_parms_match (z_candidate *c1, z_candidate *c2)
fn1 = DECL_TEMPLATE_RESULT (t1);
fn2 = DECL_TEMPLATE_RESULT (t2);
}
+ /* The changes I made here might be stuff I was told not to worry about?
+ I'm not really sure so I'm going to leave it in. */
tree parms1 = TYPE_ARG_TYPES (TREE_TYPE (fn1));
tree parms2 = TYPE_ARG_TYPES (TREE_TYPE (fn2));
if (DECL_FUNCTION_MEMBER_P (fn1)
&& DECL_FUNCTION_MEMBER_P (fn2)
- && (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn1)
- != DECL_NONSTATIC_MEMBER_FUNCTION_P (fn2)))
+ && (DECL_STATIC_FUNCTION_P (fn1)
+ != DECL_STATIC_FUNCTION_P (fn2)))
{
/* Ignore 'this' when comparing the parameters of a static member
function with those of a non-static one. */
- parms1 = skip_artificial_parms_for (fn1, parms1);
- parms2 = skip_artificial_parms_for (fn2, parms2);
+ auto skip_parms = [](tree fn, tree parms){
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ return TREE_CHAIN (parms);
+ else
+ return skip_artificial_parms_for (fn, parms);
+ };
+ parms1 = skip_parms (fn1, parms1);
+ parms2 = skip_parms (fn2, parms2);
}
return compparms (parms1, parms2);
}
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 6fdb56abfb9..d7a4ad5ad51 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -1079,8 +1079,8 @@ add_method (tree type, tree method, bool via_using)
/* Compare the quals on the 'this' parm. Don't compare
the whole types, as used functions are treated as
coming from the using class in overload resolution. */
- if (! DECL_STATIC_FUNCTION_P (fn)
- && ! DECL_STATIC_FUNCTION_P (method)
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (fn)
+ && DECL_IOBJ_MEMBER_FUNCTION_P (method)
/* Either both or neither need to be ref-qualified for
differing quals to allow overloading. */
&& (FUNCTION_REF_QUALIFIED (fn_type)
@@ -1089,6 +1089,161 @@ add_method (tree type, tree method, bool via_using)
|| type_memfn_rqual (fn_type) != type_memfn_rqual (method_type)))
continue;
+ auto get_object_param = [](tree fn)
+ {
+ gcc_assert (DECL_OBJECT_MEMBER_FUNCTION_P (fn));
+ return TREE_VALUE (TYPE_ARG_TYPES (TREE_TYPE (fn)));
+ };
+ auto reference_qual = [](tree ref)
+ {
+ gcc_assert (TYPE_REF_P (ref));
+ return TYPE_REF_IS_RVALUE (ref) ? REF_QUAL_RVALUE
+ : REF_QUAL_LVALUE;
+ };
+
+ /* Handle special correspondence rules for xobj vs xobj and xobj vs iobj
+ member function declarations.
+ We don't worry about static member functions here. */
+ if ((!DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+ && !DECL_XOBJ_MEMBER_FUNCTION_P (method))
+ || DECL_STATIC_FUNCTION_P (fn) || DECL_STATIC_FUNCTION_P (method))
+ /* Early escape. */;
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+ && DECL_XOBJ_MEMBER_FUNCTION_P (method))
+ {
+ tree fn_param = get_object_param (fn);
+ tree method_param = get_object_param (method);
+ if (!same_type_p (fn_param, method_param))
+ continue;
+ }
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)
+ || DECL_XOBJ_MEMBER_FUNCTION_P (method))
+ {
+ tree xobj_fn = DECL_XOBJ_MEMBER_FUNCTION_P (fn) ? fn : method;
+ /* A reference, pointer, or something else. */
+ tree xobj_param = get_object_param (xobj_fn);
+
+ /* An iobj member function's object parameter can't be an unrelated
+ type, we know this can't be a redeclaration if the xobj member
+ function's object parameter is an unrelated type.
+ If the iobj member function was introduced with a using
+ declaration, then the type of its object parameter is still
+ that of the class we are currently adding a member function to,
+ so this assumption holds true in that case as well.
+
+ [over.match.funcs.general.4]
+ For non-conversion functions that are implicit object member
+ functions nominated by a using-declaration in a derived class,
+ the function is considered to be a member of the derived class
+ for the purpose of defining the type of the implicit object
+ parameter.
+
+ We don't get to bail yet out even if the xobj parameter is
+ by-value as elaborated on below.
+
+ This also implicitly handles xobj parameters of type pointer. */
+ if (DECL_CONTEXT (xobj_fn)
+ != TYPE_MAIN_VARIANT (non_reference (xobj_param)))
+ continue;
+
+ /* From this point on, we know we are dealing with an xobj parameter
+ that has an object parameter of the same type as the class it
+ was declared in.
+ We still don't know if we have a reference or by-value parameter
+ yet though. */
+
+ tree iobj_fn = DECL_IOBJ_MEMBER_FUNCTION_P (fn) ? fn : method;
+ tree iobj_fn_type = TREE_TYPE (iobj_fn);
+ cp_ref_qualifier const iobj_ref_qual
+ = type_memfn_rqual (iobj_fn_type);
+ /* I am concerned about the other qualifier bits, for now I will mask
+ them off. */
+ static constexpr cp_cv_quals cv_bits = TYPE_QUAL_VOLATILE
+ | TYPE_QUAL_CONST;
+ cp_cv_quals const iobj_cv_quals
+ = type_memfn_quals (iobj_fn_type) & cv_bits;
+ /* We need to ignore the ref qualifier of the xobj parameter if the
+ iobj member function lacks a ref qualifier.
+
+ [basic.scope.scope.3]
+ Two non-static member functions have corresponding object
+ parameters if:
+ -- exactly one is an implicit object member function with no
+ ref-qualifier and the types of their object parameters
+ ([dcl.fct]), after removing top-level references, are the
+ same, or
+ -- their object parameters have the same type.
+
+ The cv qualifiers of a by-value parameter are supposed to be
+ discarded, so we ignore them.
+
+ [dcl.fct.5]
+ After producing the list of parameter types, any top-level
+ cv-qualifiers modifying a parameter type are deleted when
+ forming the function type.
+
+ They still need to be taken into account when our xobj parameter
+ is a reference that is being ignored (according to
+ [basic.scope.scope.3] quoted above), but when we are actually
+ dealing with a by-value xobj parameter we can proceed following
+ this table.
+ | iobj | xobj | equal |
+ | none | none | X |
+ | none | c | X |
+ | none | v | X |
+ | none | cv | X |
+ | c | none | O |
+ | c | c | O |
+ | c | v | O |
+ | c | cv | O |
+ | v | none | O |
+ | v | c | O |
+ | v | v | O |
+ | v | cv | O |
+ | cv | none | O |
+ | cv | c | O |
+ | cv | v | O |
+ | cv | cv | O |
+
+ Additionally, if the iobj member function is ref qualified,
+ we aren't ignoring the ref qualifier of the iobj parameter,
+ so we can't be dealing with a redeclaration in that case either.
+
+ So to recap, if we have a by-value xobj parameter, we know for
+ sure that we aren't dealing with a redeclaration if the iobj
+ member function has any cv-ref qualifiers. The only case where
+ we might still be dealing with a redeclaration is when the iobj
+ member function lacks any cv-ref qualification. */
+ if (!TYPE_REF_P (xobj_param))
+ {
+ if (iobj_ref_qual || iobj_cv_quals)
+ continue;
+ }
+ else
+ {
+ /* We are dealing with an xobj parameter that is a reference now,
+ but due to [basic.scope.scope.3] we need to ignore its
+ reference qualifier. */
+ cp_ref_qualifier const xobj_ref_qual
+ = !TYPE_REF_P (xobj_param) || !iobj_ref_qual
+ ? REF_QUAL_NONE : reference_qual (xobj_param);
+
+ /* Even if we are ignoring the reference qualifier, the xobj
+ parameter was still a reference so we still take the cv
+ qualifiers into account. */
+ cp_cv_quals const xobj_cv_quals
+ = cp_type_quals (TREE_TYPE (xobj_param)) & cv_bits;
+
+ /* Finally, if the qualifications don't match exactly, this
+ definitely isn't a redeclaration. */
+ if (iobj_ref_qual != xobj_ref_qual
+ || iobj_cv_quals != xobj_cv_quals)
+ continue;
+ }
+ }
+ else
+ gcc_unreachable ();
+
tree real_fn = fn;
tree real_method = method;
@@ -8727,21 +8882,42 @@ resolve_address_of_overloaded_function (tree target_type,
/* Good, exactly one match. Now, convert it to the correct type. */
fn = TREE_PURPOSE (matches);
- if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
- && !(complain & tf_ptrmem_ok) && !flag_ms_extensions)
+ if (DECL_OBJECT_MEMBER_FUNCTION_P (fn)
+ && !(complain & tf_ptrmem_ok))
{
- static int explained;
-
- if (!(complain & tf_error))
+ /* For iobj member functions, if if -fms_extensions was passed in, this
+ is not an error, so we do nothing. It is still an error regardless
+ for xobj member functions though, as it is a new feature we
+ (hopefully) don't need to support the behavior. */
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && flag_ms_extensions)
+ /* Early escape. */;
+ else if (!(complain & tf_error))
return error_mark_node;
-
- auto_diagnostic_group d;
- if (permerror (input_location, "assuming pointer to member %qD", fn)
- && !explained)
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
{
- inform (input_location, "(a pointer to member can only be "
+ auto_diagnostic_group d;
+ /* Error message could be better, fixit would be ideal.
+ Should also match the error in typeck.cc:cp_build_addr_expr_1. */
+ error_at (input_location,
+ "taking the address of an explicit object member "
+ "function must be qualified");
+ inform (input_location,
+ "(a pointer to explicit object member function can only be "
"formed with %<&%E%>)", fn);
- explained = 1;
+ }
+ else
+ {
+ static int explained;
+ gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !flag_ms_extensions);
+
+ auto_diagnostic_group d;
+ if (permerror (input_location, "assuming pointer to member %qD", fn)
+ && !explained)
+ {
+ inform (input_location, "(a pointer to member can only be "
+ "formed with %<&%E%>)", fn);
+ explained = 1;
+ }
}
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 9979c5da623..f118f16df0d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2946,8 +2946,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. */
@@ -3346,14 +3347,34 @@ 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, use DECL_IOBJ_MEMBER_FUNCTION_P instead. */
#define DECL_NONSTATIC_MEMBER_FUNCTION_P(NODE) \
(TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE)
+/* Nonzero for FUNCTION_DECL means that this decl is an implicit object
+ member function. */
+#define DECL_IOBJ_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 an xobj member function,
+ safely evaluates to false for all non FUNCTION_DECL nodes. */
+#define DECL_XOBJ_MEMBER_FUNCTION_P(NODE) \
+ (TREE_CODE (STRIP_TEMPLATE (NODE)) == FUNCTION_DECL \
+ && DECL_FUNCTION_XOBJ_FLAG (NODE) == 1)
+
+/* Nonzero if NODE is a member function with an object argument,
+ in other words, a non-static member function. */
+#define DECL_OBJECT_MEMBER_FUNCTION_P(NODE) \
+ (DECL_IOBJ_MEMBER_FUNCTION_P (NODE) || DECL_XOBJ_MEMBER_FUNCTION_P (NODE))
+
/* 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_OBJECT_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE)) \
/* Nonzero for FUNCTION_DECL means that this member function
has `this' as const X *const. */
@@ -6137,7 +6158,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)
@@ -6155,6 +6178,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.
@@ -6297,11 +6321,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,
@@ -6318,6 +6344,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 4b685270097..e51ede5d043 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10385,6 +10385,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,
@@ -10394,7 +10395,6 @@ grokfndecl (tree ctype,
location_t location)
{
tree decl;
- int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE;
tree t;
if (location == UNKNOWN_LOCATION)
@@ -10592,12 +10592,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;
@@ -10677,24 +10674,30 @@ grokfndecl (tree ctype,
TREE_TYPE (decl) = apply_memfn_quals (TREE_TYPE (decl),
TYPE_UNQUALIFIED,
REF_QUAL_NONE);
-
+ auto_diagnostic_group d;
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))
@@ -13066,6 +13069,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)
@@ -13181,6 +13186,91 @@ 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 || TREE_PURPOSE (parm_list) != this_identifier)
+ 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 TREE_VALUE (parm_list);
+ };
+
+ 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%>");
+
+ if (xobj_parm && decl_context == TYPENAME)
+ {
+ /* We inform in every case, just differently depending on what
+ case it is. */
+ auto_diagnostic_group d;
+ bool ptr_type = true;
+ /* If declarator->kind is cdk_function and we are at the end of
+ the declarator chain, we are looking at a function type. */
+ 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 pointer to function type cannot "
+ "have an explicit object parameter");
+ else if (declarator->declarator->kind == cdk_ptrmem)
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a pointer to member function type "
+ "cannot have an explicit object parameter");
+ else
+ gcc_unreachable ();
+
+ /* The locations being used here are probably not correct. */
+ 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. */
+ }
+ /* Since a valid xobj parm has its purpose cleared in find_xobj_parm
+ the first parm node will never erroneously be detected here. */
+ {
+ auto_diagnostic_group d;
+ 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");
+ }
+
if (reqs)
error_at (location_of (reqs), "requires-clause on return type");
reqs = declarator->u.function.requires_clause;
@@ -13468,6 +13558,38 @@ grokdeclarator (const cp_declarator *declarator,
explicitp = 2;
}
+ if (xobj_parm)
+ {
+ if (!ctype
+ && decl_context == NORMAL
+ && (in_namespace
+ || !declarator->declarator->u.id.qualifying_scope))
+ error_at (DECL_SOURCE_LOCATION (xobj_parm),
+ "a non-member function cannot have "
+ "an explicit object parameter");
+ else
+ {
+ if (virtualp)
+ {
+ auto_diagnostic_group d;
+ error_at (declspecs->locations[ds_virtual],
+ "an explicit object member function cannot "
+ "be %<virtual%>");
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "explicit object parameter declared here");
+ virtualp = false;
+ }
+ if (staticp >= 2)
+ {
+ auto_diagnostic_group d;
+ error_at (declspecs->locations[ds_storage_class],
+ "an explicit object member function cannot "
+ "be %<static%>");
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "explicit object parameter declared here");
+ }
+ }
+ }
tree pushed_scope = NULL_TREE;
if (funcdecl_p
&& decl_context != FIELD
@@ -14245,6 +14367,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)))
@@ -14466,7 +14590,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);
@@ -14801,8 +14926,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);
@@ -15502,9 +15627,10 @@ void
grok_special_member_properties (tree decl)
{
tree class_type;
-
+ /* I believe we have to make some changes in here depending on the outcome
+ of CWG2586. */
if (TREE_CODE (decl) == USING_DECL
- || !DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
+ || !DECL_OBJECT_MEMBER_FUNCTION_P (decl))
return;
class_type = DECL_CONTEXT (decl);
@@ -15603,7 +15729,7 @@ bool
grok_op_properties (tree decl, bool complain)
{
tree argtypes = TYPE_ARG_TYPES (TREE_TYPE (decl));
- bool methodp = TREE_CODE (TREE_TYPE (decl)) == METHOD_TYPE;
+ bool const methodp = DECL_IOBJ_MEMBER_FUNCTION_P (decl);
tree name = DECL_NAME (decl);
location_t loc = DECL_SOURCE_LOCATION (decl);
@@ -15696,7 +15822,7 @@ 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 (!DECL_OBJECT_MEMBER_FUNCTION_P (decl))
{
if (operator_code == TYPE_EXPR
|| operator_code == COMPONENT_REF
@@ -15785,7 +15911,7 @@ grok_op_properties (tree decl, bool complain)
}
++arity;
}
-
+ /* FIXME: We need tests for these errors with xobj member functions. */
/* Verify correct number of arguments. */
switch (op_flags)
{
@@ -17694,7 +17820,7 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
/* Start the statement-tree, start the tree now. */
DECL_SAVED_TREE (decl1) = push_stmt_list ();
- if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl1))
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (decl1))
{
/* We know that this was set up by `grokclassfn'. We do not
wait until `store_parm_decls', since evil parse errors may
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 785909c362a..b105eb6ddcf 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -838,10 +838,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
{
/* A lambda's "type" is essentially its signature. */
pp_string (pp, M_("<lambda"));
- if (lambda_function (t))
- dump_parameters (pp,
- FUNCTION_FIRST_USER_PARMTYPE (lambda_function (t)),
- flags);
+ tree const fn = lambda_function (t);
+ if (fn)
+ {
+ int const parm_flags
+ = DECL_XOBJ_MEMBER_FUNCTION_P (fn) ? TFF_XOBJ_FUNC | flags
+ : flags;
+ dump_parameters (pp, FUNCTION_FIRST_USER_PARMTYPE (fn), parm_flags);
+ }
pp_greater (pp);
}
else if (!decl || IDENTIFIER_ANON_P (DECL_NAME (decl)))
@@ -1710,7 +1714,9 @@ dump_lambda_function (cxx_pretty_printer *pp,
{
/* A lambda's signature is essentially its "type". */
dump_type (pp, DECL_CONTEXT (fn), flags);
- if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ /* Early escape. */;
+ else if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
{
pp->padding = pp_before;
pp_c_ws_string (pp, "static");
@@ -1831,7 +1837,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
{
- dump_parameters (pp, parmtypes, flags);
+ int const parm_flags
+ = DECL_XOBJ_MEMBER_FUNCTION_P (t) ? TFF_XOBJ_FUNC | flags : flags;
+ dump_parameters (pp, parmtypes, parm_flags);
if (TREE_CODE (fntype) == METHOD_TYPE)
{
@@ -1910,6 +1918,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 +3695,8 @@ function_category (tree fn)
return _("In destructor %qD");
else if (LAMBDA_FUNCTION_P (fn))
return _("In lambda function");
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ return _("In explicit object member function %qD");
else
return _("In member function %qD");
}
diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index 5990a6de736..8baf82578c7 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -404,8 +404,10 @@ build_capture_proxy (tree member, tree init)
fn = lambda_function (closure);
lam = CLASSTYPE_LAMBDA_EXPR (closure);
+ object = DECL_ARGUMENTS (fn);
/* The proxy variable forwards to the capture field. */
- object = build_fold_indirect_ref (DECL_ARGUMENTS (fn));
+ if (INDIRECT_TYPE_P (TREE_TYPE (object)))
+ object = build_fold_indirect_ref (object);
object = finish_non_static_data_member (member, object, NULL_TREE);
if (REFERENCE_REF_P (object))
object = TREE_OPERAND (object, 0);
@@ -835,7 +837,8 @@ lambda_expr_this_capture (tree lambda, int add_capture_p)
if (!LAMBDA_FUNCTION_P (containing_function))
{
/* We found a non-lambda function. */
- if (DECL_NONSTATIC_MEMBER_FUNCTION_P (containing_function))
+ /* There is no this pointer in xobj member functions. */
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (containing_function))
/* First parameter is 'this'. */
init = DECL_ARGUMENTS (containing_function);
break;
@@ -969,7 +972,7 @@ maybe_generic_this_capture (tree object, tree fns)
for (lkp_iterator iter (fns); iter; ++iter)
if (((!id_expr && TREE_CODE (*iter) != USING_DECL)
|| TREE_CODE (*iter) == TEMPLATE_DECL)
- && DECL_NONSTATIC_MEMBER_FUNCTION_P (*iter))
+ && DECL_IOBJ_MEMBER_FUNCTION_P (*iter))
{
/* Found a non-static member. Capture this. */
lambda_expr_this_capture (lam, /*maybe*/-1);
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index 0684f0e6038..84294f10a42 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -1221,9 +1221,9 @@ write_nested_name (const tree decl)
write_char ('N');
- /* Write CV-qualifiers, if this is a member function. */
+ /* Write CV-qualifiers, if this is an iobj member function. */
if (TREE_CODE (decl) == FUNCTION_DECL
- && DECL_NONSTATIC_MEMBER_FUNCTION_P (decl))
+ && DECL_IOBJ_MEMBER_FUNCTION_P (decl))
{
if (DECL_VOLATILE_MEMFUNC_P (decl))
write_char ('V');
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index a70dd5d6adc..ca3102a57a5 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -3607,7 +3607,7 @@ lazily_declare_fn (special_function_kind sfk, tree type)
tree
skip_artificial_parms_for (const_tree fn, tree list)
{
- if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (fn))
list = TREE_CHAIN (list);
else
return list;
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 33fcf396875..bae96680291 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -5683,6 +5683,7 @@ trees_out::lang_decl_bools (tree t)
WB (lang->u.fn.has_dependent_explicit_spec_p);
WB (lang->u.fn.immediate_fn_p);
WB (lang->u.fn.maybe_deleted);
+ WB (lang->u.fn.xobj_func);
goto lds_min;
case lds_decomp: /* lang_decl_decomp. */
@@ -5751,6 +5752,7 @@ trees_in::lang_decl_bools (tree t)
RB (lang->u.fn.has_dependent_explicit_spec_p);
RB (lang->u.fn.immediate_fn_p);
RB (lang->u.fn.maybe_deleted);
+ RB (lang->u.fn.xobj_func);
goto lds_min;
case lds_decomp: /* lang_decl_decomp. */
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 8d911b8cb02..93d4b1f7484 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -11754,8 +11754,16 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
else if (cxx_dialect < cxx23)
omitted_parms_loc = cp_lexer_peek_token (parser->lexer)->location;
+ /* Review note: I figured I might as well update the comments since I'm here.
+ There are also some additions to the below. */
/* In the decl-specifier-seq of the lambda-declarator, each
decl-specifier shall either be mutable or constexpr. */
+ /* [expr.prim.lambda.general]
+ lambda-specifier:
+ consteval, constexpr, mutable, static
+ [4] A lambda-specifier-seq shall contain at most one of each
+ lambda-specifier and shall not contain both constexpr and consteval.
+ The lambda-specifier-seq shall not contain both mutable and static. */
int declares_class_or_enum;
if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer))
cp_parser_decl_specifier_seq (parser,
@@ -11770,19 +11778,88 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
"%<-std=gnu++2b%>");
omitted_parms_loc = UNKNOWN_LOCATION;
}
-
- if (lambda_specs.storage_class == sc_mutable)
+ /* Peek at the params, see if we have an xobj parameter. */
+ if (param_list && TREE_PURPOSE (param_list) == this_identifier)
+ {
+ quals = TYPE_UNQUALIFIED;
+ /* We still need grokdeclarator to see that this is an xobj function
+ and finish the rest of the work, don't mutate it. */
+ tree const xobj_param = TREE_VALUE (param_list);
+ tree const param_type = TREE_TYPE (xobj_param);
+ /* [expr.prim.lambda.closure-5]
+ Given a lambda with a lambda-capture, the type of the explicit object
+ parameter, if any, of the lambda's function call operator (possibly
+ instantiated from a function call operator template) shall be either:
+ -- the closure type,
+ -- a class type derived from the closure type, or
+ -- a reference to a possibly cv-qualified such type. */
+ if ((LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
+ || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
+ /* Since a lambda's type is anonymous, we can assume an xobj
+ parameter is unrelated to the closure if it is non-dependent.
+ If it is dependent we handle it at instantiation time. */
+ && !dependent_type_p (non_reference (param_type)))
+ {
+ error_at (DECL_SOURCE_LOCATION (xobj_param),
+ "a lambda with captures may not have an explicit object "
+ "parameter of an unrelated type");
+ LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) = NULL_TREE;
+ }
+
+ /* [expr.prim.lambda.general-4]
+ If the lambda-declarator contains an explicit object parameter
+ ([dcl.fct]), then no lambda-specifier in the lambda-specifier-seq
+ shall be mutable or static. */
+ if (lambda_specs.storage_class == sc_mutable)
+ {
+ auto_diagnostic_group d;
+ error_at (lambda_specs.locations[ds_storage_class],
+ "%<mutable%> lambda specifier "
+ "with explicit object parameter");
+ /* Tell the user how to do what they probably meant, maybe fixits
+ would be apropriate later? */
+ if (!dependent_type_p (non_reference (param_type)))
+ /* If we are given a non-dependent type we will have already given
+ a diagnosis that the following would contradict with. */;
+ else if (!TYPE_REF_P (param_type))
+ inform (DECL_SOURCE_LOCATION (xobj_param),
+ "the passed in closure object will not be mutated because "
+ "it is taken by copy/move");
+ else if (TYPE_READONLY (TREE_TYPE (param_type)))
+ inform (DECL_SOURCE_LOCATION (xobj_param),
+ "declare the explicit object parameter as non-const "
+ "reference instead");
+ else
+ inform (DECL_SOURCE_LOCATION (xobj_param),
+ "explicit object parameter is already a mutable "
+ "reference");
+ }
+ else if (lambda_specs.storage_class == sc_static)
+ {
+ auto_diagnostic_group d;
+ error_at (lambda_specs.locations[ds_storage_class],
+ "%<static%> lambda specifier "
+ "with explicit object parameter");
+ inform (DECL_SOURCE_LOCATION (xobj_param),
+ "explicit object parameter declared here");
+ }
+ }
+ else if (lambda_specs.storage_class == sc_mutable)
{
quals = TYPE_UNQUALIFIED;
}
else if (lambda_specs.storage_class == sc_static)
{
+ /* [expr.prim.lambda.general-4]
+ If the lambda-specifier-seq contains static, there shall be no
+ lambda-capture. */
if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
|| LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
error_at (lambda_specs.locations[ds_storage_class],
"%<static%> lambda specifier with lambda capture");
else
{
+ /* We can remove this too, I will address it in another patch. */
LAMBDA_EXPR_STATIC_P (lambda_expr) = 1;
quals = TYPE_UNQUALIFIED;
}
@@ -11901,7 +11978,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
{
DECL_INITIALIZED_IN_CLASS_P (fco) = 1;
DECL_ARTIFICIAL (fco) = 1;
- if (!LAMBDA_EXPR_STATIC_P (lambda_expr))
+ if (DECL_IOBJ_MEMBER_FUNCTION_P (fco))
/* Give the object parameter a different name. */
DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier;
DECL_SET_LAMBDA_FUNCTION (fco, true);
@@ -16023,6 +16100,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)
{
@@ -16095,6 +16174,40 @@ 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)
+ {
+ auto_diagnostic_group d;
+ 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. */
+ richloc.add_fixit_remove ();
+ richloc.add_fixit_insert_before (first_specifier->location,
+ "this ");
+ error_at (&richloc,
+ "%<this%> must be the first specifier "
+ "in a parameter declaration");
+ }
+ }
+ 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
@@ -25463,12 +25576,14 @@ cp_parser_parameter_declaration (cp_parser *parser,
/* The restriction on defining new types applies only to the type
of the parameter, not to the default argument. */
parser->type_definition_forbidden_message = saved_message;
-
+ cp_token *eq_token = NULL;
/* If the next token is `=', then process a default argument. */
if (cp_lexer_next_token_is (parser->lexer, CPP_EQ))
{
tree type = decl_specifiers.type;
token = cp_lexer_peek_token (parser->lexer);
+ /* Used for diagnostics with an xobj parameter. */
+ eq_token = token;
if (declarator)
declarator->init_loc = token->location;
/* If we are defining a class, then the tokens that make up the
@@ -25537,6 +25652,25 @@ 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))
+ {
+ if (default_argument)
+ {
+ /* If there is a default_argument, eq_token should always be set. */
+ gcc_assert (eq_token);
+ location_t param_with_init_loc
+ = make_location (eq_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;
+ }
+
/* 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
@@ -33898,6 +34032,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
}
else
{
+ /* These correspond to cp-tree.h:cp_decl_spec,
+ changes here should also be reflected there. */
static const char *const decl_spec_names[] = {
"signed",
"unsigned",
@@ -33915,7 +34051,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/pt.cc b/gcc/cp/pt.cc
index d12d57686f2..7473ed4a41f 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -14391,9 +14391,9 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
tree ctx = closure ? closure : DECL_CONTEXT (t);
bool member = ctx && TYPE_P (ctx);
- /* If this is a static lambda, remove the 'this' pointer added in
+ /* If this is a static or xobj lambda, remove the 'this' pointer added in
tsubst_lambda_expr now that we know the closure type. */
- if (lambda_fntype && DECL_STATIC_FUNCTION_P (t))
+ if (lambda_fntype && !DECL_IOBJ_MEMBER_FUNCTION_P (t))
lambda_fntype = static_fn_type (lambda_fntype);
if (member && !closure)
@@ -14468,12 +14468,12 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
DECL_NAME (r) = make_conv_op_name (TREE_TYPE (type));
tree parms = DECL_ARGUMENTS (t);
- if (closure && !DECL_STATIC_FUNCTION_P (t))
+ if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t))
parms = DECL_CHAIN (parms);
parms = tsubst (parms, args, complain, t);
for (tree parm = parms; parm; parm = DECL_CHAIN (parm))
DECL_CONTEXT (parm) = r;
- if (closure && !DECL_STATIC_FUNCTION_P (t))
+ if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t))
{
tree tparm = build_this_parm (r, closure, type_memfn_quals (type));
DECL_NAME (tparm) = closure_identifier;
@@ -14509,6 +14509,66 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
&& !grok_op_properties (r, /*complain=*/false))
return error_mark_node;
+ /* If we are looking at an xobj lambda, we might need to check the type of
+ its xobj parameter. */
+ if (LAMBDA_FUNCTION_P (r) && DECL_XOBJ_MEMBER_FUNCTION_P (r))
+ {
+ tree closure_obj = DECL_CONTEXT (r);
+ tree lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure_obj);
+ tree obj_param = TREE_TYPE (DECL_ARGUMENTS (r));
+
+ if (!(LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
+ || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)))
+ /* If a lambda has an empty capture clause, an xobj parameter of
+ unrelated type is not an error. */;
+ else if (dependent_type_p (obj_param))
+ /* If we are coming from tsubst_lambda_expr we might not have
+ substituted into our xobj parameter yet. We can't error out until
+ we know what the type really is so do nothing...
+ ...but if we are instantiating the call op for real and we don't
+ have a real type then something has gone incredibly wrong. */
+ gcc_assert (lambda_fntype);
+ else
+ {
+ /* We have a lambda with captures, and know the type of the xobj
+ parameter, time to check it. */
+ tree obj_param_type = TYPE_MAIN_VARIANT (non_reference (obj_param));
+ if (!same_or_base_type_p (closure_obj, obj_param_type))
+ {
+ /* This error does not emit when the lambda's call operator
+ template is instantiated by taking its address, such as in
+ the following case:
+
+ auto f = [x = 0](this auto&&){};
+ int (*fp)(int&) = &decltype(f)::operator();
+
+ It only emits when explicitly calling the call operator with
+ an explicit template parameter:
+
+ template<typename T>
+ struct S : T {
+ using T::operator();
+ operator int() const {return {};}
+ };
+
+ auto s = S{[x = 0](this auto&&) {}};
+ s.operator()<int>();
+
+ This is due to resolve_address_of_overloaded_function being
+ deficient at reporting candidates when overload resolution
+ fails.
+
+ This diagnostic will be active in the first case if/when
+ resolve_address_of_overloaded_function is fixed to properly
+ emit candidates upon failure to resolve to an overload. */
+ if (complain & tf_error)
+ error ("a lambda with captures may not have an explicit "
+ "object parameter of an unrelated type");
+ return error_mark_node;
+ }
+ }
+ }
+
/* Associate the constraints directly with the instantiation. We
don't substitute through the constraints; that's only done when
they are checked. */
@@ -19473,7 +19533,11 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
which would be skipped if cp_unevaluated_operand. */
cp_evaluated ev;
- /* Fix the type of 'this'. */
+ /* Fix the type of 'this'.
+ For static and xobj member functions we use this to transport the
+ lambda's closure type. It appears that in the regular case the
+ object parameter is still pulled off, and then re-added again anyway.
+ So perhaps we could do something better here? */
fntype = build_memfn_type (fntype, type,
type_memfn_quals (fntype),
type_memfn_rqual (fntype));
@@ -25197,14 +25261,14 @@ more_specialized_fn (tree pat1, tree pat2, int len)
also. This situation occurs for operator functions where we
locate both a member function (with this pointer) and non-member
operator (with explicit first operand). */
- if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl1))
+ if (DECL_OBJECT_MEMBER_FUNCTION_P (decl1))
{
len--; /* LEN is the number of significant arguments for DECL1 */
args1 = TREE_CHAIN (args1);
if (!DECL_STATIC_FUNCTION_P (decl2))
args2 = TREE_CHAIN (args2);
}
- else if (DECL_NONSTATIC_MEMBER_FUNCTION_P (decl2))
+ else if (DECL_OBJECT_MEMBER_FUNCTION_P (decl2))
{
args2 = TREE_CHAIN (args2);
if (!DECL_STATIC_FUNCTION_P (decl1))
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index ac79b625b6e..efea5078992 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -713,6 +713,8 @@ protected_accessible_p (tree decl, tree derived, tree type, tree otype)
derived from that class) (_expr.ref_). If the access is to form
a pointer to member, the nested-name-specifier shall name the
derived class (or any class derived from that class). */
+ /* Will this cause problems for xobj member functions?
+ We definitely need tests for this case. */
if (DECL_NONSTATIC_MEMBER_P (decl)
&& !DERIVED_FROM_P (derived, otype))
return 0;
@@ -2223,10 +2225,13 @@ look_for_overrides_here (tree type, tree fndecl)
/* Not a virtual. */;
else if (DECL_CONTEXT (fn) != type)
/* Introduced with a using declaration. */;
- else if (DECL_STATIC_FUNCTION_P (fndecl))
+ else if (DECL_STATIC_FUNCTION_P (fndecl)
+ || DECL_XOBJ_MEMBER_FUNCTION_P (fndecl))
{
tree btypes = TYPE_ARG_TYPES (TREE_TYPE (fn));
tree dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+ dtypes = DECL_XOBJ_MEMBER_FUNCTION_P (fndecl) ? TREE_CHAIN (dtypes)
+ : dtypes;
if (compparms (TREE_CHAIN (btypes), dtypes))
return fn;
}
@@ -2254,6 +2259,15 @@ look_for_overrides_r (tree type, tree fndecl)
error ("%q+#D cannot be declared", fndecl);
error (" since %q+#D declared in base class", fn);
}
+ else if (DECL_XOBJ_MEMBER_FUNCTION_P (fndecl))
+ {
+ auto_diagnostic_group d;
+ error_at (DECL_SOURCE_LOCATION (fndecl),
+ "explicit object member function "
+ "overrides virtual function");
+ inform (DECL_SOURCE_LOCATION (fn),
+ "virtual function declared here");
+ }
else
{
/* It's definitely virtual, even if not explicitly set. */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index fbbc18336a0..9f4c5a91f56 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3. If not see
#include "gomp-constants.h"
#include "predict.h"
#include "memmodel.h"
+#include "gcc-rich-location.h"
/* There routines provide a modular interface to perform many parsing
operations. They may therefore be used during actual parsing, or
@@ -3093,7 +3094,31 @@ finish_this_expr (void)
return rvalue (result);
tree fn = current_nonlambda_function ();
- if (fn && DECL_STATIC_FUNCTION_P (fn))
+ if (fn && DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+ {
+ auto_diagnostic_group d;
+ error ("%<this%> is unavailable for explicit object member "
+ "functions");
+ /* Doing a fixit here is possible, but hard, might be worthwhile
+ in the future. */
+ tree xobj_parm = DECL_ARGUMENTS (fn);
+ gcc_assert (xobj_parm);
+ tree parm_name = DECL_NAME (xobj_parm);
+ if (parm_name)
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "use explicit object parameter %qD instead",
+ parm_name);
+ else
+ {
+ gcc_rich_location xobj_loc (DECL_SOURCE_LOCATION (xobj_parm));
+ /* This doesn't work and I don't know why. I'll probably remove it
+ before the final version. */
+ xobj_loc.add_fixit_insert_after (" self");
+ inform (DECL_SOURCE_LOCATION (xobj_parm),
+ "name and use the explicit object parameter instead");
+ }
+ }
+ else if (fn && DECL_STATIC_FUNCTION_P (fn))
error ("%<this%> is unavailable for static member functions");
else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn))
error ("invalid use of %<this%> before it is valid");
@@ -12812,9 +12837,11 @@ capture_decltype (tree decl)
if (WILDCARD_TYPE_P (non_reference (obtype)))
/* We don't know what the eventual obtype quals will be. */
return NULL_TREE;
- int quals = cp_type_quals (type);
- if (INDIRECT_TYPE_P (obtype))
- quals |= cp_type_quals (TREE_TYPE (obtype));
+ /* This change possibly conflicts with another patch that is currently
+ in flight. (Patrick Palka's PR83167 patch) I am just changing it for
+ now to satisfy my tests. */
+ int const quals = cp_type_quals (type)
+ | cp_type_quals (TREE_TYPE (obtype));
type = cp_build_qualified_type (type, quals);
type = build_reference_type (type);
}
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index e0b9d512adc..ac05bffea3f 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -3673,7 +3673,7 @@ build_min_non_dep_op_overload (enum tree_code op,
nargs = call_expr_nargs (non_dep);
expected_nargs = cp_tree_code_length (op);
- if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE
+ if (DECL_OBJECT_MEMBER_FUNCTION_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
@@ -3691,7 +3691,7 @@ 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 (!DECL_OBJECT_MEMBER_FUNCTION_P (overload))
{
fn = overload;
if (op == ARRAY_REF)
@@ -3702,7 +3702,7 @@ build_min_non_dep_op_overload (enum tree_code op,
vec_safe_push (args, arg);
}
}
- else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+ else
{
tree object = va_arg (p, tree);
tree binfo = TYPE_BINFO (TREE_TYPE (object));
@@ -3715,8 +3715,6 @@ build_min_non_dep_op_overload (enum tree_code op,
vec_safe_push (args, arg);
}
}
- else
- gcc_unreachable ();
va_end (p);
call = build_min_non_dep_call_vec (non_dep, fn, args);
@@ -3743,7 +3741,7 @@ 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 (DECL_OBJECT_MEMBER_FUNCTION_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 bf8ffaa7e75..73707624ce4 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -2339,7 +2339,10 @@ cxx_alignas_expr (tree e)
used only as the operand for the function call operator ().
by issuing an error message if appropriate. Returns true iff EXPR
- violates these rules. */
+ violates these rules.
+
+ The name "nonstatic" is no longer accurate with the addition of
+ xobj member functions, should we change the name? */
bool
invalid_nonstatic_memfn_p (location_t loc, tree expr, tsubst_flags_t complain)
@@ -2352,7 +2355,7 @@ invalid_nonstatic_memfn_p (location_t loc, tree expr, tsubst_flags_t complain)
if (is_overloaded_fn (expr) && !really_overloaded_fn (expr))
expr = get_first_fn (expr);
if (TREE_TYPE (expr)
- && DECL_NONSTATIC_MEMBER_FUNCTION_P (expr))
+ && DECL_IOBJ_MEMBER_FUNCTION_P (expr))
{
if (complain & tf_error)
{
@@ -7174,6 +7177,24 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
case BASELINK:
arg = BASELINK_FUNCTIONS (arg);
+ /* If we are passed a BASELINK, the operand of the address-of operator
+ was not qualified, taking the address of a non-static member function
+ must be qualified by the class.
+ It's possible we shouldn't be making this baselink optimization for
+ xobj member functions at all, we should evaluate that later. */
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (arg))
+ {
+ /* Error should match that in
+ class.cc:resolve_address_of_overloaded_function.
+ We really should do a fixit here. */
+ error_at (input_location,
+ "taking the address of an explicit object member "
+ "function must be qualified");
+ inform (input_location,
+ "(a pointer to explicit object member function can only be "
+ "formed with %<&%E%>)", arg);
+ return error_mark_node;
+ }
/* Fall through. */
case OVERLOAD:
@@ -7205,6 +7226,17 @@ 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;
+ /* Pull out the function_decl for a single xobj member function, and
+ let the rest of this function handle it. This is handled the same
+ way as the BASELINK case above, but we can't handle xobj member
+ functions there as xobj member functions must be qualified, hence
+ the error above. */
+ if (DECL_XOBJ_MEMBER_FUNCTION_P (t))
+ {
+ arg = t;
+ break;
+ }
+
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..134182c7741
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C
@@ -0,0 +1,114 @@
+// 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<typename Self>
+ 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<typename Self>
+ 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<typename Self>
+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<typename Self>
+ 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<typename Self>
+ 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<typename Self>
+auto S3::d0(this Self&&) -> void {}
+auto S3::d1(this auto&&) -> void {}
+
+template<typename T>
+void call_with_qualification()
+{
+ T obj{};
+ // by value should take any qualification (f0)
+ T{}.f0();
+ obj.f0();
+ static_cast<T&&>(obj).f0();
+ static_cast<T const&>(obj).f0();
+ static_cast<T const&&>(obj).f0();
+ // specific qualification (f1 - f4)
+ T{}.f2();
+ T{}.f3();
+ T{}.f4();
+ obj.f1();
+ obj.f3();
+ static_cast<T&&>(obj).f2();
+ static_cast<T&&>(obj).f3();
+ static_cast<T&&>(obj).f4();
+ static_cast<T const&>(obj).f3();
+ static_cast<T const&&>(obj).f4();
+ // deduced should (obviously) take any qualification (d0, d1)
+ T{}.d0();
+ obj.d0();
+ static_cast<T&&>(obj).d0();
+ static_cast<T const&>(obj).d0();
+ static_cast<T const&&>(obj).d0();
+ T{}.d1();
+ obj.d1();
+ static_cast<T&&>(obj).d1();
+ static_cast<T const&>(obj).d1();
+ static_cast<T const&&>(obj).d1();
+}
+
+void perform_calls()
+{
+ call_with_qualification<S0>();
+ call_with_qualification<S1>();
+ call_with_qualification<S2>();
+ call_with_qualification<S3>();
+}
+
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..6b2cad130fa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C
@@ -0,0 +1,28 @@
+// 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-basic3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C
new file mode 100644
index 00000000000..e466cc9761c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C
@@ -0,0 +1,496 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// bogus diagnosis of valid declarations as redeclarations
+// tests for by-value are elsewhere (todo: add filename)
+
+// each group has 8 overloads that each take
+// lvalue ref to S
+// rvalue ref to S
+// lvalue c ref to S
+// rvalue c ref to S
+// lvalue v ref to S
+// rvalue v ref to S
+// lvalue cv ref to S
+// rvalue cv ref to S
+// where S is the struct the function is declared in
+
+// only xobj (the most basic case)
+
+struct S {
+ void f(this S &);
+ void f(this S &&);
+ void f(this S const&);
+ void f(this S const&&);
+ void f(this S volatile&);
+ void f(this S volatile&&);
+ void f(this S const volatile&);
+ void f(this S const volatile&&);
+};
+
+// I* has the 1 xobj 7 iobj cases
+// X* has the 7 xobj 1 iobj cases
+// *0 has the unique function first, the rest after
+// *1 has the unique function last, the rest after
+// *2 has the functions in the order stated above
+// xobj first, 1 xobj, 7 iobj
+
+// (yes there are some redundant cases)
+
+// unique first, 1 xobj 7 iobj
+
+struct I0 {
+ void f0(this I0&);
+ void f0() &&;
+ void f0() const&;
+ void f0() const&&;
+ void f0() volatile&;
+ void f0() volatile&&;
+ void f0() const volatile&;
+ void f0() const volatile&&;
+
+ void f1(this I0&&);
+ void f1() &;
+ void f1() const&;
+ void f1() const&&;
+ void f1() volatile&;
+ void f1() volatile&&;
+ void f1() const volatile&;
+ void f1() const volatile&&;
+
+ void fc0(this I0 const&);
+ void fc0() &;
+ void fc0() &&;
+ void fc0() const&&;
+ void fc0() volatile&;
+ void fc0() volatile&&;
+ void fc0() const volatile&;
+ void fc0() const volatile&&;
+
+ void fc1(this I0 const&&);
+ void fc1() &;
+ void fc1() &&;
+ void fc1() const&;
+ void fc1() volatile&;
+ void fc1() volatile&&;
+ void fc1() const volatile&;
+ void fc1() const volatile&&;
+
+ void fv0(this I0 volatile&);
+ void fv0() &;
+ void fv0() &&;
+ void fv0() const&;
+ void fv0() const&&;
+ void fv0() volatile&&;
+ void fv0() const volatile&;
+ void fv0() const volatile&&;
+
+ void fv1(this I0 volatile&&);
+ void fv1() &;
+ void fv1() &&;
+ void fv1() const&;
+ void fv1() const&&;
+ void fv1() volatile&;
+ void fv1() const volatile&;
+ void fv1() const volatile&&;
+
+ void fcv0(this I0 const volatile&);
+ void fcv0() &;
+ void fcv0() &&;
+ void fcv0() const&;
+ void fcv0() const&&;
+ void fcv0() volatile&;
+ void fcv0() volatile&&;
+ void fcv0() const volatile&&;
+
+ void fcv1(this I0 const volatile&&);
+ void fcv1() &;
+ void fcv1() &&;
+ void fcv1() const&;
+ void fcv1() const&&;
+ void fcv1() volatile&;
+ void fcv1() volatile&&;
+ void fcv1() const volatile&;
+};
+
+// unique last, 1 xobj 7 iobj
+
+struct I1 {
+ void f0() &&;
+ void f0() const&;
+ void f0() const&&;
+ void f0() volatile&;
+ void f0() volatile&&;
+ void f0() const volatile&;
+ void f0() const volatile&&;
+ void f0(this I1&);
+
+ void f1() &;
+ void f1() const&;
+ void f1() const&&;
+ void f1() volatile&;
+ void f1() volatile&&;
+ void f1() const volatile&;
+ void f1() const volatile&&;
+ void f1(this I1&&);
+
+ void fc0() &;
+ void fc0() &&;
+ void fc0() const&&;
+ void fc0() volatile&;
+ void fc0() volatile&&;
+ void fc0() const volatile&;
+ void fc0() const volatile&&;
+ void fc0(this I1 const&);
+
+ void fc1() &;
+ void fc1() &&;
+ void fc1() const&;
+ void fc1() volatile&;
+ void fc1() volatile&&;
+ void fc1() const volatile&;
+ void fc1() const volatile&&;
+ void fc1(this I1 const&&);
+
+ void fv0() &;
+ void fv0() &&;
+ void fv0() const&;
+ void fv0() const&&;
+ void fv0() volatile&&;
+ void fv0() const volatile&;
+ void fv0() const volatile&&;
+ void fv0(this I1 volatile&);
+
+ void fv1() &;
+ void fv1() &&;
+ void fv1() const&;
+ void fv1() const&&;
+ void fv1() volatile&;
+ void fv1() const volatile&;
+ void fv1() const volatile&&;
+ void fv1(this I1 volatile&&);
+
+ void fcv0() &;
+ void fcv0() &&;
+ void fcv0() const&;
+ void fcv0() const&&;
+ void fcv0() volatile&;
+ void fcv0() volatile&&;
+ void fcv0() const volatile&&;
+ void fcv0(this I1 const volatile&);
+
+ void fcv1() &;
+ void fcv1() &&;
+ void fcv1() const&;
+ void fcv1() const&&;
+ void fcv1() volatile&;
+ void fcv1() volatile&&;
+ void fcv1() const volatile&;
+ void fcv1(this I1 const volatile&&);
+};
+
+// ordered, 1 xobj 7 iobj
+
+struct I2 {
+ void f0(this I2&);
+ void f0() &&;
+ void f0() const&;
+ void f0() const&&;
+ void f0() volatile&;
+ void f0() volatile&&;
+ void f0() const volatile&;
+ void f0() const volatile&&;
+
+ void f1() &;
+ void f1(this I2&&);
+ void f1() const&;
+ void f1() const&&;
+ void f1() volatile&;
+ void f1() volatile&&;
+ void f1() const volatile&;
+ void f1() const volatile&&;
+
+ void fc0() &;
+ void fc0() &&;
+ void fc0(this I2 const&);
+ void fc0() const&&;
+ void fc0() volatile&;
+ void fc0() volatile&&;
+ void fc0() const volatile&;
+ void fc0() const volatile&&;
+
+ void fc1() &;
+ void fc1() &&;
+ void fc1() const&;
+ void fc1(this I2 const&&);
+ void fc1() volatile&;
+ void fc1() volatile&&;
+ void fc1() const volatile&;
+ void fc1() const volatile&&;
+
+ void fv0() &;
+ void fv0() &&;
+ void fv0() const&;
+ void fv0() const&&;
+ void fv0(this I2 volatile&);
+ void fv0() volatile&&;
+ void fv0() const volatile&;
+ void fv0() const volatile&&;
+
+ void fv1() &;
+ void fv1() &&;
+ void fv1() const&;
+ void fv1() const&&;
+ void fv1() volatile&;
+ void fv1(this I2 volatile&&);
+ void fv1() const volatile&;
+ void fv1() const volatile&&;
+
+ void fcv0() &;
+ void fcv0() &&;
+ void fcv0() const&;
+ void fcv0() const&&;
+ void fcv0() volatile&;
+ void fcv0() volatile&&;
+ void fcv0(this I2 const volatile&);
+ void fcv0() const volatile&&;
+
+ void fcv1() &;
+ void fcv1() &&;
+ void fcv1() const&;
+ void fcv1() const&&;
+ void fcv1() volatile&;
+ void fcv1() volatile&&;
+ void fcv1() const volatile&;
+ void fcv1(this I2 const volatile&&);
+};
+
+
+// iobj first, 7 xobj, 1 iobj
+
+struct X0 {
+ void f0() &;
+ void f0(this X0 &&);
+ void f0(this X0 const&);
+ void f0(this X0 const&&);
+ void f0(this X0 volatile&);
+ void f0(this X0 volatile&&);
+ void f0(this X0 const volatile&);
+ void f0(this X0 const volatile&&);
+
+ void f1() &&;
+ void f1(this X0 &);
+ void f1(this X0 const&);
+ void f1(this X0 const&&);
+ void f1(this X0 volatile&);
+ void f1(this X0 volatile&&);
+ void f1(this X0 const volatile&);
+ void f1(this X0 const volatile&&);
+
+ void fc0() const&;
+ void fc0(this X0 &);
+ void fc0(this X0 &&);
+ void fc0(this X0 const&&);
+ void fc0(this X0 volatile&);
+ void fc0(this X0 volatile&&);
+ void fc0(this X0 const volatile&);
+ void fc0(this X0 const volatile&&);
+
+ void fc1() const&&;
+ void fc1(this X0 &);
+ void fc1(this X0 &&);
+ void fc1(this X0 const&);
+ void fc1(this X0 volatile&);
+ void fc1(this X0 volatile&&);
+ void fc1(this X0 const volatile&);
+ void fc1(this X0 const volatile&&);
+
+ void fv0() volatile&;
+ void fv0(this X0 &);
+ void fv0(this X0 &&);
+ void fv0(this X0 const&);
+ void fv0(this X0 const&&);
+ void fv0(this X0 volatile&&);
+ void fv0(this X0 const volatile&);
+ void fv0(this X0 const volatile&&);
+
+ void fv1() volatile&&;
+ void fv1(this X0 &);
+ void fv1(this X0 &&);
+ void fv1(this X0 const&);
+ void fv1(this X0 const&&);
+ void fv1(this X0 volatile&);
+ void fv1(this X0 const volatile&);
+ void fv1(this X0 const volatile&&);
+
+ void fcv0() const volatile&;
+ void fcv0(this X0 &);
+ void fcv0(this X0 &&);
+ void fcv0(this X0 const&);
+ void fcv0(this X0 const&&);
+ void fcv0(this X0 volatile&);
+ void fcv0(this X0 volatile&&);
+ void fcv0(this X0 const volatile&&);
+
+ void fcv1() const volatile&&;
+ void fcv1(this X0 &);
+ void fcv1(this X0 &&);
+ void fcv1(this X0 const&);
+ void fcv1(this X0 const&&);
+ void fcv1(this X0 volatile&);
+ void fcv1(this X0 volatile&&);
+ void fcv1(this X0 const volatile&);
+};
+
+// iobj last, 7 xobj 1 iobj
+
+struct X1 {
+ void f0(this X1 &&);
+ void f0(this X1 const&);
+ void f0(this X1 const&&);
+ void f0(this X1 volatile&);
+ void f0(this X1 volatile&&);
+ void f0(this X1 const volatile&);
+ void f0(this X1 const volatile&&);
+ void f0() &;
+
+ void f1(this X1 &);
+ void f1(this X1 const&);
+ void f1(this X1 const&&);
+ void f1(this X1 volatile&);
+ void f1(this X1 volatile&&);
+ void f1(this X1 const volatile&);
+ void f1(this X1 const volatile&&);
+ void f1() &&;
+
+ void fc0(this X1 &);
+ void fc0(this X1 &&);
+ void fc0(this X1 const&&);
+ void fc0(this X1 volatile&);
+ void fc0(this X1 volatile&&);
+ void fc0(this X1 const volatile&);
+ void fc0(this X1 const volatile&&);
+ void fc0() const&;
+
+ void fc1(this X1 &);
+ void fc1(this X1 &&);
+ void fc1(this X1 const&);
+ void fc1(this X1 volatile&);
+ void fc1(this X1 volatile&&);
+ void fc1(this X1 const volatile&);
+ void fc1(this X1 const volatile&&);
+ void fc1() const&&;
+
+ void fv0(this X1 &);
+ void fv0(this X1 &&);
+ void fv0(this X1 const&);
+ void fv0(this X1 const&&);
+ void fv0(this X1 volatile&&);
+ void fv0(this X1 const volatile&);
+ void fv0(this X1 const volatile&&);
+ void fv0() volatile&;
+
+ void fv1(this X1 &);
+ void fv1(this X1 &&);
+ void fv1(this X1 const&);
+ void fv1(this X1 const&&);
+ void fv1(this X1 volatile&);
+ void fv1(this X1 const volatile&);
+ void fv1(this X1 const volatile&&);
+ void fv1() volatile&&;
+
+ void fcv0(this X1 &);
+ void fcv0(this X1 &&);
+ void fcv0(this X1 const&);
+ void fcv0(this X1 const&&);
+ void fcv0(this X1 volatile&);
+ void fcv0(this X1 volatile&&);
+ void fcv0(this X1 const volatile&&);
+ void fcv0() const volatile&;
+
+ void fcv1(this X1 &);
+ void fcv1(this X1 &&);
+ void fcv1(this X1 const&);
+ void fcv1(this X1 const&&);
+ void fcv1(this X1 volatile&);
+ void fcv1(this X1 volatile&&);
+ void fcv1(this X1 const volatile&);
+ void fcv1() const volatile&&;
+};
+
+// ordered, 7 xobj 1 iobj
+
+struct X2 {
+ void f0() &;
+ void f0(this X2 &&);
+ void f0(this X2 const&);
+ void f0(this X2 const&&);
+ void f0(this X2 volatile&);
+ void f0(this X2 volatile&&);
+ void f0(this X2 const volatile&);
+ void f0(this X2 const volatile&&);
+
+ void f1(this X2 &);
+ void f1() &&;
+ void f1(this X2 const&);
+ void f1(this X2 const&&);
+ void f1(this X2 volatile&);
+ void f1(this X2 volatile&&);
+ void f1(this X2 const volatile&);
+ void f1(this X2 const volatile&&);
+
+ void fc0(this X2 &);
+ void fc0(this X2 &&);
+ void fc0() const&;
+ void fc0(this X2 const&&);
+ void fc0(this X2 volatile&);
+ void fc0(this X2 volatile&&);
+ void fc0(this X2 const volatile&);
+ void fc0(this X2 const volatile&&);
+
+ void fc1(this X2 &);
+ void fc1(this X2 &&);
+ void fc1(this X2 const&);
+ void fc1() const&&;
+ void fc1(this X2 volatile&);
+ void fc1(this X2 volatile&&);
+ void fc1(this X2 const volatile&);
+ void fc1(this X2 const volatile&&);
+
+ void fv0(this X2 &);
+ void fv0(this X2 &&);
+ void fv0(this X2 const&);
+ void fv0(this X2 const&&);
+ void fv0() volatile&;
+ void fv0(this X2 volatile&&);
+ void fv0(this X2 const volatile&);
+ void fv0(this X2 const volatile&&);
+
+ void fv1(this X2 &);
+ void fv1(this X2 &&);
+ void fv1(this X2 const&);
+ void fv1(this X2 const&&);
+ void fv1(this X2 volatile&);
+ void fv1() volatile&&;
+ void fv1(this X2 const volatile&);
+ void fv1(this X2 const volatile&&);
+
+ void fcv0(this X2 &);
+ void fcv0(this X2 &&);
+ void fcv0(this X2 const&);
+ void fcv0(this X2 const&&);
+ void fcv0(this X2 volatile&);
+ void fcv0(this X2 volatile&&);
+ void fcv0() const volatile&;
+ void fcv0(this X2 const volatile&&);
+
+ void fcv1(this X2 &);
+ void fcv1(this X2 &&);
+ void fcv1(this X2 const&);
+ void fcv1(this X2 const&&);
+ void fcv1(this X2 volatile&);
+ void fcv1(this X2 volatile&&);
+ void fcv1(this X2 const volatile&);
+ void fcv1() const volatile&&;
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C
new file mode 100644
index 00000000000..691ef6804cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C
@@ -0,0 +1,113 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// valid overloading of iobj member functions without ref qualifiers
+// with xobj member functions (and vice-versa)
+
+// this is the most you can mix these, it may look short but it does test
+// all allowed cases (other than by-value and unrelated types)
+
+// [over.match.funcs.general.4]
+// For implicit object member functions, the type of the implicit
+// object parameter is
+// -- “lvalue reference to cv X” for functions declared
+// without a ref-qualifier or with the & ref-qualifier
+// -- “rvalue reference to cv X” for functions declared with
+// the && ref-qualifier
+
+// [basic.scope.scope.3]
+// Two non-static member functions have corresponding object
+// parameters if:
+// -- exactly one is an implicit object member function with no
+// ref-qualifier and the types of their object parameters
+// ([dcl.fct]), after removing top-level references, are the
+// same, or
+
+// in simpler terms, only the cv qualification of the explicit/implicit object
+// parameter matter for determining whether these are redeclarations or overloads
+// (when a ref qualifier is not present on the iobj member function)
+
+// xobj first, iobj last
+
+struct S0 {
+ void f(this S0 &);
+ void f(this S0 &&);
+ void f() const;
+ void f() volatile;
+ void f() const volatile;
+
+ void fc(this S0 const&);
+ void fc(this S0 const&&);
+ void fc();
+ void fc() volatile;
+ void fc() const volatile;
+
+ void fv(this S0 volatile&);
+ void fv(this S0 volatile&&);
+ void fv();
+ void fv() const;
+ void fv() const volatile;
+
+ void fcv(this S0 const volatile&);
+ void fcv(this S0 const volatile&&);
+ void fcv();
+ void fcv() const;
+ void fcv() volatile;
+};
+
+// iobj first, xobj last
+
+struct S1 {
+ void f() const;
+ void f() volatile;
+ void f() const volatile;
+ void f(this S1 &);
+ void f(this S1 &&);
+
+ void fc();
+ void fc() volatile;
+ void fc() const volatile;
+ void fc(this S1 const&);
+ void fc(this S1 const&&);
+
+ void fv();
+ void fv() const;
+ void fv() const volatile;
+ void fv(this S1 volatile&);
+ void fv(this S1 volatile&&);
+
+ void fcv();
+ void fcv() const;
+ void fcv() volatile;
+ void fcv(this S1 const volatile&);
+ void fcv(this S1 const volatile&&);
+};
+
+// in order
+
+struct S2 {
+ void f(this S2 &);
+ void f(this S2 &&);
+ void f() const;
+ void f() volatile;
+ void f() const volatile;
+
+ void fc();
+ void fc(this S2 const&);
+ void fc(this S2 const&&);
+ void fc() volatile;
+ void fc() const volatile;
+
+ void fv();
+ void fv() const;
+ void fv(this S2 volatile&);
+ void fv(this S2 volatile&&);
+ void fv() const volatile;
+
+ void fcv();
+ void fcv() const;
+ void fcv() volatile;
+ void fcv(this S2 const volatile&);
+ void fcv(this S2 const volatile&&);
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C
new file mode 100644
index 00000000000..de3c4fa9a9a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic5.C
@@ -0,0 +1,31 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+inline constexpr int magic = 42;
+
+struct S0 {
+ operator int(this S0 const&) {
+ return magic;
+ }
+};
+
+struct S1 {
+ int _v;
+ int f(this int self) {
+ return self;
+ }
+ operator int(this S1 const& self) {
+ return self._v;
+ }
+};
+
+int main()
+{
+ if (S0{} != magic)
+ __builtin_abort ();
+
+ S1 s{42};
+ if (static_cast<int>(s) != magic)
+ __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..5ea5bcb7faa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C
@@ -0,0 +1,48 @@
+// 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.
+
+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..b8e8e73dfaf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C
@@ -0,0 +1,58 @@
+// 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
+
+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..e6aff0190fb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C
@@ -0,0 +1,41 @@
+// 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
+
+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 const&>(s_copy_from).f();
+ uintptr_t const move_ret = static_cast<S&&>(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..9b4e00582cf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C
@@ -0,0 +1,20 @@
+// 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-constraints.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints.C
new file mode 100644
index 00000000000..7ef9877b67a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-constraints.C
@@ -0,0 +1,470 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// overload resolution of static/xobj and iobj/xobj member functions
+// with constraints
+
+template<typename T>
+concept Constrain = true;
+
+inline constexpr int iobj_fn = 5;
+inline constexpr int xobj_fn = 10;
+inline constexpr int static_fn = 20;
+
+// NOTE, Jason Merril doesn't think the static cases are neccesarily correct
+
+/* https://gcc.gnu.org/pipermail/gcc-patches/2023-December/639052.html
+ I would actually expect the static function to be chosen as more
+ specialized before we get to considering constraints, just as with
+
+ void f(auto, Constrain auto) = delete;
+ void f(const S&, auto) {}
+ int main() { f(S{},0); } // OK
+
+ Though it looks like [temp.func.order] needs to be adjusted for explicit
+ object parameters. And more_specialized_fn in gcc still has an outdated
+ handling of object parameters that just skips them, from before the
+ clearer specification in C++11 and later; this is PR53499. No need to
+ address that preexisting bug in this patch. */
+
+// first 2 letters are the order of the definitions
+// the constraint applies to the first definition,
+// for *_r cases the constraint applies to the second
+
+struct S {
+ // xobj/static
+ int f_xs_v(this S, Constrain auto) { return xobj_fn; };
+ static int f_xs_v(auto) { return static_fn; };
+
+ int f_xs_ref(this S&, Constrain auto) { return xobj_fn; };
+ static int f_xs_ref(auto) { return static_fn; };
+
+ int f_xs_cref(this S const&, Constrain auto) { return xobj_fn; };
+ static int f_xs_cref(auto) { return static_fn; };
+
+ int f_xs_rref(this S&&, Constrain auto) { return xobj_fn; };
+ static int f_xs_rref(auto) { return static_fn; };
+
+ int f_xs_crref(this S const&&, Constrain auto) { return xobj_fn; };
+ static int f_xs_crref(auto) { return static_fn; };
+
+ int f_xs_dv(this auto, Constrain auto) { return xobj_fn; };
+ static int f_xs_dv(auto) { return static_fn; };
+
+ int f_xs_dcref(this auto const&, Constrain auto) { return xobj_fn; };
+ static int f_xs_dcref(auto) { return static_fn; };
+
+ int f_xs_dfwdref(this auto&&, Constrain auto) { return xobj_fn; };
+ static int f_xs_dfwdref(auto) { return static_fn; };
+
+ // _r
+ int f_xs_v_r(this S, auto) { return xobj_fn; };
+ static int f_xs_v_r(Constrain auto) { return static_fn; };
+
+ int f_xs_ref_r(this S&, auto) { return xobj_fn; };
+ static int f_xs_ref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_cref_r(this S const&, auto) { return xobj_fn; };
+ static int f_xs_cref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_rref_r(this S&&, auto) { return xobj_fn; };
+ static int f_xs_rref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_crref_r(this S const&&, auto) { return xobj_fn; };
+ static int f_xs_crref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_dv_r(this auto, auto) { return xobj_fn; };
+ static int f_xs_dv_r(Constrain auto) { return static_fn; };
+
+ int f_xs_dcref_r(this auto const&, auto) { return xobj_fn; };
+ static int f_xs_dcref_r(Constrain auto) { return static_fn; };
+
+ int f_xs_dfwdref_r(this auto&&, auto) { return xobj_fn; };
+ static int f_xs_dfwdref_r(Constrain auto) { return static_fn; };
+
+ // static/xobj
+ static int f_sx_v(Constrain auto) { return static_fn; };
+ int f_sx_v(this S, auto) { return xobj_fn; };
+
+ static int f_sx_ref(Constrain auto) { return static_fn; };
+ int f_sx_ref(this S&, auto) { return xobj_fn; };
+
+ static int f_sx_cref(Constrain auto) { return static_fn; };
+ int f_sx_cref(this S const&, auto) { return xobj_fn; };
+
+ static int f_sx_rref(Constrain auto) { return static_fn; };
+ int f_sx_rref(this S&&, auto) { return xobj_fn; };
+
+ static int f_sx_crref(Constrain auto) { return static_fn; };
+ int f_sx_crref(this S const&&, auto) { return xobj_fn; };
+
+ static int f_sx_dv(Constrain auto) { return static_fn; };
+ int f_sx_dv(this auto, auto) { return xobj_fn; };
+
+ static int f_sx_dcref(Constrain auto) { return static_fn; };
+ int f_sx_dcref(this auto const&, auto) { return xobj_fn; };
+
+ static int f_sx_dfwdref(Constrain auto) { return static_fn; };
+ int f_sx_dfwdref(this auto&&, auto) { return xobj_fn; };
+
+ // _r
+ static int f_sx_v_r(auto) { return static_fn; };
+ int f_sx_v_r(this S, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_ref_r(auto) { return static_fn; };
+ int f_sx_ref_r(this S&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_cref_r(auto) { return static_fn; };
+ int f_sx_cref_r(this S const&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_rref_r(auto) { return static_fn; };
+ int f_sx_rref_r(this S&&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_crref_r(auto) { return static_fn; };
+ int f_sx_crref_r(this S const&&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_dv_r(auto) { return static_fn; };
+ int f_sx_dv_r(this auto, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_dcref_r(auto) { return static_fn; };
+ int f_sx_dcref_r(this auto const&, Constrain auto) { return xobj_fn; };
+
+ static int f_sx_dfwdref_r(auto) { return static_fn; };
+ int f_sx_dfwdref_r(this auto&&, Constrain auto) { return xobj_fn; };
+
+ // xobj/iobj with matching object parameters
+
+ // We are only testing constraints here, so we need parameter lists
+ // to match, which means we need corresponding object parameters.
+ // Remember, the rules for object parameter correspondence are weird.
+ // ([basic.scope.scope-3.1])
+
+ // *_refqual the iobj member function has a reference qualifier
+ // *_r the constraint applies to the second definition
+
+ // ix
+ int f_ix_m0(Constrain auto) { return iobj_fn; };
+ int f_ix_m0(this S&, auto) { return xobj_fn; };
+ // See note
+ int f_ix_m1(Constrain auto) { return iobj_fn; };
+ int f_ix_m1(this S&&, auto) { return xobj_fn; };
+
+ int f_ix_c0(Constrain auto) const { return iobj_fn; };
+ int f_ix_c0(this S const&, auto) { return xobj_fn; };
+ // See note
+ int f_ix_c1(Constrain auto) const { return iobj_fn; };
+ int f_ix_c1(this S const&&, auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0(this S&, Constrain auto) { return xobj_fn; };
+ int f_xi_m0(auto) { return iobj_fn; };
+ // See note
+ int f_xi_m1(this S&&, Constrain auto) { return xobj_fn; };
+ int f_xi_m1(auto) { return iobj_fn; };
+
+ int f_xi_c0(this S const&, Constrain auto) { return xobj_fn; };
+ int f_xi_c0(auto) const { return iobj_fn; };
+ // See note
+ int f_xi_c1(this S const&&, Constrain auto) { return xobj_fn; };
+ int f_xi_c1(auto) const { return iobj_fn; };
+
+ // with ref qualifier
+
+ // ix
+ int f_ix_m0_refqual(Constrain auto) & { return iobj_fn; };
+ int f_ix_m0_refqual(this S&, auto) { return xobj_fn; };
+
+ int f_ix_m1_refqual(Constrain auto) && { return iobj_fn; };
+ int f_ix_m1_refqual(this S&&, auto) { return xobj_fn; };
+
+ int f_ix_c0_refqual(Constrain auto) const& { return iobj_fn; };
+ int f_ix_c0_refqual(this S const&, auto) { return xobj_fn; };
+
+ int f_ix_c1_refqual(Constrain auto) const&& { return iobj_fn; };
+ int f_ix_c1_refqual(this S const&&, auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_refqual(this S&, Constrain auto) { return xobj_fn; };
+ int f_xi_m0_refqual(auto) & { return iobj_fn; };
+
+ int f_xi_m1_refqual(this S&&, Constrain auto) { return xobj_fn; };
+ int f_xi_m1_refqual(auto) && { return iobj_fn; };
+
+ int f_xi_c0_refqual(this S const&, Constrain auto) { return xobj_fn; };
+ int f_xi_c0_refqual(auto) const& { return iobj_fn; };
+
+ int f_xi_c1_refqual(this S const&&, Constrain auto) { return xobj_fn; };
+ int f_xi_c1_refqual(auto) const&& { return iobj_fn; };
+
+ // _r without ref qualifier
+
+ // ix
+ int f_ix_m0_r(auto) { return iobj_fn; };
+ int f_ix_m0_r(this S&, Constrain auto) { return xobj_fn; };
+ // See note
+ int f_ix_m1_r(auto) { return iobj_fn; };
+ int f_ix_m1_r(this S&&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_c0_r(auto) const { return iobj_fn; };
+ int f_ix_c0_r(this S const&, Constrain auto) { return xobj_fn; };
+ // See note
+ int f_ix_c1_r(auto) const { return iobj_fn; };
+ int f_ix_c1_r(this S const&&, Constrain auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_r(this S&, auto) { return xobj_fn; };
+ int f_xi_m0_r(Constrain auto) { return iobj_fn; };
+ // See note
+ int f_xi_m1_r(this S&&, auto) { return xobj_fn; };
+ int f_xi_m1_r(Constrain auto) { return iobj_fn; };
+
+ int f_xi_c0_r(this S const&, auto) { return xobj_fn; };
+ int f_xi_c0_r(Constrain auto) const { return iobj_fn; };
+ // See note
+ int f_xi_c1_r(this S const&&, auto) { return xobj_fn; };
+ int f_xi_c1_r(Constrain auto) const { return iobj_fn; };
+
+ // _r with ref qualifier
+ // ix
+ int f_ix_m0_refqual_r(auto) & { return iobj_fn; };
+ int f_ix_m0_refqual_r(this S&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_m1_refqual_r(auto) && { return iobj_fn; };
+ int f_ix_m1_refqual_r(this S&&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_c0_refqual_r(auto) const& { return iobj_fn; };
+ int f_ix_c0_refqual_r(this S const&, Constrain auto) { return xobj_fn; };
+
+ int f_ix_c1_refqual_r(auto) const&& { return iobj_fn; };
+ int f_ix_c1_refqual_r(this S const&&, Constrain auto) { return xobj_fn; };
+
+ // xi
+ int f_xi_m0_refqual_r(this S&, auto) { return xobj_fn; };
+ int f_xi_m0_refqual_r(Constrain auto) & { return iobj_fn; };
+
+ int f_xi_m1_refqual_r(this S&&, auto) { return xobj_fn; };
+ int f_xi_m1_refqual_r(Constrain auto) && { return iobj_fn; };
+
+ int f_xi_c0_refqual_r(this S const&, auto) { return xobj_fn; };
+ int f_xi_c0_refqual_r(Constrain auto) const& { return iobj_fn; };
+
+ int f_xi_c1_refqual_r(this S const&&, auto) { return xobj_fn; };
+ int f_xi_c1_refqual_r(Constrain auto) const&& { return iobj_fn; };
+};
+
+
+int main()
+{
+ // The commented out cases are ambiguous, which is most likely the correct
+ // behavior. It is something that I want to propose to change, and I want
+ // to leave them in as they are a little weird.
+ //
+ // Furthermore, as the comment at the top of this file indicates, I am not
+ // clear on the correct behavior of the static/xobj cases in general.
+
+ S s{};
+ if (s.f_xs_v (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xs_ref (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xs_cref (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_rref (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_crref (0) != xobj_fn)
+ __builtin_abort ();
+ // if (s.f_xs_dv (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dcref (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dfwdref (0) != xobj_fn)
+ // __builtin_abort ();
+
+ if (s.f_xs_v_r (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_xs_ref_r (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_xs_cref_r (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_rref_r (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xs_crref_r (0) != static_fn)
+ __builtin_abort ();
+ // if (s.f_xs_dv_r (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dcref_r (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_xs_dfwdref_r (0) != static_fn)
+ // __builtin_abort ();
+
+ if (s.f_sx_v (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_sx_ref (0) != static_fn)
+ __builtin_abort ();
+ if (s.f_sx_cref (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_rref (0) != static_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_crref (0) != static_fn)
+ __builtin_abort ();
+ // if (s.f_sx_dv (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dcref (0) != static_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dfwdref (0) != static_fn)
+ // __builtin_abort ();
+
+ if (s.f_sx_v_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_sx_ref_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_sx_cref_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_rref_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_sx_crref_r (0) != xobj_fn)
+ __builtin_abort ();
+ // if (s.f_sx_dv_r (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dcref_r (0) != xobj_fn)
+ // __builtin_abort ();
+ // if (s.f_sx_dfwdref_r (0) != xobj_fn)
+ // __builtin_abort ();
+
+ // iobj/xobj
+
+ // The commented out cases are tested below as their correct behavior is
+ // unintuitive, see the note below for details.
+
+ if (s.f_ix_m0 (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_ix_m1
+ if (s.f_ix_c0 (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_ix_c1
+ if (s.f_xi_m0 (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_xi_m1
+ if (s.f_xi_c0 (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_xi_c1
+ if (s.f_ix_m0_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_m1_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c0_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_refqual (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m0_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c0_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_refqual (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_m0_r (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_ix_m1_r
+ if (s.f_ix_c0_r (0) != xobj_fn)
+ __builtin_abort ();
+ // s.f_ix_c1_r
+ if (s.f_xi_m0_r (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_xi_m1_r
+ if (s.f_xi_c0_r (0) != iobj_fn)
+ __builtin_abort ();
+ // s.f_xi_c1_r
+ if (s.f_ix_m0_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_m1_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c0_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_refqual_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m0_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c0_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_refqual_r (0) != iobj_fn)
+ __builtin_abort ();
+
+
+/* Note:
+ These cases are weird, the object argument correspond, but are not the same
+ type ([basic.scope.scope-3.1]), so we get this funny edge case where the
+ constraint stops them from being considered redeclarations, but isn't taken
+ into account for the lvalue case. You can't bind an lvalue to an rvalue
+ reference so the iobj member function is always taken regardless of which
+ overload is constrained.
+
+ [over.match.funcs.general-4]
+ For implicit object member functions, the type of the implicit object
+ parameter is
+ (4.1) “lvalue reference to cv X” for functions declared without a
+ ref-qualifier or with the & ref-qualifier
+
+ You would think that calling these functions with an rvalue would be the
+ same then, always taking the xobj member function. However, for backwards
+ compatibility reasons, an unqualified member function can be called on an
+ object that is an rvalue.
+
+ [over.match.funcs.general-5]
+ For implicit object member functions declared without a ref-qualifier, even
+ if the implicit object parameter is not const-qualified, an rvalue can be
+ bound to the parameter as long as in all other respects the argument can be
+ converted to the type of the implicit object parameter.
+
+ And finally, since the object parameters correspond ([basic.scope.scope-3.1])
+ the constraints are taken into account.
+
+ So in conclusion, calling these functions with an lvalue always resolves to
+ the iobj member function, and calling them with rvalues take the constraints
+ into account.
+
+ As wacky as this is, this is the correct behavior. */
+
+ // Always takes the iobj member function, can't bind an lvalue to an rvalue
+ // reference.
+ if (s.f_ix_m1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c1 (0) != iobj_fn)
+ __builtin_abort ();
+
+ if (s.f_ix_m1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_ix_c1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_m1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (s.f_xi_c1_r (0) != iobj_fn)
+ __builtin_abort ();
+
+ // Constraints are taken into account here, see note for more information.
+ if (static_cast<S&&>(s).f_ix_m1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1 (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1 (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1 (0) != xobj_fn)
+ __builtin_abort ();
+
+ if (static_cast<S&&>(s).f_ix_m1_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_ix_c1_r (0) != xobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_m1_r (0) != iobj_fn)
+ __builtin_abort ();
+ if (static_cast<S&&>(s).f_xi_c1_r (0) != iobj_fn)
+ __builtin_abort ();
+}
+
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..5043e91bb28
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
@@ -0,0 +1,7 @@
+// 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..fb2a6a0e41b
--- /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 } }
+
+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..182e294c883
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
@@ -0,0 +1,9 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// don't pass in -pedantic-errors
+// { dg-options "" }
+
+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..49b7ea0df44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
@@ -0,0 +1,8 @@
+// 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..411b70c3d9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
@@ -0,0 +1,8 @@
+// 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..dfac1188fba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
@@ -0,0 +1,139 @@
+// 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<typename Self> void d_templ_0(this Self&&) const; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ template<typename Self> void d_templ_1(this Self&&) volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ template<typename Self> void d_templ_2(this Self&&) const volatile; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+ template<typename Self> void d_templ_3(this Self&&) &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ template<typename Self> void d_templ_4(this Self&&) &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+ template<typename Self> void d_templ_5(this Self&&) const &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_6(this Self&&) const &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_7(this Self&&) volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_8(this Self&&) volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> void d_templ_9(this Self&&) const volatile &; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+ template<typename Self> 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..771200b839e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
@@ -0,0 +1,26 @@
+// 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 pointer to function 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 pointer to member function 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 non-member function cannot have an explicit object parameter" }
+void f(this int) {} // { dg-error "a non-member function cannot have an explicit object parameter" }
+
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..ec091d6ca67
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
@@ -0,0 +1,20 @@
+// 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..1744b3f2299
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
@@ -0,0 +1,16 @@
+// 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..f3b01d3c8ae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
@@ -0,0 +1,23 @@
+// 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<void>(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..ea53609a1e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C
@@ -0,0 +1,17 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis when taking address of an unqualified xobj member function
+
+struct S {
+ void f(this S&) {}
+
+ void g(this S&) {}
+ void g(this S&, int) {}
+
+ void test() {
+ void (*fp)(S&) = &f; // { dg-error "taking the address of an explicit object member function must be qualified" }
+ void (*gp)(S&) = &g; // { dg-error "taking the address of an explicit object member function must be qualified" }
+ }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp1.C
new file mode 100644
index 00000000000..e7b6785c94e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp1.C
@@ -0,0 +1,73 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// various valid cases of lambdas with dependent xobj parameter
+
+// unfinished, I want to flesh this out more but I don't want to send the patch
+// without tests that demonstrate the fixed crash and behavior
+// basically I want to add more wacky edge cases
+
+template<typename... Ts>
+struct overload : Ts... {
+ using Ts::operator()...;
+};
+
+template<template<typename...> typename Overload>
+void templ()
+{
+ int i = 5;
+ auto f0 = Overload{[=]<typename... Ts>(this Overload<Ts...>&){ return i; }};
+ auto f1 = Overload{[&]<typename... Ts>(this Overload<Ts...>&){ return i; }};
+ auto f2 = Overload{[i]<typename... Ts>(this Overload<Ts...>&){ return i; }};
+ int f_return0 = f0();
+ int f_return1 = f1();
+ int f_return2 = f2();
+
+ auto g0 = Overload{[=]<typename... Ts>(this Overload<Ts...>&, auto){ return i; }};
+ auto g1 = Overload{[&]<typename... Ts>(this Overload<Ts...>&, auto){ return i; }};
+ auto g2 = Overload{[i]<typename... Ts>(this Overload<Ts...>&, auto){ return i; }};
+ int g_return0 = g0(0);
+ int g_return1 = g1(0);
+ int g_return2 = g2(0);
+}
+
+template<typename T>
+struct convertible_to_type {
+ constexpr operator T() { return {}; }
+};
+
+template<typename T, typename C>
+struct helper : T, C {
+ using T::operator();
+};
+
+template<typename StructT>
+struct S {
+ static constexpr auto member0 = [](this StructT const&){};
+ static constexpr auto member1 = helper{[](this StructT self){ return self; }, convertible_to_type<StructT>{}};
+
+ template<typename FuncT>
+ void member_f() {
+ auto f = [](){};
+ }
+};
+
+template<typename T>
+void type()
+{
+
+}
+
+
+void instantiate()
+{
+ templ<overload>();
+ type<int>();
+}
+
+int main()
+{
+ auto member1_copy = S<int>::member1;
+ return member1_copy();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp2.C
new file mode 100644
index 00000000000..57ea1140d18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda-temp2.C
@@ -0,0 +1,38 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// various invalid cases of lambdas with dependent xobj parameter
+
+// unfinished, I want to flesh this out more but I don't want to send the patch
+// without tests that demonstrate the fixed crash and behavior
+// basically I want to add more wacky edge cases
+
+template<typename T>
+void type()
+{
+ int i;
+ auto f0 = [=](this T& self){ return i; }; // { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+ auto f1 = [&](this T& self){ return i; }; // { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+ auto f2 = [i](this T& self){ return i; }; // { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+
+ auto g0 = [=](this T& self, auto){ return i; }; // { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+ auto g1 = [&](this T& self, auto){ return i; }; // { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+ auto g2 = [i](this T& self, auto){ return i; }; // { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+}
+
+template<typename StructT>
+struct S {
+ static constexpr auto member0 = [n = 5](this StructT const&){}; // { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+
+ template<typename FuncT>
+ void member_f() {
+ auto f = [](){};
+ }
+};
+
+void instantiate()
+{
+ type<int>();
+ auto s = S<int>{};
+}
+
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..86e0471eb7f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C
@@ -0,0 +1,25 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// lambda declaration with xobj parameter
+
+struct S{};
+
+void test()
+{
+ (void)[](this auto&& self){};
+ (void)[](this auto& self){};
+ (void)[](this auto const& self){};
+ (void)[](this auto self){};
+
+ (void)[](this S&& self){};
+ (void)[](this S& self){};
+ (void)[](this S const& self){};
+ (void)[](this S self){};
+
+ (void)[x = 0](this auto&& self){};
+ (void)[x = 0](this auto& self){};
+ (void)[x = 0](this auto const& self){};
+ (void)[x = 0](this auto self){};
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C
new file mode 100644
index 00000000000..827197a6667
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda2.C
@@ -0,0 +1,23 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// recursive lambdas
+
+inline constexpr int correct_result = 5 + 4 + 3 + 2 + 1;
+
+int main()
+{
+ auto cl0 = [](this auto&& self, int n) -> int { return n ? self(n - 1) + n : 0; };
+ auto cl1 = [](this auto const& self, int n) -> int { return n ? self(n - 1) + n : 0; };
+ auto cl2 = [](this auto self, int n) -> int { return n ? self(n - 1) + n : 0; };
+ auto cl3 = [](this auto&& self, int n) { if (!n) return 0; else return self(n - 1) + n; };
+ auto cl4 = [](this auto const& self, int n){ if (!n) return 0; else return self(n - 1) + n; };
+ auto cl5 = [](this auto self, int n) { if (!n) return 0; else return self(n - 1) + n; };
+ if (cl0(5) != correct_result) __builtin_abort ();
+ if (cl1(5) != correct_result) __builtin_abort ();
+ if (cl2(5) != correct_result) __builtin_abort ();
+ if (cl3(5) != correct_result) __builtin_abort ();
+ if (cl4(5) != correct_result) __builtin_abort ();
+ if (cl5(5) != correct_result) __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C
new file mode 100644
index 00000000000..9d222b0e547
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda3.C
@@ -0,0 +1,64 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// an adaptation of one of the examples in P0847R7
+
+struct Leaf { };
+struct Node;
+
+struct Tree {
+ enum class stored {leaf, node};
+ stored _discriminator;
+ union {
+ Leaf _leaf;
+ Node* _node;
+ };
+ Tree(Leaf) : _discriminator(stored::leaf), _leaf() {}
+ Tree(Node& node) : _discriminator(stored::node), _node(&node) {}
+};
+
+struct Node {
+ Tree left;
+ Tree right;
+};
+
+template<typename Visitor>
+auto visit_tree(Visitor&& visitor, Tree const& tree)
+{
+ switch (tree._discriminator)
+ {
+ case Tree::stored::leaf:
+ return visitor (tree._leaf);
+ case Tree::stored::node:
+ return visitor (tree._node);
+ default:
+ __builtin_abort ();
+ }
+}
+
+template<typename... Ts>
+struct overload : Ts... { using Ts::operator()...; };
+
+int main()
+{
+ static constexpr int true_num_leaves = 8;
+ Node branch0{.left = Leaf{}, .right = Leaf{}};
+ Node branch1{.left = Leaf{}, .right = branch0};
+ Node branch2{.left = Leaf{}, .right = Leaf{}};
+ Node branch3{.left = branch1, .right = branch2};
+ Node branch4{.left = branch3, .right = Leaf{}};
+ Node branch5{.left = Leaf{}, .right = Leaf{}};
+ Node branch6{.left = branch4, .right = branch5};
+
+ Tree root (branch6);
+
+ int num_leaves = visit_tree (overload{
+ [](Leaf const&) { return 1; },
+ [](this auto const& self, Node* n) -> int {
+ return visit_tree (self, n->left) + visit_tree (self, n->right);
+ }},
+ root);
+ if (num_leaves != true_num_leaves)
+ __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX0.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX0.C
new file mode 100644
index 00000000000..c9776092978
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX0.C
@@ -0,0 +1,68 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// xobj lambda with invalid decl specs
+
+void test()
+{
+ auto f0 = [](this auto) mutable {}; // { dg-line line_f0 }
+ auto f1 = [](this auto&) mutable {}; // { dg-line line_f1 }
+ auto f2 = [](this auto const&) mutable {}; // { dg-line line_f2 }
+ auto f3 = [](this auto&&) mutable {}; // { dg-line line_f3 }
+
+ auto g0 = [](this auto) static {}; // { dg-line line_g0 }
+ auto g1 = [](this auto&) static {}; // { dg-line line_g1 }
+ auto g2 = [](this auto const&) static {}; // { dg-line line_g2 }
+ auto g3 = [](this auto&&) static {}; // { dg-line line_g3 }
+
+ auto fc0 = [n = 0](this auto) mutable {}; // { dg-line line_fc0 }
+ auto fc1 = [n = 0](this auto&) mutable {}; // { dg-line line_fc1 }
+ auto fc2 = [n = 0](this auto const&) mutable {}; // { dg-line line_fc2 }
+ auto fc3 = [n = 0](this auto&&) mutable {}; // { dg-line line_fc3 }
+
+ auto gc0 = [n = 0](this auto) static {}; // { dg-line line_gc0 }
+ auto gc1 = [n = 0](this auto&) static {}; // { dg-line line_gc1 }
+ auto gc2 = [n = 0](this auto const&) static {}; // { dg-line line_gc2 }
+ auto gc3 = [n = 0](this auto&&) static {}; // { dg-line line_gc3 }
+}
+
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f0 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f1 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f2 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_f3 }
+
+// { dg-note {the passed in closure object will not be mutated because it is taken by copy/move} {} { target *-*-* } line_f0 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_f1 }
+// { dg-note {declare the explicit object parameter as non-const reference instead} {} { target *-*-* } line_f2 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_f3 }
+
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g0 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g1 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g2 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_g3 }
+
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g0 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g1 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g2 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_g3 }
+
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc0 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc1 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc2 }
+// { dg-error {'mutable' lambda specifier with explicit object parameter} {} { target *-*-* } line_fc3 }
+
+// { dg-note {the passed in closure object will not be mutated because it is taken by copy/move} {} { target *-*-* } line_fc0 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_fc1 }
+// { dg-note {declare the explicit object parameter as non-const reference instead} {} { target *-*-* } line_fc2 }
+// { dg-note {explicit object parameter is already a mutable reference} {} { target *-*-* } line_fc3 }
+
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc0 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc1 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc2 }
+// { dg-error {'static' lambda specifier with explicit object parameter} {} { target *-*-* } line_gc3 }
+
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc0 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc1 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc2 }
+// { dg-note {explicit object parameter declared here} {} { target *-*-* } line_gc3 }
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX20.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX20.C
new file mode 100644
index 00000000000..a808019e28e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX20.C
@@ -0,0 +1,46 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// calling captureless lambda call operator with unrelated explicit object parameter
+// through function pointer
+
+int main()
+{
+ auto f0 = [](this auto self) { return self; };
+ auto fp0_value = static_cast<int(*)(int) >(&decltype(f0)::operator());
+ auto fp0_lref = static_cast<int(*)(int&) >(&decltype(f0)::operator());
+ auto fp0_rref = static_cast<int(*)(int&&) >(&decltype(f0)::operator());
+ auto fp0_constlref = static_cast<int(*)(int const&) >(&decltype(f0)::operator());
+ auto fp0_constrref = static_cast<int(*)(int const&&)>(&decltype(f0)::operator());
+
+ auto f1 = [](this auto&& self) { return self; };
+ auto fp1_lref = static_cast<int(*)(int&) >(&decltype(f1)::operator());
+ auto fp1_rref = static_cast<int(*)(int&&) >(&decltype(f1)::operator());
+ auto fp1_constlref = static_cast<int(*)(int const&) >(&decltype(f1)::operator());
+ auto fp1_constrref = static_cast<int(*)(int const&&)>(&decltype(f1)::operator());
+
+ // both are needed for lvalue/rvalue overloads
+ #define MAGIC 42
+ int magic = MAGIC;
+
+ if (fp0_value (magic) != magic)
+ __builtin_abort ();
+ if (fp0_lref (magic) != magic)
+ __builtin_abort ();
+ if (fp0_rref (MAGIC) != magic)
+ __builtin_abort ();
+ if (fp0_constlref (magic) != magic)
+ __builtin_abort ();
+ if (fp0_constrref (MAGIC) != magic)
+ __builtin_abort ();
+
+ if (fp1_lref (magic) != magic)
+ __builtin_abort ();
+ if (fp1_rref (MAGIC) != magic)
+ __builtin_abort ();
+ if (fp1_constlref (magic) != magic)
+ __builtin_abort ();
+ if (fp1_constrref (MAGIC) != magic)
+ __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX21.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX21.C
new file mode 100644
index 00000000000..715a2457945
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX21.C
@@ -0,0 +1,39 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// instantiating captureless lambda call operator with unrelated explicit object parameter
+
+void test0()
+{
+ auto f0 = [](this auto self) { return self; };
+ auto fp0_value = static_cast<int(*)(int) >(&decltype(f0)::operator());
+ auto fp0_lref = static_cast<int(*)(int&) >(&decltype(f0)::operator());
+ auto fp0_rref = static_cast<int(*)(int&&) >(&decltype(f0)::operator());
+ auto fp0_constlref = static_cast<int(*)(int const&) >(&decltype(f0)::operator());
+ auto fp0_constrref = static_cast<int(*)(int const&&)>(&decltype(f0)::operator());
+
+ auto f1 = [](this auto&& self) { return self; };
+ auto fp1_value = static_cast<int(*)(int) >(&decltype(f1)::operator()); // { dg-error {invalid 'static_cast' from type} }
+ auto fp1_lref = static_cast<int(*)(int&) >(&decltype(f1)::operator());
+ auto fp1_rref = static_cast<int(*)(int&&) >(&decltype(f1)::operator());
+ auto fp1_constlref = static_cast<int(*)(int const&) >(&decltype(f1)::operator());
+ auto fp1_constrref = static_cast<int(*)(int const&&)>(&decltype(f1)::operator());
+}
+
+void test1()
+{
+ auto f0 = [](this auto self) { return self; };
+ int (*fp0_value)(int) = &decltype(f0)::operator();
+ int (*fp0_lref)(int&) = &decltype(f0)::operator();
+ int (*fp0_rref)(int&&) = &decltype(f0)::operator();
+ int (*fp0_constlref)(int const&) = &decltype(f0)::operator();
+ int (*fp0_constrref)(int const&&) = &decltype(f0)::operator();
+
+ auto f1 = [](this auto&& self) { return self; };
+ int (*fp1_value)(int) = &decltype(f1)::operator(); // { dg-error {no matches converting function} }
+ int (*fp1_lref)(int&) = &decltype(f1)::operator();
+ int (*fp1_rref)(int&&) = &decltype(f1)::operator();
+ int (*fp1_constlref)(int const&) = &decltype(f1)::operator();
+ int (*fp1_constrref)(int const&&) = &decltype(f1)::operator();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX24.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX24.C
new file mode 100644
index 00000000000..e8d348a7e7d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX24.C
@@ -0,0 +1,98 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// SFINAE when the call operator for a lambda with captures is instantiated
+// with an unrelated type.
+// diagnose ambiguous overloads when the call operator for a captureless lambda is instantiated
+// with an unrelated type.
+
+/* [expr.prim.lambda.general-5]
+
+ Given a lambda with a lambda-capture, the type of the explicit object
+ parameter, if any, of the lambda's function call operator (possibly
+ instantiated from a function call operator template) shall be either:
+
+ --(5.1) the closure type,
+ --(5.2) a class type derived from the closure type, or
+ --(5.3) a reference to a possibly cv-qualified such type. */
+
+// The above wording is similar to [dcl.fct-15] which is handled by SFINAE,
+// thus we also handle the following cases the same way.
+
+// We need the 2 overloads to be ambiguous to observe substitution failure
+// for the lambda's call operator when instantiated with an unrelated type.
+// We accomplish this by introducing both overloads through using declarations.
+
+struct B0 {
+ void operator()(this auto) {}
+};
+
+template<typename T>
+struct S0 : T, B0 {
+ using T::operator();
+ using B0::operator();
+ operator int() const {return {};}
+};
+
+void test0()
+{
+ auto s0 = S0{[](this auto){}};
+ s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+
+ auto s1 = S0{[x = 42](this auto){}};
+ s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
+
+struct B1 {
+ void operator()(this auto&&) {}
+};
+template<typename T>
+struct S1 : T, B1 {
+ using T::operator();
+ using B1::operator();
+ operator int() const {return {};}
+};
+
+void test1()
+{
+ auto s0 = S1{[](this auto&&){}};
+ s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+
+ auto s1 = S1{[x = 42](this auto&&){}};
+ s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
+
+struct B2 {
+ // needs to be a template, we are explicitly passing a template argument,
+ // without the parameter here this would not be a candidate
+ template<typename U = void>
+ void operator()(this int) {}
+};
+
+template<typename T>
+struct S2 : T, B2 {
+ using T::operator();
+ using B2::operator();
+ operator int() const {return {};}
+};
+
+void test2()
+{
+ auto s0 = S2{[](this auto){}};
+ s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+
+ auto s1 = S2{[x = 42](this auto){}};
+ s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
+void test3()
+{
+ auto s0 = S2{[](this auto&&){}};
+ s0.operator()<int>(); // { dg-error {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+
+ auto s1 = S2{[x = 42](this auto&&){}};
+ s1.operator()<int>(); // { dg-bogus {call of overloaded 'operator\(\)\(\)' is ambiguous} }
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX25.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX25.C
new file mode 100644
index 00000000000..b25fa006f1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX25.C
@@ -0,0 +1,101 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// SFINAE when the call operator for a lambda with captures is instantiated
+// with an unrelated type.
+// diagnose ambiguous overloads when the call operator for a captureless lambda is instantiated
+// with an unrelated type.
+
+/* [expr.prim.lambda.general-5]
+
+ Given a lambda with a lambda-capture, the type of the explicit object
+ parameter, if any, of the lambda's function call operator (possibly
+ instantiated from a function call operator template) shall be either:
+
+ --(5.1) the closure type,
+ --(5.2) a class type derived from the closure type, or
+ --(5.3) a reference to a possibly cv-qualified such type. */
+
+// The above wording is similar to [dcl.fct-15] which is handled by SFINAE,
+// thus we also handle the following cases the same way.
+
+// We need the 2 overloads to be ambiguous to observe substitution failure
+// for the lambda's call operator when instantiated with an unrelated type.
+// We accomplish this by introducing both overloads through using declarations.
+
+struct B0 {
+ void operator()(this auto) {}
+};
+template<typename T>
+struct S0 : T, B0 {
+ using B0::operator();
+ using T::operator();
+};
+
+void test0()
+{
+ auto s0 = S0{[](this auto){}};
+ void (*p0)(int) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+ auto s1 = S0{[x = 42](this auto){}};
+ void (*p1)(int) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} }
+}
+
+struct B1 {
+ void operator()(this auto&&) {}
+};
+template<typename T>
+struct S1 : T, B1 {
+ using B1::operator();
+ using T::operator();
+};
+
+void test1()
+{
+ auto s0 = S1{[](this auto&&){}};
+ void (*p0)(int&) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+ auto s1 = S1{[x = 42](this auto&&){}};
+ void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} }
+}
+
+
+struct B2 {
+ // not a template, should be taken over the lambda's call operator
+ void operator()(this int&) {}
+};
+template<typename T>
+struct S2 : T, B2 {
+ using T::operator();
+ using B2::operator();
+};
+
+void test2()
+{
+ auto s0 = S2{[](this auto&&){}};
+ void (*p0)(int&) = &decltype(s0)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+ auto s1 = S2{[x = 42](this auto&&){}};
+ void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+}
+
+struct B3 {
+ // must be a template so it is not taken over the lambda's call operator
+ template<typename U = void>
+ void operator()(this int&) {}
+};
+template<typename T>
+struct S3 : T, B3 {
+ using B3::operator();
+ using T::operator();
+};
+
+void test3()
+{
+ auto s0 = S3{[](this auto&&){}};
+ void (*p0)(int&) = &decltype(s0)::operator(); // { dg-error {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} }
+
+ auto s1 = S3{[x = 42](this auto&&){}};
+ void (*p1)(int&) = &decltype(s1)::operator(); // { dg-bogus {converting overloaded function '[^\n\r]+' to type '[^\n\r]+' is ambiguous} {Substitution failure for a captureful lambda with an unrelated xobj parameter type failed!} }
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX4.C
new file mode 100644
index 00000000000..6ce42ebefa8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX4.C
@@ -0,0 +1,23 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// calls to call operator of a lambda with captures with an implicit object argument
+// that derives from the lambda closure object
+
+template<typename T>
+struct S : T {
+ using T::operator();
+};
+
+template<typename T>
+S(T) -> S<T>;
+
+int main()
+{
+ static constexpr int magic = 42;
+ int n = magic;
+ S s{[n](this auto&&){return n;}};
+ if (s () != magic)
+ __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX40.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX40.C
new file mode 100644
index 00000000000..1e10fb26675
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX40.C
@@ -0,0 +1,20 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// diagnose mutation of lambda capture when called with a deduced as const explicit object parameter
+
+void test()
+{
+ auto f0 = [n = 5](this auto){ n = 10; }; // { dg-bogus {assignment of read-only variable} }
+ auto f1 = [n = 5](this auto const){ n = 10; }; // { dg-error {assignment of read-only variable} }
+ auto f2 = [n = 5](this auto&){ n = 10; }; // { dg-error {assignment of read-only variable} }
+ auto f3 = [n = 5](this auto const&){ n = 10; }; // { dg-error {assignment of read-only variable} }
+ auto f4 = [n = 5](this auto&&){ n = 10; }; // { dg-error {assignment of read-only variable} }
+
+ static_cast<decltype(f0) const&>(f0)();
+ f1();
+ static_cast<decltype(f2) const&>(f2)();
+ f3();
+ static_cast<decltype(f4) const&>(f4)();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX5.C
new file mode 100644
index 00000000000..88d45d90db1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX5.C
@@ -0,0 +1,21 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// calls to (captureless) lambda with explicit object parameter of unrelated type
+// with an appropriate converting constructor
+
+inline constexpr int magic = 42;
+
+struct S {
+ int _v;
+ template<typename T>
+ S(T) : _v(magic) {}
+};
+
+int main()
+{
+ auto f = [](this S self){ return self._v; };
+ if (f () != magic)
+ __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX6.C
new file mode 100644
index 00000000000..92f025431c1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX6.C
@@ -0,0 +1,48 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// unrelated xobj parameter type in captureless lambdas and lambdas with captures
+
+// declared with unrelated type
+
+struct S0{};
+
+void test0()
+{
+ auto f0 = [](this S0){ return 5; }; // { dg-bogus "a lambda with captures may not have an explicit object parameter of an unrelated type" }
+ auto f1 = [x = 42](this S0){ return 5; }; // { dg-error "a lambda with captures may not have an explicit object parameter of an unrelated type" }
+}
+
+// instantiation by calling with explicit template arguments
+
+template<typename T>
+struct S1 : T {
+ using T::operator();
+ operator int() const {return {};}
+};
+
+void test1()
+{
+ auto s0 = S1{[](this auto&& self) { return self; }}; // { dg-bogus {a lambda with captures may not have an explicit object parameter of an unrelated type} }
+ s0.operator()<int>(); // { dg-bogus {no matching function for call to} }
+
+ auto s1 = S1{[x = 0](this auto&& self) { return self; }}; // { dg-line t1_s1 }
+ s1.operator()<int>(); // { dg-error {no matching function for call to} }
+}
+// { dg-note {candidate:} {} { target *-*-* } t1_s1 }
+// { dg-note {template argument deduction/substitution failed} {} { target *-*-* } t1_s1 }
+// { dg-error {a lambda with captures may not have an explicit object parameter of an unrelated type} {} { target *-*-* } t1_s1 }
+
+// instantiation from overload resolution when taking address of call operator
+
+void test2()
+{
+ auto f = [x = 42](this auto&&){ return x; }; // { dg-line t2_f }
+
+ int (*fp0)(decltype(f)&) = &decltype(f)::operator();
+ int (*fp1)(int&) = &decltype(f)::operator(); // { dg-error {no matches converting function} }
+}
+
+// { dg-error "a lambda with captures may not have an explicit object parameter of an unrelated type" {depends on PR112874} { xfail *-*-* } t2_f }
+// { dg-note "candidate is" "" { target *-*-* } t2_f }
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX60.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX60.C
new file mode 100644
index 00000000000..7537b25865a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX60.C
@@ -0,0 +1,279 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// decltype((x)) and decltype(x) in explicit object lambda
+
+template<typename T> inline constexpr bool is_const_v = false;
+template<typename T> inline constexpr bool is_const_v<T const> = true;
+
+template<typename T> inline constexpr bool is_lvalue_ref = false;
+template<typename T> inline constexpr bool is_lvalue_ref<T&> = true;
+
+void non_dep()
+{
+ int n = 0;
+ auto f0 = [=]<typename Self>(this Self&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f0();
+ static_cast<decltype(f0) const&>(f0)();
+
+ auto f1 = [&]<typename Self>(this Self&){
+ static_assert(__is_same (decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f1();
+ static_cast<decltype(f1) const&>(f1)();
+
+ auto f2 = [n]<typename Self>(this Self&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f2();
+ static_cast<decltype(f2) const&>(f2)();
+
+ auto f3 = [&n]<typename Self>(this Self&){
+ static_assert(__is_same(decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f3();
+ static_cast<decltype(f3) const&>(f3)();
+
+ auto f4 = []<typename Self>(this Self&){
+ static_assert(__is_same (decltype(n), int));
+ decltype((n)) a; // { dg-error {is not captured} }
+ };
+
+ auto f0_const = [=]<typename Self>(this Self const&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f0_const();
+
+ auto f1_const = [&]<typename Self>(this Self const&){
+ static_assert(__is_same (decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f1_const();
+
+ auto f2_const = [n]<typename Self>(this Self const&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f2_const();
+
+ auto f3_const = [&n]<typename Self>(this Self const&){
+ static_assert(__is_same(decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f3_const();
+
+ auto f4_const = []<typename Self>(this Self const&){
+ static_assert(__is_same (decltype(n), int));
+ decltype((n)) a; // { dg-error {is not captured} }
+ };
+}
+
+template<typename = void>
+void dep0()
+{
+ int n = 0;
+ auto f0 = [=]<typename Self>(this Self&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f0();
+ static_cast<decltype(f0) const&>(f0)();
+
+ auto f1 = [&]<typename Self>(this Self&){
+ static_assert(__is_same (decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f1();
+ static_cast<decltype(f1) const&>(f1)();
+
+ auto f2 = [n]<typename Self>(this Self&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f2();
+ static_cast<decltype(f2) const&>(f2)();
+
+ auto f3 = [&n]<typename Self>(this Self&){
+ static_assert(__is_same(decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f3();
+ static_cast<decltype(f3) const&>(f3)();
+
+ auto f4 = []<typename Self>(this Self&){
+ static_assert(__is_same (decltype(n), int));
+ decltype((n)) a; // { dg-error {is not captured} }
+ };
+
+ auto f0_const = [=]<typename Self>(this Self const&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f0_const();
+
+ auto f1_const = [&]<typename Self>(this Self const&){
+ static_assert(__is_same (decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f1_const();
+
+ auto f2_const = [n]<typename Self>(this Self const&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f2_const();
+
+ auto f3_const = [&n]<typename Self>(this Self const&){
+ static_assert(__is_same(decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f3_const();
+
+ auto f4_const = []<typename Self>(this Self const&){
+ static_assert(__is_same (decltype(n), int));
+ decltype((n)) a; // { dg-error {is not captured} }
+ };
+}
+
+// dep1 uses the template parameter
+
+template<typename T = int>
+void dep1()
+{
+ T n = 0;
+ auto f0 = [=]<typename Self>(this Self&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f0();
+ static_cast<decltype(f0) const&>(f0)();
+
+ auto f1 = [&]<typename Self>(this Self&){
+ static_assert(__is_same (decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f1();
+ static_cast<decltype(f1) const&>(f1)();
+
+ auto f2 = [n]<typename Self>(this Self&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<Self> == is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f2();
+ static_cast<decltype(f2) const&>(f2)();
+
+ auto f3 = [&n]<typename Self>(this Self&){
+ static_assert(__is_same(decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f3();
+ static_cast<decltype(f3) const&>(f3)();
+
+ auto f4 = []<typename Self>(this Self&){
+ static_assert(__is_same (decltype(n), int));
+ decltype((n)) a; // { dg-error {is not captured} }
+ };
+
+ auto f0_const = [=]<typename Self>(this Self const&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f0_const();
+
+ auto f1_const = [&]<typename Self>(this Self const&){
+ static_assert(__is_same (decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f1_const();
+
+ auto f2_const = [n]<typename Self>(this Self const&){
+ static_assert(is_lvalue_ref<decltype((n))>,
+ "decltype((n)) is not an lvalue ref");
+ static_assert(is_const_v<__remove_reference (decltype((n)))>, // { dg-bogus {static assertion failed: qualification of decltype\(\(n\)\) does not match qualification of Self} }
+ "qualification of decltype((n)) does not match qualification of Self");
+ static_assert(__is_same (__remove_cvref (decltype((n))), int),
+ "decltype((n)) is not an int");
+ static_assert(__is_same (decltype(n), int));
+ };
+ f2_const();
+
+ auto f3_const = [&n]<typename Self>(this Self const&){
+ static_assert(__is_same(decltype((n)), int&));
+ static_assert(__is_same (decltype(n), int));
+ };
+ f3_const();
+
+ auto f4_const = []<typename Self>(this Self const&){
+ static_assert(__is_same (decltype(n), int));
+ decltype((n)) a; // { dg-error {is not captured} }
+ };
+}
+
+void instantiate_dep()
+{
+ dep0();
+ dep1();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX7.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX7.C
new file mode 100644
index 00000000000..a068941413a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX7.C
@@ -0,0 +1,87 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// lambda capture mutability with explicit object parameter
+
+void capture_by_value()
+{
+ static constexpr int magic = 42;
+ auto f0 = [n = 0](this auto self){
+ n += magic;
+ return n;
+ };
+ auto f1 = [n = 0](this auto& self){
+ n += magic;
+ return n;
+ };
+ auto f2 = [n = 0](this auto&& self){
+ n += magic;
+ return n;
+ };
+
+ // passed by value, should still return a value equal to magic regardless
+ // of how many times it is called
+ if (f0 () != magic)
+ __builtin_abort ();
+ if (f0 () != magic)
+ __builtin_abort ();
+ // passed by reference, the returned value should increase by magic
+ // each time it is called
+ if (f1 () != magic)
+ __builtin_abort ();
+ if (f1 () != magic + magic)
+ __builtin_abort ();
+ if (f2 () != magic)
+ __builtin_abort ();
+ if (f2 () != magic + magic)
+ __builtin_abort ();
+}
+
+void capture_by_ref()
+{
+ static constexpr int magic = 42;
+ int n0 = 0;
+ auto f0 = [&n0](this auto self){
+ n0 += magic;
+ };
+ int n1 = 0;
+ auto f1 = [&n1](this auto& self){
+ n1 += magic;
+ };
+ int n2 = 0;
+ auto f2 = [&n2](this auto&& self){
+ n2 += magic;
+ };
+ int n3 = 0;
+ auto f3 = [&n3](this auto const& self){
+ n3 += magic;
+ };
+
+ // all calls should mutate their capture, the capture is by reference
+ if (f0 (); n0 != magic)
+ __builtin_abort ();
+ if (f0 (); n0 != magic + magic)
+ __builtin_abort ();
+
+ if (f1 (); n1 != magic)
+ __builtin_abort ();
+ if (f1 (); n1 != magic + magic)
+ __builtin_abort ();
+
+ if (f2 (); n2 != magic)
+ __builtin_abort ();
+ if (f2 (); n2 != magic + magic)
+ __builtin_abort ();
+
+ if (f3 (); n3 != magic)
+ __builtin_abort ();
+ if (f3 (); n3 != magic + magic)
+ __builtin_abort ();
+}
+
+int main()
+{
+ capture_by_value ();
+ capture_by_ref ();
+}
+
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..eb8607781bf
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C
@@ -0,0 +1,28 @@
+// 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<typename = void>
+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..bb43a0af913
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C
@@ -0,0 +1,27 @@
+// 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<typename = void>
+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..ecd6bdfd44c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C
@@ -0,0 +1,40 @@
+// 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<typename... Args>
+ void operator()(this S&, Args... args) {}
+};
+
+void non_dep()
+{
+ S s{};
+ s();
+ s(0);
+ s(0, 0);
+ s(0, 0, 0);
+}
+
+template<typename = void>
+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..3eb003062c0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C
@@ -0,0 +1,40 @@
+// 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<typename... Args>
+ void operator[](this S&, Args... args) {}
+};
+
+void non_dep()
+{
+ S s{};
+ s[];
+ s[0];
+ s[0, 0];
+ s[0, 0, 0];
+}
+
+template<typename = void>
+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..f38615e2b98
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C
@@ -0,0 +1,58 @@
+// 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<typename T = void>
+void do_calls()
+{
+ Value value{};
+ TEST_OPS(value)
+ TEST_OPS(static_cast<Value&&>(value))
+ TEST_OPS(static_cast<Value const&>(value))
+ TEST_OPS(static_cast<Value const&&>(value))
+
+ LRef l_ref{};
+ TEST_OPS(l_ref)
+ TEST_INVALID(static_cast<LRef&&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&&>(l_ref))
+
+ RRef r_ref{};
+ TEST_INVALID(r_ref)
+ TEST_OPS(static_cast<RRef&&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&&>(r_ref))
+
+ ConstLRef const_l_ref{};
+ TEST_OPS(const_l_ref)
+ TEST_OPS(static_cast<ConstLRef&&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&&>(const_l_ref))
+
+ ConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref)
+ TEST_OPS(static_cast<ConstRRef&&>(const_r_ref))
+ TEST_INVALID(static_cast<ConstRRef const&>(const_r_ref))
+ TEST_OPS(static_cast<ConstRRef const&&>(const_r_ref))
+
+ Deduced deduced{};
+ TEST_OPS(deduced)
+ TEST_OPS(static_cast<Deduced&&>(deduced))
+ TEST_OPS(static_cast<Deduced const&>(deduced))
+ TEST_OPS(static_cast<Deduced const&&>(deduced))
+
+ VALIDATE_RETURN_TYPES(deduced, Deduced&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced&&>(deduced), Deduced&&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&>(deduced), Deduced const&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&&>(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..634e878c93e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C
@@ -0,0 +1,57 @@
+// 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&&>(value))
+ TEST_OPS(static_cast<Value const&>(value))
+ TEST_OPS(static_cast<Value const&&>(value))
+
+ LRef l_ref{};
+ TEST_OPS(l_ref)
+ TEST_INVALID(static_cast<LRef&&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&>(l_ref))
+ TEST_INVALID(static_cast<LRef const&&>(l_ref))
+
+ RRef r_ref{};
+ TEST_INVALID(r_ref)
+ TEST_OPS(static_cast<RRef&&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&>(r_ref))
+ TEST_INVALID(static_cast<RRef const&&>(r_ref))
+
+ ConstLRef const_l_ref{};
+ TEST_OPS(const_l_ref)
+ TEST_OPS(static_cast<ConstLRef&&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&>(const_l_ref))
+ TEST_OPS(static_cast<ConstLRef const&&>(const_l_ref))
+
+ ConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref)
+ TEST_OPS(static_cast<ConstRRef&&>(const_r_ref))
+ TEST_INVALID(static_cast<ConstRRef const&>(const_r_ref))
+ TEST_OPS(static_cast<ConstRRef const&&>(const_r_ref))
+
+ Deduced deduced{};
+ TEST_OPS(deduced)
+ TEST_OPS(static_cast<Deduced&&>(deduced))
+ TEST_OPS(static_cast<Deduced const&>(deduced))
+ TEST_OPS(static_cast<Deduced const&&>(deduced))
+
+ VALIDATE_RETURN_TYPES(deduced, Deduced&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced&&>(deduced), Deduced&&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&>(deduced), Deduced const&)
+ VALIDATE_RETURN_TYPES(static_cast<Deduced const&&>(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..b897a4bac71
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h
@@ -0,0 +1,210 @@
+// tests for ops that must be member functions are seperate
+
+// the name of the class refers to the type of it's member functions xobj parameter
+
+#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, int) { return self; } \
+ TYPE operator*(this TYPE self) { return self; } \
+ TYPE operator->*(this TYPE self, int) { 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<typename Self> Self&& operator+=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator-=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator*=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator/=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator%=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator|=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator^=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<<=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>>=(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator++(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator--(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator++(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator--(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator+(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator-(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator+(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator-(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator*(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator/(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator%(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator|(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator^(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<<(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>>(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator!(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&&(this Self&& self, int const&) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator||(this Self&& self, int const&) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator==(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator!=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator>=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator<=>(this Self&& self, int) { return static_cast<Self&&>(self); }
+
+ template<typename Self> Self&& operator*(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator->*(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator&(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self> Self&& operator,(this Self&& self, int) { return static_cast<Self&&>(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) <=> 0; \
+ \
+ *(OPERAND); \
+ (OPERAND) ->* 0; \
+ &(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) <=> 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)))); \
+ 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..d08e938c98d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C
@@ -0,0 +1,171 @@
+// 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" }
+
+// 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<RRef&&>(self); }
+ RRef&& operator()(this RRef&& self) { return static_cast<RRef&&>(self); }
+ RRef&& operator[](this RRef&& self) { return static_cast<RRef&&>(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<ConstRRef const&&>(self); }
+ ConstRRef const&& operator()(this ConstRRef const&& self) { return static_cast<ConstRRef const&&>(self); }
+ ConstRRef const&& operator[](this ConstRRef const&& self) { return static_cast<ConstRRef const&&>(self); }
+ ConstRRef const* operator->(this ConstRRef const&& self) { return &self; }
+};
+
+// needed to implement deduced operator->
+template<typename T> struct remove_ref { using type = T; };
+template<typename T> struct remove_ref<T&> { using type = T; };
+template<typename T> struct remove_ref<T&&> { using type = T; };
+template<typename T> using remove_ref_t = typename remove_ref<T>::type;
+
+struct Deduced {
+ int _v;
+ template<typename Self>
+ Self&& operator=(this Self&& self, int) { return static_cast<Self&&>(self); }
+ template<typename Self>
+ Self&& operator()(this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self>
+ Self&& operator[](this Self&& self) { return static_cast<Self&&>(self); }
+ template<typename Self>
+ remove_ref_t<Self>* 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<typename T, typename U>
+concept same_as = __is_same(T, U);
+
+#define TEST_VALID_WITH_RETURN_TYPES(OPERAND, CORRECT_TYPE) \
+ static_assert(requires{ {(OPERAND) = 0} -> same_as<CORRECT_TYPE>; },"Unexpected failure with return type check calling operator = with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \
+ static_assert(requires{ {(OPERAND)()} -> same_as<CORRECT_TYPE>; }, "Unexpected failure with return type check calling operator () with " #OPERAND " -> expected return type: " #CORRECT_TYPE); \
+ static_assert(requires{ {(OPERAND)[]} -> same_as<CORRECT_TYPE>; }, "Unexpected failure with return type check calling operator [] with " #OPERAND " -> expected return type: " #CORRECT_TYPE);
+
+
+template<typename DepValue = Value>
+void test_value()
+{
+ DepValue value{};
+ TEST_VALID(value)
+ TEST_VALID(static_cast<DepValue&&>(value))
+ TEST_VALID(static_cast<DepValue const&>(value))
+ TEST_VALID(static_cast<DepValue const&&>(value))
+}
+
+template<typename DepLRef = LRef>
+void test_l_ref()
+{
+ DepLRef l_ref{};
+ TEST_VALID(l_ref)
+ TEST_INVALID(static_cast<DepLRef&&>(l_ref))
+ TEST_INVALID(static_cast<DepLRef const&>(l_ref))
+ TEST_INVALID(static_cast<DepLRef const&&>(l_ref))
+}
+
+template<typename DepRRef = RRef>
+void test_r_ref()
+{
+ DepRRef r_ref{};
+ TEST_INVALID(r_ref)
+ TEST_VALID(static_cast<DepRRef&&>(r_ref))
+ TEST_INVALID(static_cast<DepRRef const&>(r_ref))
+ TEST_INVALID(static_cast<DepRRef const&&>(r_ref))
+}
+
+template<typename DepConstLRef = ConstLRef>
+void test_const_l_ref()
+{
+ DepConstLRef const_l_ref{};
+ TEST_VALID(const_l_ref)
+ TEST_VALID(static_cast<DepConstLRef&&>(const_l_ref))
+ TEST_VALID(static_cast<DepConstLRef const&>(const_l_ref))
+ TEST_VALID(static_cast<DepConstLRef const&&>(const_l_ref))
+}
+
+template<typename DepConstRRef = ConstRRef>
+void test_const_r_ref()
+{
+ DepConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref)
+ TEST_VALID(static_cast<DepConstRRef&&>(const_r_ref))
+ TEST_INVALID(static_cast<DepConstRRef const&>(const_r_ref))
+ TEST_VALID(static_cast<DepConstRRef const&&>(const_r_ref))
+}
+
+template<typename DepDeduced = Deduced>
+void test_deduced()
+{
+ DepDeduced deduced{};
+
+ TEST_VALID(deduced)
+ TEST_VALID(static_cast<DepDeduced&&>(deduced))
+ TEST_VALID(static_cast<DepDeduced const&>(deduced))
+ TEST_VALID(static_cast<DepDeduced const&&>(deduced))
+
+ TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced&&>(deduced), DepDeduced&&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&>(deduced), DepDeduced const&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&&>(deduced), DepDeduced const&&)
+ // arrow operator needs to be seperate to check the type of _v
+ static_assert(requires{ {(deduced->_v)} -> same_as<int&>; }, "Unexpected failure with return type check calling operator -> with deduced->_v");
+ static_assert(requires{ {(static_cast<DepDeduced&&>(deduced)->_v)} -> same_as<int&>; }, "Unexpected failure with return type check calling operator -> with static_cast<DepDeduced&&>(deduced)->_v");
+ static_assert(requires{ {(static_cast<DepDeduced const&>(deduced)->_v)} -> same_as<int const&>; }, "Unexpected failure with return type check calling operator -> with static_cast<DepDeduced const&>(deduced)->_v");
+ static_assert(requires{ {(static_cast<DepDeduced const&&>(deduced)->_v)} -> same_as<int const&>; }, "Unexpected failure with return type check calling operator -> with static_cast<DepDeduced const&&>(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..865b1f57d4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C
@@ -0,0 +1,237 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// well-formed and ill-formed uses of non-member capable operators in a requires expression
+
+#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.
+
+template<typename T, typename U>
+concept same_as = __is_same(T, U);
+
+#define TEST_INVALID(OPERAND, CORRECT_TYPE) \
+ 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) <=> 0; }, "Unexpected success calling operator <=> with " #OPERAND); \
+ \
+ static_assert(!requires{ *(OPERAND); }, "Unexpected success calling operator unary* with " #OPERAND); \
+ static_assert(!requires{ (OPERAND) ->* 0; }, "Unexpected success calling operator ->* with " #OPERAND); \
+ /* We need to check the return type to confirm the built-in operator was not selected. */ \
+ static_assert(!requires{ {&(OPERAND)} -> same_as<CORRECT_TYPE>; }, \
+ "Unexpected success calling operator unary& with " #OPERAND); \
+ static_assert(!requires{ {(OPERAND), 0} -> same_as<CORRECT_TYPE>; }, \
+ "Unexpected success calling operator , with " #OPERAND);
+
+#define TEST_VALID(OPERAND, CORRECT_TYPE) \
+ 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) <=> 0; }, "Unexpected failure calling operator <=> with " #OPERAND); \
+ \
+ static_assert(requires{ *(OPERAND); }, "Unexpected failure calling operator unary* with " #OPERAND); \
+ static_assert(requires{ (OPERAND) ->* 0; }, "Unexpected failure calling operator ->* with " #OPERAND); \
+ /* We need to check the return type to confirm we selected our overload, not the built-in operator. */ \
+ static_assert(requires{ {&(OPERAND)} -> same_as<CORRECT_TYPE>; }, \
+ "Unexpected failure calling operator unary& with " #OPERAND); \
+ static_assert(requires{ {(OPERAND), 0} -> same_as<CORRECT_TYPE>; }, \
+ "Unexpected failure calling operator , with " #OPERAND);
+
+// Return types need to be tested for the deduced case
+
+#define TEST_VALID_WITH_RETURN_TYPES(OPERAND, CORRECT_TYPE) \
+ static_assert(requires{ {(OPERAND) += 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) -= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) *= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) /= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) %= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) &= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) |= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) ^= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) <<= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) >>= 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {++(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {--(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND)++} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND)--} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {+(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {-(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) + 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) - 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) * 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) / 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) % 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) & 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) | 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) ^ 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) << 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) >> 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {!(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) && 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) || 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {(OPERAND) == 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) != 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) < 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) > 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) <= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) >= 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) <=> 0} -> same_as<CORRECT_TYPE>; }); \
+ \
+ static_assert(requires{ {*(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND) ->* 0} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {&(OPERAND)} -> same_as<CORRECT_TYPE>; }); \
+ static_assert(requires{ {(OPERAND), 0} -> same_as<CORRECT_TYPE>; });
+
+template<typename DepValue = Value>
+void test_value()
+{
+ DepValue value{};
+ TEST_VALID(value, DepValue)
+ TEST_VALID(static_cast<DepValue&&>(value), DepValue)
+ TEST_VALID(static_cast<DepValue const&>(value), DepValue)
+ TEST_VALID(static_cast<DepValue const&&>(value), DepValue)
+}
+
+template<typename DepLRef = LRef>
+void test_l_ref()
+{
+ DepLRef l_ref{};
+ TEST_VALID(l_ref, DepLRef&)
+ TEST_INVALID(static_cast<DepLRef&&>(l_ref), DepLRef&)
+ TEST_INVALID(static_cast<DepLRef const&>(l_ref), DepLRef&)
+ TEST_INVALID(static_cast<DepLRef const&&>(l_ref), DepLRef&)
+}
+
+template<typename DepRRef = RRef>
+void test_r_ref()
+{
+ DepRRef r_ref{};
+ TEST_INVALID(r_ref, DepRRef&&)
+ TEST_VALID(static_cast<DepRRef&&>(r_ref), DepRRef&&)
+ TEST_INVALID(static_cast<DepRRef const&>(r_ref), DepRRef&&)
+ TEST_INVALID(static_cast<DepRRef const&&>(r_ref), DepRRef&&)
+}
+
+template<typename DepConstLRef = ConstLRef>
+void test_const_l_ref()
+{
+ DepConstLRef const_l_ref{};
+ TEST_VALID(const_l_ref, DepConstLRef const&)
+ TEST_VALID(static_cast<DepConstLRef&&>(const_l_ref), DepConstLRef const&)
+ TEST_VALID(static_cast<DepConstLRef const&>(const_l_ref), DepConstLRef const&)
+ TEST_VALID(static_cast<DepConstLRef const&&>(const_l_ref), DepConstLRef const&)
+}
+
+template<typename DepConstRRef = ConstRRef>
+void test_const_r_ref()
+{
+ DepConstRRef const_r_ref{};
+ TEST_INVALID(const_r_ref, DepConstRRef const&&)
+ TEST_VALID(static_cast<DepConstRRef&&>(const_r_ref), DepConstRRef const&&)
+ TEST_INVALID(static_cast<DepConstRRef const&>(const_r_ref), DepConstRRef const&&)
+ TEST_VALID(static_cast<DepConstRRef const&&>(const_r_ref), DepConstRRef const&&)
+}
+
+template<typename DepDeduced = Deduced>
+void test_deduced()
+{
+ DepDeduced deduced{};
+
+ TEST_VALID_WITH_RETURN_TYPES(deduced, DepDeduced&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced&&>(deduced), DepDeduced&&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&>(deduced), DepDeduced const&)
+ TEST_VALID_WITH_RETURN_TYPES(static_cast<DepDeduced const&&>(deduced), DepDeduced const&&)
+}
+
+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-redecl-constraints.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl-constraints.C
new file mode 100644
index 00000000000..a2ad0e35073
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl-constraints.C
@@ -0,0 +1,115 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// redeclarations with equivalent constraints
+
+template<typename T>
+concept Constrain = true;
+
+
+struct S {
+// xobj/static
+ void f_xs_v(this S, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_v(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref(this S&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref(this S const&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref(this S&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_dv(this auto, Constrain auto) {}; // { dg-note {previous declaration} {Probably well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dv(Constrain auto) {}; // { dg-error {cannot be overloaded with} {Probably well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dcref(this auto const&, Constrain auto) {}; // { dg-note {previous declaration} {Probably well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dcref(Constrain auto) {}; // { dg-error {cannot be overloaded with} {Probably well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dfwdref(this auto&&, Constrain auto) {}; // { dg-note {previous declaration} {Probably well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dfwdref(Constrain auto) {}; // { dg-error {cannot be overloaded with} {Probably well formed? TBD} { xfail *-*-* } }
+
+// static/xobj
+ static void f_sx_v(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_v(this S, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_ref(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_cref(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_rref(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_sx_crref(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_dv(Constrain auto) {}; // { dg-note {previous declaration} {Probably well formed? TBD} { xfail *-*-* } }
+ void f_sx_dv(this auto, Constrain auto) {}; // { dg-error {cannot be overloaded with} {Probably well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dcref(Constrain auto) {}; // { dg-note {previous declaration} {Probably well formed? TBD} { xfail *-*-* } }
+ void f_sx_dcref(this auto const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} {Probably well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dfwdref(Constrain auto) {}; // { dg-note {previous declaration} {Probably well formed? TBD} { xfail *-*-* } }
+ void f_sx_dfwdref(this auto&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} {Probably well formed? TBD} { xfail *-*-* } }
+
+ // iobj/xobj
+ void f_ix_lref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_ix_lref(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_rref(Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_ix_rref(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_lref(Constrain auto) const {}; // { dg-note {previous declaration} }
+ void f_ix_const_lref(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_rref(Constrain auto) const {}; // { dg-note {previous declaration} }
+ void f_ix_const_rref(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ // xobj/iobj
+ void f_xi_lref(this S&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_lref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_rref(this S&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_rref(Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_lref(this S const&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_lref(Constrain auto) const {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_rref(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_rref(Constrain auto) const {}; // { dg-error {cannot be overloaded with} }
+
+ // with ref qualifier
+
+ // iobj/xobj
+ void f_ix_lref_refqual(Constrain auto) & {}; // { dg-note {previous declaration} }
+ void f_ix_lref_refqual(this S&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_rref_refqual(Constrain auto) && {}; // { dg-note {previous declaration} }
+ void f_ix_rref_refqual(this S&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_lref_refqual(Constrain auto) const& {}; // { dg-note {previous declaration} }
+ void f_ix_const_lref_refqual(this S const&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_ix_const_rref_refqual(Constrain auto) const&& {}; // { dg-note {previous declaration} }
+ void f_ix_const_rref_refqual(this S const&&, Constrain auto) {}; // { dg-error {cannot be overloaded with} }
+
+ // xobj/iobj
+ void f_xi_lref_refqual(this S&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_lref_refqual(Constrain auto) & {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_rref_refqual(this S&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_rref_refqual(Constrain auto) && {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_lref_refqual(this S const&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_lref_refqual(Constrain auto) const& {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xi_const_rref_refqual(this S const&&, Constrain auto) {}; // { dg-note {previous declaration} }
+ void f_xi_const_rref_refqual(Constrain auto) const&& {}; // { dg-error {cannot be overloaded with} }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C
new file mode 100644
index 00000000000..7fcfcc31c30
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C
@@ -0,0 +1,246 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// redeclarations of xobj/iobj member functions where the iobj member function
+// is not ref qualified
+
+// it does not make sense to check for the inverse in this test (7 iobj, 1 xobj)
+// because you are not allowed to overload iobj member functions without ref qualifiers
+// with iobj member functions that do (and vice versa)
+
+// iobj first
+
+struct S0 {
+ void f0(); // { dg-note "previous declaration" }
+ void f0(this S0 &&); // { dg-error "cannot be overloaded with" }
+ void f0(this S0 const&); // { dg-bogus "" }
+ void f0(this S0 const&&); // { dg-bogus "" }
+ void f0(this S0 volatile&); // { dg-bogus "" }
+ void f0(this S0 volatile&&); // { dg-bogus "" }
+ void f0(this S0 const volatile&); // { dg-bogus "" }
+ void f0(this S0 const volatile&&); // { dg-bogus "" }
+
+ void f1(); // { dg-note "previous declaration" }
+ void f1(this S0 &); // { dg-error "cannot be overloaded with" }
+ void f1(this S0 const&); // { dg-bogus "" }
+ void f1(this S0 const&&); // { dg-bogus "" }
+ void f1(this S0 volatile&); // { dg-bogus "" }
+ void f1(this S0 volatile&&); // { dg-bogus "" }
+ void f1(this S0 const volatile&); // { dg-bogus "" }
+ void f1(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fc0() const; // { dg-note "previous declaration" }
+ void fc0(this S0 &); // { dg-bogus "" }
+ void fc0(this S0 &&); // { dg-bogus "" }
+ void fc0(this S0 const&&); // { dg-error "cannot be overloaded with" }
+ void fc0(this S0 volatile&); // { dg-bogus "" }
+ void fc0(this S0 volatile&&); // { dg-bogus "" }
+ void fc0(this S0 const volatile&); // { dg-bogus "" }
+ void fc0(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fc1() const; // { dg-note "previous declaration" }
+ void fc1(this S0 &); // { dg-bogus "" }
+ void fc1(this S0 &&); // { dg-bogus "" }
+ void fc1(this S0 const&); // { dg-error "cannot be overloaded with" }
+ void fc1(this S0 volatile&); // { dg-bogus "" }
+ void fc1(this S0 volatile&&); // { dg-bogus "" }
+ void fc1(this S0 const volatile&); // { dg-bogus "" }
+ void fc1(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fv0() volatile; // { dg-note "previous declaration" }
+ void fv0(this S0 &); // { dg-bogus "" }
+ void fv0(this S0 &&); // { dg-bogus "" }
+ void fv0(this S0 const&); // { dg-bogus "" }
+ void fv0(this S0 const&&); // { dg-bogus "" }
+ void fv0(this S0 volatile&&); // { dg-error "cannot be overloaded with" }
+ void fv0(this S0 const volatile&); // { dg-bogus "" }
+ void fv0(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fv1() volatile; // { dg-note "previous declaration" }
+ void fv1(this S0 &); // { dg-bogus "" }
+ void fv1(this S0 &&); // { dg-bogus "" }
+ void fv1(this S0 const&); // { dg-bogus "" }
+ void fv1(this S0 const&&); // { dg-bogus "" }
+ void fv1(this S0 volatile&); // { dg-error "cannot be overloaded with" }
+ void fv1(this S0 const volatile&); // { dg-bogus "" }
+ void fv1(this S0 const volatile&&); // { dg-bogus "" }
+
+ void fcv0() const volatile; // { dg-note "previous declaration" }
+ void fcv0(this S0 &); // { dg-bogus "" }
+ void fcv0(this S0 &&); // { dg-bogus "" }
+ void fcv0(this S0 const&); // { dg-bogus "" }
+ void fcv0(this S0 const&&); // { dg-bogus "" }
+ void fcv0(this S0 volatile&); // { dg-bogus "" }
+ void fcv0(this S0 volatile&&); // { dg-bogus "" }
+ void fcv0(this S0 const volatile&&); // { dg-error "cannot be overloaded with" }
+
+ void fcv1() const volatile; // { dg-note "previous declaration" }
+ void fcv1(this S0 &); // { dg-bogus "" }
+ void fcv1(this S0 &&); // { dg-bogus "" }
+ void fcv1(this S0 const&); // { dg-bogus "" }
+ void fcv1(this S0 const&&); // { dg-bogus "" }
+ void fcv1(this S0 volatile&); // { dg-bogus "" }
+ void fcv1(this S0 volatile&&); // { dg-bogus "" }
+ void fcv1(this S0 const volatile&); // { dg-error "cannot be overloaded with" }
+};
+
+// iobj last
+
+struct S1 {
+ void f0(this S1 &&); // { dg-note "previous declaration" }
+ void f0(this S1 const&); // { dg-bogus "" }
+ void f0(this S1 const&&); // { dg-bogus "" }
+ void f0(this S1 volatile&); // { dg-bogus "" }
+ void f0(this S1 volatile&&); // { dg-bogus "" }
+ void f0(this S1 const volatile&); // { dg-bogus "" }
+ void f0(this S1 const volatile&&); // { dg-bogus "" }
+ void f0(); // { dg-error "cannot be overloaded with" }
+
+ void f1(this S1 &); // { dg-note "previous declaration" }
+ void f1(this S1 const&); // { dg-bogus "" }
+ void f1(this S1 const&&); // { dg-bogus "" }
+ void f1(this S1 volatile&); // { dg-bogus "" }
+ void f1(this S1 volatile&&); // { dg-bogus "" }
+ void f1(this S1 const volatile&); // { dg-bogus "" }
+ void f1(this S1 const volatile&&); // { dg-bogus "" }
+ void f1(); // { dg-error "cannot be overloaded with" }
+
+ void fc0(this S1 &); // { dg-bogus "" }
+ void fc0(this S1 &&); // { dg-bogus "" }
+ void fc0(this S1 const&&); // { dg-note "previous declaration" }
+ void fc0(this S1 volatile&); // { dg-bogus "" }
+ void fc0(this S1 volatile&&); // { dg-bogus "" }
+ void fc0(this S1 const volatile&); // { dg-bogus "" }
+ void fc0(this S1 const volatile&&); // { dg-bogus "" }
+ void fc0() const; // { dg-error "cannot be overloaded with" }
+
+ void fc1(this S1 &); // { dg-bogus "" }
+ void fc1(this S1 &&); // { dg-bogus "" }
+ void fc1(this S1 const&); // { dg-note "previous declaration" }
+ void fc1(this S1 volatile&); // { dg-bogus "" }
+ void fc1(this S1 volatile&&); // { dg-bogus "" }
+ void fc1(this S1 const volatile&); // { dg-bogus "" }
+ void fc1(this S1 const volatile&&); // { dg-bogus "" }
+ void fc1() const; // { dg-error "cannot be overloaded with" }
+
+ void fv0(this S1 &); // { dg-bogus "" }
+ void fv0(this S1 &&); // { dg-bogus "" }
+ void fv0(this S1 const&); // { dg-bogus "" }
+ void fv0(this S1 const&&); // { dg-bogus "" }
+ void fv0(this S1 volatile&&); // { dg-note "previous declaration" }
+ void fv0(this S1 const volatile&); // { dg-bogus "" }
+ void fv0(this S1 const volatile&&); // { dg-bogus "" }
+ void fv0() volatile; // { dg-error "cannot be overloaded with" }
+
+ void fv1(this S1 &); // { dg-bogus "" }
+ void fv1(this S1 &&); // { dg-bogus "" }
+ void fv1(this S1 const&); // { dg-bogus "" }
+ void fv1(this S1 const&&); // { dg-bogus "" }
+ void fv1(this S1 volatile&); // { dg-note "previous declaration" }
+ void fv1(this S1 const volatile&); // { dg-bogus "" }
+ void fv1(this S1 const volatile&&); // { dg-bogus "" }
+ void fv1() volatile; // { dg-error "cannot be overloaded with" }
+
+ void fcv0(this S1 &); // { dg-bogus "" }
+ void fcv0(this S1 &&); // { dg-bogus "" }
+ void fcv0(this S1 const&); // { dg-bogus "" }
+ void fcv0(this S1 const&&); // { dg-bogus "" }
+ void fcv0(this S1 volatile&); // { dg-bogus "" }
+ void fcv0(this S1 volatile&&); // { dg-bogus "" }
+ void fcv0(this S1 const volatile&&); // { dg-note "previous declaration" }
+ void fcv0() const volatile; // { dg-error "cannot be overloaded with" }
+
+ void fcv1(this S1 &); // { dg-bogus "" }
+ void fcv1(this S1 &&); // { dg-bogus "" }
+ void fcv1(this S1 const&); // { dg-bogus "" }
+ void fcv1(this S1 const&&); // { dg-bogus "" }
+ void fcv1(this S1 volatile&); // { dg-bogus "" }
+ void fcv1(this S1 volatile&&); // { dg-bogus "" }
+ void fcv1(this S1 const volatile&); // { dg-note "previous declaration" }
+ void fcv1() const volatile; // { dg-error "cannot be overloaded with" }
+};
+
+// in order (iobj replacing one of the following in each group)
+// lvalue ref to S
+// rvalue ref to S
+// lvalue c ref to S
+// rvalue c ref to S
+// lvalue v ref to S
+// rvalue v ref to S
+// lvalue cv ref to S
+// rvalue cv ref to S
+
+struct S2 {
+ void f0(); // { dg-note "previous declaration" }
+ void f0(this S2 &&); // { dg-error "cannot be overloaded with" }
+ void f0(this S2 const&); // { dg-bogus "" }
+ void f0(this S2 const&&); // { dg-bogus "" }
+ void f0(this S2 volatile&); // { dg-bogus "" }
+ void f0(this S2 volatile&&); // { dg-bogus "" }
+ void f0(this S2 const volatile&); // { dg-bogus "" }
+ void f0(this S2 const volatile&&); // { dg-bogus "" }
+
+ void f1(this S2 &); // { dg-note "previous declaration" }
+ void f1(); // { dg-error "cannot be overloaded with" }
+ void f1(this S2 const&); // { dg-bogus "" }
+ void f1(this S2 const&&); // { dg-bogus "" }
+ void f1(this S2 volatile&); // { dg-bogus "" }
+ void f1(this S2 volatile&&); // { dg-bogus "" }
+ void f1(this S2 const volatile&); // { dg-bogus "" }
+ void f1(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fc0(this S2 &); // { dg-bogus "" }
+ void fc0(this S2 &&); // { dg-bogus "" }
+ void fc0() const; // { dg-note "previous declaration" }
+ void fc0(this S2 const&&); // { dg-error "cannot be overloaded with" }
+ void fc0(this S2 volatile&); // { dg-bogus "" }
+ void fc0(this S2 volatile&&); // { dg-bogus "" }
+ void fc0(this S2 const volatile&); // { dg-bogus "" }
+ void fc0(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fc1(this S2 &); // { dg-bogus "" }
+ void fc1(this S2 &&); // { dg-bogus "" }
+ void fc1(this S2 const&); // { dg-note "previous declaration" }
+ void fc1() const; // { dg-error "cannot be overloaded with" }
+ void fc1(this S2 volatile&); // { dg-bogus "" }
+ void fc1(this S2 volatile&&); // { dg-bogus "" }
+ void fc1(this S2 const volatile&); // { dg-bogus "" }
+ void fc1(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fv0(this S2 &); // { dg-bogus "" }
+ void fv0(this S2 &&); // { dg-bogus "" }
+ void fv0(this S2 const&); // { dg-bogus "" }
+ void fv0(this S2 const&&); // { dg-bogus "" }
+ void fv0() volatile; // { dg-note "previous declaration" }
+ void fv0(this S2 volatile&&); // { dg-error "cannot be overloaded with" }
+ void fv0(this S2 const volatile&); // { dg-bogus "" }
+ void fv0(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fv1(this S2 &); // { dg-bogus "" }
+ void fv1(this S2 &&); // { dg-bogus "" }
+ void fv1(this S2 const&); // { dg-bogus "" }
+ void fv1(this S2 const&&); // { dg-bogus "" }
+ void fv1(this S2 volatile&); // { dg-note "previous declaration" }
+ void fv1() volatile; // { dg-error "cannot be overloaded with" }
+ void fv1(this S2 const volatile&); // { dg-bogus "" }
+ void fv1(this S2 const volatile&&); // { dg-bogus "" }
+
+ void fcv0(this S2 &); // { dg-bogus "" }
+ void fcv0(this S2 &&); // { dg-bogus "" }
+ void fcv0(this S2 const&); // { dg-bogus "" }
+ void fcv0(this S2 const&&); // { dg-bogus "" }
+ void fcv0(this S2 volatile&); // { dg-bogus "" }
+ void fcv0(this S2 volatile&&); // { dg-bogus "" }
+ void fcv0() const volatile; // { dg-note "previous declaration" }
+ void fcv0(this S2 const volatile&&); // { dg-error "cannot be overloaded with" }
+
+ void fcv1(this S2 &); // { dg-bogus "" }
+ void fcv1(this S2 &&); // { dg-bogus "" }
+ void fcv1(this S2 const&); // { dg-bogus "" }
+ void fcv1(this S2 const&&); // { dg-bogus "" }
+ void fcv1(this S2 volatile&); // { dg-bogus "" }
+ void fcv1(this S2 volatile&&); // { dg-bogus "" }
+ void fcv1(this S2 const volatile&); // { dg-note "previous declaration" }
+ void fcv1() const volatile; // { dg-error "cannot be overloaded with" }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C
new file mode 100644
index 00000000000..adb6ae5a380
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C
@@ -0,0 +1,161 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+// { dg-options "-pedantic-errors -Wno-volatile" }
+
+// rejecting redeclarations of by-value xobj member functions
+// as iobj member functions that are not ref qualified (and vice-versa)
+// also check that valid overloads are accepted without diagnostic
+
+// iobj | xobj | MSVC | clang | ISOC++
+// none | none | X | X | X
+// none | c | X | O | X
+// none | v | X | O | X
+// none | cv | X | O | X
+// c | none | O | O | O
+// c | c | O | X | O
+// c | v | O | O | O
+// c | cv | O | O | O
+// v | none | O | O | O
+// v | c | O | O | O
+// v | v | O | X | O
+// v | cv | O | O | O
+// cv | none | O | O | O
+// cv | c | O | O | O
+// cv | v | O | O | O
+// cv | cv | O | X | O
+
+/* Top-level cv qualifiers are supposed to be discarded from
+ the parameters of a function declaration.
+
+ [dcl.fct.5]
+ After producing the list of parameter types, any top-level
+ cv-qualifiers modifying a parameter type are deleted when forming
+ the function type.
+
+ According to the standard, the type of an implicit object parameter
+ is always a reference. This isn't reflected in GCC but we still need
+ to take this rule into account here.
+
+ [over.match.funcs.general.4]
+ For implicit object member functions, the type of the implicit
+ object parameter is
+ -- “lvalue reference to cv X” for functions declared
+ without a ref-qualifier or with the & ref-qualifier
+ -- “rvalue reference to cv X” for functions declared with
+ the && ref-qualifier
+
+ When comparing an iobj and xobj member function to see if they are
+ redeclarations we treat them differently depending on if the iobj
+ member function has a ref qualifier or not.
+ If the iobj member function does not have a ref qualifier, we need to
+ strip the top-level references before comparing them.
+
+ [basic.scope.scope.3]
+ Two non-static member functions have corresponding object
+ parameters if:
+ -- exactly one is an implicit object member function with no
+ ref-qualifier and the types of their object parameters
+ ([dcl.fct]), after removing top-level references, are the
+ same, or
+ -- their object parameters have the same type. */
+
+struct S {
+ void f0(); // { dg-note "previous declaration" }
+ void f0(this S); // { dg-error "cannot be overloaded with" }
+
+ void f1(); // { dg-note "previous declaration" }
+ void f1(this S const); // { dg-error "cannot be overloaded with" }
+
+ void f2(); // { dg-note "previous declaration" }
+ void f2(this S volatile); // { dg-error "cannot be overloaded with" }
+
+ void f3(); // { dg-note "previous declaration" }
+ void f3(this S const volatile); // { dg-error "cannot be overloaded with" }
+
+ void fc0() const; // { dg-bogus "" }
+ void fc0(this S); // { dg-bogus "" }
+
+ void fc1() const; // { dg-bogus "" }
+ void fc1(this S const); // { dg-bogus "" }
+
+ void fc2() const; // { dg-bogus "" }
+ void fc2(this S volatile); // { dg-bogus "" }
+
+ void fc3() const; // { dg-bogus "" }
+ void fc3(this S const volatile); // { dg-bogus "" }
+
+ void fv0() volatile; // { dg-bogus "" }
+ void fv0(this S); // { dg-bogus "" }
+
+ void fv1() volatile; // { dg-bogus "" }
+ void fv1(this S const); // { dg-bogus "" }
+
+ void fv2() volatile; // { dg-bogus "" }
+ void fv2(this S volatile); // { dg-bogus "" }
+
+ void fv3() volatile; // { dg-bogus "" }
+ void fv3(this S const volatile); // { dg-bogus "" }
+
+ void fcv0() const volatile; // { dg-bogus "" }
+ void fcv0(this S); // { dg-bogus "" }
+
+ void fcv1() const volatile; // { dg-bogus "" }
+ void fcv1(this S const); // { dg-bogus "" }
+
+ void fcv2() const volatile; // { dg-bogus "" }
+ void fcv2(this S volatile); // { dg-bogus "" }
+
+ void fcv3() const volatile; // { dg-bogus "" }
+ void fcv3(this S const volatile); // { dg-bogus "" }
+
+ // same as the above f cases except reversed
+
+ void g0(this S); // { dg-note "previous declaration" }
+ void g0(); // { dg-error "cannot be overloaded with" }
+
+ void g1(this S const); // { dg-note "previous declaration" }
+ void g1(); // { dg-error "cannot be overloaded with" }
+
+ void g2(this S volatile); // { dg-note "previous declaration" }
+ void g2(); // { dg-error "cannot be overloaded with" }
+
+ void g3(this S const volatile); // { dg-note "previous declaration" }
+ void g3(); // { dg-error "cannot be overloaded with" }
+
+ void gc0(this S); // { dg-bogus "" }
+ void gc0() const; // { dg-bogus "" }
+
+ void gc1(this S const); // { dg-bogus "" }
+ void gc1() const; // { dg-bogus "" }
+
+ void gc2(this S volatile); // { dg-bogus "" }
+ void gc2() const; // { dg-bogus "" }
+
+ void gc3(this S const volatile); // { dg-bogus "" }
+ void gc3() const; // { dg-bogus "" }
+
+ void gv0(this S); // { dg-bogus "" }
+ void gv0() volatile; // { dg-bogus "" }
+
+ void gv1(this S const); // { dg-bogus "" }
+ void gv1() volatile; // { dg-bogus "" }
+
+ void gv2(this S volatile); // { dg-bogus "" }
+ void gv2() volatile; // { dg-bogus "" }
+
+ void gv3(this S const volatile); // { dg-bogus "" }
+ void gv3() volatile; // { dg-bogus "" }
+
+ void gcv0(this S); // { dg-bogus "" }
+ void gcv0() const volatile; // { dg-bogus "" }
+
+ void gcv1(this S const); // { dg-bogus "" }
+ void gcv1() const volatile; // { dg-bogus "" }
+
+ void gcv2(this S volatile); // { dg-bogus "" }
+ void gcv2() const volatile; // { dg-bogus "" }
+
+ void gcv3(this S const volatile); // { dg-bogus "" }
+ void gcv3() const volatile; // { dg-bogus "" }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C
new file mode 100644
index 00000000000..9942d6e26f8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl3.C
@@ -0,0 +1,263 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// redeclarations of xobj member functions as static member functions and vice versa
+
+// note, the deduced xobj parameter cases should probably be removed
+// I still need clarification on what the correct behavior should be here.
+
+struct S {
+// no additional params
+ void f_xs_v(this S) {}; // { dg-note {previous declaration} }
+ static void f_xs_v() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref(this S&) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref(this S const&) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref(this S&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref(this S const&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_dv(this auto) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dv() {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dcref(this auto const&) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dcref() {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dfwdref(this auto&&) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dfwdref() {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+// reversed
+ static void f_sx_v() {}; // { dg-note {previous declaration} }
+ void f_sx_v(this S) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref() {}; // { dg-note {previous declaration} }
+ void f_sx_ref(this S&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref() {}; // { dg-note {previous declaration} }
+ void f_sx_cref(this S const&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref() {}; // { dg-note {previous declaration} }
+ void f_sx_rref(this S&&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref() {}; // { dg-note {previous declaration} }
+ void f_sx_crref(this S const&&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_dv() {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dv(this auto) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dcref() {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dcref(this auto const&) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dfwdref() {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dfwdref(this auto&&) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+// one additional param
+ void f_xs_v_int(this S, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int(this S&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int(this S const&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int(this S&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int(this S const&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_dv_int(this auto, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dv_int(int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dcref_int(this auto const&, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dcref_int(int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dfwdref_int(this auto&&, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dfwdref_int(int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+// reversed
+ static void f_sx_v_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int(this S, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int(this S&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int(this S const&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int(this S&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int(this S const&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_dv_int(int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dv_int(this auto, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dcref_int(int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dcref_int(this auto const&, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dfwdref_int(int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dfwdref_int(this auto&&, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+// two additional params
+ void f_xs_v_int2(this S, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int2(this S&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int2(this S const&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int2(this S&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int2(this S const&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_dv_int2(this auto, int, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dv_int2(int, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dcref_int2(this auto const&, int, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dcref_int2(int, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ void f_xs_dfwdref_int2(this auto&&, int, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ static void f_xs_dfwdref_int2(int, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+// reversed
+ static void f_sx_v_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int2(this S, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int2(this S&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int2(this S const&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int2(this S&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int2(this S const&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_dv_int2(int, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dv_int2(this auto, int, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dcref_int2(int, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dcref_int2(this auto const&, int, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+
+ static void f_sx_dfwdref_int2(int, int) {}; // { dg-note {previous declaration} {Maybe well formed? TBD} { xfail *-*-* } }
+ void f_sx_dfwdref_int2(this auto&&, int, int) {}; // { dg-error {cannot be overloaded with} {Maybe well formed? TBD} { xfail *-*-* } }
+};
+
+// unrelated explicit object parameter type
+
+struct A {};
+
+struct S1
+{
+// no additional params
+ void f_xs_v(this A) {}; // { dg-note {previous declaration} }
+ static void f_xs_v() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref(this A&) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref(this A const&) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref(this A&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref() {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref(this A const&&) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref() {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v() {}; // { dg-note {previous declaration} }
+ void f_sx_v(this A) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref() {}; // { dg-note {previous declaration} }
+ void f_sx_ref(this A&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref() {}; // { dg-note {previous declaration} }
+ void f_sx_cref(this A const&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref() {}; // { dg-note {previous declaration} }
+ void f_sx_rref(this A&&) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref() {}; // { dg-note {previous declaration} }
+ void f_sx_crref(this A const&&) {}; // { dg-error {cannot be overloaded with} }
+
+// one additional param
+ void f_xs_v_int(this A, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int(this A&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int(this A const&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int(this A&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int(this A const&&, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int(int) {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int(this A, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int(this A&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int(this A const&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int(this A&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int(int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int(this A const&&, int) {}; // { dg-error {cannot be overloaded with} }
+
+// two additional params
+ void f_xs_v_int2(this A, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_v_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_ref_int2(this A&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_ref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_cref_int2(this A const&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_cref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_rref_int2(this A&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_rref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ void f_xs_crref_int2(this A const&&, int, int) {}; // { dg-note {previous declaration} }
+ static void f_xs_crref_int2(int, int) {}; // { dg-error {cannot be overloaded with} }
+
+// reversed
+ static void f_sx_v_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_v_int2(this A, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_ref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_ref_int2(this A&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_cref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_cref_int2(this A const&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_rref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_rref_int2(this A&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+
+ static void f_sx_crref_int2(int, int) {}; // { dg-note {previous declaration} }
+ void f_sx_crref_int2(this A const&&, int, int) {}; // { dg-error {cannot be overloaded with} }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-virtual.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-virtual.C
new file mode 100644
index 00000000000..023cdc2e0fe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-virtual.C
@@ -0,0 +1,95 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// diagnose xobj member functions that override
+// or are declared as virtual, override, or final
+
+struct B {
+ virtual void f0() {} // { dg-note {virtual function declared here} }
+ virtual void f1() {} // { dg-note {virtual function declared here} }
+ virtual void f2() {} // { dg-note {virtual function declared here} }
+ virtual void f3() {} // { dg-note {virtual function declared here} }
+ virtual void f4() {} // { dg-note {virtual function declared here} }
+ virtual void f5() {} // { dg-note {virtual function declared here} }
+ virtual void f6() {} // { dg-note {virtual function declared here} }
+ virtual void f7() {} // { dg-note {virtual function declared here} }
+ virtual ~B() {}
+};
+
+struct S : B {
+ virtual void f0(this S&) {} // { dg-line line_f0 }
+ virtual void f1(this S&) override {} // { dg-line line_f1 }
+ virtual void f2(this S&) final {} // { dg-line line_f2 }
+ virtual void f3(this S&) override final {} // { dg-line line_f3 }
+ void f4(this S&) {} // { dg-line line_f4 }
+ void f5(this S&) override {} // { dg-line line_f5 }
+ void f6(this S&) final {} // { dg-line line_f6 }
+ void f7(this S&) override final {} // { dg-line line_f7 }
+};
+
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f0 }
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f1 }
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f2 }
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f3 }
+
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f0 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f1 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f2 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f3 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f4 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f5 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f6 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f7 }
+
+// these should be suppressed, the wording conflicts with the error
+// the issue is not that they don't override, it's that they do override, and that isn't allowed
+// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_f1 }
+// { dg-bogus "marked 'final', but is not virtual" "" { xfail *-*-* } line_f2 }
+// { dg-bogus "marked '(override|final)'" "" { xfail *-*-* } line_f3 }
+
+// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_f5 }
+// { dg-bogus "marked 'final', but is not virtual" "" { xfail *-*-* } line_f6 }
+// { dg-bogus "marked '(override|final)'" "" { xfail *-*-* } line_f7 }
+
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f0 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f1 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f2 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f3 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f4 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f5 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f6 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f7 }
+
+struct S1 {
+ virtual void f0(this S&) {} // { dg-line line_S1_f0 }
+ virtual void f1(this S&) override {} // { dg-line line_S1_f1 }
+ virtual void f2(this S&) final {} // { dg-line line_S1_f2 }
+ virtual void f3(this S&) override final {} // { dg-line line_S1_f3 }
+ void f4(this S&) {}
+ void f5(this S&) override {} // { dg-line line_S1_f5 }
+ void f6(this S&) final {} // { dg-line line_S1_f6 }
+ void f7(this S&) override final {} // { dg-line line_S1_f7 }
+};
+
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f0 }
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f1 }
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f2 }
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f3 }
+
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f0 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f1 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f2 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f3 }
+
+// I think I want these suppressed, but theres a decent argument that they should stay
+// theres arguably no reason the error about virtual should suppress these
+// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_S1_f1 }
+// { dg-bogus "marked 'final', but is not virtual" "" { xfail *-*-* } line_S1_f2 }
+// { dg-bogus "marked '(override|final)'" "" { xfail *-*-* } line_S1_f3 }
+
+// I don't want to suppress these, there is nothing that could possibly be overridden
+// even if the xobj param was removed
+// { dg-error "marked 'override', but does not override" "" { target *-*-* } line_S1_f5 }
+// { dg-error "marked 'final', but is not virtual" "" { target *-*-* } line_S1_f6 }
+// { dg-error "marked '(override|final)'" "" { target *-*-* } line_S1_f7 }
+
--
2.42.1
next prev parent reply other threads:[~2023-12-06 7:33 UTC|newest]
Thread overview: 100+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-31 6:02 [PATCH 1/2] " waffl3x
2023-08-31 8:33 ` Jakub Jelinek
2023-09-02 8:43 ` waffl3x
2023-09-11 13:49 ` [PATCH v2 " waffl3x
2023-09-19 20:14 ` Jason Merrill
2023-09-20 0:30 ` waffl3x
2023-09-20 21:19 ` Jason Merrill
2023-09-21 11:28 ` waffl3x
2023-09-22 11:30 ` Jason Merrill
2023-09-26 1:56 ` [PATCH v3 " waffl3x
2023-09-27 22:43 ` Hans-Peter Nilsson
2023-09-27 23:35 ` Waffl3x
2023-10-17 20:53 ` Jason Merrill
2023-10-17 21:11 ` Jason Merrill
2023-10-18 11:46 ` waffl3x
2023-10-18 14:17 ` Jason Merrill
2023-10-18 17:28 ` waffl3x
2023-10-18 17:45 ` Jakub Jelinek
2023-10-18 18:12 ` Jason Merrill
2023-10-19 21:05 ` waffl3x
2023-10-19 21:11 ` Jakub Jelinek
2023-10-19 21:31 ` waffl3x
2023-10-19 21:53 ` Jakub Jelinek
2023-10-19 22:18 ` Jason Merrill
[not found] ` <Q2xXS2RkwtzDslDUAsi4YWupkb9s3QKvecqsxLNUr=5FL-MdSmzIJcZ96S3B9Avk30GneMm8R67JsQ4D-Uj1JB6N8dhTw6LAlNsrpLKHuLP2o=3D@protonmail.com>
2023-10-19 22:51 ` waffl3x
2023-10-19 23:35 ` waffl3x
2023-10-20 2:39 ` Jason Merrill
2023-10-20 4:34 ` waffl3x
2023-10-20 16:01 ` Jason Merrill
[not found] ` <zXWkSXEO=5FH62WXyNUeV1zNAx9wSVGQ5ooxKAfpN2InCP4X25uOC0yTZlnMqbDMoIa4lGwj0hP-KEP5UIcMs1S1zkhz=5FZx4oM3oz09DY2BRg=3D@protonmail.com>
[not found] ` <9YnRZJPkB8KCk8R86UDwWBoNnmOEAir4IU4enb1qIGgVdkB6dwy76ClgAzwpPqpToQ9sLTBs50EIRwhivBJU6RAFfLt-fjJxdYTZ35YVFWA=3D@protonmail.com>
2023-10-28 4:07 ` waffl3x
2023-10-28 23:21 ` waffl3x
2023-11-01 23:15 ` waffl3x
2023-11-02 7:01 ` waffl3x
2023-11-02 7:01 ` Jakub Jelinek
2023-11-03 3:04 ` Jason Merrill
2023-11-03 4:44 ` waffl3x
2023-11-03 18:05 ` Jason Merrill
2023-11-04 6:40 ` waffl3x
2023-11-09 18:44 ` Jason Merrill
2023-11-10 4:24 ` waffl3x
2023-11-10 23:12 ` Jason Merrill
2023-11-11 10:43 ` waffl3x
2023-11-11 11:24 ` waffl3x
2023-11-14 3:48 ` Jason Merrill
2023-11-14 4:36 ` waffl3x
2023-11-18 6:43 ` waffl3x
2023-11-19 6:22 ` Jason Merrill
2023-11-19 13:39 ` waffl3x
2023-11-19 16:31 ` Jason Merrill
2023-11-19 18:36 ` waffl3x
2023-11-19 20:34 ` Jason Merrill
2023-11-19 21:44 ` waffl3x
2023-11-20 14:35 ` Jason Merrill
[not found] ` <1MdaTybBd=5Fo4uw-Gb23fYyd5GNz7qFqoSe=5Ff5h90LY=5FBdzM2ge2qPSyCuiCLYoYcZSjmVv13fw1LmjQC=5FM2L8raS1fydY5pEJ=5Fvwvv5Z-0k=3D@protonmail.com>
2023-11-21 10:02 ` waffl3x
2023-11-21 13:04 ` [PATCH v5 1/1] " waffl3x
2023-11-22 3:22 ` Jason Merrill
2023-11-22 20:46 ` waffl3x
2023-11-22 21:38 ` Jason Merrill
[not found] ` <kltTuyDDwoyOmhBWostMKm5zF3sQCGz3HjMBrBUK6LOZp1-AbGMl5ijKKMlOncwR2yiWippyp89sFPZykNF3OVyz4yknnCVwn=5FiHJPUl25k=3D@protonmail.com>
[not found] ` <dHEpSeuiljMbH0YhwLULApd3yO3LNaVkamGW2KJBYBl0EgMrtpJZ41GeTVOc77siD1kh2vkF4zwInWWGxYXfcnW4XV7sfDPX7cY028JiORE=3D@protonmail.com>
2023-11-22 21:56 ` waffl3x
2023-11-22 22:44 ` waffl3x
2023-11-24 6:49 ` waffl3x
2023-11-24 23:26 ` waffl3x
2023-11-25 1:14 ` waffl3x
2023-11-26 21:30 ` Jason Merrill
2023-11-26 23:10 ` waffl3x
2023-11-27 1:16 ` Jason Merrill
[not found] ` <BEI8PD7nktTuX7dimb22uDnR0b8Bc8ozi4xx9KbiEFj8TjgUCxMfEPpcIPL0bkdThBBab97T1uEJ9rUM3va1eiE1TyRw=5FiLrxwKgg30ZaW0=3D@protonmail.com>
2023-11-27 1:30 ` waffl3x
2023-11-27 1:44 ` waffl3x
2023-11-27 2:40 ` Jason Merrill
2023-11-27 5:35 ` [PATCH v6 " waffl3x
2023-11-28 3:31 ` waffl3x
2023-11-28 10:00 ` waffl3x
2023-11-30 5:00 ` Jason Merrill
2023-11-30 6:36 ` waffl3x
2023-11-30 14:55 ` Jason Merrill
2023-12-01 6:02 ` waffl3x
2023-12-01 16:52 ` Jason Merrill
2023-12-02 1:31 ` waffl3x
2023-12-02 15:02 ` Jason Merrill
[not found] ` <KQegse=5FguOyql4Ok1lrAgS7gasZJd1pOoPbCTdGxcHh-G4A9Tlf5zCnGJmqtshMt72edmcXdIapaZNPp4VJp5Ar9PHZbUrbwDsPjTSUrnOI=3D@protonmail.com>
[not found] ` <59LofhYhxl7MLEuElXwZcESRB6MpjdG-iq-89B63siDRo5k0j-y6z-PVa6Y3iE1xE5LkJwpwTFi82bd0RZjB1yZbSJptFdPTBWfvOGj1W78=3D@protonmail.com>
2023-12-05 4:35 ` waffl3x
2023-12-05 4:39 ` waffl3x
2023-12-05 5:54 ` waffl3x
2023-12-06 7:33 ` waffl3x [this message]
2023-12-06 8:48 ` [PATCH v7 " Jakub Jelinek
2023-12-06 9:31 ` waffl3x
2023-12-06 11:08 ` waffl3x
2023-12-08 19:25 ` Jason Merrill
2023-12-10 15:22 ` waffl3x
2023-12-10 18:59 ` Jason Merrill
2023-12-22 9:01 ` waffl3x
2023-12-22 17:26 ` Jason Merrill
2023-12-23 7:10 ` waffl3x
2023-12-26 16:37 ` Jason Merrill
2024-01-01 15:17 ` waffl3x
2024-01-01 15:34 ` waffl3x
2024-01-01 17:12 ` waffl3x
2024-01-06 12:37 ` waffl3x
2023-11-25 17:32 ` [PATCH v5 " Jason Merrill
2023-11-25 22:59 ` waffl3x
2023-09-19 20:24 ` [PATCH 1/2] " Jason Merrill
[not found] <b=5FiIXwWwO63ZE1ZSZHUIAdWyA2sqGsE3FM7eXfsInWogDyBZRsw8CwNsvFSDmEVmBtdq0pqb4zJ55HN2JCR7boDNramlEfne-R5PWdUXjbA=3D@protonmail.com>
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='bW3WA9EOXNz9A2y4HtlYLFCozq89_esrjpM9pFr_AiOL8YzxxeR_UgieAV8MjDVKnge2GoA1PLu6p8Bvr6czxA7wYHow6IBxaUaYeD51RO8=@protonmail.com' \
--to=waffl3x@protonmail.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).