diff options
author | Lukas Rytz <lukas.rytz@gmail.com> | 2016-03-08 21:20:28 +0100 |
---|---|---|
committer | Lukas Rytz <lukas.rytz@gmail.com> | 2016-03-23 11:56:21 +0100 |
commit | e483fe26825501c175b701a662160dd0e54b035e (patch) | |
tree | ef9161418557cf69f647726af33bf3b5a8d201a6 /test/junit/scala/tools/nsc/backend/jvm/opt | |
parent | 952da60a5be15ef972b521bdaf5e650f7e0a5245 (diff) | |
download | scala-e483fe26825501c175b701a662160dd0e54b035e.tar.gz scala-e483fe26825501c175b701a662160dd0e54b035e.tar.bz2 scala-e483fe26825501c175b701a662160dd0e54b035e.zip |
Inline super calls, as they are statically resolved
Ensures that mixin methods of `@inline` annotated concrete trait methods
inline the trait method.
Fixes https://github.com/scala/scala-dev/issues/86
Diffstat (limited to 'test/junit/scala/tools/nsc/backend/jvm/opt')
5 files changed, 49 insertions, 58 deletions
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala index dfbedbaa25..1ce913006d 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/BTypesFromClassfileTest.scala @@ -67,14 +67,17 @@ class BTypesFromClassfileTest { // there's a separate InlineInfoTest. val chk1 = sameBTypes(fromSym.superClass, fromClassfile.superClass, checked) + + // was: + // val chk2 = sameBTypes(fromSym.interfaces, fromClassfile.interfaces, chk1) + // TODO: The new trait encoding emits redundant parents in the backend to avoid linkage errors in invokespecial + // Need to give this some more thought, maybe do it earlier so it is reflected in the Symbol's info, too. val fromSymInterfaces = fromSym.interfaces val fromClassFileInterfaces = fromClassfile.interfaces val (matching, other) = fromClassFileInterfaces.partition(x => fromSymInterfaces.exists(_.internalName == x.internalName)) val chk2 = sameBTypes(fromSym.interfaces, matching, chk1) for (redundant <- other) { - // TODO SD-86 The new trait encoding emits redundant parents in the backend to avoid linkage errors in invokespecial - // Need to give this some more thought, maybe do it earlier so it is reflected in the Symbol's info, too. assert(matching.exists(x => x.isSubtypeOf(redundant).orThrow), redundant) } diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala index 87794f1346..261d6beb96 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala @@ -59,9 +59,7 @@ class InlineInfoTest extends ClearAfterClass { |} |class C extends T with U """.stripMargin -// val classes = compile(code) // SD-86 - InlineInfoTest.notPerRun.foreach(_.clear()) - val classes = compileClasses(compiler)(code, allowMessage = _ => true) // SD-86 inline warnings + val classes = compile(code) val fromSyms = classes.map(c => compiler.genBCode.bTypes.classBTypeFromInternalName(c.name).info.get.inlineInfo) diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala index 3f95be8560..01d97855c8 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala @@ -67,10 +67,10 @@ class InlineWarningTest extends ClearAfterClass { "T::m2()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", "D::m2()I is annotated @inline but cannot be inlined: the method is not final and may be overridden") compile(code, allowMessage = i => {count += 1; warns.exists(i.msg contains _)}) - assert(count == 5, count) // TODO SD-85: 5th warning + assert(count == 4, count) } - // TODO SD-85: no more impl classes. this test (and the warning it tests!) can be removed + // TODO SD-86: no more impl classes. this test (and the warning it tests!) can be removed @org.junit.Ignore @Test def traitMissingImplClass(): Unit = { val codeA = "trait T { @inline final def f = 1 }" diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala index 52e19b48fd..6562f81e4c 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerSeparateCompilationTest.scala @@ -42,15 +42,10 @@ class InlinerSeparateCompilationTest { |} """.stripMargin - val warns = Set( - "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", - // TODO SD-85 - """O$::f()I is annotated @inline but could not be inlined: - |The callee O$::f()I contains the instruction INVOKESPECIAL T.f ()I - |that would cause an IllegalAccessError when inlined into class C""".stripMargin) - val List(c, o, oMod, t) = compileClassesSeparately(List(codeA, codeB), args + " -Yopt-warnings", i => warns.exists(i.msg contains _)) + val warn = "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden" + val List(c, o, oMod, t) = compileClassesSeparately(List(codeA, codeB), args + " -Yopt-warnings", _.msg contains warn) assertInvoke(getSingleMethod(c, "t1"), "T", "f") -// assertNoInvoke(getSingleMethod(c, "t2")) // SD-85 + assertNoInvoke(getSingleMethod(c, "t2")) assertNoInvoke(getSingleMethod(c, "t3")) } 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 e3e7103f46..9079ca248a 100644 --- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala +++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala @@ -324,8 +324,6 @@ class InlinerTest extends ClearAfterClass { |} """.stripMargin val List(c, t) = compile(code) - println(textify(c)) - println(textify(t)) assertNoInvoke(getSingleMethod(c, "g")) } @@ -507,8 +505,7 @@ class InlinerTest extends ClearAfterClass { "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden") var count = 0 val List(c, t) = compile(code, allowMessage = i => {count += 1; warns.exists(i.msg contains _)}) - // 3rd warnings because of mixin-method, see SD-86 - assert(count == 3, count) + assert(count == 2, count) assertInvoke(getSingleMethod(c, "t1"), "T", "f") assertInvoke(getSingleMethod(c, "t2"), "C", "f") } @@ -535,7 +532,7 @@ class InlinerTest extends ClearAfterClass { |} |object O extends T { | @inline def g = 1 - | // mixin generates `def f = T$class.f(this)`, which is inlined here (we get ICONST_0) + | // mixin generates `def f = super[T].f`, which is inlined here (we get ICONST_0) |} |class C { | def t1 = O.f // the mixin method of O is inlined, so we directly get the ICONST_0 @@ -543,19 +540,14 @@ class InlinerTest extends ClearAfterClass { | def t3(t: T) = t.f // no inlining here |} """.stripMargin - val warns = Set( - "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", - // SD-86 -- once the mixin-method O.f inlines the body of T.f, we can also inline O.g into class C. - """O$::f()I is annotated @inline but could not be inlined: - |The callee O$::f()I contains the instruction INVOKESPECIAL T.f ()I - |that would cause an IllegalAccessError when inlined into class C""".stripMargin) + val warn = "T::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden" var count = 0 - val List(c, oMirror, oModule, t) = compile(code, allowMessage = i => {count += 1; warns.exists(i.msg contains _)}) - assert(count == 3, count) // SD-86 + val List(c, oMirror, oModule, t) = compile(code, allowMessage = i => {count += 1; i.msg contains warn}) + assert(count == 1, count) -// assertNoInvoke(getSingleMethod(oModule, "f")) // SD-86 + assertNoInvoke(getSingleMethod(oModule, "f")) -// assertNoInvoke(getSingleMethod(c, "t1")) // SD-86 + assertNoInvoke(getSingleMethod(c, "t1")) assertNoInvoke(getSingleMethod(c, "t2")) assertInvoke(getSingleMethod(c, "t3"), "T", "f") } @@ -569,12 +561,10 @@ class InlinerTest extends ClearAfterClass { |} |trait Assembly extends T { | @inline final def g = 1 - | @inline final def n = m // inlined. (*) - | // (*) the declaration class of m is T. the signature of T$class.m is m(LAssembly;)I. so we need the self type to build the - | // signature. then we can look up the MethodNode of T$class.m and then rewrite the INVOKEINTERFACE to INVOKESTATIC. + | @inline final def n = m // inlined (m is final) |} |class C { - | def t1(a: Assembly) = a.f // like above, decl class is T, need self-type of T to rewrite the interface call to static. + | def t1(a: Assembly) = a.f // inlined (f is final) | def t2(a: Assembly) = a.n |} """.stripMargin @@ -618,30 +608,30 @@ class InlinerTest extends ClearAfterClass { val code = """trait T1 { | @inline def f: Int = 0 - | @inline def g1 = f // not inlined: f not final, so T1$class.g1 has an interface call T1.f + | @inline def g1 = f // not inlined: f not final |} | - |// erased self-type (used in impl class for `self` parameter): T1 + |// erased self-type: T1 |trait T2a { self: T1 with T2a => | @inline override final def f = 1 - | @inline def g2a = f // inlined: resolved as T2a.f, which is re-written to T2a$class.f, so T2a$class.g2a has ICONST_1 + | @inline def g2a = f // inlined: resolved as T2a.f |} | |final class Ca extends T1 with T2a { - | // mixin generates accessors like `def g1 = T1$class.g1`, the impl class method call is inlined into the accessor. + | // mixin generates accessors like `def g1 = super[T1].g1`, the impl super call is inlined into the accessor. | | def m1a = g1 // call to accessor, inlined, we get the interface call T1.f | def m2a = g2a // call to accessor, inlined, we get ICONST_1 | def m3a = f // call to accessor, inlined, we get ICONST_1 | - | def m4a(t: T1) = t.f // T1.f is not final, so not inlined, interface call to T1.f - | def m5a(t: T2a) = t.f // re-written to T2a$class.f, inlined, ICONST_1 + | def m4a(t: T1) = t.f // T1.f is not final, so not inlined, we get an interface call T1.f + | def m5a(t: T2a) = t.f // inlined, we get ICONST_1 |} | |// erased self-type: T2b |trait T2b { self: T2b with T1 => | @inline override final def f = 1 - | @inline def g2b = f // not inlined: resolved as T1.f, so T2b$class.g2b has an interface call T1.f + | @inline def g2b = f // not inlined: resolved as T1.f, we get an interface call T1.f |} | |final class Cb extends T1 with T2b { @@ -650,31 +640,26 @@ class InlinerTest extends ClearAfterClass { | def m3b = f // inlined, we get ICONST_1 | | def m4b(t: T1) = t.f // T1.f is not final, so not inlined, interface call to T1.f - | def m5b(t: T2b) = t.f // re-written to T2b$class.f, inlined, ICONST_1 + | def m5b(t: T2b) = t.f // inlined, ICONST_1 |} """.stripMargin - val warnings = Set( - "T1::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", - "T2b::g2b()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", - "T1::g1()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", - "T2a::g2a()I is annotated @inline but cannot be inlined: the method is not final and may be overridden", - "T1::g1()I is annotated @inline but cannot be inlined: the method is not final and may be overridden") + val warning = "T1::f()I is annotated @inline but cannot be inlined: the method is not final and may be overridden" var count = 0 - val List(ca, cb, t1, t2a, t2b) = compile(code, allowMessage = i => {count += 1; warnings.exists(i.msg contains _)}) - assert(count == 8, count) // see comments, f is not inlined 4 times, additional warnings due to SD-86 + val List(ca, cb, t1, t2a, t2b) = compile(code, allowMessage = i => {count += 1; i.msg contains warning}) + assert(count == 4, count) // see comments, f is not inlined 4 times assertNoInvoke(getSingleMethod(t2a, "g2a")) assertInvoke(getSingleMethod(t2b, "g2b"), "T1", "f") -// assertInvoke(getSingleMethod(ca, "m1a"), "T1", "f") // disabled due to SD-86: m1a calls the mixin-method g1a, which calls super[T1].g1a. we inline the mixin-method and end up with the super call. -// assertNoInvoke(getSingleMethod(ca, "m2a")) // no invoke, see comment on def g2a // SD-86 + assertInvoke(getSingleMethod(ca, "m1a"), "T1", "f") + assertNoInvoke(getSingleMethod(ca, "m2a")) // no invoke, see comment on def g2a assertNoInvoke(getSingleMethod(ca, "m3a")) assertInvoke(getSingleMethod(ca, "m4a"), "T1", "f") assertNoInvoke(getSingleMethod(ca, "m5a")) -// assertInvoke(getSingleMethod(cb, "m1b"), "T1", "f") // SD-86 -// assertInvoke(getSingleMethod(cb, "m2b"), "T1", "f") // invoke, see comment on def g2b // SD-86 + assertInvoke(getSingleMethod(cb, "m1b"), "T1", "f") + assertInvoke(getSingleMethod(cb, "m2b"), "T1", "f") // invoke, see comment on def g2b assertNoInvoke(getSingleMethod(cb, "m3b")) assertInvoke(getSingleMethod(cb, "m4b"), "T1", "f") assertNoInvoke(getSingleMethod(cb, "m5b")) @@ -772,8 +757,8 @@ class InlinerTest extends ClearAfterClass { | 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 f = 5 + | @noinline final def g = 6 | | @noinline def h: Int | @inline def i: Int @@ -786,8 +771,8 @@ class InlinerTest extends ClearAfterClass { | 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 f = 5 + | @noinline final def g = 6 | | @noinline def h: Int | @inline def i: Int @@ -1508,4 +1493,14 @@ class InlinerTest extends ClearAfterClass { val List(a, b) = compileClassesSeparately(List(codeA, codeB), extraArgs = "-Yopt:l:project -Yopt-warnings") assertInvoke(getSingleMethod(b, "t"), "A", "f") } + + @Test + def sd86(): Unit = { + val code = + """trait T { @inline def f = 1 } // note that f is not final + |class C extends T + """.stripMargin + val List(c, t) = compile(code, allowMessage = _ => true) + assertSameSummary(getSingleMethod(c, "f"), List(ICONST_1, IRETURN)) + } } |