From 2d88143f37144f3db5a1d1d27806518bea13ba47 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Wed, 11 Mar 2015 10:39:40 -0700 Subject: Ensure to re-write only trait method calls of actual trait methods The inliner would incorrectly treat trait field accessors like ordinary trait member methods and try to re-write invocations to the corresponding static method in the implementation class. This rewrite usually failed because no method was found in the impl class. However, for lazy val fields, there exists a member in the impl class with the same name, and the rewrite was performed. The result was that every field access would execute the lazy initializer instead of reading the field. This commit checks the traitMethodWithStaticImplementation field of the ScalaInlineInfo classfile attribute and puts an explicit `safeToRewrite` flag to each call site in the call graph. This cleans up the code in the inliner that deals with rewriting trait callsites. --- .../tools/nsc/backend/jvm/opt/InlinerTest.scala | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) (limited to 'test/junit') diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala index d32c1b2958..caaa65bf7e 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -791,4 +791,74 @@ class InlinerTest extends ClearAfterClass { compile(code, allowMessage = info => {i += 1; info.msg contains err}) assert(i == 2, i) } + + @Test + def noInlineTraitFieldAccessors(): Unit = { + val code = + """sealed trait T { + | lazy val a = 0 + | val b = 1 + | final lazy val c = 2 + | final val d = 3 + | final val d1: Int = 3 + | + | @noinline def f = 5 // re-written to T$class + | @noinline final def g = 6 // re-written + | + | @noinline def h: Int + | @inline def i: Int + |} + | + |trait U { // not sealed + | lazy val a = 0 + | val b = 1 + | final lazy val c = 2 + | final val d = 3 + | final val d1: Int = 3 + | + | @noinline def f = 5 // not re-written (not final) + | @noinline final def g = 6 // re-written + | + | @noinline def h: Int + | @inline def i: Int + |} + | + |class C { + | def m1(t: T) = t.a + t.b + t.c + t.d1 + | def m2(t: T) = t.d // inlined by the type-checker's constant folding + | def m3(t: T) = t.f + t.g + t.h + t.i + | + | def m4(u: U) = u.a + u.b + u.c + u.d1 + | def m5(u: U) = u.d + | def m6(u: U) = u.f + u.g + u.h + u.i + |} + """.stripMargin + + val List(c, t, tClass, u, uClass) = compile(code, allowMessage = _.msg contains "i()I is annotated @inline but cannot be inlined") + val m1 = getSingleMethod(c, "m1") + assertInvoke(m1, "T", "a") + assertInvoke(m1, "T", "b") + assertInvoke(m1, "T", "c") + + assertNoInvoke(getSingleMethod(c, "m2")) + + val m3 = getSingleMethod(c, "m3") + assertInvoke(m3, "T$class", "f") + assertInvoke(m3, "T$class", "g") + assertInvoke(m3, "T", "h") + assertInvoke(m3, "T", "i") + + val m4 = getSingleMethod(c, "m4") + assertInvoke(m4, "U", "a") + assertInvoke(m4, "U", "b") + assertInvoke(m4, "U", "c") + + assertNoInvoke(getSingleMethod(c, "m5")) + + val m6 = getSingleMethod(c, "m6") + assertInvoke(m6, "U", "f") + assertInvoke(m6, "U$class", "g") + assertInvoke(m6, "U", "h") + assertInvoke(m6, "U", "i") + } } -- cgit v1.2.3