Skip to content

Regression call outer class super method from inner class #9225

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
FabioPinheiro opened this issue Jun 23, 2020 · 5 comments · Fixed by #14867
Closed

Regression call outer class super method from inner class #9225

FabioPinheiro opened this issue Jun 23, 2020 · 5 comments · Fixed by #14867

Comments

@FabioPinheiro
Copy link
Contributor

FabioPinheiro commented Jun 23, 2020

Minimized code

trait A {
  val s = "same val in A"
  def f: String = s
}

class B extends A {
  class C {
    def call_f_in_A: String = B.super[A].f
  }
}
val b = new B                                                                                                                                                                                                                          
val c = new b.C
c.call_f_in_A // AbstractMethodError

Output

java.lang.AbstractMethodError:
Receiver class rs$line$3$B$C does not define or inherit an implementation of the 
resolved method 'abstract java.lang.String rs$line$3$A$$s()' of interface rs$line$3$A.
// defined trait A
// defined class B
java.lang.AbstractMethodError: Receiver class rs$line$3$B$C does not define or inherit an implementation of the resolved method 'abstract java.lang.String rs$line$3$A$$s()' of interface rs$line$3$A.
	at rs$line$3$A.f(rs$line$3:3)
	at rs$line$3$B$C.call_f_in_A(rs$line$3:8)
	at rs$line$3$.<init>(rs$line$3:13)
	at rs$line$3$.<clinit>(rs$line$3)
	at rs$line$3.b(rs$line$3)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at dotty.tools.repl.Rendering.$anonfun$3(Rendering.scala:84)
	at scala.Option.map(Option.scala:242)
	at dotty.tools.repl.Rendering.valueOf(Rendering.scala:84)
	at dotty.tools.repl.Rendering.renderVal(Rendering.scala:121)
	at dotty.tools.repl.ReplDriver.$anonfun$13(ReplDriver.scala:306)
	at scala.collection.immutable.List.flatMap(List.scala:293)
	at scala.collection.immutable.List.flatMap(List.scala:79)
	at dotty.tools.repl.ReplDriver.extractAndFormatMembers$2(ReplDriver.scala:306)
	at dotty.tools.repl.ReplDriver.renderDefinitions$$anonfun$2(ReplDriver.scala:327)
	at scala.Option.map(Option.scala:242)
	at dotty.tools.repl.ReplDriver.renderDefinitions(ReplDriver.scala:330)
	at dotty.tools.repl.ReplDriver.compile$$anonfun$2(ReplDriver.scala:250)
	at scala.util.Either.fold(Either.scala:189)
	at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:265)
	at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:195)
	at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:128)
	at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:131)
	at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:150)
	at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:131)
	at dotty.tools.repl.Main$.main(Main.scala:6)
	at dotty.tools.repl.Main.main(Main.scala)

java.lang.NoClassDefFoundError: Could not initialize class rs$line$3$
	at rs$line$3.c(rs$line$3)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at dotty.tools.repl.Rendering.$anonfun$3(Rendering.scala:84)
	at scala.Option.map(Option.scala:242)
	at dotty.tools.repl.Rendering.valueOf(Rendering.scala:84)
	at dotty.tools.repl.Rendering.renderVal(Rendering.scala:121)
	at dotty.tools.repl.ReplDriver.$anonfun$13(ReplDriver.scala:306)
	at scala.collection.immutable.List.flatMap(List.scala:293)
	at scala.collection.immutable.List.flatMap(List.scala:79)
	at dotty.tools.repl.ReplDriver.extractAndFormatMembers$2(ReplDriver.scala:306)
	at dotty.tools.repl.ReplDriver.renderDefinitions$$anonfun$2(ReplDriver.scala:327)
	at scala.Option.map(Option.scala:242)
	at dotty.tools.repl.ReplDriver.renderDefinitions(ReplDriver.scala:330)
	at dotty.tools.repl.ReplDriver.compile$$anonfun$2(ReplDriver.scala:250)
	at scala.util.Either.fold(Either.scala:189)
	at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:265)
	at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:195)
	at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:128)
	at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:131)
	at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:150)
	at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:131)
	at dotty.tools.repl.Main$.main(Main.scala:6)
	at dotty.tools.repl.Main.main(Main.scala)

