diff mbox series

[1/2] c++: Initial support for P0847R7 (Deducing This) [PR102609]

Message ID b_iIXwWwO63ZE1ZSZHUIAdWyA2sqGsE3FM7eXfsInWogDyBZRsw8CwNsvFSDmEVmBtdq0pqb4zJ55HN2JCR7boDNramlEfne-R5PWdUXjbA=@protonmail.com
State New
Headers show
Series [1/2] c++: Initial support for P0847R7 (Deducing This) [PR102609] | expand

Commit Message

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

I would like to quickly thank everyone who helped me for their patience as I
learned the ropes of the codebase and toolchain. It is much appretiated and this would
have been much much more difficult without the support.

This patch handles the new explicit object member functions by bypassing
special behavior of implicit object member functions, but opting back into the special
behavior during a function call through member access. This is mainly accomplished by
bypassing becoming a METHOD_TYPE and remaining as a FUNCTION_TYPE. Normally, this would
be treated as a static member function, but a new explicit object member function
flag is added to lang_decl_base and is set for the declaration of explicit object
member functions. This sets it apart from static member functions when it is
relevant, and is the criteria used to opt-in to passing the implicit object argument
during a member function call. The benefit of this design is less code needs to be
modified to support the new feature, as most of the semantics of explicit object member
functions matches those of static member functions. There is very little left to add,
and hopefully there are few bugs in the implementation despite the minimal
changes.

It is possible there are hidden problems with passing the implicit object
argument, but none of the tests I made exhibit such a thing EXCEPT for in the
pathological case as I describe below. Upon reflection, a by value explicit object
parameter might be broken as well, I can't recall if there's a good test for that case.

Lambdas do not work yet, but you can work around it by marking it as mutable
so I suspect it could be supported with minimal changes, I just ran out of time.
The other thing that does not work is the pathological case with an explicit
object parameter of an unrelated type and relying on a user-defined conversion
operator to cast to said type in a call to that function. You can observe the failing
test for that case in explicit-object-param-valid4.C, the result is somewhat
interesting, but is also why I mention that there might be hidden problems here.

I selectively excluded all the diagnostics from this patch, it's possible I
made a mistake and the patch will be non-functional without the addition of the
diagnostics patch. If that ends up being the case, please apply the following patch that
includes the diagnostics and tests and judge the functionality from that. I believe
that even if I mess up this patch, there should still be value in splitting up the
changes into the two patches as it should make the changes to the behavior of the
compiler much more clear.
With that said, I believe I didn't make any mistakes while seperating the two
patches, hopefully that is the case.

I left in a FIXME (in call.cc) as I noticed last minute that I made a mistake,
it should be benign and removing it appears to not break anything, but I don't
have time to do another bootstrap at the moment. My priority is to get eyes on the
changes I've made and recieve feedback.

The patch including the diagnostics will follow shortly, assuming I don't run
out of time and need to rush to catch my flight :).

PS: Are there any circumstances where TREE_CODE is FUNCTION_DECL but the
lang_specific member is null? I have a null check for that case in DECL_IS_XOBJ_MEMBER_FUNC
but I question if it's necessary.

Comments

Jakub Jelinek Aug. 31, 2023, 8:33 a.m. UTC | #1
On Thu, Aug 31, 2023 at 06:02:36AM +0000, waffl3x via Gcc-patches wrote:

> From e485a79ec5656e72ba46053618843c3d69331eab Mon Sep 17 00:00:00 2001
> From: Waffl3x <waffl3x@protonmail.com>
> Date: Thu, 31 Aug 2023 01:05:25 -0400
> Subject: [PATCH] P0847R7 (deducing this) Initial support
> 
> Most things should be functional, lambdas need a little more work though.
> Limitations: Missing support for lambdas, and user defined conversion functions when passing the implicit object argument does not work properly. See explicit-object-param-valid4.C for an example of the current (errent) behavior.
> 
> There is a slight mistake in call.cc, it should be benign.

