From c8e6050c3c190dd064642b6b77fc179f27b0495d Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 16 Mar 2016 16:14:27 +1000 Subject: New trait encoding: use default methods, jettison impl classes Until now, concrete methods in traits were encoded with "trait implementation classes". - Such a trait would compile to two class files - the trait interface, a Java interface, and - the implementation class, containing "trait implementation methods" - trait implementation methods are static methods has an explicit self parameter. - some methods don't require addition of an interface method, such as private methods. Calls to these directly call the implementation method - classes that mixin a trait install "trait forwarders", which implement the abstract method in the interface by forwarding to the trait implementation method. The new encoding: - no longer emits trait implementation classes or trait implementation methods. - instead, concrete methods are simply retained in the interface, as JVM 8 default interface methods (the JVM spec changes in [JSR-335](http://download.oracle.com/otndocs/jcp/lambda-0_9_3-fr-eval-spec/index.html) pave the way) - use `invokespecial` to call private or particular super implementations of a method (rather `invokestatic`) - in cases when we `invokespecial` to a method in an indirect ancestor, we add that ancestor redundantly as a direct parent. We are investigating alternatives approaches here. - we still emit trait fowrarders, although we are [investigating](https://github.com/scala/scala-dev/issues/98) ways to only do this when the JVM would be unable to resolve the correct method using its rules for default method resolution. Here's an example: ``` trait T { println("T") def m1 = m2 private def m2 = "m2" } trait U extends T { println("T") override def m1 = super[T].m1 } class C extends U { println("C") def test = m1 } ``` The old and new encodings are displayed and diffed here: https://gist.github.com/retronym/f174d23f859f0e053580 Some notes in the implementation: - No need to filter members from class decls at all in AddInterfaces (although we do have to trigger side effecting info transformers) - We can now emit an EnclosingMethod attribute for classes nested in private trait methods - Created a factory method for an AST shape that is used in a number of places to symbolically bind to a particular super method without needed to specify the qualifier of the `Super` tree (which is too limiting, as it only allows you to refer to direct parents.) - I also found a similar tree shape created in Delambdafy, that is better expressed with an existing tree creation factory method, mkSuperInit. --- test/files/jvm/innerClassAttribute/Classes_1.scala | 2 +- test/files/jvm/innerClassAttribute/Test.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'test/files/jvm/innerClassAttribute') diff --git a/test/files/jvm/innerClassAttribute/Classes_1.scala b/test/files/jvm/innerClassAttribute/Classes_1.scala index 5b821d43f8..0abed140f1 100644 --- a/test/files/jvm/innerClassAttribute/Classes_1.scala +++ b/test/files/jvm/innerClassAttribute/Classes_1.scala @@ -222,7 +222,7 @@ trait SI_9124 { def f = new A { def f2 = 0 } // enclosing method is f in the interface SI_9124 - private def g = new A { def f3 = 0 } // only encl class (SI_9124), encl meth is null because the interface SI_9124 doesn't have a method g + private def g: Object = new A { def f3 = 0 } // only encl class (SI_9124), encl meth can be g in 2.12 because the interface SI_9124 now has the method g object O { // member, no encl meth attribute new A { def f4 = 0 } // enclosing class is O$, no enclosing method diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala index 903d08f72d..490128d5d2 100644 --- a/test/files/jvm/innerClassAttribute/Test.scala +++ b/test/files/jvm/innerClassAttribute/Test.scala @@ -340,7 +340,7 @@ object Test extends BytecodeTest { assertNoEnclosingMethod("SI_9124$A") assertEnclosingMethod(classes("f1"), "SI_9124", null, null) assertEnclosingMethod(classes("f2"), "SI_9124", "f", "()LSI_9124$A;") - assertEnclosingMethod(classes("f3"), "SI_9124", null, null) + assertEnclosingMethod(classes("f3"), "SI_9124", "g", "()Ljava/lang/Object;") assertEnclosingMethod(classes("f4"), "SI_9124$O$", null, null) assertEnclosingMethod(classes("f5"), "SI_9124", null, null) assertEnclosingMethod(classes("f6"), "SI_9124", null, null) -- cgit v1.2.3