summaryrefslogtreecommitdiff
path: root/test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala
blob: fedc074a1554281858dded7d2ec8546b2d81ad06 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package scala.tools.nsc
package backend.jvm
package opt

import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
import scala.collection.generic.Clearable
import scala.collection.mutable.ListBuffer
import scala.reflect.internal.util.BatchSourceFile
import scala.tools.asm.Opcodes._
import org.junit.Assert._

import scala.tools.asm.tree._
import scala.tools.asm.tree.analysis._
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils.AsmAnalyzer
import scala.tools.nsc.io._
import scala.tools.nsc.reporters.StoreReporter
import scala.tools.testing.AssertUtil._

import CodeGenTools._
import scala.tools.partest.ASMConverters
import ASMConverters._
import AsmUtils._

import BackendReporting._

import scala.collection.convert.decorateAsScala._
import scala.tools.testing.ClearAfterClass

object InlineWarningTest extends ClearAfterClass.Clearable {
  val argsNoWarn = "-Ybackend:GenBCode -Yopt:l:classpath"
  val args = argsNoWarn + " -Yopt-warnings"
  var compiler = newCompiler(extraArgs = args)
  def clear(): Unit = { compiler = null }
}

@RunWith(classOf[JUnit4])
class InlineWarningTest extends ClearAfterClass {
  ClearAfterClass.stateToClear = InlineWarningTest

  val compiler = InlineWarningTest.compiler

  def compile(scalaCode: String, javaCode: List[(String, String)] = Nil, allowMessage: StoreReporter#Info => Boolean = _ => false): List[ClassNode] = {
    compileClasses(compiler)(scalaCode, javaCode, allowMessage)
  }

  @Test
  def nonFinal(): Unit = {
    val code =
      """class C {
        |  @inline def m1 = 1
        |}
        |trait T {
        |  @inline def m2 = 1
        |}
        |class D extends C with T
        |
        |class Test {
        |  def t1(c: C, t: T, d: D) = c.m1 + t.m2 + d.m1 + d.m2
        |}
      """.stripMargin
    var count = 0
    val warns = Set(
      "C::m1()I is annotated @inline but cannot be inlined: the method is not final and may be overridden",
      "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 == 4, count)
  }

  @Test
  def traitMissingImplClass(): Unit = {
    val codeA = "trait T { @inline final def f = 1 }"
    val codeB = "class C { def t1(t: T) = t.f }"

    val removeImpl = (outDir: AbstractFile) => {
      val f = outDir.lookupName("T$class.class", directory = false)
      if (f != null) f.delete()
    }

    val warn =
      """T::f()I is annotated @inline but cannot be inlined: the trait method call could not be rewritten to the static implementation method. Possible reason:
        |The method f(LT;)I could not be found in the class T$class or any of its parents.
        |Note that the following parent classes could not be found on the classpath: T$class""".stripMargin

    var c = 0
    compileSeparately(List(codeA, codeB), extraArgs = InlineWarningTest.args, afterEach = removeImpl, allowMessage = i => {c += 1; i.msg contains warn})
    assert(c == 1, c)

    // only summary here
    compileSeparately(List(codeA, codeB), extraArgs = InlineWarningTest.argsNoWarn, afterEach = removeImpl, allowMessage = _.msg contains "there was one inliner warning")
  }

  @Test
  def handlerNonEmptyStack(): Unit = {
    val code =
      """class C {
        |  @noinline def q = 0
        |  @inline final def foo = try { q } catch { case e: Exception => 2 }
        |  def t1 = println(foo) // inline warning here: foo cannot be inlined on top of a non-empty stack
        |}
      """.stripMargin

    var c = 0
    compile(code, allowMessage = i => {c += 1; i.msg contains "operand stack at the callsite in C::t1()V contains more values"})
    assert(c == 1, c)
  }

  @Test
  def mixedWarnings(): Unit = {
    val javaCode =
      """public class A {
        |  public static final int bar() { return 100; }
        |}
      """.stripMargin

    val scalaCode =
      """class B {
        |  @inline final def flop = A.bar
        |  def g = flop
        |}
      """.stripMargin

    val warns = List(
      """failed to determine if bar should be inlined:
        |The method bar()I could not be found in the class A or any of its parents.
        |Note that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: A""".stripMargin,

      """B::flop()I is annotated @inline but could not be inlined:
        |Failed to check if B::flop()I can be safely inlined to B without causing an IllegalAccessError. Checking instruction INVOKESTATIC A.bar ()I failed:
        |The method bar()I could not be found in the class A or any of its parents.
        |Note that the following parent classes are defined in Java sources (mixed compilation), no bytecode is available: A""".stripMargin)

    var c = 0
    val List(b) = compile(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; warns.tail.exists(i.msg contains _)})
    assert(c == 1, c)

    // no warnings here
    compileClasses(newCompiler(extraArgs = InlineWarningTest.argsNoWarn + " -Yopt-warnings:none"))(scalaCode, List((javaCode, "A.java")))

    c = 0
    compileClasses(newCompiler(extraArgs = InlineWarningTest.argsNoWarn + " -Yopt-warnings:no-inline-mixed"))(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; warns.exists(i.msg contains _)})
    assert(c == 2, c)
  }
}