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: [PATCH v5 1/1] c++: Initial support for P0847R7 (Deducing This) [PR102609]
Date: Tue, 21 Nov 2023 13:04:35 +0000	[thread overview]
Message-ID: <e1dWYu-AKeY4Tx7EvAMCbTpi8vcvr8Xl_o3ZRez4jYtClI9TD5vOf8Qk41sEh6gBbyjlvCOQHPI1woLPVnHY9g9JJu8FRm6eqUtE2L0hsNc=@protonmail.com> (raw)
In-Reply-To: <1MdaTybBd_o4uw-Gb23fYyd5GNz7qFqoSe_f5h90LY_BdzM2ge2qPSyCuiCLYoYcZSjmVv13fw1LmjQC_M2L8raS1fydY5pEJ_vwvv5Z-0k=@protonmail.com>

[-- Attachment #1: Type: text/plain, Size: 754 bytes --]

Bootstrapped and tested on x86_64-linux with no regressions.

Hopefully this patch is legible enough for reviewing purposes, I've not
been feeling the greatest so it was a task to get this finished.
Tomorrow I will look at putting the diagnostics in
start_preparsed_function and also fixing up anything else.

To reiterate in case it wasn't abundantly clear by the barren changelog
and commit message, this version is not intended as the final revision.

Handling re-declarations was kind of nightmarish, so the comments in
there are lengthy, but I am fairly certain I implemented them correctly.

I am going to get some sleep now, hopefully I will feel better tomorrow
and be ready to polish off the patch. Thanks for the patience.

Alex

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-temp-deducing-this.patch --]
[-- Type: text/x-patch; name=0001-temp-deducing-this.patch, Size: 174488 bytes --]

