On Thu, 4 Sep 2025, Richard Biener wrote:

> On Thu, Sep 4, 2025 at 10:27 AM Jonathan Wakely <jwak...@redhat.com> wrote:
> >
> > On Thu, 4 Sept 2025 at 08:26, Richard Biener <rguent...@suse.de> wrote:
> > >
> > > On Wed, 3 Sep 2025, Jakub Jelinek wrote:
> > >
> > > > On Wed, Sep 03, 2025 at 06:41:08PM +0200, Richard Biener wrote:
> > > > > > I'd be worried about C++23 deducing this, but e.g.
> > > > > > struct S;
> > > > > > extern S t;
> > > > > > struct S
> > > > > > {
> > > > > >  void foo (this S *p, int x);
> > > > > >  int s;
> > > > > > };
> > > > > > void S::foo (this S *p, int x)
> > > > > > {
> > > > > >  p->s += x;
> > > > > >  p = &t;
> > > > > >  p->s += x;
> > > > > > }
> > > > >
> > > > > Huh, you can assign to the ‚this‘ parameter?  I’ve read it can be a 
> > > > > non-pointer.  But assigning to it makes the non-SSA match broken.  
> > > > > What’s the use of such assignment?
> > > > > Is p = nullptr valid?  Or delete p;?
> > > >
> > > > Clearly both g++ and clang++ compile this (but I'm then getting errors
> > > > when trying to call it as t.foo (42); or ptr->foo (42)).
> > > > Guess normally for deducing this the argument is this S &p instead and
> > > > references can't be changed.  But normal this in methods is actually
> > > > a pointer and keyword such that one can't change it either.
> > > > Or in some tests it is this S and so passed by value.  That then can
> > > > be called as t.foo (42).
> > >
> > > I've skimmed through the paper for the feature and found no wording
> > > constraining assignment but also no example doing it ...
> > >
> > > We also accept
> > >
> > > struct B {};
> > > struct S
> > > {
> > >   void foo (this B *p, int x);
> > >   int s;
> > > };
> > > extern S t;
> > > void S::foo (this B *p, int x)
> > > {
> > > }
> > >
> > > though I'm not sure you'd ever be able to call this?
> >
> > You can if S has an implicit conversion to B*
> >
> > struct B {};
> > struct S
> > {
> >   void foo (this B *p, int x);
> >   int s;
> >   operator B*() { return &b; }
> >   B b;
> > };
> > extern S t;
> > void S::foo (this B *p, int x)
> > {
> > }
> >
> > int main()
> > {
> >   S s;
> >   s.foo(1);
> > }
> >
> >
> >
> > > int main() { B b; b.foo (1); }
> > >
> > > is rejected obviously, so is b.S::foo (1).  I think this would
> > > warrant a diagnostic at least.  Maybe there's a trick to make
> > > name lookup succeed though?
> > >
> > > That said, the invocation syntax suggests that the 'this' object
> > > must be valid, the question is whether it's constrained by the
> > > type of the formal argument only or by the class type
> > > (TYPE_METHOD_BASETYPE if it were a method).
> >
> > Yes, it is. You can't call Foo::mem on an object that isn't a Foo or a
> > type derived from Foo.
> >
> > > That said, assigning
> > > to the this parameter adds a dataflow problem, easy for the SSA
> > > side but impossible to resolve while in GENERIC - this makes
> > > it necessary to perform EH pruning during SSA rewrite.
> > >
> > > That said, it's odd that the proposed "just alternative syntax"
> > > produces a non-method type and also allows assigning to 'this'.
> >
> > It's not 'this' though! It's not a method, it's a static member
> > function, so there is no implicit 'this' pointer. The parameter is
> > declared  as 'this S' or 'this B' but that's just a token that says
> > the function uses the new syntax, it doesn't mean that the parameter
> > *is* the 'this' pointer.
> 
> Hmm, but the paper explicitly says it's _not_ a static member function
> which to me suggests that the 'this' marked parameter must bind to
> a valid object (of the declared type then, I guess).  But all these details
> seem to be absent from the paper ...
> 
> So I hope
> 
> struct S
> {
>   void foo (this S *);
>   int i;
> };
> 
> ((S *)0)->foo ();
> char c;
> ((S *)&c)->foo ();
> 
> are both invalid, aka invoking UB.

To somewhat answer myself the fact that &S::foo is a regular
function pointer that can of course the be called like

(&S::foo)((S *)0);

this _might_ suggest the parameter marked as 'this' doesn't necessarily
bind to an object of the declared type.

While the paper suggests that the simple non-template

struct S
{
  void foo (this S *);
};

is merely an alternate syntax to

struct
{
  void foo ();
};

it more and more looks like it isn't.

But who knows ...

Richard.

Reply via email to