Thanks for working on this.

Just some random mostly formatting comments, defering the actual patch
review to Jason.

The ChangeLog should refer to
	PR c++/102609

and ideally mention somewhere that it implements
C++23 P0847R7 - Deducing this.
so that when one quickly searches when that was implemented it can be found
easily.
ChangeLog entries should start with capital letter after ): and
end with .  And they should fit into 80 columns, one can wrap lines (but use
tab indentation even on the subsequent lines).
More importantly, should describe what changed and not why, if the why needs
explanation, it should go into comments in the code.

> gcc/cp/ChangeLog:
> 
> 	* call.cc (add_function_candidate): (Hopefully) benign mistake

So, this both misses . at the end and doesn't describe what changed.
	* call.cc (add_function_candidate): Don't call build_this_conversion
	for DECL_IS_XOBJ_MEMBER_FUNC.
?

> 	(add_candidates): Treat explicit object member functions as member functions when considering candidates

Too long line and missing . at the end

> 	(build_over_call): Enable passing an implicit object argument when calling an explicit object member function
> 	* cp-tree.h (struct lang_decl_base): Added member xobj_flag for differentiating explicit object member functions from static member functions

Just mention that xobj_flag member has been added, not what it is for.

> 	(DECL_FUNC_XOBJ_FLAG): New, checked access for xobj_flag
> 	(DECL_PARM_XOBJ_FLAG): New, access decl_flag_3
> 	(DECL_IS_XOBJ_MEMBER_FUNC): New, safely check if a node is an explicit object member function

These are macros, just say Define.
etc.

> 	(enum cp_decl_spec): Support parsing 'this' as a decl spec, change is mirrored in parser.cc:set_and_check_decl_spec_loc
> 	* decl.cc (grokfndecl): Sets the xobj flag for the FUNCTION_DECL if the first parameter is an explicit object parameter
> 	(grokdeclarator): Sets the xobj flag for PARM_DECL if 'this' spec is present in declspecs, bypasses conversion from FUNCTION_DECL to METHOD_DECL if an xobj flag is set for the first parameter of the given function declarator
> 	* parser.cc (cp_parser_decl_specifier_seq): check for 'this' specifier
> 	(set_and_check_decl_spec_loc): extended decl_spec_names to support 'this', change is mirrored in cp-tree.h:cp_decl_spec
> 
> gcc/ChangeLog:
> 
> 	* tree-core.h (struct tree_decl_common): Added comment describing new use of decl_flag_3
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp23/explicit-object-param-valid1.C: New test.
> 	* g++.dg/cpp23/explicit-object-param-valid2.C: New test.
> 	* g++.dg/cpp23/explicit-object-param-valid3.C: New test.
> 	* g++.dg/cpp23/explicit-object-param-valid4.C: New test.

I think usually we don't differentiate in testcase names whether
the test is to be accepted or rejected.
So, one would just go with explicit-object-param{1,2,3,4,5,6}.C etc.
for everything related to the feature.
Isn't explicit-object-param too long though?
explicit-this or deducing-this might be shorter...

> +	      /* FIXME: This doesn't seem to be neccesary, upon review I
> +		 realized that it doesn't make sense (an xobj member func
> +		 is not a nonstatic_member_function, so this check will
> +		 never change anything) */

Comments should end with a dot and two spaces before */

> @@ -9995,7 +10001,8 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
>  	}
>      }
>    /* Bypass access control for 'this' parameter.  */
> -  else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
> +  else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
> +	   || DECL_IS_XOBJ_MEMBER_FUNC (fn) )

No space between fn) and )

> +/* these need to moved to somewhere appropriate */

Comments should start with a capital letter and like above, end with
appropriate.  */

> +   && DECL_LANG_SPECIFIC (STRIP_TEMPLATE (NODE))->u.base.xobj_flag == 1) \

No \ after the last line of the macro.

> +}
> \ No newline at end of file

