Skip to content

Methods defined in traits are not inlined #4767

Closed
@scabug

Description

@scabug

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.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions