summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2016-09-13 13:43:43 +1000
committerJason Zaugg <jzaugg@gmail.com>2016-09-27 10:21:38 +1000
commite3e1e30c08d8bb532ac1d36d191fc8d4dbab0eb9 (patch)
tree5d175eb2155a16c96bc77c39ac74340bca16042e
parentde7cddd1905c730685f5ef2b74a6c0c1f30f2dd2 (diff)
downloadscala-e3e1e30c08d8bb532ac1d36d191fc8d4dbab0eb9.tar.gz
scala-e3e1e30c08d8bb532ac1d36d191fc8d4dbab0eb9.tar.bz2
scala-e3e1e30c08d8bb532ac1d36d191fc8d4dbab0eb9.zip
SI-9920 Avoid linkage errors with captured local objects + self types
An outer parameter of a nested class is typed with the self type of the enclosing class: ``` class C; trait T { _: C => def x = 42; class D { x } } ``` leads to: ``` class D extends Object { def <init>($outer: C): T.this.D = { D.super.<init>(); () }; D.this.$outer().$asInstanceOf[T]().x(); ``` Note that a cast is inserted before the call to `x`. If we modify that a little, to instead capture a local module: ``` class C; trait T { _: C => def y { object O; class D { O } } } ``` Scala 2.11 used to generate (after lambdalift): ``` class D$1 extends Object { def <init>($outer: C, O$module$1: runtime.VolatileObjectRef): C#D$1 = { D$1.super.<init>(); () }; D$1.this.$outer().O$1(O$module$1); ``` That isn't type correct, `D$1.this.$outer() : C` does not have a member `O$1`. However, the old trait encoding would rewrite this in mixin to: ``` T$class.O$1($outer, O$module$1); ``` Trait implementation methods also used to accept the self type: ``` trait T$class { final <stable> def O$1($this: C, O$module$1: runtime.VolatileObjectRef): T$O$2.type } ``` So the problem was hidden. This commit changes replaces manual typecheckin of the selection in LambdaLift with a use of the local (erasure) typer, which will add casts as needed. For `run/t9220.scala`, this changes the post LambdaLift AST as follows: ``` class C1$1 extends Object { def <init>($outer: C0, Local$module$1: runtime.VolatileObjectRef): T#C1$1 = { C1$1.super.<init>(); () }; - C1$1.this.$outer.Local$1(Local$module$1); + C1$1.this.$outer.$asInstanceOf[T]().Local$1(Local$module$1); <synthetic> <paramaccessor> <artifact> private[this] val $outer: C0 = _; <synthetic> <stable> <artifact> def $outer(): C0 = C1$1.this.$outer } ```
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala9
-rw-r--r--test/files/pos/t9920.scala6
-rw-r--r--test/files/run/t9920.scala17
-rw-r--r--test/files/run/t9920b.scala17
-rw-r--r--test/files/run/t9920c.scala21
-rw-r--r--test/files/run/t9920d.scala14
7 files changed, 84 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index f3d5ceb0f0..7d50c12852 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -8,7 +8,7 @@ package tools.nsc
package transform
import symtab._
-import Flags.{ CASE => _, _ }
+import Flags.{CASE => _, _}
import scala.collection.mutable.ListBuffer
/** This class ...
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index 74e6c58388..10d9c5627b 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -319,7 +319,14 @@ abstract class LambdaLift extends InfoTransform {
else if (clazz.isStaticOwner) gen.mkAttributedQualifier(clazz.thisType)
else outerValue match {
case EmptyTree => prematureSelfReference()
- case o => outerPath(o, currentClass.outerClass, clazz)
+ case o =>
+ val path = outerPath(o, currentClass.outerClass, clazz)
+ if (path.tpe <:< clazz.tpeHK) path
+ else {
+ // SI-9920 The outer accessor might have an erased type of the self type of a trait,
+ // rather than the trait itself. Add a cast if necessary.
+ gen.mkAttributedCast(path, clazz.tpeHK)
+ }
}
}
diff --git a/test/files/pos/t9920.scala b/test/files/pos/t9920.scala
new file mode 100644
index 0000000000..8612618cc4
--- /dev/null
+++ b/test/files/pos/t9920.scala
@@ -0,0 +1,6 @@
+object Test {
+ def o = {
+ def i: Int = { i; 0 }
+ i
+ }
+}
diff --git a/test/files/run/t9920.scala b/test/files/run/t9920.scala
new file mode 100644
index 0000000000..5dc32e99b7
--- /dev/null
+++ b/test/files/run/t9920.scala
@@ -0,0 +1,17 @@
+class C0
+trait T { self: C0 =>
+ def test = {
+ object Local
+
+ class C1 {
+ Local
+ }
+ new C1()
+ }
+}
+
+object Test extends C0 with T {
+ def main(args: Array[String]): Unit = {
+ test
+ }
+}
diff --git a/test/files/run/t9920b.scala b/test/files/run/t9920b.scala
new file mode 100644
index 0000000000..fab196b669
--- /dev/null
+++ b/test/files/run/t9920b.scala
@@ -0,0 +1,17 @@
+class C0
+trait T {
+ def test = {
+ object Local
+
+ class C1 {
+ Local
+ }
+ new C1()
+ }
+}
+
+object Test extends C0 with T {
+ def main(args: Array[String]): Unit = {
+ test
+ }
+}
diff --git a/test/files/run/t9920c.scala b/test/files/run/t9920c.scala
new file mode 100644
index 0000000000..9541dc650a
--- /dev/null
+++ b/test/files/run/t9920c.scala
@@ -0,0 +1,21 @@
+class C0
+trait T { self: C0 =>
+ def test = {
+ object Local
+
+ class C2 {
+ class C1 {
+ Local
+ }
+ T.this.toString
+ new C1
+ }
+ new C2()
+ }
+}
+
+object Test extends C0 with T {
+ def main(args: Array[String]): Unit = {
+ test
+ }
+}
diff --git a/test/files/run/t9920d.scala b/test/files/run/t9920d.scala
new file mode 100644
index 0000000000..debc99e199
--- /dev/null
+++ b/test/files/run/t9920d.scala
@@ -0,0 +1,14 @@
+class C { object O }
+trait T { _: C =>
+ def foo {
+ class D { O }
+ new D
+ }
+}
+
+
+object Test extends C with T {
+ def main(args: Array[String]): Unit = {
+ foo
+ }
+}