Skip to content

Runtime crash for Unit-returning closures passed to Java functions #3984

Closed
@odersky

Description

@odersky

Given:

import java.util.function.Function
object Test {

  def computeScala[R](f: String => R) = f("abc")
  def computeJava[R](f: Function[String, R]) = f("abc")

  def main(args: Array[String]) = {
    computeScala(s => println(s)) // OK
    computeJava(s => println(s)) // java.lang.BootstrapMethodError: call site initialization exception
  }
}

I get a BootstrapMethodError when calling computeJava. The full output is:

~/workspace/dotty/tests/run> java Test
abc
Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
at Test$.main(voidfun.scala:10)
at Test.main(voidfun.scala)
Caused by: java.lang.invoke.LambdaConversionException: Type mismatch for lambda expected return: void is not convertible to class java.lang.Object
at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:286)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
at java.lang.invoke.CallSite.makeSite(CallSite.java:302)
... 4 more

I printed the code abobe at phase LabelDefs, the only difference in the two calls is that the closure passed to the Java function has an expected result type this.main$$anonfun$2:java.util.function.Function.

package <empty> {
  @scala.annotation.internal.SourceFile("voidfun.scala") final module class
    Test$
   extends Object {
    def <init>(): Unit =
      {
        super()
        ()
      }
    def computeScala(f: Function1): Object = f.apply("abc")
    def computeJava(f: java.util.function.Function): Object = f.apply("abc")
    def main(args: String[]): Unit =
      {
        Test.computeScala(
          {
            closure(this.main$$anonfun$1)
          }
        )
        {
          Test.computeJava(
            {
              closure(this.main$$anonfun$2:java.util.function.Function)
            }
          )
          ()
        }
      }
    private def main$$anonfun$1(s: String): Unit = println(s)
    private def main$$anonfun$2(s: String): Unit = println(s)
  }
  final lazy module val Test: Test$ = new Test$()
}

@retronym, @lrytz: Is there some backend transformation that handles this and that we are missing? @DarkDimius any advice how to handle this?

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions