From 32a713d9826a042b260e84dcfbfd31c619a122fb Mon Sep 17 00:00:00 2001
From: Waffl3x <waffl3x@protonmail.com>
Date: Fri, 5 Jan 2024 14:34:34 -0700
Subject: [PATCH 3/4] C++23 P0847R7 (deducing this) - diagnostics. [PR102609]

Diagnostics for xobj member functions. Also includes some diagnostics for xobj
lambdas which are not implemented here. CWG2554 is also implemented here, we
explicitly error when an xobj member function overrides a virtual function.

	PR c++/102609

gcc/c-family/ChangeLog:

	PR c++/102609
	C++23 P0847R7 (deducing this) - diagnostics.
	* c-cppbuiltin.cc (c_cpp_builtins): Define
	__cpp_explicit_this_parameter=202110L feature test macro.

gcc/cp/ChangeLog:

	PR c++/102609
	C++23 P0847R7 (deducing this) - diagnostics.
	* class.cc (resolve_address_of_overloaded_function): Diagnostics.
	* cp-tree.h (TFF_XOBJ_FUNC): Define.
	* decl.cc (grokfndecl): Diagnostics.
	(grokdeclarator): Diagnostics.
	* error.cc (dump_aggr_type): Pass TFF_XOBJ_FUNC.
	(dump_lambda_function): Formatting for xobj lambda.
	(dump_function_decl): Pass TFF_XOBJ_FUNC.
	(dump_parameters): Formatting for xobj member functions.
	(function_category): Formatting for xobj member functions.
	* parser.cc (cp_parser_decl_specifier_seq): Diagnostics.
	(cp_parser_parameter_declaration): Diagnostics.
	* search.cc (look_for_overrides_here): Make xobj member functions
	override.
	(look_for_overrides_r): Reject an overriding xobj member function
	and diagnose it.
	* semantics.cc (finish_this_expr): Diagnostics.
	* typeck.cc (cp_build_addr_expr_1): Diagnostics.

gcc/testsuite/ChangeLog:

	PR c++/102609
	C++23 P0847R7 (deducing this) - diagnostics.
	* g++.dg/cpp23/feat-cxx2b.C: Test existance and value of
	__cpp_explicit_this_parameter feature test macro.
	* g++.dg/cpp26/feat-cxx26.C: Likewise.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-A.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-B.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-C.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-D.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-E.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics1.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics2.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics3.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics4.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics5.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics6.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics7.C: New test.

Signed-off-by: Waffl3x <waffl3x@protonmail.com>
---
 gcc/c-family/c-cppbuiltin.cc                  |   1 +
 gcc/cp/class.cc                               |  55 ++++-
 gcc/cp/cp-tree.h                              |   5 +-
 gcc/cp/decl.cc                                | 138 ++++++++++--
 gcc/cp/error.cc                               |  24 +-
 gcc/cp/parser.cc                              |  38 +++-
 gcc/cp/search.cc                              |  14 +-
 gcc/cp/semantics.cc                           |  25 ++-
 gcc/cp/typeck.cc                              |  45 +++-
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C |   7 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C |   7 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C |   9 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C |   8 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C |   8 +
 .../g++.dg/cpp23/explicit-obj-diagnostics1.C  | 139 ++++++++++++
 .../g++.dg/cpp23/explicit-obj-diagnostics2.C  |  26 +++
 .../g++.dg/cpp23/explicit-obj-diagnostics3.C  |  20 ++
 .../g++.dg/cpp23/explicit-obj-diagnostics4.C  |  16 ++
 .../g++.dg/cpp23/explicit-obj-diagnostics5.C  |  23 ++
 .../g++.dg/cpp23/explicit-obj-diagnostics6.C  | 206 ++++++++++++++++++
 .../g++.dg/cpp23/explicit-obj-diagnostics7.C  |  95 ++++++++
 gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C       |   6 +
 gcc/testsuite/g++.dg/cpp26/feat-cxx26.C       |   6 +
 23 files changed, 871 insertions(+), 50 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C

diff --git a/gcc/c-family/c-cppbuiltin.cc b/gcc/c-family/c-cppbuiltin.cc
index 2d1249f29ed..73c14f6d570 100644
--- a/gcc/c-family/c-cppbuiltin.cc
+++ b/gcc/c-family/c-cppbuiltin.cc
@@ -1082,6 +1082,7 @@ c_cpp_builtins (cpp_reader *pfile)
 	  cpp_define (pfile, "__cpp_named_character_escapes=202207L");
 	  cpp_define (pfile, "__cpp_static_call_operator=202207L");
 	  cpp_define (pfile, "__cpp_implicit_move=202207L");
