diff mbox series

[2/2] c++: Extended diagnostics for P0847R7 (Deducing This) [PR102609]

Message ID 1xDDGk_jeMvdx0yPQAJHLLCzkYYCTdAk2f11ONoe0TAT4MBfhz7MeJnVZcuTwFFR7_CtGNiknplSXIjf8sjogjaYbJNmgXh9M60EC7-DgN0=@protonmail.com
State New
Headers show
Series None | expand

Commit Message

waffl3x Aug. 31, 2023, 6:46 a.m. UTC
Tested and Bootstrapped and tested on x86_64-linux with no regressions.

There's a few test cases that are not properly diagnosed yet, but everything
that is known to fail is marked as xfail. When I tested the new tests I got 390
expected passes and 64 expected failures.

Alright, I have a flight to catch, hopefully my patch is at least
satisfactory. There's a few thing's I'm not totally happy with so I will be happy to address
any criticism as soon as I am able. 

Thanks again to everyone who helped me prepare this, I probably would have
given up otherwise.
diff mbox series

Patch

From d82a34432364b391abde44a23ceacb3c398a519d Mon Sep 17 00:00:00 2001
From: Waffl3x <waffl3x@protonmail.com>
Date: Thu, 31 Aug 2023 02:13:52 -0400
Subject: [PATCH 2/2] P0847R7 (deducing this) Extended diagnostics

gcc/cp/ChangeLog:

	* cp-tree.h (TFF_XOBJ_FUNC): new flag to identify that an explicit object member function's parameters are being printed
	* decl.cc (grokdeclarator): diagnose type declarations using 'this', diagnose function declarations using this
	(grokparms): diagnose an explicit object parameter with a default argument
	* error.cc (dump_function_decl): prevent explicit object member functions from being pretty printed with 'static', communicate to dump_parameters that the current function is an explicit object member function
	(dump_parameters): pretty print the explicit object parameter with a leading 'this'
	(function_category): support reporting that the current context is within an explicit object member function
	* parser.cc (cp_parser_decl_specifier_seq): diagnose uses of 'this' when it is not the first specifier of a decl-specifier-seq
	(cp_parser_parameter_declaration_list): diagnose when an explicit object parameter is not the first parameter
	* semantics.cc (finish_this_expr): diagnose when 'this' is used in the body of an explicit object member function

gcc/testsuite/ChangeLog:

	* g++.dg/cpp23/explicit-object-param-ill-formed1.C: New test.
	* g++.dg/cpp23/explicit-object-param-ill-formed2.C: New test.
	* g++.dg/cpp23/explicit-object-param-ill-formed3.C: New test.
	* g++.dg/cpp23/explicit-object-param-ill-formed4.C: New test.
	* g++.dg/cpp23/explicit-object-param-ill-formed5.C: New test.
	* g++.dg/cpp23/explicit-object-param-ill-formed6.C: New test.
	* g++.dg/cpp23/explicit-object-param-no-cxx23.C: New test.

Signed-off-by: Waffl3x <waffl3x@protonmail.com>
---
 gcc/cp/cp-tree.h                              |   5 +-
 gcc/cp/decl.cc                                |  71 +++++++++
 gcc/cp/error.cc                               |  12 +-
 gcc/cp/parser.cc                              |  62 ++++++++
 gcc/cp/semantics.cc                           |  24 ++-
 .../cpp23/explicit-object-param-ill-formed1.C | 141 ++++++++++++++++++
 .../cpp23/explicit-object-param-ill-formed2.C |  25 ++++
 .../cpp23/explicit-object-param-ill-formed3.C | 102 +++++++++++++
 .../cpp23/explicit-object-param-ill-formed4.C |  42 ++++++
 .../cpp23/explicit-object-param-ill-formed5.C |   9 ++
 .../cpp23/explicit-object-param-ill-formed6.C |  22 +++
 .../cpp23/explicit-object-param-no-cxx23.C    |   7 +
 12 files changed, 517 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-no-cxx23.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3aca23da105..325d0fb3ca0 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6111,7 +6111,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)
