From f07019ffa56ec2dfab8ab0d9a83133005761a877 Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Fri, 15 Apr 2016 14:05:02 +1000 Subject: SI-9390 Avoid needless outer capture with local classes An existing optimization in `Constructors` elides the outer field in member and local classes, if the class doesn't use the outer reference. (Member classes also need to be final, which is a secret handshake to say we're also happy to weaken prefix matching in the pattern matcher.) That optimization leaves the constructor signature as is: the constructor still accepts the outer instance, but does not store it. For member classes, this means that we can separately compile code that calls the constructor. Local classes need not be hampered by this constraint, we could remove the outer instance from the constructor call too. Why would we want to do this? Let's look at the case before and after this commit. Before: ``` class C extends Object { def foo(): Function1 = $anonfun(); final def $anonfun$foo$1($this: C, x: Object): Object = new <$anon: Object>($this); def (): C = { C.super.(); () } }; final class anon$1 extends Object { def ($outer: C): <$anon: Object> = { anon$1.super.(); () } } ``` After: ``` class C extends Object { def foo(): Function1 = $anonfun(); final def $anonfun$foo$1(x: Object): Object = new <$anon: Object>(null); def (): C = { C.super.(); () } }; final class anon$1 extends Object { def ($outer: C): <$anon: Object> = { anon$1.super.(); () } } ``` However, the status quo means that a lambda that This in turn makes lambdas that refer to such classes serializable even when the outer class is not itself serialiable. I have not attempted to extend this to calls to secondary constructors. --- test/files/run/t9390d.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/files/run/t9390d.scala (limited to 'test/files/run/t9390d.scala') diff --git a/test/files/run/t9390d.scala b/test/files/run/t9390d.scala new file mode 100644 index 0000000000..3c5de3abf7 --- /dev/null +++ b/test/files/run/t9390d.scala @@ -0,0 +1,12 @@ +class C { // C is not serializable + def foo: () => Any = { + { () => class UseOuterInConstructor { C.this.toString }; new UseOuterInConstructor : Any} + } +} +object Test { + def main(args: Array[String]): Unit = { + val c = new C + val f = c.foo + f() // Doesn't NPE, as we didn't elide the outer instance in the constructor call. + } +} -- cgit v1.2.3