+	  cpp_define (pfile, "__cpp_explicit_this_parameter=202110L");
 	}
       if (cxx_dialect > cxx23)
 	{
diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
index 4b37e1176cd..7322f10b8b1 100644
--- a/gcc/cp/class.cc
+++ b/gcc/cp/class.cc
@@ -8968,20 +8968,51 @@ resolve_address_of_overloaded_function (tree target_type,
   fn = TREE_PURPOSE (matches);
 
   if (DECL_OBJECT_MEMBER_FUNCTION_P (fn)
-      && !(complain & tf_ptrmem_ok) && !flag_ms_extensions)
-    {
-      static int explained;
-
-      if (!(complain & tf_error))
+      && !(complain & tf_ptrmem_ok))
+    {
+      /* Previously we allowed this behavior for iobj member functions when the
+	 -fms-extensions flag is passed as MSVC allows this as a language
+	 extension.  MSVC also allows this for xobj member functions, but the
+	 documentation for -fms-extensions states it's purpose is to support
+	 the use of microsoft headers.  Until otherwise demonstrated, we should
+	 assume xobj member functions are not used in this manner in microsoft
+	 headers and indiscriminately forbid the incorrect syntax instead of
+	 supporting it for non-legacy uses.  This should hopefully encourage
+	 conformance going forward.
+	 This comment is referred to in typeck.cc:cp_build_addr_expr_1.  */
+      if (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && flag_ms_extensions)
+	/* Early escape.  */;
+      else if (!(complain & tf_error))
 	return error_mark_node;
-
-      auto_diagnostic_group d;
-      if (permerror (input_location, "assuming pointer to member %qD", fn)
-	  && !explained)
+      else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+	{
+	  auto_diagnostic_group d;
+	  /* Should match the error in typeck.cc:cp_build_addr_expr_1.
+	     We seem to lack the details here to match that diagnostic exactly,
+	     perhaps this could be fixed in the future? See PR113075 bug 2.  */
+	  error_at (input_location,
+		    "ISO C++ forbids taking the address of an unqualified"
+		    " or parenthesized non-static member function to form"
+		    " a pointer to explicit object member function.");
+	  /* This is incorrect, see PR113075 bug 3.  */
+	  inform (input_location,
+		  "a pointer to explicit object member function can only be "
+		  "formed with %<&%E%>", fn);
+	}
+      else
 	{
-	  inform (input_location, "(a pointer to member can only be "
-		  "formed with %<&%E%>)", fn);
-	  explained = 1;
+	  static int explained;
+	  gcc_assert (DECL_IOBJ_MEMBER_FUNCTION_P (fn) && !flag_ms_extensions);
+	  /* Is there a reason this error message doesn't match the one in
+	     typeck.cc:cp_build_addr_expr_1?  */
+	  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 bdc4d7e3349..8f1e0df6f06 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6181,7 +6181,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)
@@ -6199,6 +6201,7 @@ enum auto_deduction_context
 #define TFF_NO_OMIT_DEFAULT_TEMPLATE_ARGUMENTS	(1 << 12)
 #define TFF_NO_TEMPLATE_BINDINGS		(1 << 13)
 #define TFF_POINTER		                (1 << 14)
+#define TFF_XOBJ_FUNC				(1 << 15)
 
 /* These constants can be used as bit flags to control strip_typedefs.
 
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 7b792ebc5c1..91d4cfa01f8 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10676,24 +10676,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))
@@ -13188,17 +13194,85 @@ grokdeclarator (const cp_declarator *declarator,
 		/* There is no need to iterate over the list,
 		   only the first parm can be a valid xobj parm.  */
 		if (!parm_list || TREE_PURPOSE (parm_list) != this_identifier)
-		  return false;
+		  return NULL_TREE;
 		/* If we make it here, we are looking at an xobj parm.
 
 		   Non-null 'purpose' usually means the parm has a default
 		   argument, we don't want to violate this assumption.  */
 		TREE_PURPOSE (parm_list) = NULL_TREE;
-		return true;
+		return TREE_VALUE (parm_list);
 	      };
 
-	    is_xobj_member_function
+	    tree xobj_parm
 	      = find_xobj_parm (declarator->u.function.parameters);
+	    is_xobj_member_function = xobj_parm;
+
+	    if (xobj_parm && cxx_dialect < cxx23)
+	      pedwarn (DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions,
+		       "explicit object member function only available "
+		       "with %<-std=c++23%> or %<-std=gnu++23%>");
+
+	    if (xobj_parm && decl_context == TYPENAME)
+	      {
+		/* We inform in every case, just differently depending on what
+		   case it is.  */
+		auto_diagnostic_group d;
+		bool ptr_type = true;
+		/* If declarator->kind is cdk_function and we are at the end of
+		   the declarator chain, we are looking at a function type.  */
+		if (!declarator->declarator)
+		  {
+		    error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			      "a function type cannot "
+			      "have an explicit object parameter");
+		    ptr_type = false;
+		  }
+		else if (declarator->declarator->kind == cdk_pointer)
+		  error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			    "a pointer to function type cannot "
+			    "have an explicit object parameter");
+		else if (declarator->declarator->kind == cdk_ptrmem)
+		  error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			    "a pointer to member function type "
+			    "cannot have an explicit object parameter");
+		else
+		  gcc_unreachable ();
+
+		/* The locations being used here are probably not correct.  */
+		if (ptr_type)
+		  inform (DECL_SOURCE_LOCATION (xobj_parm),
+			  "the type of a pointer to explicit object member "
+			  "function is a regular pointer to function type");
+		else
+		  inform (DECL_SOURCE_LOCATION (xobj_parm),
+			  "the type of an explicit object "
+			  "member function is a regular function type");
+		/* Ideally we should synthesize the correct syntax
+		   for the user, perhaps this could be added later.  */
+	      }
+	    /* Since a valid xobj parm has its purpose cleared in find_xobj_parm
+	       the first parm node will never erroneously be detected here.  */
+	    {
+	      auto_diagnostic_group d;
+	      bool bad_xobj_parm_encountered = false;
+	      for (tree parm = declarator->u.function.parameters;
+		   parm && parm != void_list_node;
+		   parm = TREE_CHAIN (parm))
+		{
+		  if (TREE_PURPOSE (parm) != this_identifier)
+		    continue;
+		  bad_xobj_parm_encountered = true;
+		  gcc_rich_location bad_xobj_parm
+		    (DECL_SOURCE_LOCATION (TREE_VALUE (parm)));
+		  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;
@@ -13486,6 +13560,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
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 626f9cab9bd..27282ccae4a 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -840,10 +840,14 @@ dump_aggr_type (cxx_pretty_printer *pp, tree t, int flags)
     {
       /* A lambda's "type" is essentially its signature.  */
       pp_string (pp, M_("<lambda"));
-      if (lambda_function (t))
-	dump_parameters (pp,
-                         FUNCTION_FIRST_USER_PARMTYPE (lambda_function (t)),
-			 flags);
+      tree const fn = lambda_function (t);
+      if (fn)
+	{
+	  int const parm_flags
+	    = DECL_XOBJ_MEMBER_FUNCTION_P (fn) ? TFF_XOBJ_FUNC | flags
+					       : flags;
+	  dump_parameters (pp, FUNCTION_FIRST_USER_PARMTYPE (fn), parm_flags);
+	}
       pp_greater (pp);
     }
   else if (!decl || IDENTIFIER_ANON_P (DECL_NAME (decl)))
@@ -1712,7 +1716,9 @@ dump_lambda_function (cxx_pretty_printer *pp,
 {
   /* A lambda's signature is essentially its "type".  */
   dump_type (pp, DECL_CONTEXT (fn), flags);
-  if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
+  if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+    /* Early escape.  */;
+  else if (TREE_CODE (TREE_TYPE (fn)) == FUNCTION_TYPE)
     {
       pp->padding = pp_before;
       pp_c_ws_string (pp, "static");
@@ -1833,7 +1839,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
-      dump_parameters (pp, parmtypes, flags);
+      int const parm_flags
+	= DECL_XOBJ_MEMBER_FUNCTION_P (t) ? TFF_XOBJ_FUNC | flags : flags;
+      dump_parameters (pp, parmtypes, parm_flags);
 
       if (TREE_CODE (fntype) == METHOD_TYPE)
 	{
@@ -1912,6 +1920,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;
@@ -3687,6 +3697,8 @@ function_category (tree fn)
 	return _("In destructor %qD");
       else if (LAMBDA_FUNCTION_P (fn))
 	return _("In lambda function");
+      else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+	return _("In explicit object member function %qD");
       else
 	return _("In member function %qD");
     }
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 3f0a31b7407..f3b228d3251 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16073,6 +16073,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)
     {
@@ -16145,12 +16147,32 @@ 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);
+		  /* Works, need to add tests for it though.  */
+		  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;
 	}
@@ -25542,12 +25564,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
@@ -25618,6 +25642,18 @@ cp_parser_parameter_declaration (cp_parser *parser,
 
   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;
diff --git a/gcc/cp/search.cc b/gcc/cp/search.cc
index 3473d66b88c..d2ef93826e0 100644
--- a/gcc/cp/search.cc
+++ b/gcc/cp/search.cc
@@ -2224,10 +2224,13 @@ look_for_overrides_here (tree type, tree fndecl)
 	/* Not a virtual.  */;
       else if (DECL_CONTEXT (fn) != type)
 	/* Introduced with a using declaration.  */;
-      else if (DECL_STATIC_FUNCTION_P (fndecl))
+      else if (DECL_STATIC_FUNCTION_P (fndecl)
+	       || DECL_XOBJ_MEMBER_FUNCTION_P (fndecl))
 	{
 	  tree btypes = TYPE_ARG_TYPES (TREE_TYPE (fn));
 	  tree dtypes = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
+	  dtypes = DECL_XOBJ_MEMBER_FUNCTION_P (fndecl) ? TREE_CHAIN (dtypes)
+							: dtypes;
 	  if (compparms (TREE_CHAIN (btypes), dtypes))
 	    return fn;
 	}
@@ -2255,6 +2258,15 @@ look_for_overrides_r (tree type, tree fndecl)
 	  error ("%q+#D cannot be declared", fndecl);
 	  error ("  since %q+#D declared in base class", fn);
 	}
+      else if (DECL_XOBJ_MEMBER_FUNCTION_P (fndecl))
+	{
+	  auto_diagnostic_group d;
+	  error_at (DECL_SOURCE_LOCATION (fndecl),
+		    "explicit object member function "
+		    "overrides virtual function");
+	  inform (DECL_SOURCE_LOCATION (fn),
+		  "virtual function declared here");
+	}
       else
 	{
 	  /* It's definitely virtual, even if not explicitly set.  */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 3bc426fa130..c0c97600483 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3091,7 +3091,30 @@ finish_this_expr (void)
     return rvalue (result);
 
   tree fn = current_nonlambda_function ();
-  if (fn && DECL_STATIC_FUNCTION_P (fn))
+  if (fn && DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+    {
+      auto_diagnostic_group d;
+      error ("%<this%> is unavailable for explicit object member "
+	     "functions");
+      tree xobj_parm = DECL_ARGUMENTS (fn);
+      gcc_assert (xobj_parm);
+      tree parm_name = DECL_NAME (xobj_parm);
+
+      static tree remembered_fn = NULL_TREE;
+      /* Only output this diagnostic once per function.  */
+      if (remembered_fn == fn)
+	/* Early escape.  */;
+      else if (parm_name)
+	inform (DECL_SOURCE_LOCATION (xobj_parm),
+		"use explicit object parameter %qs instead",
+		IDENTIFIER_POINTER (parm_name));
+      else
+	inform (DECL_SOURCE_LOCATION (xobj_parm),
+		"name the explicit object parameter");
+
+      remembered_fn = fn;
+    }
+  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/typeck.cc b/gcc/cp/typeck.cc
index ef5df6b404c..59a61c54f19 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -7069,26 +7069,47 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain)
       tree fn = get_first_fn (TREE_OPERAND (arg, 1));
       if (!mark_used (fn, complain) && !(complain & tf_error))
 	return error_mark_node;
-
-      if (! flag_ms_extensions)
+      /* Until microsoft headers are known to incorrectly take the address of
+	 unqualified xobj member functions we should not support this
+	 extension.
+	 See comment in class.cc:resolve_address_of_overloaded_function for
+	 the extended reasoning.  */
+      if (!flag_ms_extensions || DECL_XOBJ_MEMBER_FUNCTION_P (fn))
 	{
+	  auto_diagnostic_group d;
 	  tree name = DECL_NAME (fn);
 	  if (!(complain & tf_error))
 	    return error_mark_node;
 	  else if (current_class_type
 		   && TREE_OPERAND (arg, 0) == current_class_ref)
 	    /* An expression like &memfn.  */
-	    permerror (loc,
-		       "ISO C++ forbids taking the address of an unqualified"
-		       " or parenthesized non-static member function to form"
-		       " a pointer to member function.  Say %<&%T::%D%>",
-		       base, name);
+	    if (!DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+	      permerror (loc,
+			 "ISO C++ forbids taking the address of an unqualified"
+			 " or parenthesized non-static member function to form"
+			 " a pointer to member function.  Say %<&%T::%D%>",
+			 base, name);
+	    else
+	      error_at (loc,
+			"ISO C++ forbids taking the address of an unqualified"
+			" or parenthesized non-static member function to form"
+			" a pointer to explicit object member function");
 	  else
-	    permerror (loc,
-		       "ISO C++ forbids taking the address of a bound member"
-		       " function to form a pointer to member function."
-		       "  Say %<&%T::%D%>",
-		       base, name);
+	    if (!DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+	      permerror (loc,
+			 "ISO C++ forbids taking the address of a bound member"
+			 " function to form a pointer to member function."
+			 "  Say %<&%T::%D%>",
+			 base, name);
+	    else
+	      error_at (loc,
+			"ISO C++ forbids taking the address of a bound member"
+			" function to form a pointer to explicit object member"
+			" function");
+	  if (DECL_XOBJ_MEMBER_FUNCTION_P (fn))
+	    inform (loc,
+		    "a pointer to explicit object member function can only be "
+		    "formed with %<&%T::%D%>", base, name);
 	}
       arg = build_offset_ref (base, fn, /*address_p=*/true, complain);
     }
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
new file mode 100644
index 00000000000..5043e91bb28
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
new file mode 100644
index 00000000000..fb2a6a0e41b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
@@ -0,0 +1,7 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+
+struct S {
+    void f(this S); // { dg-error {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
new file mode 100644
index 00000000000..182e294c883
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
@@ -0,0 +1,9 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// don't pass in -pedantic-errors
+// { dg-options "" }
+
+struct S {
+    void f(this S); // { dg-warning {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
new file mode 100644
index 00000000000..49b7ea0df44
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
@@ -0,0 +1,8 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions" }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
new file mode 100644
index 00000000000..411b70c3d9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
@@ -0,0 +1,8 @@
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions -pedantic-errors" }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
new file mode 100644
index 00000000000..dfac1188fba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
@@ -0,0 +1,139 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of xobj member functions that have member function qualifiers.
+
+struct S {
+    void f_value_0(this S) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_1(this S) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_2(this S) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_3(this S) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_value_4(this S) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_value_5(this S) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_6(this S) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_7(this S) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_8(this S) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_9(this S) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_A(this S) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_ref_0(this S&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_1(this S&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_2(this S&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_3(this S&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_ref_4(this S&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_ref_5(this S&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_6(this S&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_7(this S&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_8(this S&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_9(this S&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_A(this S&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_refref_0(this S&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_1(this S&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_2(this S&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_3(this S&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_refref_4(this S&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_refref_5(this S&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_6(this S&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_7(this S&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_8(this S&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_9(this S&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_A(this S&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cref_0(this S const&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_1(this S const&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_2(this S const&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_3(this S const&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cref_4(this S const&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cref_5(this S const&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_6(this S const&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_7(this S const&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_8(this S const&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_9(this S const&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_A(this S const&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_crefref_0(this S const&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_1(this S const&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_2(this S const&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_3(this S const&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_crefref_4(this S const&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_crefref_5(this S const&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_6(this S const&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_7(this S const&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_8(this S const&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_9(this S const&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_A(this S const&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_vref_0(this S volatile&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_1(this S volatile&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_2(this S volatile&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_3(this S volatile&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vref_4(this S volatile&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vref_5(this S volatile&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_6(this S volatile&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_7(this S volatile&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_8(this S volatile&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_9(this S volatile&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_A(this S volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_vrefref_0(this S volatile&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_1(this S volatile&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_2(this S volatile&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_3(this S volatile&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vrefref_4(this S volatile&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vrefref_5(this S volatile&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_6(this S volatile&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_7(this S volatile&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_8(this S volatile&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_9(this S volatile&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_A(this S volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cvref_0(this S const volatile&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_1(this S const volatile&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_2(this S const volatile&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_3(this S const volatile&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvref_4(this S const volatile&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvref_5(this S const volatile&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_6(this S const volatile&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_7(this S const volatile&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_8(this S const volatile&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_9(this S const volatile&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_A(this S const volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cvrefref_0(this S const volatile&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_1(this S const volatile&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_2(this S const volatile&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_3(this S const volatile&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvrefref_4(this S const volatile&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvrefref_5(this S const volatile&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_6(this S const volatile&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_7(this S const volatile&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_8(this S const volatile&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_9(this S const volatile&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_A(this S const volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    template<typename Self> void d_templ_0(this Self&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    template<typename Self> void d_templ_1(this Self&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    template<typename Self> void d_templ_2(this Self&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    template<typename Self> void d_templ_3(this Self&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    template<typename Self> void d_templ_4(this Self&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    template<typename Self> void d_templ_5(this Self&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_6(this Self&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_7(this Self&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_8(this Self&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_9(this Self&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_A(this Self&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void d_auto_0(this auto&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void d_auto_1(this auto&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void d_auto_2(this auto&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void d_auto_3(this auto&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void d_auto_4(this auto&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void d_auto_5(this auto&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_6(this auto&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_7(this auto&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_8(this auto&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_9(this auto&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_A(this auto&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
new file mode 100644
index 00000000000..771200b839e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
@@ -0,0 +1,26 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of incorrect uses of 'this' in declarations and definitions
+
+using func_type = void(this int); // { dg-line func_type_line }
+// { dg-error "a function type cannot have an explicit object parameter" "" { target *-*-* } func_type_line }
+// { dg-note "the type of an explicit object member function is a regular function type" "" { target *-*-* } func_type_line }
+
+using func_ptr_type = void(*)(this int); // { dg-line func_ptr_type_line }
+// { dg-error "a pointer to function type cannot have an explicit object parameter" "" { target *-*-* } func_ptr_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } func_ptr_type_line }
+
+struct S {
+    static void f(this S) {} // { dg-line static_member_func_line }
+};
+// { dg-error "an explicit object member function cannot be 'static'" "" { target *-*-* } static_member_func_line }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } static_member_func_line }
+
+using mem_func_type = void (S::*)(this S&); // { dg-line mem_func_type_line }
+// { dg-error "a pointer to member function type cannot have an explicit object parameter" "" { target *-*-* } mem_func_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } mem_func_type_line }
+
+void f(this int); // { dg-error "a non-member function cannot have an explicit object parameter" }
+void f(this int) {} // { dg-error "a non-member function cannot have an explicit object parameter" }
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C
new file mode 100644
index 00000000000..ec091d6ca67
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C
@@ -0,0 +1,20 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of an xobj parameter declared with a default argument
+
+struct S {
+  void f0(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+  void f1(this S = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+  void f2(this S);
+  void f10(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+  void f11(this S s = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+  void f12(this S s);
+};
+
+void S::f1(this S) {}
+void S::f2(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+
+void S::f11(this S s) {}
+void S::f12(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
new file mode 100644
index 00000000000..1744b3f2299
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
@@ -0,0 +1,16 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// location diagnostic text when an error is emitted from an xobj member function
+// this does not test for specific ill-formed code, just the additional diagnostic message
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S {
+  void f(this S s) {
+    // The specific diagnosis issued here does not matter
+    // we just need to force an error to be emitted
+    +s; // { dg-error "" }
+  }
+};
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
new file mode 100644
index 00000000000..7ec43f641b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
@@ -0,0 +1,23 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of invalid uses of 'this' in body of xobj member functions
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S0 {
+  int _n;
+  void f(this S0& s) { // { dg-note {use explicit object parameter 's' instead} } 
+    this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" }
+    // suppress unused variable warning
+    static_cast<void>(s);
+  }
+};
+
+struct S1 {
+  int _n;
+  void f(this S1&) { // { dg-note "name the explicit object parameter" }
+    this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" }
+  }
+};
+
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..77ace49b843
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
@@ -0,0 +1,206 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis when taking address of an unqualified xobj member function
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S {
+  void f(this S&) {}
+
+  void g(this S&) {}
+  void g(this S&, int) {}
+
+  void test0() {
+    void (*fp)(S&) = &f; // { dg-line line_sf }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } line_sf }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&S::f'} "" { target *-*-* } line_sf }
+    void (*gp)(S&) = &g; // { dg-line line_sg }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } line_sg }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&S::g'} "" { target *-*-* } line_sg }
+  }
+
+  void test1(this S& self) {
+    void (*fp)(S&) = &self.f; // { dg-line s_test1_f }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } s_test1_f }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&S::f'} "" { target *-*-* } s_test1_f }
+    void (*gp)(S&) = &self.g; // { dg-line s_test1_g }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_test1_g }
+    // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_test1_g }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&S::g'} "" { target *-*-* } s_test1_g }
+  }
+};
+
+void test0()
+{
+  S s{};
+
+  void (*fp)(S&) = &s.f; // { dg-line s_free_test0_f }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } s_free_test0_f }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&S::f'} "" { target *-*-* } s_free_test0_f }
+  void (*gp)(S&) = &s.g; // { dg-line s_free_test0_g }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_free_test0_g }
+  // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } s_free_test0_g }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&S::g'} "" { target *-*-* } s_free_test0_g }
+}
+
+struct D;
+
+struct B {
+  void fb(this B&) {}
+
+  void gb(this B&) {}
+  void gb(this B&, int) {}
+
+  void fd(this D&) {}
+
+  void gd(this D&) {}
+  void gd(this D&, int) {}
+};
+
+struct D : B {
+  void fb2(this B&) {}
+
+  void gb2(this B&) {}
+  void gb2(this B&, int) {}
+
+  void fd2(this D&) {}
+
+  void gd2(this D&) {}
+  void gd2(this D&, int) {}
+
+  void test0() {
+    void (*fbp)(B&) = &fb; // { dg-line d_test0_fb }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_fb }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb'} "" { target *-*-* } d_test0_fb }
+    void (*gbp)(B&) = &gb; // { dg-line d_test0_gb }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_gb }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb'} "PR113075" { xfail *-*-* } d_test0_gb }
+    // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gb'} "PR113075" { xfail *-*-* } d_test0_gb }
+
+    void (*fdp)(D&) = &fd; // { dg-line d_test0_fd }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_fd }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd'} "" { target *-*-* } d_test0_fd }
+    void (*gdp)(D&) = &gd; // { dg-line d_test0_gd }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test0_gd }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd'} "PR113075" { xfail *-*-* } d_test0_gd }
+    // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gd'} "PR113075" { xfail *-*-* } d_test0_gd }
+  }
+
+  void test1(this B& self) {
+    void (*fbp)(B&) = &self.fb; // { dg-line d_test1_fb }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test1_fb }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&B::fb'} "" { target *-*-* } d_test1_fb }
+    void (*gbp)(B&) = &self.gb; // { dg-line d_test1_gb }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gb }
+    // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gb }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&B::gb'} "" { target *-*-* } d_test1_gb }
+
+    void (*fdp)(D&) = &self.fd; // { dg-line d_test1_fd }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test1_fd }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&B::fd'} "" { target *-*-* } d_test1_fd }
+    void (*gdp)(D&) = &self.gd; // { dg-line d_test1_gd }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gd }
+    // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test1_gd }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&B::gd'} "" { target *-*-* } d_test1_gd }
+  }
+
+  void test2(this D& self) {
+    void (*fbp)(B&) = &self.fb; // { dg-line d_test2_fb }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test2_fb }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb'} "" { target *-*-* } d_test2_fb }
+    void (*gbp)(B&) = &self.gb; // { dg-line d_test2_gb }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gb }
+    // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gb }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb'} "PR113075" { xfail *-*-* } d_test2_gb }
+    // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gb'} "PR113075" { xfail *-*-* } d_test2_gb }
+
+    void (*fdp)(D&) = &self.fd; // { dg-line d_test2_fd }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test2_fd }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd'} "" { target *-*-* } d_test2_fd }
+    void (*gdp)(D&) = &self.gd; // { dg-line d_test2_gd }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gd }
+    // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test2_gd }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd'} "PR113075" { xfail *-*-* } d_test2_gd }
+    // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gd'} "PR113075" { xfail *-*-* } d_test2_gd }
+  }
+
+  void test3() {
+    void (*fbp)(B&) = &fb2; // { dg-line d_test3_fb2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_fb2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb2'} "" { target *-*-* } d_test3_fb2 }
+    void (*gbp)(B&) = &gb2; // { dg-line d_test3_gb2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_gb2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb2'} "" { target *-*-* } d_test3_gb2 }
+
+    void (*fdp)(D&) = &fd2; // { dg-line d_test3_fd2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_fd2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd2'} "" { target *-*-* } d_test3_fd2 }
+    void (*gdp)(D&) = &gd2; // { dg-line d_test3_gd2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "" { target *-*-* } d_test3_gd2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd2'} "" { target *-*-* } d_test3_gd2 }
+  }
+
+  void test4(this D& self) {
+    void (*fbp)(B&) = &self.fb2; // { dg-line d_test4_fb2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test4_fb2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb2'} "" { target *-*-* } d_test4_fb2 }
+    void (*gbp)(B&) = &self.gb2; // { dg-line d_test4_gb2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gb2 }
+    // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gb2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb2'} "" { target *-*-* } d_test4_gb2 }
+
+    void (*fdp)(D&) = &self.fd2; // { dg-line d_test4_fd2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_test4_fd2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd2'} "" { target *-*-* } d_test4_fd2 }
+    void (*gdp)(D&) = &self.gd2; // { dg-line d_test4_gd2 }
+    // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gd2 }
+    // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_test4_gd2 }
+    // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd2'} "" { target *-*-* } d_test4_gd2 }
+  }
+};
+
+void test1()
+{
+  D d{};
+
+  void (*fbp)(B&) = &d.fb; // { dg-line d_free_test1_fb }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test1_fb }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb'} "" { target *-*-* } d_free_test1_fb }
+  void (*gbp)(B&) = &d.gb; // { dg-line d_free_test1_gb }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gb }
+  // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gb }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb'} "PR113075" { xfail *-*-* } d_free_test1_gb }
+  // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gb'} "PR113075" { xfail *-*-* } d_free_test1_gb }
+
+  void (*fdp)(D&) = &d.fd; // { dg-line d_free_test1_fd }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test1_fd }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd'} "" { target *-*-* } d_free_test1_fd }
+  void (*gdp)(D&) = &d.gd; // { dg-line d_free_test1_gd }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gd }
+  // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test1_gd }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd'} "PR113075" { xfail *-*-* } d_free_test1_gd }
+  // { dg-bogus {a pointer to explicit object member function can only be formed with '&B::gd'} "PR113075" { xfail *-*-* } d_free_test1_gd }
+}
+
+void test2()
+{
+  D d{};
+
+  void (*fbp)(B&) = &d.fb2; // { dg-line d_free_test2_fb2 }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test2_fb2 }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::fb2'} "" { target *-*-* } d_free_test2_fb2 }
+  void (*gbp)(B&) = &d.gb2; // { dg-line d_free_test2_gb2 }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gb2 }
+  // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gb2 }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::gb2'} "" { target *-*-* } d_free_test2_gb2 }
+
+  void (*fdp)(D&) = &d.fd2; // { dg-line d_free_test2_fd2 }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "" { target *-*-* } d_free_test2_fd2 }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::fd2'} "" { target *-*-* } d_free_test2_fd2 }
+  void (*gdp)(D&) = &d.gd2; // { dg-line d_free_test2_gd2 }
+  // { dg-error {ISO C\+\+ forbids taking the address of a bound member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gd2 }
+  // { dg-bogus {ISO C\+\+ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to explicit object member function} "PR113075" { xfail *-*-* } d_free_test2_gd2 }
+  // { dg-note {a pointer to explicit object member function can only be formed with '&D::gd2'} "" { target *-*-* } d_free_test2_gd2 }
+}
+
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..023cdc2e0fe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C
@@ -0,0 +1,95 @@
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// diagnose xobj member functions that override
+// or are declared as virtual, override, or final
+
+struct B {
+  virtual void f0() {} // { dg-note {virtual function declared here} }
+  virtual void f1() {} // { dg-note {virtual function declared here} }
+  virtual void f2() {} // { dg-note {virtual function declared here} }
+  virtual void f3() {} // { dg-note {virtual function declared here} }
+  virtual void f4() {} // { dg-note {virtual function declared here} }
+  virtual void f5() {} // { dg-note {virtual function declared here} }
+  virtual void f6() {} // { dg-note {virtual function declared here} }
+  virtual void f7() {} // { dg-note {virtual function declared here} }
+  virtual ~B() {}
+};
+
+struct S : B {
+  virtual void f0(this S&) {}		     // { dg-line line_f0 }
+  virtual void f1(this S&) override {}	     // { dg-line line_f1 }
+  virtual void f2(this S&) final {}	     // { dg-line line_f2 }
+  virtual void f3(this S&) override final {} // { dg-line line_f3 }
+  void f4(this S&) {}			     // { dg-line line_f4 }
+  void f5(this S&) override {}		     // { dg-line line_f5 }
+  void f6(this S&) final {}		     // { dg-line line_f6 }
+  void f7(this S&) override final {}	     // { dg-line line_f7 }
+};
+
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f0 }
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f1 }
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f2 }
+// { dg-error {an explicit object member function cannot be 'virtual'} "" { target *-*-* } line_f3 }
+
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f0 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f1 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f2 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f3 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f4 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f5 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f6 }
+// { dg-error {explicit object member function overrides virtual function} "" { target *-*-* } line_f7 }
+
+// these should be suppressed, the wording conflicts with the error
+// the issue is not that they don't override, it's that they do override, and that isn't allowed
+// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_f1 }
+// { dg-bogus "marked 'final', but is not virtual"	 "" { xfail *-*-* } line_f2 }
+// { dg-bogus "marked '(override|final)'"		 "" { xfail *-*-* } line_f3 }
+
+// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_f5 }
+// { dg-bogus "marked 'final', but is not virtual"	 "" { xfail *-*-* } line_f6 }
+// { dg-bogus "marked '(override|final)'"		 "" { xfail *-*-* } line_f7 }
+
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f0 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f1 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f2 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_f3 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f4 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f5 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f6 }
+// { dg-note "explicit object parameter declared here" "" { xfail *-*-* } line_f7 }
+
+struct S1 {
+  virtual void f0(this S&) {}		     // { dg-line line_S1_f0 }
+  virtual void f1(this S&) override {}	     // { dg-line line_S1_f1 }
+  virtual void f2(this S&) final {}	     // { dg-line line_S1_f2 }
+  virtual void f3(this S&) override final {} // { dg-line line_S1_f3 }
+  void f4(this S&) {}
+  void f5(this S&) override {}		     // { dg-line line_S1_f5 }
+  void f6(this S&) final {}		     // { dg-line line_S1_f6 }
+  void f7(this S&) override final {}	     // { dg-line line_S1_f7 }
+};
+
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f0 }
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f1 }
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f2 }
+// { dg-error "an explicit object member function cannot be 'virtual'" "" { target *-*-* } line_S1_f3 }
+
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f0 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f1 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f2 }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } line_S1_f3 }
+
+// I think I want these suppressed, but theres a decent argument that they should stay
+// theres arguably no reason the error about virtual should suppress these
+// { dg-bogus "marked 'override', but does not override" "" { xfail *-*-* } line_S1_f1 }
+// { dg-bogus "marked 'final', but is not virtual"	 "" { xfail *-*-* } line_S1_f2 }
+// { dg-bogus "marked '(override|final)'"		 "" { xfail *-*-* } line_S1_f3 }
+
+// I don't want to suppress these, there is nothing that could possibly be overridden
+// even if the xobj param was removed
+// { dg-error "marked 'override', but does not override" "" { target *-*-* } line_S1_f5 }
+// { dg-error "marked 'final', but is not virtual"	 "" { target *-*-* } line_S1_f6 }
+// { dg-error "marked '(override|final)'"		 "" { target *-*-* } line_S1_f7 }
+
diff --git a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
index 2b21bd1bc0d..d81aab75638 100644
--- a/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
+++ b/gcc/testsuite/g++.dg/cpp23/feat-cxx2b.C
@@ -578,6 +578,12 @@
 #  error "__cpp_implicit_move != 202207"
 #endif
 
+#ifndef __cpp_explicit_this_parameter
+#  error "__cpp_explicit_this_parameter"
+#elif __cpp_explicit_this_parameter != 202110
+#  error "__cpp_explicit_this_parameter != 202110"
+#endif
+
 #ifndef __cpp_auto_cast
 #  error "__cpp_auto_cast"
 #elif __cpp_auto_cast != 202110
diff --git a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
index 4507ea07d1c..d19fca49995 100644
--- a/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
+++ b/gcc/testsuite/g++.dg/cpp26/feat-cxx26.C
@@ -578,6 +578,12 @@
 #  error "__cpp_implicit_move != 202207"
 #endif
 
+#ifndef __cpp_explicit_this_parameter
+#  error "__cpp_explicit_this_parameter"
+#elif __cpp_explicit_this_parameter != 202110
+#  error "__cpp_explicit_this_parameter != 202110"
+#endif
+
 #ifndef __cpp_auto_cast
 #  error "__cpp_auto_cast"
 #elif __cpp_auto_cast != 202110
-- 
2.43.0