Please avoid these (unless testing preprocessor etc. that
it can handle even sources which don't end with a newline).

> diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C
> new file mode 100644
> index 00000000000..2f9a08207d4
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C
> @@ -0,0 +1,24 @@
> +// P0847R7
> +// { dg-do run { target c++23 } }

This raises an important question whether we as an extension
should support deducing this even in older standards or not.
I admit I haven't studied the paper enough to figure that out.
The syntax is certainly something that wasn't valid in older standards,
so from that POV it could be accepted say with pedwarn with
OPT_Wc__23_extensions if cxx_dialect < cxx23.  But perhaps some
of the rules in the paper change something unconditionally even when
the new syntax doesn't appear.
And, if it is accepted in older standards, the question is if it
shouldn't be banned say from C++98.

> +// explicit object member function pointer type deduction and conversion to function pointer
> +// and calling through pointer to function
> +
> +struct S {
> +  int _n;
> +  int f(this S& self) { return self._n; }
> +};
> +
> +using f_type = int(*)(S&);
> +
> +static_assert(__is_same(f_type, decltype(&S::f)));
> +
> +int main()
> +{
> +  auto fp0 = &S::f;
> +  f_type fp1 = &S::f;
> +  static_assert(__is_same(decltype(fp0), decltype(fp1)));
> +  S s{42};
> +  // { dg-output "42" }
> +  __builtin_printf("%d\n%d\n", fp0(s), fp1(s));

Usually runtime tests don't try to print something with dg-output
trying to match it, but instead just compare the values directly and
__builtin_abort () if something has incorrect value.
Also, in the above case, dg-output matches 42 anywhere in the output,
so if fp0(s) is 42 or if fp1(s) is 42.  I'd expect
  if (fp0 (s) != 42 || fp1 (s) != 42)
    __builtin_abort ();

	Jakub
waffl3x Sept. 2, 2023, 8:43 a.m. UTC | #2
Hey Jakub, thanks for the response
and criticism, as soon as I am
back at a computer I will address
the issues you raised, I have a few
questions though.

I apologize in advanced for any
errors in formatting this message,
I'm writing it from a hotel room
on a phone so errors are inevitable,
but I'll try my best.

>More importantly, should describe
>what changed and not why
I was under the impression that
if someone wants to see the what,
they can just check the diff.
I don't understand what should be
written if I'm just to say what,
wouldn't I just be repeating what
is already said in the code itself?

>I think usually we don't
>differentiate
>in testcase names whether
Yeah, for sure, but I felt like
there is value in differentiating
them and that it would be harmless
to do so. If you feel like it's
more important that convention is
followed here I won't object, but I
think this should be considered.

>Isn't explicit-object-param too long
>though?
>explicit-this or deducing-this might
>be shorter...
I agree, but I felt like I should
stick to the wording of the standard
for it, but I don't feel strongly
about that justification, so I
wouldn't object to changing it.
Truthfully I flip flopped many
times around the names, I'll defer
to whatever is decided by the
maintainers on that without complaint.

>> +}
>> \ No newline at end of file
>Please avoid these
Yeah, I noticed it last minute and
wasn't sure how big a deal it was.
I will make sure to fix it along
with everything else you noted.

>Usually runtime tests don't try to
>print something
Yeah, I didn't like printing, but
I'm not sure I like aborting either.
I value granularity in testing, if
one part of the test passes but
another fails, you would want to know
that. If you abort before the second
you lose that granularity.
Once again, I'll defer to the
maintainers for this, but I think
my points are valid here.

>This raises an important question whether we as an extension
>should support deducing this even in older standards or not.
>I admit I haven't studied the paper enough to figure that out.

I'm glad you think so, I fully agree.
I had planned to raise that once
the initial patch made it in. I don't
believe there is anything in the paper
in particular that breaks previous
standards.
I can imagine there being problems
if older projects tried to convert
everything to use it as there are
alignment differences (at least in
the current patch version) between
explicit object member functions and
implicit object member functions.
This is mostly a side effect of
treating them as static functions
most of the time, but I wasn't sure
what would be ideal, nor how to change
it. I decided that the difference
was likely mainly due to virtual
functions (if you know more about
this, please be sure to correct me),
and as virtual is not
(currently) allowed, I just left it
as it is.
In short, I agree, and furthermore
I think the syntax should be allowed
with virtual just so style can
be maintained. That would have greater
implications than what you mentioned
though and would need some extra
hacks to make work, instead of just
allowing what should -just- work.

Thanks again for the input, I will
get on it asap. Unfortunately that
will be in a while, but I am
determined to get this feature into
GCC14 so one way or another I will
make it happen.

-Alex
Jason Merrill Sept. 19, 2023, 8:24 p.m. UTC | #3
On 8/31/23 04:33, Jakub Jelinek wrote:
> On Thu, Aug 31, 2023 at 06:02:36AM +0000, waffl3x via Gcc-patches wrote:
> 
>> +++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C
>> @@ -0,0 +1,24 @@
>> +// P0847R7
>> +// { dg-do run { target c++23 } }
> 
> This raises an important question whether we as an extension
> should support deducing this even in older standards or not.
> I admit I haven't studied the paper enough to figure that out.
> The syntax is certainly something that wasn't valid in older standards,
> so from that POV it could be accepted say with pedwarn with
> OPT_Wc__23_extensions if cxx_dialect < cxx23.  But perhaps some
> of the rules in the paper change something unconditionally even when
> the new syntax doesn't appear.
> And, if it is accepted in older standards, the question is if it
> shouldn't be banned say from C++98.

I don't think there's any obstacle to allowing it as an extension in 
older standards (with a pedwarn, of course).

Jason
diff mbox series

Patch

From e485a79ec5656e72ba46053618843c3d69331eab Mon Sep 17 00:00:00 2001
From: Waffl3x <waffl3x@protonmail.com>
Date: Thu, 31 Aug 2023 01:05:25 -0400
Subject: [PATCH] P0847R7 (deducing this) Initial support

Most things should be functional, lambdas need a little more work though.
Limitations: Missing support for lambdas, and user defined conversion functions when passing the implicit object argument does not work properly. See explicit-object-param-valid4.C for an example of the current (errent) behavior.

There is a slight mistake in call.cc, it should be benign.

gcc/cp/ChangeLog:

	* call.cc (add_function_candidate): (Hopefully) benign mistake
	(add_candidates): Treat explicit object member functions as member functions when considering candidates
	(build_over_call): Enable passing an implicit object argument when calling an explicit object member function
	* cp-tree.h (struct lang_decl_base): Added member xobj_flag for differentiating explicit object member functions from static member functions
	(DECL_FUNC_XOBJ_FLAG): New, checked access for xobj_flag
	(DECL_PARM_XOBJ_FLAG): New, access decl_flag_3
	(DECL_IS_XOBJ_MEMBER_FUNC): New, safely check if a node is an explicit object member function
	(enum cp_decl_spec): Support parsing 'this' as a decl spec, change is mirrored in parser.cc:set_and_check_decl_spec_loc
	* decl.cc (grokfndecl): Sets the xobj flag for the FUNCTION_DECL if the first parameter is an explicit object parameter
	(grokdeclarator): Sets the xobj flag for PARM_DECL if 'this' spec is present in declspecs, bypasses conversion from FUNCTION_DECL to METHOD_DECL if an xobj flag is set for the first parameter of the given function declarator
	* parser.cc (cp_parser_decl_specifier_seq): check for 'this' specifier
	(set_and_check_decl_spec_loc): extended decl_spec_names to support 'this', change is mirrored in cp-tree.h:cp_decl_spec

gcc/ChangeLog:

	* tree-core.h (struct tree_decl_common): Added comment describing new use of decl_flag_3

gcc/testsuite/ChangeLog:

	* g++.dg/cpp23/explicit-object-param-valid1.C: New test.
	* g++.dg/cpp23/explicit-object-param-valid2.C: New test.
	* g++.dg/cpp23/explicit-object-param-valid3.C: New test.
	* g++.dg/cpp23/explicit-object-param-valid4.C: New test.

Signed-off-by: Waffl3x <waffl3x@protonmail.com>
---
 gcc/cp/call.cc                                |  13 +-
 gcc/cp/cp-tree.h                              |  20 +++-
 gcc/cp/decl.cc                                |  25 ++++
 gcc/cp/parser.cc                              |  15 ++-
 .../cpp23/explicit-object-param-valid1.C      | 113 ++++++++++++++++++
 .../cpp23/explicit-object-param-valid2.C      |  24 ++++
 .../cpp23/explicit-object-param-valid3.C      |  14 +++
 .../cpp23/explicit-object-param-valid4.C      |  33 +++++
 gcc/tree-core.h                               |   3 +-
 9 files changed, 254 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid4.C

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 673ec91d60e..ac5e414b084 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -2509,7 +2509,12 @@  add_function_candidate (struct z_candidate **candidates,
 	  tree parmtype = TREE_VALUE (parmnode);
 	  if (i == 0
 	      && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
-	      && !DECL_CONSTRUCTOR_P (fn))
+	      && !DECL_CONSTRUCTOR_P (fn)
+	      /* FIXME: This doesn't seem to be neccesary, upon review I
+		 realized that it doesn't make sense (an xobj member func
+		 is not a nonstatic_member_function, so this check will
+		 never change anything) */
+	      && !DECL_IS_XOBJ_MEMBER_FUNC (fn))
 	    t = build_this_conversion (fn, ctype, parmtype, argtype, arg,
 				       flags, complain);
 	  else
@@ -6566,7 +6571,8 @@  add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
       tree fn_first_arg = NULL_TREE;
       const vec<tree, va_gc> *fn_args = args;
 
-      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn))
+      if (DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
+	  || DECL_IS_XOBJ_MEMBER_FUNC (fn))
 	{
 	  /* Figure out where the object arg comes from.  If this
 	     function is a non-static member and we didn't get an
@@ -9995,7 +10001,8 @@  build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	}
     }
   /* Bypass access control for 'this' parameter.  */
-  else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
+  else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE
+	   || DECL_IS_XOBJ_MEMBER_FUNC (fn) )
     {
       tree arg = build_this (first_arg != NULL_TREE
 			     ? first_arg
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index eb901683b6d..3aca23da105 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -2878,7 +2878,9 @@  struct GTY(()) lang_decl_base {
   /* VAR_DECL or FUNCTION_DECL has keyed decls.     */
   unsigned module_keyed_decls_p : 1;
 
-  /* 12 spare bits.  */
+  /* FUNCTION_DECL explicit object member function flag */
+  unsigned xobj_flag : 1;
+  /* 11 spare bits.  */
 };
 
 /* True for DECL codes which have template info and access.  */
@@ -3086,6 +3088,21 @@  struct GTY(()) lang_decl {
 
 #endif /* ENABLE_TREE_CHECKING */
 
+/* these need to moved to somewhere appropriate */
+
+/* the flag is a member of base, but the value is meaningless for other
+   decl types so checking is still justified I imagine */
+#define DECL_FUNC_XOBJ_FLAG(NODE) \
+  (LANG_DECL_FN_CHECK (NODE)->min.base.xobj_flag)
+/* not a lang_decl field, but still specific to c++ */
+#define DECL_PARM_XOBJ_FLAG(NODE) \
+  (PARM_DECL_CHECK (NODE)->decl_common.decl_flag_3)
+/* evaluates false for non func nodes and nodes with a null lang_decl member */
+#define DECL_IS_XOBJ_MEMBER_FUNC(NODE) 					 \
+  (TREE_CODE (STRIP_TEMPLATE (NODE)) == FUNCTION_DECL			 \
+   && DECL_LANG_SPECIFIC (STRIP_TEMPLATE (NODE))			 \
+   && DECL_LANG_SPECIFIC (STRIP_TEMPLATE (NODE))->u.base.xobj_flag == 1) \
+
 /* For a FUNCTION_DECL or a VAR_DECL, the language linkage for the
    declaration.  Some entities (like a member function in a local
    class, or a local variable) do not have linkage at all, and this
@@ -6275,6 +6292,7 @@  enum cp_decl_spec {
   ds_complex,
   ds_constinit,
   ds_consteval,
+  ds_this, /* inserting here to match decl_spec_names in parser.cc*/
   ds_thread,
   ds_type_spec,
   ds_redefined_builtin_type_spec,
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index bea0ee92106..a6d0cfb0ecc 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10314,6 +10314,9 @@  grokfndecl (tree ctype,
   type = build_cp_fntype_variant (type, rqual, raises, late_return_type_p);
 
   decl = build_lang_decl_loc (location, FUNCTION_DECL, declarator, type);
+  /* all error checking has been done by now, just copy the flag over
+     parms is null (appears to be anyway) for 0 parm functions */
+  DECL_FUNC_XOBJ_FLAG (decl) = parms ? DECL_PARM_XOBJ_FLAG (parms) : false;
 
   /* Set the constraints on the declaration. */
   if (flag_concepts)
@@ -12966,6 +12969,19 @@  grokdeclarator (const cp_declarator *declarator,
   if (attrlist)
     diagnose_misapplied_contracts (*attrlist);
 
+  /* Only used for skipping over build_memfn_type, grokfndecl handles
+     copying the flag to the correct field for a func_decl.
+     I wish there was a better way to do this, but there doesn't seem to be */
+  bool is_xobj_member_function = false;
+  auto get_xobj_parm = [](tree parm_list)
+    {
+      if (!parm_list)
+	return NULL_TREE;
+      tree first_parm = TREE_VALUE (parm_list);
+      if (first_parm == void_type_node)
+	return NULL_TREE;
+      return DECL_PARM_XOBJ_FLAG (first_parm) == 1 ? first_parm : NULL_TREE;
+    };
   /* Determine the type of the entity declared by recurring on the
      declarator.  */
   for (; declarator; declarator = declarator->declarator)
@@ -13061,6 +13077,9 @@  grokdeclarator (const cp_declarator *declarator,
 
 	case cdk_function:
 	  {
+	    tree xobj_parm
+	      = get_xobj_parm (declarator->u.function.parameters);
+	    is_xobj_member_function = xobj_parm;
 	    tree arg_types;
 	    int funcdecl_p;
 
@@ -14145,6 +14164,8 @@  grokdeclarator (const cp_declarator *declarator,
     }
 
   if (ctype && TREE_CODE (type) == FUNCTION_TYPE && staticp < 2
+      /* bypass conversion to METHOD_TYPE if an xobj parm is present */
+      && !is_xobj_member_function
       && !(unqualified_id
 	   && identifier_p (unqualified_id)
 	   && IDENTIFIER_NEWDEL_OP_P (unqualified_id)))
@@ -14163,6 +14184,10 @@  grokdeclarator (const cp_declarator *declarator,
       {
 	decl = cp_build_parm_decl (NULL_TREE, unqualified_id, type);
 	DECL_ARRAY_PARAMETER_P (decl) = array_parameter_p;
+	/* Set the xobj flag for this parm, unfortunately
+	   I don't think there is a better way to do this */
+	DECL_PARM_XOBJ_FLAG (decl)
+	  = decl_spec_seq_has_spec_p (declspecs, ds_this);
 
 	bad_specifiers (decl, BSP_PARM, virtualp,
 			memfn_quals != TYPE_UNQUALIFIED,
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index eeb22e44fb4..ed5dcbde3ed 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -15875,6 +15875,18 @@  cp_parser_decl_specifier_seq (cp_parser* parser,
 	    decl_specs->locations[ds_attribute] = token->location;
 	  continue;
 	}
+      /* 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
+	 so I'm not worrying about it too much.
+	 The error diagnostics might be better elsewhere though. */
+      if (token->keyword == RID_THIS)
+	{
+	  cp_lexer_consume_token (parser->lexer);
+	  set_and_check_decl_spec_loc (decl_specs, ds_this, token);
+	  continue;
+	}
+
       /* Assume we will find a decl-specifier keyword.  */
       found_decl_spec = true;
       /* If the next token is an appropriate keyword, we can simply
@@ -33624,7 +33636,8 @@  set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
 	    "constexpr",
 	    "__complex",
 	    "constinit",
-	    "consteval"
+	    "consteval",
+	    "this"
 	  };
 	  gcc_rich_location richloc (location);
 	  richloc.add_fixit_remove ();
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid1.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid1.C
new file mode 100644
index 00000000000..12230cfc3d5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid1.C
@@ -0,0 +1,113 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// basic use cases and calling
+
+// non-trailing return
+// definitions
+struct S0 {
+  void f0(this S0) {}
+  void f1(this S0&) {}
+  void f2(this S0&&) {}
+  void f3(this S0 const&) {}
+  void f4(this S0 const&&) {}
+  template<typename Self>
+  void d0(this Self&&) {}
+  void d1(this auto&&) {}
+};
+// declarations
+struct S1 {
+  void f0(this S1);
+  void f1(this S1&);
+  void f2(this S1&&);
+  void f3(this S1 const&);
+  void f4(this S1 const&&);
+  template<typename Self>
+  void d0(this Self&&);
+  void d1(this auto&&);
+};
+// out of line definitions
+void S1::f0(this S1) {}
+void S1::f1(this S1&) {}
+void S1::f2(this S1&&) {}
+void S1::f3(this S1 const&) {}
+void S1::f4(this S1 const&&) {}
+template<typename Self>
+void S1::d0(this Self&&) {}
+void S1::d1(this auto&&) {}
+
+// trailing return
+// definitions
+struct S2 {
+  auto f0(this S2) -> void {}
+  auto f1(this S2&) -> void {}
+  auto f2(this S2&&) -> void {}
+  auto f3(this S2 const&) -> void {}
+  auto f4(this S2 const&&) -> void {}
+  template<typename Self>
+  auto d0(this Self&&) -> void {}
+
+  auto d1(this auto&&) -> void {}
+};
+// declarations
+struct S3 {
+  auto f0(this S3) -> void;
+  auto f1(this S3&) -> void;
+  auto f2(this S3&&) -> void;
+  auto f3(this S3 const&) -> void;
+  auto f4(this S3 const&&) -> void;
+  template<typename Self>
+  auto d0(this Self&&) -> void;
+  auto d1(this auto&&) -> void;
+};
+// out of line definitions
+auto S3::f0(this S3) -> void {}
+auto S3::f1(this S3&) -> void {}
+auto S3::f2(this S3&&) -> void {}
+auto S3::f3(this S3 const&) -> void {}
+auto S3::f4(this S3 const&&) -> void {}
+template<typename Self>
+auto S3::d0(this Self&&) -> void {}
+auto S3::d1(this auto&&) -> void {}
+
+template<typename T>
+void call_with_qualification()
+{
+  T obj{};
+  // by value should take any qualification (f0)
+  T{}.f0();
+  obj.f0();
+  static_cast<T&&>(obj).f0(); 
+  static_cast<T const&>(obj).f0();
+  static_cast<T const&&>(obj).f0();
+  // specific qualification (f1 - f4)
+  T{}.f2();
+  T{}.f3();
+  T{}.f4();
+  obj.f1();
+  obj.f3();
+  static_cast<T&&>(obj).f2();
+  static_cast<T&&>(obj).f3();
+  static_cast<T&&>(obj).f4();
+  static_cast<T const&>(obj).f3();
+  static_cast<T const&&>(obj).f4();
+  // deduced should (obviously) take any qualification (d0, d1)
+  T{}.d0();
+  obj.d0();
+  static_cast<T&&>(obj).d0();
+  static_cast<T const&>(obj).d0();
+  static_cast<T const&&>(obj).d0();
+  T{}.d1();
+  obj.d1();
+  static_cast<T&&>(obj).d1();
+  static_cast<T const&>(obj).d1();
+  static_cast<T const&&>(obj).d1();
+}
+
+void perform_calls()
+{
+  call_with_qualification<S0>();
+  call_with_qualification<S1>();
+  call_with_qualification<S2>();
+  call_with_qualification<S3>();
+}
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C
new file mode 100644
index 00000000000..2f9a08207d4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid2.C
@@ -0,0 +1,24 @@ 
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// explicit object member function pointer type deduction and conversion to function pointer
+// and calling through pointer to function
+
+struct S {
+  int _n;
+  int f(this S& self) { return self._n; }
+};
+
+using f_type = int(*)(S&);
+
+static_assert(__is_same(f_type, decltype(&S::f)));
+
+int main()
+{
+  auto fp0 = &S::f;
+  f_type fp1 = &S::f;
+  static_assert(__is_same(decltype(fp0), decltype(fp1)));
+  S s{42};
+  // { dg-output "42" }
+  __builtin_printf("%d\n%d\n", fp0(s), fp1(s));
+}
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid3.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid3.C
new file mode 100644
index 00000000000..2b2bc458df8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid3.C
@@ -0,0 +1,14 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// recursive lambdas
+
+// { dg-excess-errors "deducing this with lambdas not implemented yet" { xfail *-*-* } }
+
+int main()
+{
+  auto cl0 = [](this auto&& self, int n){ return n ? self(n - 1) : 42 };
+  auto cl1 = [](this auto self, int n){ return n ? self(n - 1) : 42};
+  int a = cl0(5);
+  int b = cl1(5);
+}
\ No newline at end of file
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid4.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid4.C
new file mode 100644
index 00000000000..1e9ade62a51
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param-valid4.C
@@ -0,0 +1,33 @@ 
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// test implicit conversion of the object argument
+// to the explicit object parameter
+
+// we compare &s to ret because early on, the
+// object parameter would not convert, it would just get
+// reinterpreted as the type of the explicit object param
+
+// { dg-output "ret != &s : 1" { xfail *-*-* } }
+// { dg-output "ret == 42 : 1" { xfail *-*-* } }
+
+using uintptr_t = __UINTPTR_TYPE__;
+
+struct S {
+    operator uintptr_t() const {
+	return 42;
+    }
+    uintptr_t f(this uintptr_t n) {
+        return n;
+    }
+};
+
+int main() 
+{
+    S s{};
+    uintptr_t ret = s.f();
+    __builtin_printf("ret != &s : %d\n"
+		     "ret == 42 : %d\n",
+		     ret != reinterpret_cast<uintptr_t>(&s) ? 1 : 0,
+		     ret == 42 ? 1 : 0);
+}
\ No newline at end of file
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 91551fde900..e434bd7c9ac 100644
--- a/gcc/tree-core.h
+++ b/gcc/tree-core.h
@@ -1808,7 +1808,8 @@  struct GTY(()) tree_decl_common {
      DECL_HAS_VALUE_EXPR_P.  */
   unsigned decl_flag_2 : 1;
   /* In FIELD_DECL, this is DECL_PADDING_P.
-     In VAR_DECL, this is DECL_MERGEABLE.  */
+     In VAR_DECL, this is DECL_MERGEABLE.
+     In PARM_DECL, this is DECL_XOBJ_PARM. */
   unsigned decl_flag_3 : 1;
   /* Logically, these two would go in a theoretical base shared by var and
      parm decl. */
-- 
2.41.0