Closed
Description
Compiling
trait Foo {
@inline final def foo(x: Int): Unit = {
println(x)
}
}
class Bar {
def bar(y: Foo): Unit = {
y.foo(7)
}
}
yields (with -Ydebug and -Ylog:inline)
[log inliner] Treating CALL_METHOD test2.Foo.foo (dynamic)
receiver: trait Foo
icodes.available: true
concreteMethod.isEffectivelyFinal: true
[log inliner] inline failed for test2.Foo.foo:
pair.sameSymbols: false
inc.numInlined < 2: true
inc.hasCode: false
isSafeToInline: false
shouldInline: false
test2.scala:12: warning: Could not inline required method foo because bytecode was unavailable.
y.foo(7)
^
[log inliner] test2.Bar.bar blocks before inlining: 1 (4) after: 1 (4)
[log inliner] Analyzing test2.Foo$class
[log inliner] Not inlining into foo because it is marked @inline.
[log inliner] test2.Foo$class.foo blocks before inlining: 1 (7) after: 1 (7)
Expected behavior would be inlining foo
into bar
. It all works if you change Foo
to be a class instead of a trait.
I did some digging around and apparently ICode reading does not resolve methods bodies in traits:
scala> global.icodes.icode(global.definitions.getClass("scala.collection.immutable.MapLike"),true).methods.map(_.code)
res0: List[global.icodes.global.icodes.Code] = List(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, ICode 'companion', null, null, null, null, null, null, null, null, null, null, ...
Furthermore, it looks like class file reading will never load a trait's impl class (which actually contains the method bodies):
scala> global.definitions.getClass("scala.collection.immutable.MapLike")
res2: global.Symbol = trait MapLike
scala> res2.implClass
res3: global.Symbol = <none>
There's really no way to get at it:
scala> global.definitions.getClass("scala.collection.immutable.MapLike$class")
scala.reflect.internal.MissingRequirementError: class scala.collection.immutable.MapLike$class not found.
at scala.reflect.internal.Definitions$definitions$.getModuleOrClass(Definitions.scala:662)
at scala.reflect.internal.Definitions$definitions$.getClass(Definitions.scala:615)
The reason is that ClassPath.scala
explicitly excludes these class files (ending in $class.class
):
/** A useful name filter. */
def isTraitImplementation(name: String) = name endsWith "$class.class"
...
object DefaultJavaContext extends JavaContext {
override def isValidName(name: String) = !isTraitImplementation(name)
}
I believe this is a bug on its own - commenting out isValidName
fixes the implClass
behavior but not the inlining issue altogether.