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