From 2c0fc2b4704743558d595eb00ec046f5554643a6 Mon Sep 17 00:00:00 2001
From: waffl3x <waffl3x@protonmail.com>
Date: Tue, 21 Nov 2023 05:42:38 -0700
Subject: [PATCH] This is a temporary message. gcc/cp/ChangeLog:

	* call.cc (add_candidates):
	(build_over_call):
	* class.cc (add_method):
	(resolve_address_of_overloaded_function):
	* cp-tree.h (struct lang_decl_fn):
	(DECL_IOBJ_MEMBER_FUNC_P):
	(DECL_FUNCTION_XOBJ_FLAG):
	(DECL_XOBJ_MEMBER_FUNC_P):
	(DECL_OBJECT_MEMBER_FUNC_P):
	(DECL_FUNCTION_MEMBER_P):
	(enum auto_deduction_context):
	(TFF_XOBJ_FUNC):
	(enum cp_decl_spec):
	* decl.cc (grokfndecl):
	(grokdeclarator):
	(grok_op_properties):
	(start_preparsed_function):
	* error.cc (dump_function_decl):
	(dump_parameters):
	(function_category):
	* lambda.cc (build_capture_proxy):
	* 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 (instantiate_body):
	* search.cc (look_for_overrides_here):
	(look_for_overrides_r):
	* semantics.cc (finish_this_expr):
	* tree.cc (build_min_non_dep_op_overload):
	* typeck.cc (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-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-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-lambda1.C: New test.
	* g++.dg/cpp23/explicit-obj-lambdaX2.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.C: New test.
	* g++.dg/cpp23/explicit-obj-redecl2.C: New test.
---
 gcc/cp/call.cc                                | 155 +++---
 gcc/cp/class.cc                               | 205 +++++++-
 gcc/cp/cp-tree.h                              |  41 +-
 gcc/cp/decl.cc                                | 181 ++++++-
 gcc/cp/error.cc                               |   8 +-
 gcc/cp/lambda.cc                              |   8 +-
 gcc/cp/module.cc                              |   2 +
 gcc/cp/parser.cc                              | 159 +++++-
 gcc/cp/pt.cc                                  |  22 +-
 gcc/cp/search.cc                              |  16 +-
 gcc/cp/semantics.cc                           |  35 +-
 gcc/cp/tree.cc                                |  25 +-
 gcc/cp/typeck.cc                              |  23 +
 .../g++.dg/cpp23/explicit-obj-basic1.C        | 113 ++++
 .../g++.dg/cpp23/explicit-obj-basic2.C        |  27 +
 .../g++.dg/cpp23/explicit-obj-basic3.C        | 495 ++++++++++++++++++
 .../g++.dg/cpp23/explicit-obj-basic4.C        | 111 ++++
 .../g++.dg/cpp23/explicit-obj-by-value1.C     |  49 ++
 .../g++.dg/cpp23/explicit-obj-by-value2.C     |  59 +++
 .../g++.dg/cpp23/explicit-obj-by-value3.C     |  42 ++
 .../g++.dg/cpp23/explicit-obj-by-value4.C     |  19 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C |   6 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C |   6 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C |   8 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C |   7 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C |   7 +
 .../g++.dg/cpp23/explicit-obj-diagnostics1.C  | 138 +++++
 .../g++.dg/cpp23/explicit-obj-diagnostics2.C  |  25 +
 .../g++.dg/cpp23/explicit-obj-diagnostics4.C  |  19 +
 .../g++.dg/cpp23/explicit-obj-diagnostics5.C  |  15 +
 .../g++.dg/cpp23/explicit-obj-diagnostics6.C  |  22 +
 .../g++.dg/cpp23/explicit-obj-diagnostics7.C  |  25 +
 .../g++.dg/cpp23/explicit-obj-lambda1.C       |  11 +
 .../g++.dg/cpp23/explicit-obj-lambdaX2.C      |  22 +
 .../g++.dg/cpp23/explicit-obj-ops-mem-arrow.C |  27 +
 .../cpp23/explicit-obj-ops-mem-assignment.C   |  26 +
 .../g++.dg/cpp23/explicit-obj-ops-mem-call.C  |  39 ++
 .../cpp23/explicit-obj-ops-mem-subscript.C    |  39 ++
 .../cpp23/explicit-obj-ops-non-mem-dep.C      |  57 ++
 .../cpp23/explicit-obj-ops-non-mem-non-dep.C  |  56 ++
 .../g++.dg/cpp23/explicit-obj-ops-non-mem.h   | 209 ++++++++
 .../cpp23/explicit-obj-ops-requires-mem.C     | 170 ++++++
 .../cpp23/explicit-obj-ops-requires-non-mem.C | 236 +++++++++
 .../g++.dg/cpp23/explicit-obj-redecl.C        | 245 +++++++++
 .../g++.dg/cpp23/explicit-obj-redecl2.C       | 160 ++++++
 45 files changed, 3229 insertions(+), 141 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-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-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-lambda1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.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.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 2eb54b5b6ed..82ae5facd20 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -6527,7 +6527,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_FUNC_P (fn))
 	{
 	  /* Figure out where the object arg comes from.  If this
 	     function is a non-static member and we didn't get an
@@ -9728,14 +9728,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;
@@ -9921,45 +9916,70 @@ 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;
 
+  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;
+    };
+
+  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_FUNC_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)
 	    {
@@ -10034,25 +10054,37 @@ 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;
+    }
+  else if (DECL_XOBJ_MEMBER_FUNC_P (fn))
+    {
+      /* We currently handle for the case where first_arg is NULL_TREE
+	 in the future this should be changed and the assert reactivated.  */
+      #if 0
+      gcc_assert (first_arg);
+      #endif
+      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
@@ -10074,47 +10106,38 @@ 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;
-
-      /* 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);
+      auto determine_conversion_warning = [&]()
+	{
+	  return !(null_node_p (current_arg)
+	    && DECL_TEMPLATE_INFO (fn)
+	    && cand->template_decl
+	    && !cand->explicit_targs);
+	};
 
-      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],
+			determine_conversion_warning ());
 
       if (val == 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;
+      argarray[argarray_size++] = val;
     }
 
   /* Ellipsis */
@@ -10151,11 +10174,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.
@@ -10171,7 +10194,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.  */
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 0d8b780ba2f..4537de15fdc 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_FUNC_P (fn)
+	  && DECL_IOBJ_MEMBER_FUNC_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,164 @@ 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_FUNC_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_FUNC_P (fn) && !DECL_XOBJ_MEMBER_FUNC_P (method))
+	   || DECL_STATIC_FUNCTION_P (fn) || DECL_STATIC_FUNCTION_P (method))
+        /* Early escape.  */;
+      else if (DECL_XOBJ_MEMBER_FUNC_P (fn)
+	       && DECL_XOBJ_MEMBER_FUNC_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_FUNC_P (fn)
+	       || DECL_XOBJ_MEMBER_FUNC_P (method))
+        {
+	  tree xobj_fn = DECL_XOBJ_MEMBER_FUNC_P (fn) ? fn : method;
+	  /* A reference, pointer, or something else.  */
+	  tree xobj_param = get_object_param (xobj_fn);
+
+	  /* We know an iobj parameter must be a reference. If our xobj
+	     parameter is a pointer, we know this is not a redeclaration.
+	     This also catches array parameters, those are pointers too.  */
+	  if (TYPE_PTR_P (xobj_param))
+	    continue;
+
+	  /* 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 it's 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.  */
+	  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_FUNC_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 procede 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 it's
+		 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;
 
@@ -8724,21 +8882,40 @@ 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_FUNC_P (fn)
+      && !(complain & tf_ptrmem_ok))
     {
-      static int explained;
-
-      if (!(complain & tf_error))
-	return error_mark_node;
-
-      auto_diagnostic_group d;
-      if (permerror (input_location, "assuming pointer to member %qD", fn)
-	  && !explained)
+      /* Don't disable error even if -fms-extensions is passed, this is a new
+	 feature so we (hopefully) don't need to support the behavior. */
+      if (DECL_XOBJ_MEMBER_FUNC_P (fn))
 	{
-	  inform (input_location, "(a pointer to member can only be "
+	  if (!(complain & tf_error))
+	    return error_mark_node;
+	  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 if (DECL_IOBJ_MEMBER_FUNC_P (fn) && !flag_ms_extensions)
+	{
+	  static int explained;
+
+	  if (!(complain & tf_error))
+	    return error_mark_node;
+
+	  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 98b29e9cf81..cdc98504c99 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2938,8 +2938,9 @@ struct GTY(()) lang_decl_fn {
   unsigned maybe_deleted : 1;
   unsigned coroutine_p : 1;
   unsigned implicit_constexpr : 1;
+  unsigned xobj_func : 1;
 
-  unsigned spare : 9;
+  unsigned spare : 8;
 
   /* 32-bits padding on 64-bit host.  */
 
@@ -3338,14 +3339,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_FUNC_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_FUNC_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_FUNC_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_FUNC_P(NODE) \
+  (DECL_IOBJ_MEMBER_FUNC_P (NODE) || DECL_XOBJ_MEMBER_FUNC_P (NODE))
+
 /* Nonzero for FUNCTION_DECL means that this decl is a member function
-   (static or non-static).  */
+   (static or object).  */
 #define DECL_FUNCTION_MEMBER_P(NODE) \
-  (DECL_NONSTATIC_MEMBER_FUNCTION_P (NODE) || DECL_STATIC_FUNCTION_P (NODE))
+  (DECL_OBJECT_MEMBER_FUNC_P (NODE) || DECL_STATIC_FUNCTION_P (NODE)) \
 
 /* Nonzero for FUNCTION_DECL means that this member function
    has `this' as const X *const.  */
@@ -6104,7 +6125,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)
@@ -6122,6 +6145,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.
 
@@ -6264,11 +6288,13 @@ enum cp_storage_class {
 
 /* An individual decl-specifier.  This is used to index the array of
    locations for the declspecs in struct cp_decl_specifier_seq
-   below.  */
+   below.
+   A subset of these enums also corresponds to elements of
+   cp_parser_set_decl_spec_type:decl_spec_names in parser.cc.  */
 
 enum cp_decl_spec {
   ds_first,
-  ds_signed = ds_first,
+  ds_signed = ds_first, /* Index of first element of decl_spec_names.  */
   ds_unsigned,
   ds_short,
   ds_long,
@@ -6285,6 +6311,7 @@ enum cp_decl_spec {
   ds_complex,
   ds_constinit,
   ds_consteval,
+  ds_this, /* Index of last element of decl_spec_names.  */
   ds_thread,
   ds_type_spec,
   ds_redefined_builtin_type_spec,
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 16af59de696..1234544b4fb 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10318,6 +10318,7 @@ grokfndecl (tree ctype,
 	    int publicp,
 	    int inlinep,
 	    bool deletedp,
+	    bool xobj_func_p,
 	    special_function_kind sfk,
 	    bool funcdef_flag,
 	    bool late_return_type_p,
@@ -10327,7 +10328,6 @@ grokfndecl (tree ctype,
 	    location_t location)
 {
   tree decl;
-  int staticp = ctype && TREE_CODE (type) == FUNCTION_TYPE;
   tree t;
 
   if (location == UNKNOWN_LOCATION)
@@ -10525,12 +10525,9 @@ grokfndecl (tree ctype,
 		  (IDENTIFIER_POINTER (declarator))))))
     SET_DECL_LANGUAGE (decl, lang_c);
 
-  /* Should probably propagate const out from type to decl I bet (mrs).  */
-  if (staticp)
-    {
-      DECL_STATIC_FUNCTION_P (decl) = 1;
-      DECL_CONTEXT (decl) = ctype;
-    }
+  DECL_STATIC_FUNCTION_P (decl)
+    = !xobj_func_p && ctype && TREE_CODE (type) == FUNCTION_TYPE;
+  DECL_FUNCTION_XOBJ_FLAG (decl) = xobj_func_p;
 
   if (deletedp)
     DECL_DELETED_FN (decl) = 1;
@@ -10610,24 +10607,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))
@@ -12998,6 +13001,8 @@ grokdeclarator (const cp_declarator *declarator,
   if (attrlist)
     diagnose_misapplied_contracts (*attrlist);
 
+  /* Skip over build_memfn_type when a FUNCTION_DECL is an xobj memfn.  */
+  bool is_xobj_member_function = false;
   /* Determine the type of the entity declared by recurring on the
      declarator.  */
   for (; declarator; declarator = declarator->declarator)
@@ -13113,6 +13118,92 @@ 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 "?  */
+			    "a function pointer type cannot "
+			    "have an explicit object parameter");
+		else if (declarator->declarator->kind == cdk_ptrmem)
+		  error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			    "a 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 it's 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;
@@ -13400,6 +13491,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
@@ -14177,6 +14300,8 @@ grokdeclarator (const cp_declarator *declarator,
     }
 
   if (ctype && TREE_CODE (type) == FUNCTION_TYPE && staticp < 2
+      /* Don't convert xobj member functions to METHOD_TYPE.  */
+      && !is_xobj_member_function
       && !(unqualified_id
 	   && identifier_p (unqualified_id)
 	   && IDENTIFIER_NEWDEL_OP_P (unqualified_id)))
@@ -14398,7 +14523,8 @@ grokdeclarator (const cp_declarator *declarator,
 			       friendp ? -1 : 0, friendp, publicp,
 			       inlinep | (2 * constexpr_p) | (4 * concept_p)
 				       | (8 * consteval_p),
-			       initialized == SD_DELETED, sfk,
+			       initialized == SD_DELETED,
+			       is_xobj_member_function, sfk,
 			       funcdef_flag, late_return_type_p,
 			       template_count, in_namespace,
 			       attrlist, id_loc);
@@ -14733,8 +14859,8 @@ grokdeclarator (const cp_declarator *declarator,
 			   inlinep | (2 * constexpr_p) | (4 * concept_p)
 				   | (8 * consteval_p),
 			   initialized == SD_DELETED,
-                           sfk,
-                           funcdef_flag,
+			   is_xobj_member_function, sfk,
+			   funcdef_flag,
 			   late_return_type_p,
 			   template_count, in_namespace, attrlist,
 			   id_loc);
@@ -15628,7 +15754,8 @@ grok_op_properties (tree decl, bool complain)
   /* An operator function must either be a non-static member function
      or have at least one parameter of a class, a reference to a class,
      an enumeration, or a reference to an enumeration.  13.4.0.6 */
-  if (! methodp || DECL_STATIC_FUNCTION_P (decl))
+  if ((!methodp && !DECL_XOBJ_MEMBER_FUNC_P (decl))
+      || DECL_STATIC_FUNCTION_P (decl))
     {
       if (operator_code == TYPE_EXPR
 	  || operator_code == COMPONENT_REF
@@ -17382,6 +17509,8 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
   tree fntype = TREE_TYPE (decl1);
   if (TREE_CODE (fntype) == METHOD_TYPE)
     ctype = TYPE_METHOD_BASETYPE (fntype);
+  else if (DECL_XOBJ_MEMBER_FUNC_P (decl1))
+    ctype = DECL_CONTEXT (decl1);
   else
     {
       ctype = DECL_FRIEND_CONTEXT (decl1);
@@ -17609,7 +17738,9 @@ 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 (ctype && !doing_friend && !DECL_STATIC_FUNCTION_P (decl1))
+  /* We don't need deal with 'this' or vtable for xobj member functions.  */
+  if (ctype && !doing_friend &&
+      !(DECL_STATIC_FUNCTION_P (decl1) || DECL_XOBJ_MEMBER_FUNC_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 0ed69bca6fc..c9870d45706 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -1831,7 +1831,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
-      dump_parameters (pp, parmtypes, flags);
+      dump_parameters (pp, parmtypes,
+		       DECL_XOBJ_MEMBER_FUNC_P (t) ? TFF_XOBJ_FUNC | flags
+						    : flags);
 
       if (TREE_CODE (fntype) == METHOD_TYPE)
 	{
@@ -1910,6 +1912,8 @@ dump_parameters (cxx_pretty_printer *pp, tree parmtypes, int flags)
   for (first = 1; parmtypes != void_list_node;
        parmtypes = TREE_CHAIN (parmtypes))
     {
+      if (first && flags & TFF_XOBJ_FUNC)
+	pp_string (pp, "this ");
       if (!first)
 	pp_separate_with_comma (pp);
       first = 0;
@@ -3685,6 +3689,8 @@ function_category (tree fn)
 	return _("In destructor %qD");
       else if (LAMBDA_FUNCTION_P (fn))
 	return _("In lambda function");
+      else if (DECL_XOBJ_MEMBER_FUNC_P (fn))
+	return _("In explicit object member function %qD");
       else
 	return _("In member function %qD");
     }
diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc
index a359bc6ee8d..53d678f2bed 100644
--- a/gcc/cp/lambda.cc
+++ b/gcc/cp/lambda.cc
@@ -405,8 +405,12 @@ build_capture_proxy (tree member, tree init)
   fn = lambda_function (closure);
   lam = CLASSTYPE_LAMBDA_EXPR (closure);
 
-  /* The proxy variable forwards to the capture field.  */
-  object = build_fold_indirect_ref (DECL_ARGUMENTS (fn));
+  object = DECL_ARGUMENTS (fn);
+  /* Making this conditional prevents the ICE, but does not actually work
+     correctly.  */
+  if (INDIRECT_TYPE_P (TREE_TYPE (object)))
+    /* The proxy variable forwards to the capture field.  */
+    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);
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index c1c8c226bc1..8723f23c4ae 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 20e18365906..f2deeca40f2 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -11750,6 +11750,31 @@ 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;
+  /* We just need to peek here,
+     grokdeclarator does the rest with this so don't mutate it.  */
+  tree const xobj_param
+    = param_list && TREE_PURPOSE (param_list) == this_identifier
+      ? TREE_VALUE (param_list) : NULL_TREE;
+
+  if (xobj_param
+      && (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 explicit object
+	 parameter is unrelated... at least I believe so. I can think of a
+	 paradoxical case that almost works, where a class is forward declared
+	 and then defined deriving from a lambda that has an explicit object
+	 parameter of that class type, but the only way that can work is with
+	 decltype... okay yeah I think it can work for a struct defined in
+	 block scope, but I'm leaving this as is until I can confirm my
+	 hypothesis.  */
+      if (!TEMPLATE_PARM_P (non_reference (TREE_TYPE (xobj_param))))
+	{
+          error("a lambda with captures may not have an explicit object "
+		"parameter of an unrelated type");
+	  LAMBDA_EXPR_CAPTURE_LIST (lambda_expr) = NULL_TREE;
+	}
+    }
 
   /* In the decl-specifier-seq of the lambda-declarator, each
      decl-specifier shall either be mutable or constexpr.  */
@@ -11767,24 +11792,81 @@ 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)
     {
-      LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
-      quals = TYPE_UNQUALIFIED;
+      /* This might not be the most appropriate place for this, but I'm going
+	 to hold back on changing it for the time being since we are short on
+	 time. It's not really a parser error, it's more a semantic error.
+	 But this is where the other errors were so...  */
+      if (xobj_param)
+	{
+	  auto_diagnostic_group d;
+	  error_at (lambda_specs.locations[ds_storage_class],
+		    "%<mutable%> lambda specifier "
+		    "with explicit object parameter");
+	  inform (DECL_SOURCE_LOCATION(xobj_param),
+		  "explicit object parameter declared here");
+	  /* Error reporting here is a little awkward, if the type of the
+	     object parameter is deduced, we should tell them the lambda
+	     is effectively already const, or to make the param const if it is
+	     not, but if it is deduced and taken by value shouldn't we say
+	     that it's taken by copy and won't mutate?
+	     Seems right to me, but it's a little strange.  */
+
+	  /* An xobj parameter with an unrelated type should already have been
+	     diagnosed, that means we definitely have a template type param.
+	     We don't suppress these informs right now when the xobj param is
+	     unrelated, we probably should though.  */
+	  if (!TYPE_REF_P (TREE_TYPE (xobj_param)))
+	    inform (DECL_SOURCE_LOCATION(xobj_param),
+		    "a by-value explicit object parameter will never be "
+		    "mutated");
+	  else if (TYPE_READONLY (TREE_TYPE (TREE_TYPE (xobj_param))))
+	    inform (DECL_SOURCE_LOCATION(xobj_param),
+		    "declare the explicit object parameter without const");
+	  else
+	    inform (DECL_SOURCE_LOCATION(xobj_param),
+		    "explicit object parameter is already mutable");
+	}
+      else
+	{
+	  LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
+	  quals = TYPE_UNQUALIFIED;
+	}
     }
   else if (lambda_specs.storage_class == sc_static)
     {
+      bool error_emitted = false;
       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
+	{
+	  error_at (lambda_specs.locations[ds_storage_class],
+		    "%<static%> lambda specifier with lambda capture");
+	  error_emitted = true;
+	}
+      if (xobj_param)
+	{
+	  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");
+	  error_emitted = true;
+	}
+      if (!error_emitted)
 	{
 	  LAMBDA_EXPR_STATIC_P (lambda_expr) = 1;
 	  quals = TYPE_UNQUALIFIED;
 	}
     }
+  if (xobj_param)
+    {
+      quals = TYPE_UNQUALIFIED;
+      if (TYPE_REF_P (xobj_param)
+	  && !(cp_type_quals (TREE_TYPE (xobj_param)) & TYPE_QUAL_CONST))
+        LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
+    }
 
   tx_qual = cp_parser_tx_qualifier_opt (parser);
   if (omitted_parms_loc && tx_qual)
@@ -11899,7 +11981,8 @@ 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 (!LAMBDA_EXPR_STATIC_P (lambda_expr)
+	    && !DECL_XOBJ_MEMBER_FUNC_P (fco))
 	  /* Give the object parameter a different name.  */
 	  DECL_NAME (DECL_ARGUMENTS (fco)) = closure_identifier;
 	DECL_SET_LAMBDA_FUNCTION (fco, true);
@@ -16019,6 +16102,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
   /* Assume no class or enumeration type is declared.  */
   *declares_class_or_enum = 0;
 
+  /* Keep a token that additionally will be used for diagnostics.  */
+  cp_token *first_specifier = NULL;
   /* Keep reading specifiers until there are no more to read.  */
   while (true)
     {
@@ -16091,6 +16176,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
@@ -25421,12 +25540,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
@@ -25495,6 +25616,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
@@ -33873,7 +34013,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 86c95b278ba..9a8ebb4e5d7 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -26740,8 +26740,26 @@ instantiate_body (tree pattern, tree args, tree d, bool nested_p)
 			tf_warning_or_error, d);
       else
 	{
-	  tsubst_stmt (DECL_SAVED_TREE (code_pattern), args,
-		       tf_warning_or_error, DECL_TI_TEMPLATE (d));
+	  bool error_emitted = false;
+	  if (LAMBDA_FUNCTION_P (d) && DECL_XOBJ_MEMBER_FUNC_P (d))
+	    {
+	      tree lambda_expr = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d));
+	      if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
+		  || LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
+		{
+		  gcc_assert (TREE_VEC_LENGTH (args) > 0);
+		  tree object_arg = TREE_VEC_ELT (args, 0);
+		  tree lambda = DECL_CONTEXT (d);
+		  if (!same_or_base_type_p (lambda, non_reference (object_arg)))
+		    {
+		      error ("call to lambda with captures object type must be the lambda or derived from the lambda");
+		      error_emitted = true;
+		    }
+		}
+	    }
+	  if (!error_emitted)
+	    tsubst_stmt (DECL_SAVED_TREE (code_pattern), args,
+		         tf_warning_or_error, DECL_TI_TEMPLATE (d));
 
 	  /* Set the current input_location to the end of the function
 	     so that finish_function knows where we are.  */
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index cd80f285ac9..08c22bcd5d8 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -2212,11 +2212,14 @@ 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_FUNC_P (fndecl))
 	{
 	  tree btypes = TYPE_ARG_TYPES (TREE_TYPE (fn));
 	  tree dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
-	  if (compparms (TREE_CHAIN (btypes), dtypes))
+	  if (compparms (TREE_CHAIN (btypes),
+			 DECL_XOBJ_MEMBER_FUNC_P (fndecl)
+			   ? TREE_CHAIN (dtypes) : dtypes))
 	    return fn;
 	}
       else if (same_signature_p (fndecl, fn))
@@ -2243,6 +2246,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_FUNC_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 52044be7af8..512b2f45950 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
@@ -3085,7 +3086,39 @@ finish_this_expr (void)
     return rvalue (result);
 
   tree fn = current_nonlambda_function ();
-  if (fn && DECL_STATIC_FUNCTION_P (fn))
+  if (fn && DECL_XOBJ_MEMBER_FUNC_P (fn))
+    {
+      auto_diagnostic_group d;
+      error ("%<this%> is unavailable for explicit object member "
+	     "functions");
+      /* I can imagine doing a fixit here, suggesting replacing
+	 this / *this / this-> with &name / name / "name." but it would be
+	 very difficult to get it perfect and I've been advised against
+	 making imperfect fixits.
+	 Perhaps it would be as simple as the replacements listed,
+	 even if one is move'ing/forward'ing, if the replacement is just
+	 done in the same place, it will be exactly what the user wants?
+	 Even if this is true though, there's still a problem of getting the
+	 context of the expression to find which tokens to replace.
+	 I would really like for this to be possible though.
+	 I will decide whether or not to persue this after review.  */
+      tree xobj_parm = 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));
+	  xobj_loc.add_fixit_insert_after(" self");
+	  inform (DECL_SOURCE_LOCATION (xobj_parm),
+		  "name and use the explicit object parameter instead");
+	  /* Maybe suggest self as a name here?  */
+	}
+    }
+  else if (fn && DECL_STATIC_FUNCTION_P (fn))
     error ("%<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");
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 417c92ba76f..d861593d9e4 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -3659,7 +3659,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_FUNC_P (overload)
       /* For ARRAY_REF, operator[] is either a non-static member or newly
 	 static member, never out of class and for the static member case
 	 if user uses single index the operator[] needs to have a single
@@ -3677,24 +3677,25 @@ 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_FUNC_P (overload))
     {
-      fn = overload;
-      if (op == ARRAY_REF)
-	obj = va_arg (p, tree);
+      tree object = va_arg (p, tree);
+      tree binfo = TYPE_BINFO (TREE_TYPE (object));
+      tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
+      fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
+		      object, method, NULL_TREE);
       for (int i = 0; i < nargs; i++)
 	{
 	  tree arg = va_arg (p, tree);
 	  vec_safe_push (args, arg);
 	}
     }
-  else if (TREE_CODE (TREE_TYPE (overload)) == METHOD_TYPE)
+  else if (TREE_CODE (TREE_TYPE (overload)) == FUNCTION_TYPE)
     {
-      tree object = va_arg (p, tree);
-      tree binfo = TYPE_BINFO (TREE_TYPE (object));
-      tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
-      fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
-		      object, method, NULL_TREE);
+      gcc_assert (!DECL_XOBJ_MEMBER_FUNC_P (overload));
+      fn = overload;
+      if (op == ARRAY_REF)
+	obj = va_arg (p, tree);
       for (int i = 0; i < nargs; i++)
 	{
 	  tree arg = va_arg (p, tree);
@@ -3729,7 +3730,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_FUNC_P (overload))
     {
       tree binfo = TYPE_BINFO (TREE_TYPE (object));
       tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 49afbd8fb5e..1aa6545ca4c 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -7133,6 +7133,19 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
 
     case BASELINK:
       arg = BASELINK_FUNCTIONS (arg);
+      if (DECL_XOBJ_MEMBER_FUNC_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:
@@ -7164,6 +7177,16 @@ 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,
+	   similar to the BASELINK case above. If we did the same optimization
+	   as we do for single static member functions (passing in a BASELINK)
+	   then it could be handled the same too.  */
+	if (DECL_XOBJ_MEMBER_FUNC_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..1e44c9123b7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic1.C
@@ -0,0 +1,113 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// basic use cases and calling
+
+// non-trailing return
+// definitions
+struct S0 {
+  void f0(this S0) {}
+  void f1(this S0&) {}
+  void f2(this S0&&) {}
+  void f3(this S0 const&) {}
+  void f4(this S0 const&&) {}
+  template<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..2c2b69ad362
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic2.C
@@ -0,0 +1,27 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// explicit object member function pointer type deduction,
+// conversion to function pointer,
+// and calling through pointer to function
+
+struct S {
+  int _n;
+  int f(this S& self) { return self._n; }
+};
+
+using f_type = int(*)(S&);
+
+static_assert (__is_same (f_type, decltype (&S::f)));
+
+int main()
+{
+  auto fp0 = &S::f;
+  f_type fp1 = &S::f;
+  static_assert (__is_same (decltype (fp0), decltype (fp1)));
+  S s{42};
+  if (fp0 (s) != 42)
+    __builtin_abort ();
+  if (fp1 (s) != 42)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C
new file mode 100644
index 00000000000..5f27d45012e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic3.C
@@ -0,0 +1,495 @@
+// 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..042ac0478e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-basic4.C
@@ -0,0 +1,111 @@
+// 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
+// member function matters for determining whether these are redeclarations or overloads
+
+// 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-by-value1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C
new file mode 100644
index 00000000000..e85c9ab03b0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value1.C
@@ -0,0 +1,49 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// conversion of the implicit object argument to an xobj parameter
+// when calling by value xobj member functions
+
+// The initial implementation of xobj member functions incorrectly did not
+// convert the implicit object argument when binding to the xobj
+// parameter. In spite of this, it did correctly check to see if such a
+// conversion would be valid, thus no diagnostic would be emitted when a
+// conversion was valid, but instead of applying the conversion, the
+// argument would silently be reinterpreted as the type of the parameter. 
+
+// This is why we use uintptr_t for the value in S and compare the result
+// of f to &s, we want to test for simple reinterpretation of the
+// argument. To accurately test for this we make sure to use an object
+// that has a different address than the value of our magic number. It's
+// an impossibly improbable edge case but it's trivial to work around. We
+// still compare against both the address of s and the magic number so we
+// can additionally test for bugged conversions, while also
+// differentiating that case from reinterpretation of the argument.
+
+// { dg-xfail-run-if "by value explicit object parameter is not supported yet" { *-*-* } }
+
+using uintptr_t = __UINTPTR_TYPE__;
+inline constexpr uintptr_t magic = 42;
+
+struct S {
+    uintptr_t _v;
+    uintptr_t f(this S self) {
+        return self._v;
+    }
+};
+
+int main() 
+{
+  S s0{magic};
+  S s1{magic};
+  // prevent (absurdly improbable) bogus failures
+  S& s = magic != (uintptr_t)(&s0) ? s0 : s1;
+
+  uintptr_t const ret = s.f();
+  // check for reinterpretation of the object argument
+  if (ret == (uintptr_t)(&s))
+    __builtin_abort ();
+  // check for a bugged conversion
+  if (ret != magic)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C
new file mode 100644
index 00000000000..051439bb1df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value2.C
@@ -0,0 +1,59 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// conversion of the implicit object argument to an xobj parameter
+// using a user defined conversion or converting constructor
+// when calling by value xobj member functions
+
+// see explicit-obj-by-value1.C for details on this test
+
+// { dg-xfail-run-if "user defined conversions from an implicit object argument to an explicit object parameter are not supported yet" { *-*-* } }
+
+using uintptr_t = __UINTPTR_TYPE__;
+inline constexpr uintptr_t magic = 42;
+
+struct S;
+
+struct FromS {
+  uintptr_t _v;
+  FromS(S);
+};
+
+struct S {
+  operator uintptr_t() const {
+    return magic;
+  }
+  uintptr_t f(this uintptr_t n) {
+    return n;
+  }
+  uintptr_t g(this FromS from_s) {
+    return from_s._v;
+  }
+};
+
+FromS::FromS(S) : _v(magic) {}
+
+
+int main() 
+{
+  S s0{};
+  S s1{};
+  // prevent (absurdly improbable) bogus failures
+  S& s = magic != (uintptr_t)(&s0) ? s0 : s1;
+
+  uintptr_t const ret0 = s.f();
+  // check for reinterpretation of the object argument
+  if (ret0 == (uintptr_t)(&s))
+    __builtin_abort ();
+  // check for a bugged conversion
+  if (ret0 != magic)
+    __builtin_abort ();
+
+  uintptr_t const ret1 = s.g();
+  // check for reinterpretation of the object argument
+  if (ret1 == (uintptr_t)(&s))
+    __builtin_abort ();
+  // check for a bugged conversion
+  if (ret1 != magic)
+    __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C
new file mode 100644
index 00000000000..30e556bd6cb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value3.C
@@ -0,0 +1,42 @@
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// correct constructor selection when initializing a by value xobj parameter
+
+// see explicit-obj-by-value1.C for details on this test
+
+// { dg-xfail-run-if "by value explicit object parameter is not supported yet" { *-*-* } }
+
+using uintptr_t = __UINTPTR_TYPE__;
+inline constexpr uintptr_t magic = 42;
+inline constexpr uintptr_t copy_magic = 5;
+inline constexpr uintptr_t move_magic = 10;
+
+struct S {
+  uintptr_t _v;
+  explicit S(uintptr_t v) : _v(v) {}
+  S(S const& other) : _v(other._v + copy_magic) {}
+  S(S&& other) : _v(other._v + move_magic) {}
+  uintptr_t f(this S self) {
+    return self._v;
+  }
+};
+
+int main() 
+{
+  S s0{magic};
+  S s1{magic};
+  // prevent (absurdly improbable (^2)) bogus results
+  // it's virtually impossible for both to have a bogus result,
+  // but we can guarantee correct results from both easily, so why not?
+  S& s_copy_from = magic + copy_magic != (uintptr_t)(&s0) ? s0 : s1;
+  S& s_move_from = magic + move_magic != (uintptr_t)(&s0) ? s0 : s1;
+  uintptr_t const copy_ret = static_cast<S 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..d3c5e393e7b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-by-value4.C
@@ -0,0 +1,19 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// diagnosis of ill-formed calls to by-value xobj member functions
+// due to an absence of valid conversion functions
+
+struct NotFromS {};
+
+struct S {
+  void f(this int) {}
+  void g(this NotFromS) {}
+};
+
+void test()
+{
+  S s{};
+  s.f(); // { dg-error {cannot convert 'S' to 'int'} }
+  s.g(); // { dg-error {cannot convert 'S' to 'NotFromS'} }
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
new file mode 100644
index 00000000000..033745d5784
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
@@ -0,0 +1,6 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
new file mode 100644
index 00000000000..4774750255b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
@@ -0,0 +1,6 @@
+// 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..7dbdc64a302
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
@@ -0,0 +1,8 @@
+// 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..dccb0cf07df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions" }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
new file mode 100644
index 00000000000..1924212fb23
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions -pedantic-errors" }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
new file mode 100644
index 00000000000..7b94f7e9c12
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
@@ -0,0 +1,138 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of xobj member functions that have member function qualifiers.
+
+struct S {
+    void f_value_0(this S) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_1(this S) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_2(this S) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_3(this S) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_value_4(this S) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_value_5(this S) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_6(this S) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_7(this S) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_8(this S) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_9(this S) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_A(this S) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_ref_0(this S&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_1(this S&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_2(this S&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_3(this S&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_ref_4(this S&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_ref_5(this S&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_6(this S&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_7(this S&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_8(this S&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_9(this S&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_A(this S&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_refref_0(this S&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_1(this S&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_2(this S&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_3(this S&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_refref_4(this S&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_refref_5(this S&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_6(this S&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_7(this S&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_8(this S&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_9(this S&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_A(this S&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cref_0(this S const&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_1(this S const&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_2(this S const&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_3(this S const&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cref_4(this S const&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cref_5(this S const&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_6(this S const&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_7(this S const&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_8(this S const&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_9(this S const&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_A(this S const&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_crefref_0(this S const&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_1(this S const&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_2(this S const&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_3(this S const&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_crefref_4(this S const&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_crefref_5(this S const&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_6(this S const&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_7(this S const&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_8(this S const&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_9(this S const&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_A(this S const&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_vref_0(this S volatile&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_1(this S volatile&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_2(this S volatile&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_3(this S volatile&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vref_4(this S volatile&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vref_5(this S volatile&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_6(this S volatile&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_7(this S volatile&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_8(this S volatile&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_9(this S volatile&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_A(this S volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_vrefref_0(this S volatile&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_1(this S volatile&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_2(this S volatile&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_3(this S volatile&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vrefref_4(this S volatile&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vrefref_5(this S volatile&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_6(this S volatile&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_7(this S volatile&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_8(this S volatile&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_9(this S volatile&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_A(this S volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cvref_0(this S const volatile&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_1(this S const volatile&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_2(this S const volatile&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_3(this S const volatile&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvref_4(this S const volatile&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvref_5(this S const volatile&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_6(this S const volatile&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_7(this S const volatile&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_8(this S const volatile&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_9(this S const volatile&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_A(this S const volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cvrefref_0(this S const volatile&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_1(this S const volatile&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_2(this S const volatile&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_3(this S const volatile&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvrefref_4(this S const volatile&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvrefref_5(this S const volatile&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_6(this S const volatile&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_7(this S const volatile&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_8(this S const volatile&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_9(this S const volatile&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_A(this S const volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    template<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..7c930859dae
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
@@ -0,0 +1,25 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of incorrect uses of 'this' in declarations and definitions
+
+using func_type = void(this int); // { dg-line func_type_line }
+// { dg-error "a function type cannot have an explicit object parameter" "" { target *-*-* } func_type_line }
+// { dg-note "the type of an explicit object member function is a regular function type" "" { target *-*-* } func_type_line }
+
+using func_ptr_type = void(*)(this int); // { dg-line func_ptr_type_line }
+// { dg-error "a function pointer type cannot have an explicit object parameter" "" { target *-*-* } func_ptr_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } func_ptr_type_line }
+
+struct S {
+    static void f(this S) {} // { dg-line static_member_func_line }
+};
+// { dg-error "an explicit object member function cannot be 'static'" "" { target *-*-* } static_member_func_line }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } static_member_func_line }
+
+using mem_func_type = void (S::*)(this S&); // { dg-line mem_func_type_line }
+// { dg-error "a 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..1f743a8509f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
@@ -0,0 +1,19 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of an xobj parameter declared with a default argument
+
+struct S {
+  void f0(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+  void f1(this S = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+  void f2(this S);
+  void f10(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+  void f11(this S s = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+  void f12(this S s);
+};
+
+void S::f1(this S) {}
+void S::f2(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+
+void S::f11(this S s) {}
+void S::f12(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
new file mode 100644
index 00000000000..65a5c63f20b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
@@ -0,0 +1,15 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// location diagnostic text when an error is emitted from an xobj member function
+// this does not test for specific ill-formed code, just the additional diagnostic message
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S {
+  void f(this S s) {
+    // The specific diagnosis issued here does not matter
+    // we just need to force an error to be emitted
+    +s; // { dg-error "" }
+  }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
new file mode 100644
index 00000000000..e56d6265ea1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
@@ -0,0 +1,22 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of invalid uses of 'this' in body of xobj member functions
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S0 {
+  int _n;
+  void f(this S0& s) { // { dg-note "use explicit object parameter 's' instead" } 
+    this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" }
+    // suppress unused variable warning
+    static_cast<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..17ba23df77e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C
@@ -0,0 +1,25 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis when taking address of an unqualified xobj member function
+
+struct S {
+  static void static_f(S&) {}
+  void iobj_member_f() {}
+  void xobj_member_f(this S&) {}
+
+  void test() {
+    using func_ptr_type = void(*)(S&);
+    // using mem_func_ptr_type = void (S::*)();
+
+    // allowed (not testing for this)
+    // func_ptr_type static_f_ptr = &static_f;
+
+    // not allowed (also not testing for this)
+    // mem_func_ptr_type iobj_mem_f_ptr = &iobj_member_f;
+
+    // not allowed (this is what we are testing for)
+    func_ptr_type xobj_mem_f_ptr = &xobj_member_f; // { dg-error "taking the address of an explicit object member function must be qualified" }
+  }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C
new file mode 100644
index 00000000000..913fb3ca5ce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambda1.C
@@ -0,0 +1,11 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// lambda declaration with xobj parameter
+
+// { dg-excess-errors "explicit object parameter with lambdas not implemented yet" { xfail *-*-* } }
+
+void test()
+{
+  (void)[](this auto&& self){};
+}
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.C
new file mode 100644
index 00000000000..e792eafb80b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-lambdaX2.C
@@ -0,0 +1,22 @@
+// 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-ops-mem-arrow.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C
new file mode 100644
index 00000000000..c5b2c805a2f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-arrow.C
@@ -0,0 +1,27 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// uses of member only operators (arrow)
+
+struct S {
+  int _v;
+  S* operator->(this S& self) { return &self; }
+};
+
+void non_dep()
+{
+  S s{};
+  (void)s->_v;
+}
+
+template<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..829c7137abc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-assignment.C
@@ -0,0 +1,26 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// uses of member only operators (assignment)
+
+struct S {
+  void operator=(this S&, int) {}
+};
+
+void non_dep()
+{
+  S s{};
+  s = 0;
+}
+
+template<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..1dfe764de83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-call.C
@@ -0,0 +1,39 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// uses of member only operators (call op)
+
+// execution paths for subscript with 1 argument and 0 and 2+ arguments are different
+// just to be safe, also test 0 and 2 argument cases here too
+
+struct S {
+  void operator()(this S&) {}
+  void operator()(this S&, int) {}
+  void operator()(this S&, int, int) {}
+  template<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..cee5f6e135c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-mem-subscript.C
@@ -0,0 +1,39 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// uses of member only operators (subscript)
+
+// execution paths for subscript with 1 argument and 0 and 2+ arguments are different
+// therefore we should additionally test the 0 and 2 argument cases as well
+
+struct S {
+  void operator[](this S&) {}
+  void operator[](this S&, int) {}
+  void operator[](this S&, int, int) {}
+  template<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..134c7e99a29
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-dep.C
@@ -0,0 +1,57 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// operators that are not required to be members
+// called in a dependent context (as non dependent exprs)
+// see header
+#include "explicit-obj-ops-non-mem.h"
+
+// noop, indicates which versions are ill-formed
+// I could not find a way to test the invalid cases
+// without requires expressions
+#define TEST_INVALID(X)
+
+template<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..9b7af676e4f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem-non-dep.C
@@ -0,0 +1,56 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// operators that are not required to be members
+// called in a non-dependent context
+// see header
+#include "explicit-obj-ops-non-mem.h"
+
+// noop, indicates which versions are ill-formed
+// I could not find a way to test the invalid cases
+// without requires expressions
+#define TEST_INVALID(X)
+
+void do_calls()
+{
+  Value value{};
+  TEST_OPS(value)
+  TEST_OPS(static_cast<Value&&>(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..5e0a8d993bd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-non-mem.h
@@ -0,0 +1,209 @@
+// 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..f9a642ad483
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-mem.C
@@ -0,0 +1,170 @@
+// 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..9f9c7296157
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-ops-requires-non-mem.C
@@ -0,0 +1,236 @@
+// 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.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C
new file mode 100644
index 00000000000..a1e49c88750
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl.C
@@ -0,0 +1,245 @@
+// 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 those 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" }
+}; // { dg-bogus "" }
+
+// 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..83c7756f0fd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-redecl2.C
@@ -0,0 +1,160 @@
+// 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
+
+//  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 "" }
+};
-- 
2.42.1


  reply	other threads:[~2023-11-21 13:04 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                                                                                   ` waffl3x [this message]
2023-11-22  3:22                                                                                     ` [PATCH v5 1/1] " 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                                                                                                                                 ` [PATCH v7 " waffl3x
2023-12-06  8:48                                                                                                                                   ` 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='e1dWYu-AKeY4Tx7EvAMCbTpi8vcvr8Xl_o3ZRez4jYtClI9TD5vOf8Qk41sEh6gBbyjlvCOQHPI1woLPVnHY9g9JJu8FRm6eqUtE2L0hsNc=@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).