diff mbox series

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

Message ID WBulHlEWtAC34Gaya2RM4wi1XPmBh-8mmo1nSuaXMEDszh07eeF8nzl7-GLWrO4yPF9GAa6DAiGE3IyYkgKCefnp54JfiQZGl4r-C9qy-tU=@protonmail.com
State New
Headers show
Series [v2,1/2] c++: Initial support for P0847R7 (Deducing This) [PR102609] | expand

Commit Message

waffl3x Sept. 11, 2023, 1:49 p.m. UTC
Bootstrapped and tested on x86_64-linux with no regressions.

Hopefully I fixed all the issues. I also took the opportunity to remove the
small mistake present in v1, so that is no longer a concern.

Thanks again for all the patience.
  -Alex

Comments

Jason Merrill Sept. 19, 2023, 8:14 p.m. UTC | #1
On 9/11/23 09:49, waffl3x via Gcc-patches wrote:
> Bootstrapped and tested on x86_64-linux with no regressions.
> 
> Hopefully I fixed all the issues. I also took the opportunity to remove the
> small mistake present in v1, so that is no longer a concern.
> 
> Thanks again for all the patience.
>    -Alex

Thank you, this is great!

One legal hurdle to start with: our DCO policy 
(https://gcc.gnu.org/dco.html) requires real names in the sign-off, not 
pseudonyms.  If you would prefer to contribute under this pseudonym, I 
encourage you to file a copyright assignment with the FSF, who are set 
up to handle that.

> +/* These need to moved to somewhere appropriate.  */

This isn't a bad spot for these macros, but you could also move them 
down lower, maybe near DECL_THIS_STATIC and DECL_ARRAY_PARAMETER_P for 
some thematic connection.

> +/* The flag is a member of base, but the value is meaningless for other
> +   decl types so checking is still justified I imagine.  */

Absolutely, we often reuse bits for other purposes if they're disjoint 
from the use they were added for.

> +/* 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)

Better to use a DECL_LANG_FLAG than claim one of the 
language-independent flags for C++.

There's a list at the top of cp-tree.h of the uses of *_LANG_FLAG_* on 
various kinds of tree node.  DECL_LANG_FLAG_4 seems free on PARM_DECL.

> +  /* Only used for skipping over build_memfn_type, grokfndecl handles
> +     copying the flag to the correct field for a func_decl.
> +     There must be a better way to do this, but it isn't obvious how.  */
> +  bool is_xobj_member_function = false;
> +  auto get_xobj_parm = [](tree parm_list)

I guess you could add a flag to the declarator, but this is fine too. 
Though I'd move this lambda down into the cdk_function case or out to a 
separate function.

>  	case cdk_function:
>  	  {
> +	    tree xobj_parm
> +	      = get_xobj_parm (declarator->u.function.parameters);
> +	    is_xobj_member_function = xobj_parm;

I'd also move these down a few lines after the setting of 'raises'.

> +	/* 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);

This seems like a fine way to handle this.

> +      /* 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.  */

This seems like a reasonable place for it since 'this' is supposed to 
precede the decl-specifiers, and since we are parsing initial attributes 
here rather than in the caller.  You will want to give an error if 
found_decl_spec is set.  And elsewhere complain about 'this' on 
parameters after the first (in cp_parser_parameter_declaration_list?), 
or in a non-member/lambda (in grokdeclarator?).

Jason
waffl3x Sept. 20, 2023, 12:30 a.m. UTC | #2
> Thank you, this is great!

Thanks!

> One legal hurdle to start with: our DCO policy
> (https://gcc.gnu.org/dco.html) requires real names in the sign-off, not
> pseudonyms. If you would prefer to contribute under this pseudonym, I
> encourage you to file a copyright assignment with the FSF, who are set
> up to handle that.

I will get on that right away.

> > +/* These need to moved to somewhere appropriate. */
> 
> 
> This isn't a bad spot for these macros, but you could also move them
> down lower, maybe near DECL_THIS_STATIC and DECL_ARRAY_PARAMETER_P for
> some thematic connection.

Sounds good, I will move them down.

> > +/* The flag is a member of base, but the value is meaningless for other
> > + decl types so checking is still justified I imagine. */
> 
> 
> Absolutely, we often reuse bits for other purposes if they're disjoint
> from the use they were added for.

Would it be more appropriate to give it a general name in base instead
then? If so, I can also change that.

> > +/* 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)
> 
> 
> Better to use a DECL_LANG_FLAG than claim one of the
> language-independent flags for C++.
> 
> There's a list at the top of cp-tree.h of the uses of LANG_FLAG on
> various kinds of tree node. DECL_LANG_FLAG_4 seems free on PARM_DECL.

Okay, I will switch to that instead, I didn't like using such a general
purpose flag for what is only relevant until the FUNC_DECL is created
and then never again.

If you don't mind answering right now, what are the consequences of
claiming language-independent flags for C++? Or to phrase it
differently, why would this be claiming it for C++? My guess was that
those flags could be used by any front ends and there wouldn't be any
conflicts, as you can't really have crossover between two front ends at
the same time. Or is that the thing, that kind of cross-over is
actually viable and claiming a language independent flag inhibits that
possibility? Like I eluded to, this is kinda off topic from the patch
so feel free to defer the answer to someone else but I just want to
clear up my understanding for the future.

> > + /* Only used for skipping over build_memfn_type, grokfndecl handles
> > + copying the flag to the correct field for a func_decl.
> > + There must be a better way to do this, but it isn't obvious how. */
> > + bool is_xobj_member_function = false;
> > + auto get_xobj_parm = [](tree parm_list)
> 
> 
> I guess you could add a flag to the declarator, but this is fine too.
> Though I'd move this lambda down into the cdk_function case or out to a
> separate function.

Okay, I will move the lambda.

> > case cdk_function:
> > {
> > + tree xobj_parm
> > + = get_xobj_parm (declarator->u.function.parameters);
> > + is_xobj_member_function = xobj_parm;
> 
> 
> I'd also move these down a few lines after the setting of 'raises'.

Will do.
Also, I forgot to mention it anywhere, the diagnostic patch utilizes
xobj_parm which is why it's a separate variable.

> > + /* 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);
> 
> 
> This seems like a fine way to handle this.

Okay good, I had my doubt's there.
> > + /* 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. */
> 
> 
> This seems like a reasonable place for it since 'this' is supposed to
> precede the decl-specifiers, and since we are parsing initial attributes
> here rather than in the caller. You will want to give an error if
> found_decl_spec is set. And elsewhere complain about 'this' on
> parameters after the first (in cp_parser_parameter_declaration_list?),
> or in a non-member/lambda (in grokdeclarator?).
> 
> Jason

Yeah, I separated all the diagnostics out into the second patch. This
patch was meant to include the bare minimum of what was necessary to
get the feature functional. As for the diagnostics patch, I'm not happy
with how scattered about the code base it is, but you'll be able to
judge for yourself when I resubmit that patch, hopefully later today.
So not to worry, I didn't neglect diagnostics, it's just in a follow
up. The v1 of it was submitted on August 31st if you want to find it,
but I wouldn't recommend it. I misunderstood how some things were to be
formatted so it's probably best you just wait for me to finish a v2 of
it.

One last thing, I assume I should clean up the comments and replace
them with more typical ones right? I'm going to go forward with that
assumption in v3, I just want to mention it in advanced just in case I
have the wrong idea.

I will get started on v3 of this patch and v2 of the diagnostic patch
as soon as I have the ball rolling on legal stuff. I should have it all
finished tonight. Thanks for the detailed response, it cleared up a lot
of my doubts.

Alex
Jason Merrill Sept. 20, 2023, 9:19 p.m. UTC | #3
On 9/19/23 20:30, waffl3x wrote:
>> Thank you, this is great!
> 
> Thanks!
> 
>> One legal hurdle to start with: our DCO policy
>> (https://gcc.gnu.org/dco.html) requires real names in the sign-off, not
>> pseudonyms. If you would prefer to contribute under this pseudonym, I
>> encourage you to file a copyright assignment with the FSF, who are set
>> up to handle that.
> 
> I will get on that right away.
> 
>>> +/* These need to moved to somewhere appropriate. */
>>
>> This isn't a bad spot for these macros, but you could also move them
>> down lower, maybe near DECL_THIS_STATIC and DECL_ARRAY_PARAMETER_P for
>> some thematic connection.
> 
> Sounds good, I will move them down.
> 
>>> +/* The flag is a member of base, but the value is meaningless for other
>>> + decl types so checking is still justified I imagine. */
>>
>> Absolutely, we often reuse bits for other purposes if they're disjoint
>> from the use they were added for.
> 
> Would it be more appropriate to give it a general name in base instead
> then? If so, I can also change that.

That would make sense.

>>> +/* 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)
>>
>> Better to use a DECL_LANG_FLAG than claim one of the
>> language-independent flags for C++.
>>
>> There's a list at the top of cp-tree.h of the uses of LANG_FLAG on
>> various kinds of tree node. DECL_LANG_FLAG_4 seems free on PARM_DECL.
> 
> Okay, I will switch to that instead, I didn't like using such a general
> purpose flag for what is only relevant until the FUNC_DECL is created
> and then never again.

That's a good point, but the flag you chose seems even more general purpose.

A better option might be, instead of putting this flag on the PARM_DECL, 
to put it on the short-lived TREE_LIST which is only used for 
communication between cp_parser_parameter_declaration_list and 
grokparms, and have grokdeclarator grab it from 
declarator->u.function.parameters?

> If you don't mind answering right now, what are the consequences of
> claiming language-independent flags for C++? Or to phrase it
> differently, why would this be claiming it for C++? My guess was that
> those flags could be used by any front ends and there wouldn't be any
> conflicts, as you can't really have crossover between two front ends at
> the same time. Or is that the thing, that kind of cross-over is
> actually viable and claiming a language independent flag inhibits that
> possibility? Like I eluded to, this is kinda off topic from the patch
> so feel free to defer the answer to someone else but I just want to
> clear up my understanding for the future.

Generally the flags that aren't specifically specified to be 
language-specific are reserved for language-independent uses; even if 
only one front-end actually uses the feature, it should be for 
communication to language-independent code rather than communication 
within the particular front-end.  The patch modified tree-core.h to 
refer to a macro in cp-tree.h.

> Yeah, I separated all the diagnostics out into the second patch. This
> patch was meant to include the bare minimum of what was necessary to
> get the feature functional. As for the diagnostics patch, I'm not happy
> with how scattered about the code base it is, but you'll be able to
> judge for yourself when I resubmit that patch, hopefully later today.
> So not to worry, I didn't neglect diagnostics, it's just in a follow
> up. The v1 of it was submitted on August 31st if you want to find it,
> but I wouldn't recommend it. I misunderstood how some things were to be
> formatted so it's probably best you just wait for me to finish a v2 of
> it.

Ah, oops, I assumed that v2 completely replaced v1.

> One last thing, I assume I should clean up the comments and replace
> them with more typical ones right? I'm going to go forward with that
> assumption in v3, I just want to mention it in advanced just in case I
> have the wrong idea.

Yes, please.

> I will get started on v3 of this patch and v2 of the diagnostic patch
> as soon as I have the ball rolling on legal stuff. I should have it all
> finished tonight. Thanks for the detailed response, it cleared up a lot
> of my doubts.

Sounds good!

Jason
waffl3x Sept. 21, 2023, 11:28 a.m. UTC | #4
> This seems like a reasonable place for it since 'this' is supposed to
> precede the decl-specifiers, and since we are parsing initial attributes
> here rather than in the caller. You will want to give an error if
> found_decl_spec is set. And elsewhere complain about 'this' on
> parameters after the first (in cp_parser_parameter_declaration_list?),
> or in a non-member/lambda (in grokdeclarator?).

Bringing this back up, I recalled another detail regarding this.

I'm pretty sure that found_decl_spec can be false when parsing the
second or latter decl-specifier. I tested it quickly and I believe I am
correct. I raise this as my diagnostics patch introduces another
variable to track whether we are on the first decl-specifier, given the
results of my quick test, I believe that was the correct choice.

This kinda unclear machinery is what makes me really want to refactor
this code, but I've resisted as it would be inappropriate to try to do
so while implementing a feature. Once I am finished implementing
`deducing this` would you be open to me refactoring grokdeclarator and
it's various auxiliary functions?

As for where the complaining happens, I believe I implemented this
particular error in cp_parser_decl_specifier_seq, I don't plan to be
stubborn on any of the diagnostic code though as I'm pretty unhappy
with how it got scattered about. I intend to get more input on that
after I finish v2 of the diagnostic patch though.


> That's a good point, but the flag you chose seems even more general purpose.

Yeah, I had to just settle on it because I was bikeshedding it for a
couple hours despite being very unhappy with it.

> A better option might be, instead of putting this flag on the PARM_DECL,
> to put it on the short-lived TREE_LIST which is only used for
> communication between cp_parser_parameter_declaration_list and
> grokparms, and have grokdeclarator grab it from
> declarator->u.function.parameters?

That does sound ideal! I will look into doing it this way.

> Generally the flags that aren't specifically specified to be
> language-specific are reserved for language-independent uses; even if
> only one front-end actually uses the feature, it should be for
> communication to language-independent code rather than communication
> within the particular front-end.

Ah okay, that makes perfect sense to me, understood.

> The patch modified tree-core.h to
> refer to a macro in cp-tree.h.

Yeah, I wasn't sure about doing that, I will refrain from that in the
future, (along with removing it from v3, but the other change you
suggested should eliminate the referred to macro anyway.)

> > Yeah, I separated all the diagnostics out into the second patch. This
> > patch was meant to include the bare minimum of what was necessary to
> > get the feature functional. As for the diagnostics patch, I'm not happy
> > with how scattered about the code base it is, but you'll be able to
> > judge for yourself when I resubmit that patch, hopefully later today.
> > So not to worry, I didn't neglect diagnostics, it's just in a follow
> > up. The v1 of it was submitted on August 31st if you want to find it,
> > but I wouldn't recommend it. I misunderstood how some things were to be
> > formatted so it's probably best you just wait for me to finish a v2 of
> > it.
> 
> 
> Ah, oops, I assumed that v2 completely replaced v1.

I had intended to complete v2 of it quite some time ago, I've just been
busy. Today as well I got sidetracked with some job hunting, but I plan
on finishing v3 of the initial support patch (the one related to this
thread) tonight at the very least. I can't commit to diagnostics v2
tonight, but if it happens it happens. :)

I might even have to leave out communicating that a PARM_DECL is an
xobj parm cp_parser_parameter_declaration_list if I have too hard a
time figuring out how to work it in, if that is the case then I will
make that change in a v4.

Alex
Jason Merrill Sept. 22, 2023, 11:30 a.m. UTC | #5
On 9/21/23 07:28, waffl3x wrote:
>> This seems like a reasonable place for it since 'this' is supposed to
>> precede the decl-specifiers, and since we are parsing initial attributes
>> here rather than in the caller. You will want to give an error if
>> found_decl_spec is set. And elsewhere complain about 'this' on
>> parameters after the first (in cp_parser_parameter_declaration_list?),
>> or in a non-member/lambda (in grokdeclarator?).
> 
> Bringing this back up, I recalled another detail regarding this.
> 
> I'm pretty sure that found_decl_spec can be false when parsing the
> second or latter decl-specifier. I tested it quickly and I believe I am
> correct. I raise this as my diagnostics patch introduces another
> variable to track whether we are on the first decl-specifier, given the
> results of my quick test, I believe that was the correct choice.

Makes sense.

> This kinda unclear machinery is what makes me really want to refactor
> this code, but I've resisted as it would be inappropriate to try to do
> so while implementing a feature. Once I am finished implementing
> `deducing this` would you be open to me refactoring grokdeclarator and
> it's various auxiliary functions?

Yes, but I'll warn you that grokdeclarator has resisted refactoring for 
a long time...

Jason
diff mbox series

Patch

From 0db52146880faf20e7a7b786dad47c686a5f26d6 Mon Sep 17 00:00:00 2001
From: Waffl3x <waffl3x@protonmail.com>
Date: Mon, 11 Sep 2023 04:21:10 -0600
Subject: [PATCH] c++: Initial support for C++23 P0847R7 (Deducing This)
 [PR102609]

This patch implements initial support for P0847R7, without additions to
diagnostics.  Almost everything should work correctly, barring a few
limitations which are listed below.  I attempted to minimize changes to the
existing code, treating explicit object member functions as static functions,
while flagging them to give them extra powers seemed to be the best way of
achieving this.  For this patch, the flag is only utilized in call.cc for
resolving overloads and making the actual function call.  To achieve this,
conversion of a FUNC_TYPE to a METHOD_TYPE is suppressed when the first
parameter of the FUNC_TYPE is an explicit object parameter, this appears to be
sufficient.  I opted to create a new variable to achieve this instead of
using "staticp" to avoid any possible confusion.

As for the previously mentioned limitations, lambdas do not work correctly yet,
but I suspect that a few tweaks are all it will take to have them fully
functional.  User defined conversion functions are not called when an explicit
object member function with an explicit object parameter of an unrelated type
is called.  The following case does not behave correctly because of this.

struct S {
  operator size_t() {
    return 42;
  }
  size_t f(this size_t n) {
    return n;
  }
};

int main()
{
  S s{};
  size_t a = s.f();
}

Currently, it appears that the object argument is simply reinterpreted as
a size_t instead of properly calling the user defined conversion function.
The validity of such a conversion is still considered however, if there is no
way to convert S to a size_t an appropriate compile error will be emitted.
I have an idea of what changes need to be made to fix this, but I did not
persue this for the initial implementation patch.
This bug can be observed in the explicit-object-param4.C test case, while
explicit-object-param3.C demonstrates the non functioning lambdas.

	PR c++/102609

gcc/cp/ChangeLog:

	PR c++/102609
	Initial support for C++23 P0847R7 - Deducing this.
	* call.cc (add_candidates): Check if fn is an xobj member function.
	(build_over_call): Ditto.
	* cp-tree.h (struct lang_decl_base::xobj_flag): New data member.
	(DECL_FUNC_XOBJ_FLAG): Define.
	(DECL_PARM_XOBJ_FLAG): Define.
	(DECL_IS_XOBJ_MEMBER_FUNC): Define.
	(enum cp_decl_spec): Add ds_this.
	* decl.cc (grokfndecl): Set xobj_flag if first param is an xobj param.
	(grokdeclarator): For xobj member functions, Don't change type to
	METHOD_TYPE, leave it as FUNC_TYPE. For PARM decl_context, set
	decl_flag_3 if param is an xobj param.
	* parser.cc (cp_parser_decl_specifier_seq): Handle this specifier.
	(set_and_check_decl_spec_loc): Add "this".

gcc/ChangeLog:

	PR c++/102609
	Initial support for C++23 P0847R7 - Deducing this.
	* tree-core.h (struct tree_decl_common): Comment use of decl_flag_3.

gcc/testsuite/ChangeLog:

	PR c++/102609
	Initial support for C++23 P0847R7 - Deducing this.
	* g++.dg/cpp23/explicit-object-param1.C: New test.
	* g++.dg/cpp23/explicit-object-param2.C: New test.
	* g++.dg/cpp23/explicit-object-param3.C: New test.
	* g++.dg/cpp23/explicit-object-param4.C: New test.

Signed-off-by: Waffl3x <waffl3x@protonmail.com>
---
 gcc/cp/call.cc                                |   6 +-
 gcc/cp/cp-tree.h                              |  21 +++-
 gcc/cp/decl.cc                                |  24 ++++
 gcc/cp/parser.cc                              |  15 ++-
 .../g++.dg/cpp23/explicit-object-param1.C     | 114 ++++++++++++++++++
 .../g++.dg/cpp23/explicit-object-param2.C     |  28 +++++
 .../g++.dg/cpp23/explicit-object-param3.C     |  15 +++
 .../g++.dg/cpp23/explicit-object-param4.C     |  33 +++++
 gcc/tree-core.h                               |   3 +-
 9 files changed, 254 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-object-param4.C

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 399345307ea..37690fdae25 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -6547,7 +6547,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
@@ -9969,7 +9970,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 3ca011c61c8..522ac2d067e 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,22 @@  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)
+/* First checks if the node has a meaningful value for xobj_flag,
+   evaluates false if not, otherwise evaluates to the value of the flag.  */
+#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 +6293,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 255c4026bdb..d0026059f0f 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10345,6 +10345,8 @@  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.  */
+  DECL_FUNC_XOBJ_FLAG (decl) = parms ? DECL_PARM_XOBJ_FLAG (parms) : false;
 
   /* Set the constraints on the declaration. */
   if (flag_concepts)
@@ -12997,6 +12999,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.
+     There must be a better way to do this, but it isn't obvious how.  */
+  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)
@@ -13092,6 +13107,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;
 
@@ -14176,6 +14194,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)))
@@ -14194,6 +14214,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 ed0675c9599..354297bfff8 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16049,6 +16049,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
@@ -33809,7 +33821,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-param1.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param1.C
new file mode 100644
index 00000000000..134182c7741
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param1.C
@@ -0,0 +1,114 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// basic use cases and calling
+
+// non-trailing return
+// definitions
+struct S0 {
+  void f0(this S0) {}
+  void f1(this S0&) {}
+  void f2(this S0&&) {}
+  void f3(this S0 const&) {}
+  void f4(this S0 const&&) {}
+  template<typename Self>
+  void d0(this Self&&) {}
+  void d1(this auto&&) {}
+};
+// declarations
+struct S1 {
+  void f0(this S1);
+  void f1(this S1&);
+  void f2(this S1&&);
+  void f3(this S1 const&);
+  void f4(this S1 const&&);
+  template<typename Self>
+  void d0(this Self&&);
+  void d1(this auto&&);
+};
+// out of line definitions
+void S1::f0(this S1) {}
+void S1::f1(this S1&) {}
+void S1::f2(this S1&&) {}
+void S1::f3(this S1 const&) {}
+void S1::f4(this S1 const&&) {}
+template<typename Self>
+void S1::d0(this Self&&) {}
+void S1::d1(this auto&&) {}
+
+// trailing return
+// definitions
+struct S2 {
+  auto f0(this S2) -> void {}
+  auto f1(this S2&) -> void {}
+  auto f2(this S2&&) -> void {}
+  auto f3(this S2 const&) -> void {}
+  auto f4(this S2 const&&) -> void {}
+  template<typename Self>
+  auto d0(this Self&&) -> void {}
+
+  auto d1(this auto&&) -> void {}
+};
+// declarations
+struct S3 {
+  auto f0(this S3) -> void;
+  auto f1(this S3&) -> void;
+  auto f2(this S3&&) -> void;
+  auto f3(this S3 const&) -> void;
+  auto f4(this S3 const&&) -> void;
+  template<typename Self>
+  auto d0(this Self&&) -> void;
+  auto d1(this auto&&) -> void;
+};
+// out of line definitions
+auto S3::f0(this S3) -> void {}
+auto S3::f1(this S3&) -> void {}
+auto S3::f2(this S3&&) -> void {}
+auto S3::f3(this S3 const&) -> void {}
+auto S3::f4(this S3 const&&) -> void {}
+template<typename Self>
+auto S3::d0(this Self&&) -> void {}
+auto S3::d1(this auto&&) -> void {}
+
+template<typename T>
+void call_with_qualification()
+{
+  T obj{};
+  // by value should take any qualification (f0)
+  T{}.f0();
+  obj.f0();
+  static_cast<T&&>(obj).f0(); 
+  static_cast<T const&>(obj).f0();
+  static_cast<T const&&>(obj).f0();
+  // specific qualification (f1 - f4)
+  T{}.f2();
+  T{}.f3();
+  T{}.f4();
+  obj.f1();
+  obj.f3();
+  static_cast<T&&>(obj).f2();
+  static_cast<T&&>(obj).f3();
+  static_cast<T&&>(obj).f4();
+  static_cast<T const&>(obj).f3();
+  static_cast<T const&&>(obj).f4();
+  // deduced should (obviously) take any qualification (d0, d1)
+  T{}.d0();
+  obj.d0();
+  static_cast<T&&>(obj).d0();
+  static_cast<T const&>(obj).d0();
+  static_cast<T const&&>(obj).d0();
+  T{}.d1();
+  obj.d1();
+  static_cast<T&&>(obj).d1();
+  static_cast<T const&>(obj).d1();
+  static_cast<T const&&>(obj).d1();
+}
+
+void perform_calls()
+{
+  call_with_qualification<S0>();
+  call_with_qualification<S1>();
+  call_with_qualification<S2>();
+  call_with_qualification<S3>();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param2.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param2.C
new file mode 100644
index 00000000000..a3164c2537c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param2.C
@@ -0,0 +1,28 @@ 
+// P0847R7
+// { dg-do run { target c++23 } }
+
+// explicit object member function pointer type deduction,
+// conversion to function pointer, and
+// calling through pointer to function
+
+struct S {
+  int _n;
+  int f(this S& self) { return self._n; }
+};
+
+using f_type = int(*)(S&);
+
+static_assert (__is_same (f_type, decltype (&S::f)));
+
+int main()
+{
+  auto fp0 = &S::f;
+  f_type fp1 = &S::f;
+  static_assert (__is_same (decltype (fp0), decltype (fp1)));
+  S s{42};
+  if (fp0 (s) != 42)
+    __builtin_abort ();
+  if (fp1 (s) != 42)
+    __builtin_abort ();
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param3.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param3.C
new file mode 100644
index 00000000000..fa92e4cd440
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param3.C
@@ -0,0 +1,15 @@ 
+// 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);
+}
+
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-object-param4.C b/gcc/testsuite/g++.dg/cpp23/explicit-object-param4.C
new file mode 100644
index 00000000000..746f6d99b94
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-object-param4.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-xfail-run-if "user defined conversions from an implicit object argument to an explicit object parameter are not supported yet" { *-*-* } }
+
+using uintptr_t = __UINTPTR_TYPE__;
+
+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();
+  if (ret == reinterpret_cast<uintptr_t>(&s))
+    __builtin_abort ();
+  if (ret != 42)
+    __builtin_abort ();
+}
+
diff --git a/gcc/tree-core.h b/gcc/tree-core.h
index 91551fde900..336064fb270 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.42.0