Dotty: Dotty does not generate accessors for protected calls from inner classes, resulting in IllegalAccessError

Created on 18 Jun 2017  路  4Comments  路  Source: lampepfl/dotty

I hazard a guess this is an issue with handling multiple classloaders. This small project demonstrates the issue.

The problem seems to occur when an anonymous or inner class is created that accesses a protected or private member in the outer class. However, I've only been able to reproduce the error when my outer class extends a class implemented in some (java) library - this is the HelloInput.scala example in the repo (and the protected variable in question is speed). The example in Main.scala uses the same approach with a minimal example implemented completely in the project, and seems to work fine.

Stack trace in dotty:

SEVERE: Uncaught exception thrown in Thread[jME3 Main,5,run-main-group-0]
java.lang.IllegalAccessError: tried to access field com.jme3.app.LegacyApplication.speed from class HelloInput$$anon$1
        at HelloInput$$anon$1.onAnalog(HelloInput.scala:47)
        at com.jme3.input.InputManager.invokeAnalogs(InputManager.java:245)
        at com.jme3.input.InputManager.invokeUpdateActions(InputManager.java:215)
        at com.jme3.input.InputManager.update(InputManager.java:908)
        at com.jme3.app.LegacyApplication.update(LegacyApplication.java:725)
        at com.jme3.app.SimpleApplication.update(SimpleApplication.java:227)
        at com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:151)
        at com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:193)
        at com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:232)
        at java.lang.Thread.run(Thread.java:748)

To reproduce:

  1. sbt runHelloInput
  2. Press 'j' or 'k' to trigger the method that will calles the IAE.

To compare to scalac, just change the scalaVersion := in build.sbt.

transform bug high

All 4 comments

Thanks for providing a simple example demonstrating the problem! Here's a minmized test case showing the issue:
Base.java:

package bla;

public class Base {
  protected String foo = "";
}

i2780.scala:

class Foo extends bla.Base {
  class Inner {
    println(foo)
  }
}

object Test {
  def main(args: Array[String]): Unit = {
    val f = new Foo
    new f.Inner
  }
}

```shell
% javac try/Base.java -d .
% scalac try/i2780.scala
% scala Test

% dotc try/i2780.scala
% dotr Test
Exception in thread "main" java.lang.IllegalAccessError: tried to access field bla.Base.foo from class Foo$Inner
at Foo$Inner.(i2780.scala:3)
at Test$.main(i2780.scala:10)
at Test.main(i2780.scala)


This is easily explained by comparing the decompiled Foo.class:
`scalac 2.12.2`:
```java
public class Foo
extends Base {
    public /* synthetic */ String protected$foo(Foo x$1) {
        return x$1.foo;
    }

    public Foo() {
    }

    public class Inner {
        public final /* synthetic */ Foo $outer;

        public /* synthetic */ Foo Foo$Inner$$$outer() {
            return this.$outer;
        }

        public Inner(Foo $outer) {
            if ($outer == null) {
                throw null;
            }
            this.$outer = $outer;
            Predef..MODULE$.println((Object)$outer.protected$foo($outer));
        }
    }

}

dotc:

public class Foo
extends Base {
    public Foo() {
    }

    public static class Inner {
        private final Foo $outer;

        public Inner(Foo $outer) {
            if ($outer == null) {
                throw new NullPointerException();
            }
            this.$outer = $outer;
            Predef..MODULE$.println((Object)this.Foo$Inner$$$outer().foo);
        }

        private Foo $outer() {
            return this.$outer;
        }

        public final Foo Foo$Inner$$$outer() {
            return this.$outer();
        }
    }

}

We failed to generate an accessor for the protected field foo accessed from the inner class Inner.

There's at least one issue in SuperAccesors#needsProtectedAccessor, selfType is the host self-type but is used to do a subtype check instead of the current class self-type, compare https://github.com/lampepfl/dotty/blob/21b314992439203345154bf417cc503c007d7f1b/compiler/src/dotty/tools/dotc/transform/SuperAccessors.scala#L305 with https://github.com/scala/scala/blob/d1ec01aa1cf65c3f9b161b2f0c0025ad0c9ade97/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala#L550. However, fixing this results in an error in SuperAccessors#protectedAccessorCall (variable foo in class Base does not take parameters) which I haven't managed to fix.

Here's a WIP branch with the fix I suggested above if anyone wants to try to finish this, I don't think I'll have time to do it myself in the near future: https://github.com/dotty-staging/dotty/commits/fix-i2780

@bbarker This should be fixed in the latest nightly, you can try it out by setting scalaVersion := 0.2.0-bin-20170620-1d05796-NIGHTLY in your build (make sure to also use the latest version of the sbt-dotty plugin: 0.1.3)

Was this page helpful?
0 / 5 - 0 ratings