@@ -6129,6 +6131,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 a6d0cfb0ecc..da9235ced30 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -13080,6 +13080,67 @@  grokdeclarator (const cp_declarator *declarator,
 	    tree xobj_parm
 	      = get_xobj_parm (declarator->u.function.parameters);
 	    is_xobj_member_function = xobj_parm;
+	    if (!xobj_parm)
+	      /* early escape */;
+	    else if (decl_context == TYPENAME)
+	      {
+		bool ptr_type = true;
+		if (!declarator->declarator)
+		  {
+		    error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			      "a function type cannot "
+			      "have an explicit object parameter");
+		    ptr_type = false;
+		  }
+		else if (declarator->declarator->kind == cdk_pointer)
+		  error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			    "a function pointer type cannot "
+			    "have an explicit object parameter");
+		else if (declarator->declarator->kind == cdk_ptrmem)
+		  error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			    "a member function pointer type "
+			    "cannot have an explicit object parameter");
+
+		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 can synthesize the correct syntax
+		   and display it, perhaps it can be added later */
+	      }
+	    /* I am unsure if this is the best way of determining this
+	       but I don't like it either way, it should be improved on*/
+	    else if (decl_context == NORMAL
+		     && (in_namespace
+			 || !declarator->declarator->u.id.qualifying_scope))
+		error_at (DECL_SOURCE_LOCATION (xobj_parm),
+			  "a free function cannot have "
+			  "an explicit object parameter");
+	    else /* if (xobj_parm) */
+	      {
+		if (virtualp)
+		  {
+		    error_at (declspecs->locations[ds_virtual],
+			      "an explicit object member function cannot be "
+			      "%<virtual%>");
+		    inform (DECL_SOURCE_LOCATION (xobj_parm),
+			    "explicit object parameter declared here");
+		    virtualp = false;
+		  }
+		else if (staticp >= 2)
+		  {
+		    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 arg_types;
 	    int funcdecl_p;
 
@@ -15156,6 +15217,11 @@  grokparms (tree parmlist, tree *parms)
   tree parm;
   int any_error = 0;
 
+  /* If any parm other than the first is an xobj parm, it should have
+     been diagnosed already, so don't bother diagnosing a default argument
+     on any other parms */
+  bool first_parm = true;
+
   for (parm = parmlist; parm != NULL_TREE; parm = TREE_CHAIN (parm))
     {
       tree type = NULL_TREE;
@@ -15170,6 +15236,11 @@  grokparms (tree parmlist, tree *parms)
 	  any_error = 1;
 	  continue;
 	}
+      if (first_parm && !any_error && DECL_PARM_XOBJ_FLAG (decl) && init)
+	  error_at (DECL_SOURCE_LOCATION (decl),
+		    "an explicit object parameter "
+		    "may not have a default argument");
+      first_parm = false;
 
       type = TREE_TYPE (decl);
       if (VOID_TYPE_P (type))
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 8a5219a68a1..09466edf87e 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -1790,7 +1790,7 @@  dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 
   if (flags & TFF_DECL_SPECIFIERS)
     {
-      if (DECL_STATIC_FUNCTION_P (t))
+      if (DECL_STATIC_FUNCTION_P (t) && !DECL_IS_XOBJ_MEMBER_FUNC (t))
 	pp_cxx_ws_string (pp, "static");
       else if (DECL_VIRTUAL_P (t))
 	pp_cxx_ws_string (pp, "virtual");
@@ -1835,7 +1835,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_IS_XOBJ_MEMBER_FUNC (t) ? TFF_XOBJ_FUNC | flags
+						    : flags);
 
       if (TREE_CODE (fntype) == METHOD_TYPE)
 	{
@@ -1914,6 +1916,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;
@@ -3683,7 +3687,9 @@  function_category (tree fn)
   if (DECL_LANG_SPECIFIC (STRIP_TEMPLATE (fn))
       && DECL_FUNCTION_MEMBER_P (fn))
     {
-      if (DECL_STATIC_FUNCTION_P (fn))
+      if (DECL_IS_XOBJ_MEMBER_FUNC (fn))
+	return G_("In explicit object member function %qD");
+      else if (DECL_STATIC_FUNCTION_P (fn))
 	return _("In static member function %qD");
       else if (DECL_COPY_CONSTRUCTOR_P (fn))
 	return _("In copy constructor %qD");
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index ed5dcbde3ed..2bd276435df 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -15803,6 +15803,7 @@  cp_parser_decl_specifier_seq (cp_parser* parser,
   /* Assume no class or enumeration type is declared.  */
   *declares_class_or_enum = 0;
 
+  cp_token *first_non_attribute = NULL;
   /* Keep reading specifiers until there are no more to read.  */
   while (true)
     {
@@ -15875,6 +15876,9 @@  cp_parser_decl_specifier_seq (cp_parser* parser,
 	    decl_specs->locations[ds_attribute] = token->location;
 	  continue;
 	}
+      /* to properly place fixit diagnostic for xobj parm specifier */
+      if (!first_non_attribute)
+	first_non_attribute = token;
       /* Special case for xobj parm, doesn't really belong up here
 	 (it applies to parm decls and those are mostly handled below
 	 the following specifiers) but I intend to refactor this function
@@ -15883,6 +15887,27 @@  cp_parser_decl_specifier_seq (cp_parser* parser,
       if (token->keyword == RID_THIS)
 	{
 	  cp_lexer_consume_token (parser->lexer);
+	  if (token != first_non_attribute)
+	    {
+	      /* let set_and_check_decl_spec_loc
+		 report the diagnostic for duplicates */
+	      if (decl_specs->locations[ds_this] == 0)
+		{
+		  gcc_rich_location richloc (token->location);
+		  /* we can do better than this, it doesn't show a full
+		     rewrite when there are keywords between it and the start
+		     'f(const this S s)' for example, but it will properly
+		     rewrite 'f(S this & s)'
+		     Also fails to give a proper diagnostic
+		     for 'f(S& this s)' this case*/
+		  richloc.add_fixit_insert_before
+		    (first_non_attribute->location, "this ");
+		  richloc.add_fixit_remove ();
+		  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;
 	}
@@ -24833,6 +24858,10 @@  cp_parser_parameter_declaration_list (cp_parser* parser,
     = parser->in_unbraced_linkage_specification_p;
   parser->in_unbraced_linkage_specification_p = false;
 
+  /* Check if anything but the first parm is declared as an xobj parm.
+     I want to come back and refactor this with a better solution later. */
+  cp_parameter_declarator *first_parm = nullptr;
+
   /* Look for more parameters.  */
   while (true)
     {
@@ -24845,6 +24874,39 @@  cp_parser_parameter_declaration_list (cp_parser* parser,
 	= cp_parser_parameter_declaration (parser, flags,
 					   /*template_parm_p=*/false,
 					   &parenthesized_p);
+      if (!first_parm)
+	 first_parm = parameter;
+      /* this has to go here (as opposed to grokdeclarator)
+	 to emit the best possible diagnostics */
+      if (parameter && first_parm != parameter
+	  && parameter->decl_specifiers.locations[ds_this] != 0)
+	{
+	  gcc_rich_location bad_this
+	    (parameter->decl_specifiers.locations[ds_this]);
+	  /* this is unneccesary if the order of fixit/inform/error
+	     does not matter, but I am under the impression that it does */
+	  auto emit_error = [&bad_this]()
+	    {
+	      error_at (&bad_this,
+			"Only the first parameter of a member function "
+			"can be declared as an explicit object parameter");
+	    };
+	  if (first_parm->decl_specifiers.locations[ds_this] == 0)
+	    {
+	      /* We should probably check if the type of the first parm would
+		 be sane as an xobj parm before making this suggestion */
+	      /* I couldn't figure out how to get this quite the way I wanted.
+		 It should definitely be improved on */
+	      bad_this.add_fixit_insert_before (first_parm->loc, "this ");
+	      emit_error ();
+	    }
+	  else
+	    {
+	      emit_error ();
+	      inform (first_parm->loc,
+		      "Valid explicit object parameter declared here");
+	    }
+	}
 
       /* We don't know yet if the enclosing context is unavailable or deprecated,
 	 so wait and deal with it in grokparms if appropriate.  */
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index e810bc41fc8..23480dd0ecf 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3063,7 +3063,29 @@  finish_this_expr (void)
     return rvalue (result);
 
   tree fn = current_nonlambda_function ();
-  if (fn && DECL_STATIC_FUNCTION_P (fn))
+  if (fn && DECL_IS_XOBJ_MEMBER_FUNC (fn))
+    {
+      /* I want to move this to an earlier
+	 function to have access to the full expr */
+      tree xobj_parm = FUNCTION_DECL_CHECK (fn)->function_decl.arguments;
+      tree parm_name = DECL_MINIMAL_CHECK (xobj_parm)->decl_minimal.name;
+      if (parm_name)
+	{
+	  error ("%<this%> is unavailable for explicit object member "
+		 "functions");
+	  inform (DECL_SOURCE_LOCATION (xobj_parm),
+		  "use explicit object parameter %qD instead",
+		  parm_name);
+	}
+      else
+	{
+	  error ("%<this%> is unavailable for explicit object member "
+		 "functions");
+	  inform (DECL_SOURCE_LOCATION (xobj_parm),
+		  "name and use the explicit object parameter instead");
+	}
+    }
+  else if (fn && DECL_STATIC_FUNCTION_P (fn))
     error ("%<this%> is unavailable for static member functions");
   else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn))
     error ("invalid use of %<this%> before it is valid");
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed1.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed1.C
new file mode 100644
index 00000000000..d9924ced21a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed1.C
@@ -0,0 +1,141 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// I want to do better than what these currently are, but where the code is currently makes it impossible
+// they should at least be grouped or something
+
+// should at least note to apply qualifiers to the explicit object parameter instead (fixit should be possible) 
+
+struct S {
+    void f_value_0(this S) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_value_1(this S) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_value_2(this S) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_value_3(this S) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_value_4(this S) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_value_5(this S) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_value_6(this S) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_value_7(this S) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_value_8(this S) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_value_9(this S) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_value_A(this S) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_ref_0(this S&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_ref_1(this S&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_ref_2(this S&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_ref_3(this S&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_ref_4(this S&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_ref_5(this S&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_ref_6(this S&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_ref_7(this S&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_ref_8(this S&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_ref_9(this S&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_ref_A(this S&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_refref_0(this S&&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_refref_1(this S&&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_refref_2(this S&&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_refref_3(this S&&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_refref_4(this S&&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_refref_5(this S&&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_refref_6(this S&&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_refref_7(this S&&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_refref_8(this S&&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_refref_9(this S&&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_refref_A(this S&&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_cref_0(this S const&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_cref_1(this S const&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_cref_2(this S const&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_cref_3(this S const&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_cref_4(this S const&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_cref_5(this S const&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cref_6(this S const&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cref_7(this S const&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cref_8(this S const&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cref_9(this S const&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cref_A(this S const&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_crefref_0(this S const&&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_crefref_1(this S const&&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_crefref_2(this S const&&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_crefref_3(this S const&&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_crefref_4(this S const&&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_crefref_5(this S const&&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_crefref_6(this S const&&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_crefref_7(this S const&&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_crefref_8(this S const&&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_crefref_9(this S const&&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_crefref_A(this S const&&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_vref_0(this S volatile&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_vref_1(this S volatile&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_vref_2(this S volatile&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_vref_3(this S volatile&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_vref_4(this S volatile&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_vref_5(this S volatile&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vref_6(this S volatile&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vref_7(this S volatile&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vref_8(this S volatile&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vref_9(this S volatile&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vref_A(this S volatile&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_vrefref_0(this S volatile&&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_vrefref_1(this S volatile&&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_vrefref_2(this S volatile&&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_vrefref_3(this S volatile&&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_vrefref_4(this S volatile&&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_vrefref_5(this S volatile&&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vrefref_6(this S volatile&&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vrefref_7(this S volatile&&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vrefref_8(this S volatile&&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vrefref_9(this S volatile&&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_vrefref_A(this S volatile&&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_cvref_0(this S const volatile&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_cvref_1(this S const volatile&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_cvref_2(this S const volatile&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_cvref_3(this S const volatile&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_cvref_4(this S const volatile&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_cvref_5(this S const volatile&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvref_6(this S const volatile&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvref_7(this S const volatile&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvref_8(this S const volatile&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvref_9(this S const volatile&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvref_A(this S const volatile&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void f_cvrefref_0(this S const volatile&&) const;             // { dg-error "cannot have cv-qualifier" }
+    void f_cvrefref_1(this S const volatile&&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void f_cvrefref_2(this S const volatile&&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void f_cvrefref_3(this S const volatile&&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void f_cvrefref_4(this S const volatile&&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void f_cvrefref_5(this S const volatile&&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_6(this S const volatile&&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_7(this S const volatile&&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_8(this S const volatile&&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_9(this S const volatile&&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_A(this S const volatile&&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    template<typename Self> void d_templ_0(this Self&&) const;             // { dg-error "cannot have cv-qualifier" }
+    template<typename Self> void d_templ_1(this Self&&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    template<typename Self> void d_templ_2(this Self&&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    template<typename Self> void d_templ_3(this Self&&) &;                 // { dg-error "cannot have ref-qualifier" }
+    template<typename Self> void d_templ_4(this Self&&) &&;                // { dg-error "cannot have ref-qualifier" }
+    template<typename Self> void d_templ_5(this Self&&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_6(this Self&&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_7(this Self&&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_8(this Self&&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_9(this Self&&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_A(this Self&&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+
+    void d_auto_0(this auto&&) const;             // { dg-error "cannot have cv-qualifier" }
+    void d_auto_1(this auto&&) volatile;          // { dg-error "cannot have cv-qualifier" }
+    void d_auto_2(this auto&&) const volatile;    // { dg-error "cannot have cv-qualifier" }
+    void d_auto_3(this auto&&) &;                 // { dg-error "cannot have ref-qualifier" }
+    void d_auto_4(this auto&&) &&;                // { dg-error "cannot have ref-qualifier" }
+    void d_auto_5(this auto&&) const &;           // { dg-error "cannot have (ref|cv)-qualifier" }
+    void d_auto_6(this auto&&) const &&;          // { dg-error "cannot have (ref|cv)-qualifier" }
+    void d_auto_7(this auto&&) volatile &;        // { dg-error "cannot have (ref|cv)-qualifier" }
+    void d_auto_8(this auto&&) volatile &&;       // { dg-error "cannot have (ref|cv)-qualifier" }
+    void d_auto_9(this auto&&) const volatile &;  // { dg-error "cannot have (ref|cv)-qualifier" }
+    void d_auto_A(this auto&&) const volatile &&; // { dg-error "cannot have (ref|cv)-qualifier" }
+};
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed2.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed2.C
new file mode 100644
index 00000000000..f55c43cc374
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed2.C
@@ -0,0 +1,25 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// diagnose some incorrect uses of 'this' keyword in declarations and definitions
+
+using func_type = void(this int); // { dg-line func_type_line }
+// { dg-error "a function type cannot have an explicit object parameter" "" { target *-*-* } func_type_line }
+// { dg-note "the type of an explicit object member function is a regular function type" "" { target *-*-* } func_type_line }
+
+using func_ptr_type = void(*)(this int); // { dg-line func_ptr_type_line }
+// { dg-error "a function pointer type cannot have an explicit object parameter" "" { target *-*-* } func_ptr_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } func_ptr_type_line }
+
+struct S {
+    static void f(this S) {} // { dg-line static_member_func_line }
+};
+// { dg-error "an explicit object member function cannot be 'static'" "" { target *-*-* } static_member_func_line }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } static_member_func_line }
+
+using mem_func_type = void (S::*)(this S&); // { dg-line mem_func_type_line }
+// { dg-error "a member function pointer type cannot have an explicit object parameter" "" { target *-*-* } mem_func_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } mem_func_type_line }
+
+void f(this int); // { dg-error "a free function cannot have an explicit object parameter" }
+void f(this int) {} // { dg-error "a free function cannot have an explicit object parameter" }
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed3.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed3.C
new file mode 100644
index 00000000000..8fca102d0f9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed3.C
@@ -0,0 +1,102 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+// { dg-additional-options -Woverloaded-virtual }
+
+// invalid specifiers
+
+struct B {
+  virtual void f0() {} // { dg-line line_base_f0 }
+  virtual void f1() {} // { dg-line line_base_f1 }
+  virtual void f2() {} // { dg-line line_base_f2 }
+  virtual void f3() {} // { dg-line line_base_f3 }
+  virtual void f4() {} // { dg-line line_base_f4 }
+  virtual void f5() {} // { dg-line line_base_f5 }
+  virtual void f6() {} // { dg-line line_base_f6 }
+  virtual void f7() {} // { dg-line line_base_f7 }
+  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 }
+};
+
+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_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-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-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 }
+
+// { dg-bogus "an explicit object member function cannot be 'virtual'" "Surely this will never happen, but this test is here just in case it does" { target *-*-* } line_f4 }
+
+// I want to change the diagnostics below, the tests are check for the current error, and should be changed if the error is changed
+// (the following diagnostic should still take lower priority and thus be suppressed)
+
+// { dg-bogus "marked 'override', but does not override" "previous error should silence diagnostics about virt-specifiers (not implemented yet)" { xfail *-*-* } line_f1 }
+// { dg-bogus "marked 'final', but is not virtual"	  "previous error should silence diagnostics about virt-specifiers (not implemented yet)" { xfail *-*-* } line_f2 }
+// { dg-bogus "marked '(override|final)'"		  "previous error should silence diagnostics about virt-specifiers (not implemented yet)" { xfail *-*-* } line_f3 }
+
+// { dg-bogus "marked 'override', but does not override" "previous error should silence diagnostics about virt-specifiers (not implemented yet)" { xfail *-*-* } line_S1_f1 }
+// { dg-bogus "marked 'final', but is not virtual"	  "previous error should silence diagnostics about virt-specifiers (not implemented yet)" { xfail *-*-* } line_S1_f2 }
+// { dg-bogus "marked '(override|final)'"		  "previous error should silence diagnostics about virt-specifiers (not implemented yet)" { xfail *-*-* } line_S1_f3 }
+
+// { dg-error "marked 'override', but does not override" "" { target *-*-* } line_f5 }
+// { dg-error "marked 'final', but is not virtual" "" { target *-*-* } line_f6 }
+// { dg-error "marked '(override|final)'" "" { target *-*-* } line_f7 }
+
+// { 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 }
+
+// for now, these all stay active, I want to discuss with others on whether the warnings should be suppressed by other errors (I think they should be)
+
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f0 }
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f1 }
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f2 }
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f3 }
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f4 }
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f5 }
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f6 }
+// { dg-warning "was hidden" "" { target *-*-* } line_base_f7 }
+
+// { dg-note "by '" "" { target *-*-* } line_f0 }
+// { dg-note "by '" "" { target *-*-* } line_f1 }
+// { dg-note "by '" "" { target *-*-* } line_f2 }
+// { dg-note "by '" "" { target *-*-* } line_f3 }
+// { dg-note "by '" "" { target *-*-* } line_f4 }
+// { dg-note "by '" "" { target *-*-* } line_f5 }
+// { dg-note "by '" "" { target *-*-* } line_f6 }
+// { dg-note "by '" "" { target *-*-* } line_f7 }
+
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed4.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed4.C
new file mode 100644
index 00000000000..735d5932317
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed4.C
@@ -0,0 +1,42 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// redeclarations
+
+struct S {
+  void f0(this S&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f0(); // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f1(this S const&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f1() const; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f2(this S volatile&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f2() volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f3(this S const volatile&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f3() const volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f4(this S&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f4() &; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f5(this S&&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f5() &&; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f6(this S const&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f6() const&; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f7(this S const&&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f7() const&&; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f8(this S volatile&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f8() volatile&; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void f9(this S volatile&&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void f9() volatile&&; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void fA(this S const volatile&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void fA() const volatile&; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+
+  void fB(this S const volatile&&); // { dg-note "previous declaration" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+  void fB() const volatile&&; // { dg-error "cannot be overloaded with" "detecting redeclarations not implemented yet" { xfail *-*-* } }
+};
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed5.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed5.C
new file mode 100644
index 00000000000..0c64da3b671
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed5.C
@@ -0,0 +1,9 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+struct S {
+  void f0(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+  void f1(this S);
+};
+
+void S::f1(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed6.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed6.C
new file mode 100644
index 00000000000..df53617a002
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-ill-formed6.C
@@ -0,0 +1,22 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// using 'this' in body disabled
+
+// { 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" }
+    // solely for suppressing a potential 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" }
+  }
+};
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-no-cxx23.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-no-cxx23.C
new file mode 100644
index 00000000000..7f56b61c50a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-no-cxx23.C
@@ -0,0 +1,7 @@ 
+// P0847R7
+// { dg-do compile { target c++20_down } }
+
+struct S {
+    void f(this S); // { dg-error "explicit object parameter requires" }
+    void g(this S self); // { dg-error "explicit object parameter requires" }
+};
\ No newline at end of file
-- 
2.41.0