java.lang.NoClassDefFoundError: Could not initialize class rs$line$3$
	at rs$line$3.res1(rs$line$3)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at dotty.tools.repl.Rendering.$anonfun$3(Rendering.scala:84)
	at scala.Option.map(Option.scala:242)
	at dotty.tools.repl.Rendering.valueOf(Rendering.scala:84)
	at dotty.tools.repl.Rendering.renderVal(Rendering.scala:121)
	at dotty.tools.repl.ReplDriver.$anonfun$13(ReplDriver.scala:306)
	at scala.collection.immutable.List.flatMap(List.scala:293)
	at scala.collection.immutable.List.flatMap(List.scala:79)
	at dotty.tools.repl.ReplDriver.extractAndFormatMembers$2(ReplDriver.scala:306)
	at dotty.tools.repl.ReplDriver.renderDefinitions$$anonfun$2(ReplDriver.scala:327)
	at scala.Option.map(Option.scala:242)
	at dotty.tools.repl.ReplDriver.renderDefinitions(ReplDriver.scala:330)
	at dotty.tools.repl.ReplDriver.compile$$anonfun$2(ReplDriver.scala:250)
	at scala.util.Either.fold(Either.scala:189)
	at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:265)
	at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:195)
	at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:128)
	at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:131)
	at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:150)
	at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:131)
	at dotty.tools.repl.Main$.main(Main.scala:6)
	at dotty.tools.repl.Main.main(Main.scala)

Expectation

It should compile.
I had the impression this was working one week ago. Maybe it was long ago or I'm just mistaken.

@FabioPinheiro
Copy link
Contributor Author

Ok. Does not matter if B is a Class ra an Object.
C just needs only to be an enclosing class. So we can use the syntax super[<Something>].
If we remove the [A] after super the example works fine.
The syntax super[<Something>] needs to disambiguate names from multiple inheritance (or close enough when extends multiple traits).

@odersky
Copy link
Contributor

odersky commented Jun 24, 2020

The unimplemented method is def s(): String in class C (!). I printed out the code at the end of all miniphases but there is no such member defined in C. Could it be that the backend creates it somehow?

@FabioPinheiro To troubleshoot it's generallty better to have self-contained test cases instead of the REPL. Less moving parts that way. To be sure, this problem is present also in the self-contained test:

trait A {
  val s = "same val in A"
  def f: String = s
}

class B extends A {
  class C {
    def call_f_in_A: String = B.super[A].f
  }
}
object Test extends App {
  val b = new B
  val c = new b.C
  c.call_f_in_A // AbstractMethodError
}

@FabioPinheiro
Copy link
Contributor Author

OK. I will do that If I open any other ticket.

@FabioPinheiro
Copy link
Contributor Author

FabioPinheiro commented Jun 25, 2020

Could it be that the backend creates it somehow?

@odersky I'm not sure if that was or not a question for me.
I'm trying things with the scalajs enable. There I get the error A expected but B$C found for tree of type org.scalajs.ir.Trees$This

Forgot to mention on the ticket.
If instead of a val s = "same val in A" we define a method def s = "same val in A". This example works.
This behavior makes more sense now to me.
After you mention that def s(): String is an unimplemented method on C. I tried to define an s in C (with val, def or var). It works*, that value is used instead.
For example, if s is a private val in A and we define something named A$$s in C. The value of A$$s will be used.

trait A {
  private val s = "same val in A"
  def f: String = s
}
class B extends A {
  class C {
    val A$$s = "This will be called and it shouldn't"
    def call_f_in_A: String = B.super[A].f
  }
}
object Test extends App {
  val b = new B
  val c = new b.C
  println(c.call_f_in_A) // It will print "This will be called and it shouldn't"
}

From my limited knowledge of reading the output from each phase.
Here are my assumptions and I hope this makes sense.

If we define s as a method in trait A and some val s in C

trait A {
  def s = "same val in A"
  def f: String = s
}
class B extends A {
  class C {
    val s = "This will be called and it shouldn't"
    def call_f_in_A: String = B.super[A].f
  }
}

Before the byte code generation, we have

  A() extends Object {
    def s(): String = "same val in A"
    def f(): String = this.s()
  }
...
  class B$C extends Object {
    ...
    private val s: String
    def s(): String = this.s
    def call_f_in_A(): String = B.super[A].f()
  }

The body of the method f(B.super[A].f()) is this.s(). That in the context of inner class B$C s will be a local method and it behaves as an overwrite method (the method s from A is never called).

If we define s as a val in trait A and nothing in C called s.
Before the byte code generation we end up with:

  A extends Object {
    def $init$(): Unit = 
      {
        ()
      }
    def s(): String
    protected def initial$s(): String = 
      {
        "same val in A"
      }
    def f(): String = this.s()
  }

Here the method s is unimplemented.

@odersky
Copy link
Contributor

odersky commented Jun 25, 2020

Yes, but that still begs the question why an (abstract) s is defined in C if there is no explicit val s. The question whether it happens in the backend was a general one, for people who know the backend or have touched it recently.

@smarter smarter removed their assignment Jul 3, 2020
odersky added a commit to dotty-staging/dotty that referenced this issue Apr 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants