Skip to content

Commit 2c2fd4f

Browse files
committed
SD-20 Inlcude static methods in the InlineInfo in mixed compilation
In mixed compilation, the InlineInfo for a Java-defined class is created using the class symbol (vs in separate compilation, where the info is created by looking at the classfile and its methods). The scala compiler puts static java methods into the companion symbol, and we forgot to include them in the list of methods in the InlineInfo.
1 parent 31db427 commit 2c2fd4f

File tree

6 files changed

+67
-10
lines changed

6 files changed

+67
-10
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,8 +1145,7 @@ object BTypes {
11451145
final case class InlineInfo(isEffectivelyFinal: Boolean,
11461146
sam: Option[String],
11471147
methodInfos: Map[String, MethodInlineInfo],
1148-
warning: Option[ClassInlineInfoWarning]) {
1149-
}
1148+
warning: Option[ClassInlineInfoWarning])
11501149

11511150
val EmptyInlineInfo = InlineInfo(false, None, Map.empty, None)
11521151

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -557,9 +557,16 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
557557

558558
var warning = Option.empty[ClassSymbolInfoFailureSI9111]
559559

560+
def keepMember(sym: Symbol) = sym.isMethod && !scalaPrimitives.isPrimitive(sym)
561+
val classMethods = classSym.info.decls.iterator.filter(keepMember)
562+
val methods = if (!classSym.isJavaDefined) classMethods else {
563+
val staticMethods = classSym.companionModule.info.decls.iterator.filter(m => !m.isConstructor && keepMember(m))
564+
staticMethods ++ classMethods
565+
}
566+
560567
// Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
561568
// primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
562-
val methodInlineInfos = classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).flatMap({
569+
val methodInlineInfos = methods.flatMap({
563570
case methodSym =>
564571
if (completeSilentlyAndCheckErroneous(methodSym)) {
565572
// Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.

src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ object BackendReporting {
9696
val missingClassWarning = missingClass match {
9797
case None => ""
9898
case Some(c) =>
99-
if (c.definedInJavaSource) s"\nNote that the parent class ${c.internalName} is defined in a Java source (mixed compilation), no bytecode is available."
100-
else s"\nNote that the parent class ${c.internalName} could not be found on the classpath."
99+
if (c.definedInJavaSource) s"\nNote that class ${c.internalName} is defined in a Java source (mixed compilation), no bytecode is available."
100+
else s"\nNote that class ${c.internalName} could not be found on the classpath."
101101
}
102102
s"The method $name$descriptor could not be found in the class $ownerInternalName or any of its parents." + missingClassWarning
103103

test/junit/scala/tools/nsc/backend/jvm/opt/InlineInfoTest.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@ package scala.tools.nsc
22
package backend.jvm
33
package opt
44

5+
import org.junit.Assert._
56
import org.junit.Test
67
import org.junit.runner.RunWith
78
import org.junit.runners.JUnit4
89

910
import scala.collection.JavaConverters._
1011
import scala.collection.generic.Clearable
12+
import scala.tools.nsc.backend.jvm.BTypes.MethodInlineInfo
1113
import scala.tools.nsc.backend.jvm.BackendReporting._
1214
import scala.tools.testing.BytecodeTesting
1315

1416
@RunWith(classOf[JUnit4])
1517
class InlineInfoTest extends BytecodeTesting {
16-
import compiler.global
18+
import compiler._
1719
import global.genBCode.bTypes
1820

1921
override def compilerArgs = "-opt:l:classpath"
@@ -59,4 +61,20 @@ class InlineInfoTest extends BytecodeTesting {
5961

6062
assert(fromSyms == fromAttrs)
6163
}
64+
65+
@Test // scala-dev#20
66+
def javaStaticMethodsInlineInfoInMixedCompilation(): Unit = {
67+
val jCode =
68+
"""public class A {
69+
| public static final int bar() { return 100; }
70+
| public final int baz() { return 100; }
71+
|}
72+
""".stripMargin
73+
compileClasses("class C { new A }", javaCode = List((jCode, "A.java")))
74+
val info = global.genBCode.bTypes.classBTypeFromInternalName("A").info.get.inlineInfo
75+
assertEquals(info.methodInfos, Map(
76+
"bar()I" -> MethodInlineInfo(true,false,false),
77+
"<init>()V" -> MethodInlineInfo(false,false,false),
78+
"baz()I" -> MethodInlineInfo(true,false,false)))
79+
}
6280
}

test/junit/scala/tools/nsc/backend/jvm/opt/InlineWarningTest.scala

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ class InlineWarningTest extends BytecodeTesting {
7575
val warns = List(
7676
"""failed to determine if bar should be inlined:
7777
|The method bar()I could not be found in the class A or any of its parents.
78-
|Note that the parent class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin,
78+
|Note that class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin,
7979

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

8585
var c = 0
8686
val List(b) = compileToBytes(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; warns.tail.exists(i.msg contains _)})
@@ -168,4 +168,37 @@ class InlineWarningTest extends BytecodeTesting {
168168
compileToBytes(code, allowMessage = i => { c += 1; i.msg contains warn })
169169
assert(c == 1, c)
170170
}
171+
172+
@Test // scala-dev#20
173+
def mixedCompilationSpuriousWarning(): Unit = {
174+
val jCode =
175+
"""public class A {
176+
| public static final int bar() { return 100; }
177+
| public final int baz() { return 100; }
178+
|}
179+
""".stripMargin
180+
181+
val sCode =
182+
"""class C {
183+
| @inline final def foo = A.bar()
184+
| @inline final def fii(a: A) = a.baz()
185+
| def t = foo + fii(new A)
186+
|}
187+
""".stripMargin
188+
189+
val warns = List(
190+
"""C::foo()I is annotated @inline but could not be inlined:
191+
|Failed to check if C::foo()I can be safely inlined to C without causing an IllegalAccessError. Checking instruction INVOKESTATIC A.bar ()I failed:
192+
|The method bar()I could not be found in the class A or any of its parents.
193+
|Note that class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin,
194+
195+
"""C::fii(LA;)I is annotated @inline but could not be inlined:
196+
|Failed to check if C::fii(LA;)I can be safely inlined to C without causing an IllegalAccessError. Checking instruction INVOKEVIRTUAL A.baz ()I failed:
197+
|The method baz()I could not be found in the class A or any of its parents.
198+
|Note that class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin
199+
)
200+
var c = 0
201+
compileClasses(sCode, javaCode = List((jCode, "A.java")), allowMessage = i => { c += 1; warns.exists(i.msg.contains)})
202+
assert(c == 2)
203+
}
171204
}

test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ class InlinerTest extends BytecodeTesting {
416416
"""B::flop()I is annotated @inline but could not be inlined:
417417
|Failed to check if B::flop()I can be safely inlined to B without causing an IllegalAccessError. Checking instruction INVOKESTATIC A.bar ()I failed:
418418
|The method bar()I could not be found in the class A or any of its parents.
419-
|Note that the parent class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin
419+
|Note that class A is defined in a Java source (mixed compilation), no bytecode is available.""".stripMargin
420420

421421
var c = 0
422422
val List(b) = compile(scalaCode, List((javaCode, "A.java")), allowMessage = i => {c += 1; i.msg contains warn})
@@ -819,7 +819,7 @@ class InlinerTest extends BytecodeTesting {
819819
val warn =
820820
"""failed to determine if <init> should be inlined:
821821
|The method <init>()V could not be found in the class A$Inner or any of its parents.
822-
|Note that the parent class A$Inner could not be found on the classpath.""".stripMargin
822+
|Note that class A$Inner could not be found on the classpath.""".stripMargin
823823

824824
var c = 0
825825

0 commit comments

Comments
 (0)