[v4,2/2] c++: Diagnostics for P0847R7 (Deducing this) [PR102609]

Message ID RqBEJfZ4EpHLvN6GOqUoQV6JJ5d2ahvs7gLHynIQOKulN-kWOn0MN9lUwk82_hKCpdsZKyK8FS2KYmdTY2K7WQsqsXzy5p7IdFtX7sE5O-0=@protonmail.com
State Accepted
Headers
Series [v4,1/2] c++: Initial support for P0847R7 (Deducing this) [PR102609] |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

waffl3x Nov. 5, 2023, 3:06 p.m. UTC
  Bootstrapped and tested on x86_64-linux with no regressions.

Finally, the fabled diagnostics patch. I would like to note really
quickly that there was never a v2 and v3 of this patch, only the first
of these 2 had those versions. Originally I had planned to revise this
patch alongside the first but it just didn't happen. Anyhow, I decided
to match the version of this second patch to the current first patch to
avoid any confusion. 

With that out of the way, I feel mostly okay about the code in this
patch, but I have a feeling it will need a revision, especially with
the large amounts of comments I left in. At the very least I expect to
need to pull those out before the patch can be accepted.

I had wanted to write about some of my frustrations with trying to
write a test for virtual specifiers and errors/warnings for
shadowing/overloading virtual functions, but I am a bit too tired at
the moment and I don't want to delay getting this up for another night.
In short, the standard does not properly specify the criteria for
overriding functions, which leaves a lot of ambiguity in how exactly we
should be handling these cases. The standard also really poorly
specifies things related to the implicit object parameter and implicit
object argument which also causes some trouble. Anyhow, for the time
being I am not including my test for diagnostics related to a virtual
specifier on xobj member functions. I can't get it to a point I am
happy with it and I think there will need to be some discussion on how
exactly we want to handle that.

I was fairly lazy with the changelog and commit message in this patch
as I expect to need to do another round on this patch before it can be
accepted. One specific question I have is whether I should be listing
out all the diagnostics that were added to a function. For the cases
where there were only one diagnostic added I stated it, but for
grokdeclarator which has the majority of the diagnostics I did not. I
welcome input here, really I request it, because the changelogs are
still fairly difficult for me to write. Hell, the commit messages are
hard to write, I feel I went overboard on the first patch but I guess
it's a fairly large patch so maybe it's alright? Again, I am looking
for feedback here if anyone is willing to provide it.

I've written more than I want here, so I'll wrap this e-mail up and go
to bed. I am very happy to be getting close to a final product here.
Hopefully if all goes well I'll be able to fit in the final missing
features before feature lock hits.

Alex
  

Comments

Jason Merrill Nov. 9, 2023, 11:41 p.m. UTC | #1
On 11/5/23 10:06, waffl3x wrote:
> I had wanted to write about some of my frustrations with trying to
> write a test for virtual specifiers and errors/warnings for
> shadowing/overloading virtual functions, but I am a bit too tired at
> the moment and I don't want to delay getting this up for another night.
> In short, the standard does not properly specify the criteria for
> overriding functions, which leaves a lot of ambiguity in how exactly we
> should be handling these cases. 

Agreed, this issue came up in the C++ committee meeting today.  See

https://cplusplus.github.io/CWG/issues/2553.html
https://cplusplus.github.io/CWG/issues/2554.html

for draft changes to clarify some of these issues.

> The standard also really poorly
> specifies things related to the implicit object parameter and implicit
> object argument which also causes some trouble. Anyhow, for the time
> being I am not including my test for diagnostics related to a virtual
> specifier on xobj member functions. I can't get it to a point I am
> happy with it and I think there will need to be some discussion on how
> exactly we want to handle that.

The discussion might be easier with the testcase to refer to?

> I was fairly lazy with the changelog and commit message in this patch
> as I expect to need to do another round on this patch before it can be
> accepted. One specific question I have is whether I should be listing
> out all the diagnostics that were added to a function. For the cases
> where there were only one diagnostic added I stated it, but for
> grokdeclarator which has the majority of the diagnostics I did not. I
> welcome input here, really I request it, because the changelogs are
> still fairly difficult for me to write. Hell, the commit messages are
> hard to write, I feel I went overboard on the first patch but I guess
> it's a fairly large patch so maybe it's alright? Again, I am looking
> for feedback here if anyone is willing to provide it.

ChangeLog entries are very brief summaries of the changes, there's 
absolutely no need to enumerate multiple diagnostics.  If someone wants 
more detail they can look at the patch.

> +      if (xobj_func_p && (quals || rqual))
> +	inform (DECL_SOURCE_LOCATION (DECL_ARGUMENTS (decl)),
> +		"explicit object parameter declared here");

When you add an inform after a diagnostic, you also need to add an 
auto_diagnostic_group declaration before the error so that they get 
grouped together for JSON/SARIF diagnostic output.

This applies to a lot of the diagnostics in the patch.

