public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
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


  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).