The handling for "this" and "super" are separate enough that we could support 
different behaviors.  I think I am looking to make 2 changes to start with:

1) super.@x cannot access a private field and does not try getX(), isX() or any 
other alternatives.  STC should produce an error for this case.  Currently, if 
the field is not accessible, "super.getX()" is substituted and so errors for 
"no getX() method" can be confusing.

2) super.x does not bypass accessor methods.  So getX() if it exists, isX() if 
its boolean, field if field is accessible and no accessor exists, and finally 
propertyMissing/MissingPropertyException.


As you stated below, #1 is a breaking change.  Today an inaccessible super.@x 
produces a super.getX() substitution.  And I think #2 is a refinement; if user 
wants to bypass the accessor method, super.@x is and has been available.

I suppose the third change is that when "super.x" fails, the compiler says "No 
such property x for B" when A is really the start of the search.  Fixing the 
error messages would also be beneficial.


I'm not proposing to enable access to private fields, just improve the 
consistency of errors and remove workarounds that IMO go against the spirit of 
".@" operator.


-----Original Message-----
From: Jochen Theodorou <blackd...@gmx.org> 
Sent: Thursday, June 25, 2020 3:55 PM
To: dev@groovy.apache.org
Subject: Re: "super" object expression for attribute, property, and method call

On 25.06.20 18:05, Milles, Eric (TR Tech, Content & Ops) wrote:
> I've been looking into GROOVY-8999 since I have a better understanding 
> of where some of the unexpected error messages come from.  What 
> exactly is the expectation for use of super qualifier in terms of 
> accessible and inaccessible members?  It is strange that I can safely call 
> "new A().@x"

if B extends A, then new B().@x is not safe through.

> anywhere but not "super.@x" from within B (when there are no accessor 
> methods).  With x private, I would expect an error from the static 
> type checker.
>
> class A {
>    private x
>    def getX() { x }
> }
>
> class B extends A {
>    void test() {
>
>      println super.x // uses getter (expected); direct access to x if 
> it's made accessible -- skips getter and MOP (getProperty, 
> propertyMissing, ...) (*unexpected*)
>
>      println super.@x // uses getter (*unexpected*); direct access to 
> x if it's made accessible (expected)
>
>      println super.getX() // uses getter (expected); even if getter is 
> inaccessible (*unexpected*)
>
>      // similar story for setting x
>    }
> }

First of all the Java version of this would not allow B to access a private 
field in A via super or actually in any other normal way.

We decided to let this.x bypass the mop for a field access of the current class 
to access the field directly. It makes sense to me to handle super the same 
way, if applicable.

So the first basic question is, should super.x access a private field in the 
super class? At least let us make this more easy and assume there is no getter 
for this. And actually this already gets difficult here. If we say we want to 
allow it I foresee a problem if A is a Java class and without reflection. I 
doubt invokedynamic will really allow access to the field. And that is what 
changed compared to the past. In the past we could always fall back to 
reflection and allow the access. If we do not want to make a difference between 
A been written in Java or Groovy, then I see no way besides maybe bytecode 
transformation - which I am unsure of, since we usually do not have enough 
control over the system and self attaching agents also seem to be a thing of 
the past.

So I tend to say that we cannot access x in super (breaking change!).
Which means, super.x should access the field if accessible and use the MOP 
otherwise. super.@x then behaves the same, only that the MOP is slightly 
different. This also means that super.getX() should not work in your example.

now there is a big BUT....

> class X {
>   private f
>   boolean equals(Object o) {
>     X other = o
>     return this.@f == other.@f
>   }
> }

this should still work. As should

> class X {
>   private f
>   boolean equals(Object o) {
>     X other = o
>     // always access X.f
>     return this.@f == other.@f
>   }
> }
> class Y extends X {
>   private f
>   boolean equals(Object o) {
>     Y other = o
>     return super.equals(o)  &&
>       // always access Y.f
>       this.@f == other.@f
>   }
> }

maybe you will say, that this has nothing to do with super and you should be 
right, but considering how some things are implemented I doubt it is really 
independent.

bye Jochen

Reply via email to