> +	      pedwarn(DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions,

Missing space before (

> +		/* If   */

I think this comment doesn't add much.  :)

> +		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");

Let's say "a pointer to member function type "

> +		/* Ideally we should synthesize the correct syntax
> +		   for the user, perhaps this could be added later.  */

Should be pretty simple to produce an add_fixit_remove() for the 'this' 
token here?

> +	    /* Free function case,
> +	       surely there is a better way to identify it?  */

Move these diagnostics down past where ctype gets set?

> +	    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");

Let's say "non-member function".

> +		  /* Ideally we synthesize a full rewrite, at the moment
> +		     there are issues with it though.
> +		     It rewrites "f(S this & s)" correctly,
> +		     but fails to rewrite "f(const this S s)" correctly.
> +		     It also does not handle "f(S& this s)" correctly at all.

David Malcolm would be the one to ask for advice about fixit tricks, if 
you want.

> +		     It's also possible we want to wait and see if the parm
> +		     could even be a valid xobj parm as it might be confusing
> +		     to the user to see an error, fix it, and then see another
> +		     error for something new.

I don't see how that applies here; we don't bail out after this error, 
so we should continue to give any other needed errors.

> +	  /* If default_argument is non-null token should always be the
> +	     the location of the `=' token, this is brittle code though
> +	     and should be rectified in the future.  */

It would be easy enough to add an eq_token variable?

> +      /* I can imagine doing a fixit here, suggesting replacing
> +	 this / *this / this-> with &name / name / "name." but it would be
> +	 very difficult to get it perfect and I've been advised against
> +	 making imperfect fixits.
> +	 Perhaps it would be as simple as the replacements listed,
> +	 even if one is move'ing/forward'ing, if the replacement is just
> +	 done in the same place, it will be exactly what the user wants?
> +	 Even if this is true though, there's still a problem of getting the
> +	 context of the expression to find which tokens to replace.
> +	 I would really like for this to be possible though.
> +	 I will decide whether or not to persue this after review.  */

You could pass the location of 'this' from the parser to 
finish_this_expr and always replace it with (&name)?

> +      tree xobj_parm = FUNCTION_DECL_CHECK (fn)->function_decl.arguments;

DECL_ARGUMENTS (fn)

> +      tree parm_name = DECL_MINIMAL_CHECK (xobj_parm)->decl_minimal.name;

DECL_NAME (xobj_parm)

> +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
> @@ -0,0 +1,7 @@
> +// P0847R7
> +// { dg-do compile { target c++20_down } }
> +// { dg-options "-pedantic-errors" }

-pedantic-errors is the default in the testsuite if there is no dg-options.

> +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
> @@ -0,0 +1,7 @@
> +// P0847R7
> +// { dg-do compile { target c++20_down } }
> +// { dg-options "-Wno-error=pedantic" }

This has the same effect as { dg-options "" } since any dg-options 
replaces the default -pedantic-errors.

Also, -Wno-error=pedantic does not turn off -pedantic-errors for 
-Wc++23-extensions.

> +    func_ptr_type xobj_mem_f_ptr = &xobj_member_f; // { dg-error "undetermined error" "disallowing address of unqualified explicit object member function is not implemented yet" { xfail *-*-* } }

This would be complaining in cp_build_addr_expr_1 if you end up taking 
the address of an xobj fn without arg being an OFFSET_REF (with 
PTRMEM_OK_P).

> +  void f1() const; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
> +  void f1(this S1 const&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }

In add_method, the code

>       /* Compare the quals on the 'this' parm.  Don't compare                                                                       
>          the whole types, as used functions are treated as                                                                          
>          coming from the using class in overload resolution.  */
>       if (! DECL_STATIC_FUNCTION_P (fn)
>           && ! DECL_STATIC_FUNCTION_P (method)
>           /* Either both or neither need to be ref-qualified for                                                                    
>              differing quals to allow overloading.  */
>           && (FUNCTION_REF_QUALIFIED (fn_type)
>               == FUNCTION_REF_QUALIFIED (method_type))
>           && (type_memfn_quals (fn_type) != type_memfn_quals (method_type)
>               || type_memfn_rqual (fn_type) != type_memfn_rqual (method_type)))
>           continue;

needs adjustment for the new "corresponding object parameters".

Jason
  
waffl3x Nov. 10, 2023, 8:47 a.m. UTC | #2
> > I had wanted to write about some of my frustrations with trying to
> > write a test for virtual specifiers and errors/warnings for
> > shadowing/overloading virtual functions, but I am a bit too tired at
> > the moment and I don't want to delay getting this up for another night.
> > In short, the standard does not properly specify the criteria for
> > overriding functions, which leaves a lot of ambiguity in how exactly we
> > should be handling these cases.
> 
> 
> Agreed, this issue came up in the C++ committee meeting today. See
> 
> https://cplusplus.github.io/CWG/issues/2553.html
> https://cplusplus.github.io/CWG/issues/2554.html
> 
> for draft changes to clarify some of these issues.

Ah I guess I should have read all your responses before sending any
back instead of going one by one. I ended up writing about this again I
think.

struct B {
    virtual void f() const {}
};

struct S0 : B {
    void f() {}
};

struct S1 : B {
    void f(this S1&) {}
};

I had a bit of a debate with a peer, I initially disagreed with the
standard resolution because it disallows S1's declaration of f, while
S0's is an overload. I won't bore you with all the details of going
back and forth about the proposed wording, my feelings are mostly that
being able to overload something with an iobj member function but not
being able to with an xobj member function was inconsistent. He argued
that keeping restrictions high at first and lowering them later is
better, and overloading virtual functions is already not something you
should really ever do, so he was in favor of the proposed wording.

In light of our debate, my stance is that we should implement things as
per the proposed wording. 

struct B {
  virtual void foo() const&;
};

struct D : B {
  void foo(this int);
};

This would be ill-formed now according to the change in wording. My
laymans interpretation of the semantics are that, the parameter list is
the same when the xobj parameter is ignore, so it overrides. And since
it overrides, and xobj member functions are not allowed to override, it
is an error.

To be honest, I still don't understand where the wording accounts for
the qualifiers of B::f, but my friend assured me that it is.

> > The standard also really poorly
> > specifies things related to the implicit object parameter and implicit
> > object argument which also causes some trouble. Anyhow, for the time
> > being I am not including my test for diagnostics related to a virtual
> > specifier on xobj member functions. I can't get it to a point I am
> > happy with it and I think there will need to be some discussion on how
> > exactly we want to handle that.
> 
> 
> The discussion might be easier with the testcase to refer to?

I'm pretty clear on how to proceed now. My biggest issue with the tests
is I didn't know what combination of errors to emit, whether to warn
for overriding, etc. There is still a bit of an issue on how to emit
errors for virtual specifiers on xobj member functions, it gets noisy
quickly, but maybe we do just emit all of them? This aside, what should
be done here is clear now.

Somewhat related is some warnings I wanted to implement for impossible
to call by-value xobj member functions. Ones that involve an unrelated
type get dicey because people could in theory have legitimate use cases
for that, P0847R7 includes an example of this combining recursive
lambdas and the overload pattern to create a visitor. However, I think
it would be reasonable to warn when a by-value xobj member function can
not be called due to the presence of overloads that take references.
Off the top of my head I don't recall how many overloads it would take
to prevent a by-value version from being called, nor have I looked into
how to implement this. But I do know that redeclarations of xobj member
functions as iobj member functions (and vice-versa) are not properly
identified when ref qualifiers are omitted from the corresponding (is
this the correct term here?) iobj member function.

> > I was fairly lazy with the changelog and commit message in this patch
> > as I expect to need to do another round on this patch before it can be
> > accepted. One specific question I have is whether I should be listing
> > out all the diagnostics that were added to a function. For the cases
> > where there were only one diagnostic added I stated it, but for
> > grokdeclarator which has the majority of the diagnostics I did not. I
> > welcome input here, really I request it, because the changelogs are
> > still fairly difficult for me to write. Hell, the commit messages are
> > hard to write, I feel I went overboard on the first patch but I guess
> > it's a fairly large patch so maybe it's alright? Again, I am looking
> > for feedback here if anyone is willing to provide it.
> 
> 
> ChangeLog entries are very brief summaries of the changes, there's
> absolutely no need to enumerate multiple diagnostics. If someone wants
> more detail they can look at the patch.

Noted, thank goodness.

> > + if (xobj_func_p && (quals || rqual))
> > + inform (DECL_SOURCE_LOCATION (DECL_ARGUMENTS (decl)),
> > + "explicit object parameter declared here");
> 
> 
> When you add an inform after a diagnostic, you also need to add an
> auto_diagnostic_group declaration before the error so that they get
> grouped together for JSON/SARIF diagnostic output.
> 
> This applies to a lot of the diagnostics in the patch.

AH, okay, I will go through and add them.

> > + pedwarn(DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions,
> 
> 
> Missing space before (

Oops :)

> > + /* If */
> 
> 
> I think this comment doesn't add much. :)

I wish I remembered what I even meant to put here, oh well, must not
have been all that important. Oh wait, I remember it now, I was going
to note that the next element in the chain of declarators being null
seems to indicate that the declaration is a function type. I will
probably put that comment in again, it's on the line of commenting
exactly what the code does but it was cryptic to figure this out, I'm
not sure if I should lean towards including the comment here or not.

> > + 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");
> 
> 
> Let's say "a pointer to member function type "

Noted.

> > + /* Ideally we should synthesize the correct syntax
> > + for the user, perhaps this could be added later. */
> 
> 
> Should be pretty simple to produce an add_fixit_remove() for the 'this'
> token here?

Unfortunately no, for the regular function case yeah but for member
function pointers it gets more complicated. Upon writing this and
revising 3 times referring to different cases where the user might have
meant something different, I'm realizing that a fixit is probably just
not appropriate for the pointer to member function case.

> > + /* Free function case,
> > + surely there is a better way to identify it? */
> 
> 
> Move these diagnostics down past where ctype gets set?

Sure, I'll see how it looks refactored that way.

> > + 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");
> 
> 
> Let's say "non-member function".

Noted.

> > + /* Ideally we synthesize a full rewrite, at the moment
> > + there are issues with it though.
> > + It rewrites "f(S this & s)" correctly,
> > + but fails to rewrite "f(const this S s)" correctly.
> > + It also does not handle "f(S& this s)" correctly at all.
> 
> 
> David Malcolm would be the one to ask for advice about fixit tricks, if
> you want.

I'll be sure to ask him about it, thanks.

> > + It's also possible we want to wait and see if the parm
> > + could even be a valid xobj parm as it might be confusing
> > + to the user to see an error, fix it, and then see another
> > + error for something new.
> 
> 
> I don't see how that applies here; we don't bail out after this error,
> so we should continue to give any other needed errors.
> 
> > + /* If default_argument is non-null token should always be the
> > + the location of the `=' token, this is brittle code though
> > + and should be rectified in the future. */
> 
> 
> It would be easy enough to add an eq_token variable?

I suppose so, I'll just do that then.

> > + /* I can imagine doing a fixit here, suggesting replacing
> > + this / *this / this-> with &name / name / "name." but it would be
> > + very difficult to get it perfect and I've been advised against
> > + making imperfect fixits.
> > + Perhaps it would be as simple as the replacements listed,
> > + even if one is move'ing/forward'ing, if the replacement is just
> > + done in the same place, it will be exactly what the user wants?
> > + Even if this is true though, there's still a problem of getting the
> > + context of the expression to find which tokens to replace.
> > + I would really like for this to be possible though.
> > + I will decide whether or not to persue this after review. */
> 
> 
> You could pass the location of 'this' from the parser to
> finish_this_expr and always replace it with (&name)?

I've been reluctant to make any changes to parameters, but if you're
suggesting it I will consider it. Just replacing it with '(&name)'
seems really mediocre to me though. Yeah, sure, it's always going to be
syntactically valid but I worry that it wont be semantically correct
often enough.

Yeah, considering that I think I will leave it for now until I have
time to come up with a robust solution. I might still pass in the
location of the this token though and use error_at instead of error.
I'll see how I feel once I finish higher priority stuff.

> > + tree xobj_parm = FUNCTION_DECL_CHECK (fn)->function_decl.arguments;
> 
> 
> DECL_ARGUMENTS (fn)
> 
> > + tree parm_name = DECL_MINIMAL_CHECK (xobj_parm)->decl_minimal.name;
> 
> 
> DECL_NAME (xobj_parm)

Ah yes, now I'm making the mistake that I hate to see around the code
base. I will definitely fix that.

I often wish these were member functions but a refactor supporting that
would not be trivial. Especially since node parameters are just tree
type, not the type you actually want to use them as, which would be an
even bigger refactor to support. Maybe one day perhaps.

> > +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
> > @@ -0,0 +1,7 @@
> > +// P0847R7
> > +// { dg-do compile { target c++20_down } }
> > +// { dg-options "-pedantic-errors" }
> 
> 
> -pedantic-errors is the default in the testsuite if there is no dg-options.

I know, I just wanted to be explicit about what was being tested. I
will remove it if inhibiting the other defaults is a significant issue.

> > +++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
> > @@ -0,0 +1,7 @@
> > +// P0847R7
> > +// { dg-do compile { target c++20_down } }
> > +// { dg-options "-Wno-error=pedantic" }
> 
> 
> This has the same effect as { dg-options "" } since any dg-options
> replaces the default -pedantic-errors.
> 
> Also, -Wno-error=pedantic does not turn off -pedantic-errors for
> -Wc++23-extensions.

Yeah I had my doubt that -Wno-error=pedantic would work, so I suppose
the better option here is to just do { dg-options "" } yeah? I remember
it seemed to give me the behavior I wanted so I didn't worry about it.

To be clear, I should be replacing { dg-options "-Wno-error=pedantic" }
with { dg-options "" } and you would prefer I just remove { dg-options
"-pedantic-errors" } yeah?

> > + func_ptr_type xobj_mem_f_ptr = &xobj_member_f; // { dg-error "undetermined error" "disallowing address of unqualified explicit object member function is not implemented yet" { xfail --* } }
> 
> 
> This would be complaining in cp_build_addr_expr_1 if you end up taking
> the address of an xobj fn without arg being an OFFSET_REF (with
> PTRMEM_OK_P).

Thanks, I will get this in when I fix the other nearby problem (not
setting expr location if I remember correctly) you mentioned in your
other reply.


> > + void f1() const; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail --* } }
> > + void f1(this S1 const&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail --* } }
> 
> 
> In add_method, the code
> 
> > /* Compare the quals on the 'this' parm. Don't compare
> > the whole types, as used functions are treated as
> > coming from the using class in overload resolution. /
> > if (! DECL_STATIC_FUNCTION_P (fn)
> > && ! DECL_STATIC_FUNCTION_P (method)
> > / Either both or neither need to be ref-qualified for
> > differing quals to allow overloading. */
> > && (FUNCTION_REF_QUALIFIED (fn_type)
> > == FUNCTION_REF_QUALIFIED (method_type))
> > && (type_memfn_quals (fn_type) != type_memfn_quals (method_type)
> > || type_memfn_rqual (fn_type) != type_memfn_rqual (method_type)))
> > continue;
> 
> 
> needs adjustment for the new "corresponding object parameters".
> 
> Jason

Thanks, this will speed things up considerably. I imagine this is also
where I would need to implement the warnings I mentioned regarding
by-value xobj member functions that are impossible to call.

I still have some other stuff to do today so it's unlikely I'll be able
to get anything (besides replying to e-mail) done. Thanks as always for
the detailed write up and pointing me in the right direction for the
missing diagnostics. Hopefully I can get started tomorrow if not
tonight.

Alex
  

Patch

From c8e8155a635fab7f326d0ad32326da352d7c323e Mon Sep 17 00:00:00 2001
From: waffl3x <waffl3x@protonmail.com>
Date: Sun, 5 Nov 2023 05:17:18 -0700
Subject: [PATCH 2/2] c++: Diagnostics for C++23 P0847R7 (Deducing this)
 [PR102609]

This patch adds diagnostics for various ill-formed code related to xobj member
functions. Some of the code in here leaves something to be desired, but the
majority of cases should be handled. I opted to add a new TFF flag despite only
using it in a single place, other solutions seemed non ideal and there are
plenty of bits left. Some of the diagnostics are more scattered around than I
would like, perhaps this could be refactored in the future, especially those in
grokfndecl.

	PR c++/102609

gcc/cp/ChangeLog:

	PR c++/102609
	Diagnostics for C++23 P0847R7 - Deducing this.
	* cp-tree.h (TFF_XOBJ_FUNC): Define.
	* decl.cc (grokfndecl): Diagnose cvref-qualifiers on an xobj member
	function.
	(grokdeclarator): Diagnostics
	* error.cc (dump_function_decl): For xobj member function add
	TFF_XOBJ_FUNC bit to dump_parameters flags argument.
	(dump_parameters): When printing xobj member function's params add
	"this" to the first param.
	(function_category): Say so when in an xobj member function.
	* parser.cc (cp_parser_decl_specifier_seq): Diagnose incorrectly
	positioned "this" specifier.
	(cp_parser_parameter_declaration): Diagnose default argument on
	xobj params.
	* semantics.cc (finish_this_expr): Diagnose uses of "this" in body
	of xobj member function.

gcc/testsuite/ChangeLog:

	PR c++/102609
	Diagnostics for C++23 P0847R7 - Deducing this.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-A.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-B.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-C.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-D.C: New test.
	* g++.dg/cpp23/explicit-obj-cxx-dialect-E.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics1.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics2.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics3.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics4.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics5.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics6.C: New test.
	* g++.dg/cpp23/explicit-obj-diagnostics7.C: New test.

Signed-off-by: waffl3x <waffl3x@protonmail.com>
---
 gcc/cp/cp-tree.h                              |   5 +-
 gcc/cp/decl.cc                                | 133 ++++++++++++++---
 gcc/cp/error.cc                               |   8 +-
 gcc/cp/parser.cc                              |  45 ++++++
 gcc/cp/semantics.cc                           |  28 +++-
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-A.C |   6 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-B.C |   7 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-C.C |   7 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-D.C |   7 +
 .../g++.dg/cpp23/explicit-obj-cxx-dialect-E.C |   7 +
 .../g++.dg/cpp23/explicit-obj-diagnostics1.C  | 138 ++++++++++++++++++
 .../g++.dg/cpp23/explicit-obj-diagnostics2.C  |  25 ++++
 .../g++.dg/cpp23/explicit-obj-diagnostics3.C  |  84 +++++++++++
 .../g++.dg/cpp23/explicit-obj-diagnostics4.C  |  19 +++
 .../g++.dg/cpp23/explicit-obj-diagnostics5.C  |  15 ++
 .../g++.dg/cpp23/explicit-obj-diagnostics6.C  |  22 +++
 .../g++.dg/cpp23/explicit-obj-diagnostics7.C  |  24 +++
 17 files changed, 560 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
 create mode 100644 gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 901fb1f4616..1d118fdeb7e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6116,7 +6116,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)
@@ -6134,6 +6136,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 c02d78b8102..26acf96322c 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -10607,24 +10607,29 @@  grokfndecl (tree ctype,
 	TREE_TYPE (decl) = apply_memfn_quals (TREE_TYPE (decl),
 					      TYPE_UNQUALIFIED,
 					      REF_QUAL_NONE);
-
       if (quals)
-	{
-	  error (ctype
+	error (!ctype
+	       ? G_("non-member function %qD cannot have cv-qualifier")
+	       : !xobj_func_p
 		 ? G_("static member function %qD cannot have cv-qualifier")
-		 : G_("non-member function %qD cannot have cv-qualifier"),
-		 decl);
-	  quals = TYPE_UNQUALIFIED;
-	}
-
+		 : G_("explicit object member function "
+		      "%qD cannot have cv-qualifier"),
+	       decl);
       if (rqual)
-	{
-	  error (ctype
+	error (!ctype
+	       ? G_("non-member function %qD cannot have ref-qualifier")
+	       : !xobj_func_p
 		 ? G_("static member function %qD cannot have ref-qualifier")
-		 : G_("non-member function %qD cannot have ref-qualifier"),
+		 : G_("explicit object member function "
+		      "%qD cannot have ref-qualifier"),
 		 decl);
-	  rqual = REF_QUAL_NONE;
-	}
+
+      if (xobj_func_p && (quals || rqual))
+	inform (DECL_SOURCE_LOCATION (DECL_ARGUMENTS (decl)),
+		"explicit object parameter declared here");
+      quals = TYPE_UNQUALIFIED;
+      rqual = REF_QUAL_NONE;
+
     }
 
   if (deduction_guide_p (decl))
@@ -13117,19 +13122,113 @@  grokdeclarator (const cp_declarator *declarator,
 		/* There is no need to iterate over the list,
 		   only the first parm can be a valid xobj parm.  */
 		if (!parm_list || parm_list == void_list_node)
-		  return false;
+		  return NULL_TREE;
 		if (TREE_PURPOSE (parm_list) != this_identifier)
-		  return false;
+		  return NULL_TREE;
 		/* If we make it here, we are looking at an xobj parm.
 
 		   Non-null 'purpose' usually means the parm has a default
 		   argument, we don't want to violate this assumption.  */
 		TREE_PURPOSE (parm_list) = NULL_TREE;
-		return true;
+		return TREE_VALUE (parm_list);
 	      };
 
-	    is_xobj_member_function
+	    tree xobj_parm
 	      = find_xobj_parm (declarator->u.function.parameters);
+	    is_xobj_member_function = xobj_parm;
+
+	    if (xobj_parm && cxx_dialect < cxx23)
+	      pedwarn(DECL_SOURCE_LOCATION (xobj_parm), OPT_Wc__23_extensions,
+		      "explicit object member function only available "
+		      "with %<-std=c++23%> or %<-std=gnu++23%>");
+
+	    /* Error handling for explicit object member functions.  */
+	    if (!xobj_parm)
+	      /* Early escape.  */;
+	    else if (decl_context == TYPENAME)
+	      {
+		bool ptr_type = true;
+		/* If   */
+		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");
+		else
+		  gcc_unreachable ();
+
+		if (ptr_type)
+		  inform (DECL_SOURCE_LOCATION (xobj_parm),
+			  "the type of a pointer to explicit object member "
+			  "function is a regular pointer to function type");
+		else
+		  inform (DECL_SOURCE_LOCATION (xobj_parm),
+			  "the type of an explicit object "
+			  "member function is a regular function type");
+		/* Ideally we should synthesize the correct syntax
+		   for the user, perhaps this could be added later.  */
+	      }
+	    /* Free function case,
+	       surely there is a better way to identify it?  */
+	    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;
+		  }
+		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");
+		  }
+	      }
+	    /* We don't need to skip over the first node even if it is an xobj
+	       parm, find_xobj_parm clears the flag so it will be treated as a
+	       normal parm, thus immediately going to the next node without
+	       emitting an errant diagnosis.  */
+	    bool bad_xobj_parm_encountered = false;
+	    for (tree parm = declarator->u.function.parameters;
+		 parm && parm != void_list_node;
+		 parm = TREE_CHAIN (parm))
+	      {
+		if (TREE_PURPOSE (parm) != this_identifier)
+		  continue;
+		bad_xobj_parm_encountered = true;
+		gcc_rich_location bad_xobj_parm
+		  (DECL_SOURCE_LOCATION (TREE_VALUE (parm)));
+		/* I'm keeping it more basic for now.  */
+		error_at (&bad_xobj_parm,
+			  "Only the first parameter of a member function "
+			  "can be declared as an explicit object parameter");
+	      }
+	    if (bad_xobj_parm_encountered && xobj_parm)
+	      inform (DECL_SOURCE_LOCATION (xobj_parm),
+		      "Valid explicit object parameter declared here");
+	    /* End of explicit object member function diagnostics.  */
 
 	    if (reqs)
 	      error_at (location_of (reqs), "requires-clause on return type");
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 0ed69bca6fc..c9870d45706 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -1831,7 +1831,9 @@  dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
 
   if (!(flags & TFF_NO_FUNCTION_ARGUMENTS))
     {
-      dump_parameters (pp, parmtypes, flags);
+      dump_parameters (pp, parmtypes,
+		       DECL_XOBJ_MEMBER_FUNC_P (t) ? TFF_XOBJ_FUNC | flags
+						    : flags);
 
       if (TREE_CODE (fntype) == METHOD_TYPE)
 	{
@@ -1910,6 +1912,8 @@  dump_parameters (cxx_pretty_printer *pp, tree parmtypes, int flags)
   for (first = 1; parmtypes != void_list_node;
        parmtypes = TREE_CHAIN (parmtypes))
     {
+      if (first && flags & TFF_XOBJ_FUNC)
+	pp_string (pp, "this ");
       if (!first)
 	pp_separate_with_comma (pp);
       first = 0;
@@ -3685,6 +3689,8 @@  function_category (tree fn)
 	return _("In destructor %qD");
       else if (LAMBDA_FUNCTION_P (fn))
 	return _("In lambda function");
+      else if (DECL_XOBJ_MEMBER_FUNC_P (fn))
+	return _("In explicit object member function %qD");
       else
 	return _("In member function %qD");
     }
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 4fbcc52c222..77e1f405c61 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -16019,6 +16019,8 @@  cp_parser_decl_specifier_seq (cp_parser* parser,
   /* Assume no class or enumeration type is declared.  */
   *declares_class_or_enum = 0;
 
+  /* Keep a token that additionally will be used for diagnostics.  */
+  cp_token *first_specifier = NULL;
   /* Keep reading specifiers until there are no more to read.  */
   while (true)
     {
@@ -16091,12 +16093,42 @@  cp_parser_decl_specifier_seq (cp_parser* parser,
 	    decl_specs->locations[ds_attribute] = token->location;
 	  continue;
 	}
+      /* We know by this point that the token is not part of an attribute.  */
+      if (!first_specifier)
+	first_specifier = token;
       /* Special case for "this" specifier, indicating a parm is an xobj parm.
 	 The "this" specifier must be the first specifier in the declaration,
 	 after any attributes.  */
       if (token->keyword == RID_THIS)
 	{
 	  cp_lexer_consume_token (parser->lexer);
+	  if (token != first_specifier)
+	    {
+	      /* Don't emit diagnostics if we have already seen "this",
+		 leave it for set_and_check_decl_spec_loc.  */
+	      if (decl_specs->locations[ds_this] == 0)
+		{
+		  gcc_rich_location richloc (token->location);
+		  /* Ideally we synthesize a full rewrite, at the moment
+		     there are issues with it though.
+		     It rewrites "f(S this & s)" correctly,
+		     but fails to rewrite "f(const this S s)" correctly.
+		     It also does not handle "f(S& this s)" correctly at all.
+
+		     It's also possible we want to wait and see if the parm
+		     could even be a valid xobj parm as it might be confusing
+		     to the user to see an error, fix it, and then see another
+		     error for something new.
+
+		     In short, this area needs improvement.  */
+		  richloc.add_fixit_insert_before
+		    (first_specifier->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;
 	}
@@ -25507,6 +25539,19 @@  cp_parser_parameter_declaration (cp_parser *parser,
 
   if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_this))
     {
+      if (default_argument)
+	{
+	  /* If default_argument is non-null token should always be the
+	     the location of the `=' token, this is brittle code though
+	     and should be rectified in the future.  */
+	  location_t param_with_init_loc
+	    = make_location (token->location,
+			     decl_spec_token_start->location,
+			     input_location);
+	  error_at (param_with_init_loc,
+		    "an explicit object parameter "
+		    "may not have a default argument");
+	}
       /* Xobj parameters can not have default arguments, thus
 	 we can reuse the default argument field to flag the param as such.  */
       default_argument = this_identifier;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 52044be7af8..0588ed25d2c 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -3085,7 +3085,33 @@  finish_this_expr (void)
     return rvalue (result);
 
   tree fn = current_nonlambda_function ();
-  if (fn && DECL_STATIC_FUNCTION_P (fn))
+  if (fn && DECL_XOBJ_MEMBER_FUNC_P (fn))
+    {
+      /* I can imagine doing a fixit here, suggesting replacing
+	 this / *this / this-> with &name / name / "name." but it would be
+	 very difficult to get it perfect and I've been advised against
+	 making imperfect fixits.
+	 Perhaps it would be as simple as the replacements listed,
+	 even if one is move'ing/forward'ing, if the replacement is just
+	 done in the same place, it will be exactly what the user wants?
+	 Even if this is true though, there's still a problem of getting the
+	 context of the expression to find which tokens to replace.
+	 I would really like for this to be possible though.
+	 I will decide whether or not to persue this after review.  */
+      tree xobj_parm = FUNCTION_DECL_CHECK (fn)->function_decl.arguments;
+      tree parm_name = DECL_MINIMAL_CHECK (xobj_parm)->decl_minimal.name;
+	error ("%<this%> is unavailable for explicit object member "
+	       "functions");
+      if (parm_name)
+	inform (DECL_SOURCE_LOCATION (xobj_parm),
+		"use explicit object parameter %qD instead",
+		parm_name);
+      else
+	inform (DECL_SOURCE_LOCATION (xobj_parm),
+		"name and use the explicit object parameter instead");
+	/* Maybe suggest self as a name here?  */
+    }
+  else if (fn && DECL_STATIC_FUNCTION_P (fn))
     error ("%<this%> is unavailable for static member functions");
   else if (fn && processing_contract_condition && DECL_CONSTRUCTOR_P (fn))
     error ("invalid use of %<this%> before it is valid");
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
new file mode 100644
index 00000000000..033745d5784
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-A.C
@@ -0,0 +1,6 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
new file mode 100644
index 00000000000..a427506b61e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-B.C
@@ -0,0 +1,7 @@ 
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-pedantic-errors" }
+
+struct S {
+    void f(this S); // { dg-error {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
new file mode 100644
index 00000000000..ed6259b70cd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-C.C
@@ -0,0 +1,7 @@ 
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-error=pedantic" }
+
+struct S {
+    void f(this S); // { dg-warning {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
new file mode 100644
index 00000000000..dccb0cf07df
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-D.C
@@ -0,0 +1,7 @@ 
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions" }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
new file mode 100644
index 00000000000..1924212fb23
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-cxx-dialect-E.C
@@ -0,0 +1,7 @@ 
+// P0847R7
+// { dg-do compile { target c++20_down } }
+// { dg-options "-Wno-c++23-extensions -pedantic-errors" }
+
+struct S {
+    void f(this S); // { dg-bogus {explicit object member function only available with '-std=c\+\+23' or '-std=gnu\+\+23'} }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
new file mode 100644
index 00000000000..7b94f7e9c12
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics1.C
@@ -0,0 +1,138 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of xobj member functions that have member function qualifiers.
+
+struct S {
+    void f_value_0(this S) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_1(this S) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_2(this S) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_value_3(this S) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_value_4(this S) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_value_5(this S) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_6(this S) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_7(this S) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_8(this S) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_9(this S) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_value_A(this S) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_ref_0(this S&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_1(this S&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_2(this S&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_ref_3(this S&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_ref_4(this S&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_ref_5(this S&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_6(this S&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_7(this S&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_8(this S&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_9(this S&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_ref_A(this S&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_refref_0(this S&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_1(this S&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_2(this S&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_refref_3(this S&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_refref_4(this S&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_refref_5(this S&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_6(this S&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_7(this S&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_8(this S&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_9(this S&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_refref_A(this S&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cref_0(this S const&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_1(this S const&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_2(this S const&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cref_3(this S const&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cref_4(this S const&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cref_5(this S const&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_6(this S const&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_7(this S const&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_8(this S const&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_9(this S const&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cref_A(this S const&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_crefref_0(this S const&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_1(this S const&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_2(this S const&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_crefref_3(this S const&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_crefref_4(this S const&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_crefref_5(this S const&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_6(this S const&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_7(this S const&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_8(this S const&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_9(this S const&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_crefref_A(this S const&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_vref_0(this S volatile&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_1(this S volatile&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_2(this S volatile&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vref_3(this S volatile&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vref_4(this S volatile&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vref_5(this S volatile&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_6(this S volatile&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_7(this S volatile&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_8(this S volatile&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_9(this S volatile&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vref_A(this S volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_vrefref_0(this S volatile&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_1(this S volatile&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_2(this S volatile&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_vrefref_3(this S volatile&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vrefref_4(this S volatile&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_vrefref_5(this S volatile&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_6(this S volatile&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_7(this S volatile&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_8(this S volatile&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_9(this S volatile&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_vrefref_A(this S volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cvref_0(this S const volatile&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_1(this S const volatile&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_2(this S const volatile&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvref_3(this S const volatile&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvref_4(this S const volatile&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvref_5(this S const volatile&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_6(this S const volatile&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_7(this S const volatile&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_8(this S const volatile&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_9(this S const volatile&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvref_A(this S const volatile&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void f_cvrefref_0(this S const volatile&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_1(this S const volatile&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_2(this S const volatile&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void f_cvrefref_3(this S const volatile&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvrefref_4(this S const volatile&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void f_cvrefref_5(this S const volatile&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_6(this S const volatile&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_7(this S const volatile&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_8(this S const volatile&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_9(this S const volatile&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void f_cvrefref_A(this S const volatile&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    template<typename Self> void d_templ_0(this Self&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    template<typename Self> void d_templ_1(this Self&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    template<typename Self> void d_templ_2(this Self&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    template<typename Self> void d_templ_3(this Self&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    template<typename Self> void d_templ_4(this Self&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    template<typename Self> void d_templ_5(this Self&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_6(this Self&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_7(this Self&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_8(this Self&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_9(this Self&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    template<typename Self> void d_templ_A(this Self&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+
+    void d_auto_0(this auto&&) const;             // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void d_auto_1(this auto&&) volatile;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void d_auto_2(this auto&&) const volatile;    // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have cv-qualifier" }
+    void d_auto_3(this auto&&) &;                 // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void d_auto_4(this auto&&) &&;                // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have ref-qualifier" }
+    void d_auto_5(this auto&&) const &;           // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_6(this auto&&) const &&;          // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_7(this auto&&) volatile &;        // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_8(this auto&&) volatile &&;       // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_9(this auto&&) const volatile &;  // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+    void d_auto_A(this auto&&) const volatile &&; // { dg-error "explicit object member function '(?!static)\[^\n\r\]+' cannot have (ref|cv)-qualifier" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
new file mode 100644
index 00000000000..f09f8dea5a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics2.C
@@ -0,0 +1,25 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of incorrect uses of 'this' in declarations and definitions
+
+using func_type = void(this int); // { dg-line func_type_line }
+// { dg-error "a function type cannot have an explicit object parameter" "" { target *-*-* } func_type_line }
+// { dg-note "the type of an explicit object member function is a regular function type" "" { target *-*-* } func_type_line }
+
+using func_ptr_type = void(*)(this int); // { dg-line func_ptr_type_line }
+// { dg-error "a function pointer type cannot have an explicit object parameter" "" { target *-*-* } func_ptr_type_line }
+// { dg-note "the type of a pointer to explicit object member function is a regular pointer to function type" "" { target *-*-* } func_ptr_type_line }
+
+struct S {
+    static void f(this S) {} // { dg-line static_member_func_line }
+};
+// { dg-error "an explicit object member function cannot be 'static'" "" { target *-*-* } static_member_func_line }
+// { dg-note "explicit object parameter declared here" "" { target *-*-* } static_member_func_line }
+
+using mem_func_type = void (S::*)(this S&); // { dg-line mem_func_type_line }
+// { dg-error "a 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" }
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C
new file mode 100644
index 00000000000..70c6a825f58
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics3.C
@@ -0,0 +1,84 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of equivalent redeclarations of member functions
+
+// xobj redeclared as iobj
+
+struct S0 {
+  void f0(this S0&); // { dg-note "previous declaration" }
+  void f0(); // { dg-error "cannot be overloaded with" }
+
+  void f1(this S0 const&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+  void f1() const; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+
+  void f2(this S0 volatile&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+  void f2() volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+
+  void f3(this S0 const volatile&); // { dg-note "previous declaration" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+  void f3() const volatile; // { dg-error "cannot be overloaded with" "detecting redeclarations of xobj member functions as iobj member functions without a ref qualifier is known to be broken" { xfail *-*-* } }
+
+  void f4(this S0&); // { dg-note "previous declaration" }
+  void f4() &; // { dg-error "cannot be overloaded with" }
+
+  void f5(this S0&&); // { dg-note "previous declaration" }
+  void f5() &&; // { dg-error "cannot be overloaded with" }
+
+  void f6(this S0 const&); // { dg-note "previous declaration" }
+  void f6() const&; // { dg-error "cannot be overloaded with" }
+
+  void f7(this S0 const&&); // { dg-note "previous declaration" }
+  void f7() const&&; // { dg-error "cannot be overloaded with" }
+
+  void f8(this S0 volatile&); // { dg-note "previous declaration" }
+  void f8() volatile&; // { dg-error "cannot be overloaded with" }
+
+  void f9(this S0 volatile&&); // { dg-note "previous declaration" }
+  void f9() volatile&&; // { dg-error "cannot be overloaded with" }
+
+  void fA(this S0 const volatile&); // { dg-note "previous declaration" }
+  void fA() const volatile&; // { dg-error "cannot be overloaded with" }
+
+  void fB(this S0 const volatile&&); // { dg-note "previous declaration" }
+  void fB() const volatile&&; // { dg-error "cannot be overloaded with" }
+};
+
+// iobj redeclared as xobj
+
+struct S1 {
+  void f0(); // { dg-note "previous declaration" }
+  void f0(this S1&); // { dg-error "cannot be overloaded with" }
+
+  void f1() const; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+  void f1(this S1 const&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+
+  void f2() volatile; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+  void f2(this S1 volatile&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+
+  void f3() const volatile; // { dg-note "previous declaration" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+  void f3(this S1 const volatile&); // { dg-error "cannot be overloaded with" "detecting redeclarations of iobj member functions without a ref qualifier as xobj member functions is known to be broken" { xfail *-*-* } }
+
+  void f4() &; // { dg-note "previous declaration" }
+  void f4(this S1&); // { dg-error "cannot be overloaded with" }
+
+  void f5() &&; // { dg-note "previous declaration" }
+  void f5(this S1&&); // { dg-error "cannot be overloaded with" }
+
+  void f6() const&; // { dg-note "previous declaration" }
+  void f6(this S1 const&); // { dg-error "cannot be overloaded with" }
+
+  void f7() const&&; // { dg-note "previous declaration" }
+  void f7(this S1 const&&); // { dg-error "cannot be overloaded with" }
+
+  void f8() volatile&; // { dg-note "previous declaration" }
+  void f8(this S1 volatile&); // { dg-error "cannot be overloaded with" }
+
+  void f9() volatile&&; // { dg-note "previous declaration" }
+  void f9(this S1 volatile&&); // { dg-error "cannot be overloaded with" }
+
+  void fA() const volatile&; // { dg-note "previous declaration" }
+  void fA(this S1 const volatile&); // { dg-error "cannot be overloaded with" }
+
+  void fB() const volatile&&; // { dg-note "previous declaration" }
+  void fB(this S1 const volatile&&); // { dg-error "cannot be overloaded with" }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
new file mode 100644
index 00000000000..1f743a8509f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics4.C
@@ -0,0 +1,19 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of an xobj parameter declared with a default argument
+
+struct S {
+  void f0(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+  void f1(this S = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+  void f2(this S);
+  void f10(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+  void f11(this S s = {}); // { dg-error "an explicit object parameter may not have a default argument" }
+  void f12(this S s);
+};
+
+void S::f1(this S) {}
+void S::f2(this S = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
+
+void S::f11(this S s) {}
+void S::f12(this S s = {}) {} // { dg-error "an explicit object parameter may not have a default argument" }
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
new file mode 100644
index 00000000000..65a5c63f20b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics5.C
@@ -0,0 +1,15 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// location diagnostic text when an error is emitted from an xobj member function
+// this does not test for specific ill-formed code, just the additional diagnostic message
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S {
+  void f(this S s) {
+    // The specific diagnosis issued here does not matter
+    // we just need to force an error to be emitted
+    +s; // { dg-error "" }
+  }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
new file mode 100644
index 00000000000..e56d6265ea1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics6.C
@@ -0,0 +1,22 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis of invalid uses of 'this' in body of xobj member functions
+
+// { dg-message "In explicit object member function" "" { target *-*-* } 0 }
+
+struct S0 {
+  int _n;
+  void f(this S0& s) { // { dg-note "use explicit object parameter 's' instead" } 
+    this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" }
+    // suppress unused variable warning
+    static_cast<void>(s);
+  }
+};
+
+struct S1 {
+  int _n;
+  void f(this S1&) { // { dg-note "name and use the explicit object parameter instead" }
+    this->_n = 10; // { dg-error "'this' is unavailable for explicit object member functions" }
+  }
+};
diff --git a/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C
new file mode 100644
index 00000000000..3c1d018cd68
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp23/explicit-obj-diagnostics7.C
@@ -0,0 +1,24 @@ 
+// P0847R7
+// { dg-do compile { target c++23 } }
+
+// rejection and diagnosis when taking address of an unqualified xobj member function
+
+struct S {
+  static void static_f(S&) {}
+  void iobj_member_f() {}
+  void xobj_member_f(this S&) {}
+
+  void test() {
+    using func_ptr_type = void(*)(S&);
+    // using mem_func_ptr_type = void (S::*)();
+
+    // allowed (not testing for this)
+    // func_ptr_type static_f_ptr = &static_f;
+
+    // not allowed (also not testing for this)
+    // mem_func_ptr_type iobj_mem_f_ptr = &iobj_member_f;
+
+    // not allowed (this is what we are testing for)
+    func_ptr_type xobj_mem_f_ptr = &xobj_member_f; // { dg-error "undetermined error" "disallowing address of unqualified explicit object member function is not implemented yet" { xfail *-*-* } }
+  }
+};
-- 
2.42.0