Dotty: @vararg annotation when called from Java does not compile

Created on 12 Sep 2019  Â·  9Comments  Â·  Source: lampepfl/dotty

minimized code

object foo {
  def main(args: Array[String]): Unit = {
    println("Dotty")
    // calling from Java does not compile
    val config = new SimpleConfig
    config.checkValid(config, "foo")
  }

  import scala.annotation.varargs

  trait Config {
    @varargs def checkValid(reference: Config, restrictToPaths: String*): Unit
  }

  class SimpleConfig extends Config {
    // This method in Config uses @varargs so it can be called from Java with varargs
    // See https://github.com/scala/bug/issues/10658
    // Now the code goes through the Scala varargs method but we need this one for Java
    def checkValid(reference: Config, restrictToPaths: Array[String]): Unit = {
      println("called from Java checkValid")
      checkValid(reference, restrictToPaths.toIndexedSeq: _*)
    }   

    override def checkValid(reference: Config, restrictToPaths: String*): Unit = {
      println("called from Scala checkValid")
    }
  }
}

expectation

Calling from Scala works as expected compared to Scala 2. In Dotty calling from Java does not compile and it looks like it calls the Scala method based on the error.

[error] /Users/eric/workspace/sconfig/examples/java/simple-lib/src/main/java/simplelib/SimpleLibContext.java:21:1: incompatible types: java.lang.String cannot be converted to scala.collection.immutable.Seq<java.lang.String>
[error]         config.checkValid(ConfigFactory.defaultReference(), "simple-lib");

Reference used when porting https://github.com/ekrich/sconfig from Java to Scala. https://github.com/scala/bug/issues/10658

java bug

Most helpful comment

Works great!

All 9 comments

Someone (SO) asked about overloaded

  def set[S](values: Array[S]): Unit = { 
    println("Array overload")
  }

  @annotation.varargs
  def set[S](value: S, values: S*): Unit = {
    println("Varargs overload")
  } 

and invocation from Java

set(true)

In Scala 3, modulo varargs, the signature is

  public <S extends java.lang.Object> void set(S[]);
    descriptor: (Ljava/lang/Object;)V

instead of

  public <S extends java.lang.Object> void set(java.lang.Object);
    descriptor: (Ljava/lang/Object;)V

Now, whenever something doesn't work as expected, we ask, WWDD?

Are there any workaround for this issue now?
(How can we define a varargs method in dotty that can be called from java?)

Minimal test code:

class Dotty {
  @annotation.varargs def f(a: String*)= ???
}

```java
public class Java {
Dotty d = null;
public void test() {
d.f("a", "b");
}
}

```scala
[error] .../Java.java:4:1: method f in class Dotty cannot be applied to given types;
[error]   required: scala.collection.immutable.Seq<java.lang.String>
[error]   found: java.lang.String,java.lang.String
[error]   reason: actual and formal argument lists differ in length
[error] d.f

I just did the thing where I forgot to import annotation._. That sure is annoying.

Anyway, the ball is now in javac's court:

➜  dotty git:(review/7212) ✗ javac  t7212.java 
An exception has occurred in the compiler (11.0.5). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.
java.lang.NullPointerException
    at jdk.compiler/com.sun.tools.javac.comp.Flow$FlowAnalyzer.visitApply(Flow.java:1235)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1634)
    at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:398)
    at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.visitExec(TreeScanner.java:213)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1452)
    at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:398)
    at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:57)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$FlowAnalyzer.visitBlock(Flow.java:997)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:1020)
    at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:398)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$FlowAnalyzer.visitMethodDef(Flow.java:964)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:866)
    at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:398)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$FlowAnalyzer.visitClassDef(Flow.java:927)
    at jdk.compiler/com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:774)
    at jdk.compiler/com.sun.tools.javac.tree.TreeScanner.scan(TreeScanner.java:49)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$BaseAnalyzer.scan(Flow.java:398)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$FlowAnalyzer.analyzeTree(Flow.java:1327)
    at jdk.compiler/com.sun.tools.javac.comp.Flow$FlowAnalyzer.analyzeTree(Flow.java:1317)
    at jdk.compiler/com.sun.tools.javac.comp.Flow.analyzeTree(Flow.java:218)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1401)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.flow(JavaCompiler.java:1375)
    at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:973)
    at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:311)
    at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:170)
    at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:57)
    at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:43)
➜  dotty git:(review/7212) ✗ 

Edit: I see that was already reported on the linked scala ticket.

Is there a link to the test or anything we can review?

It turns out that dotty generates no java varargs method, as if the @varargs annotation was ignored. It seems that @deprecated isn't processed correctly/at all either. I guess the problem lies in the backend?

Side note : javac NPE is gone in openjdk 14

as if the @varargs annotation was ignored

Yeah, it doesn't do anything with it right now.

I guess the problem lies in the backend?

I don't think this needs to be handled in the backend (it isn't in scalac as far as I can see). It should probably be handled in https://github.com/lampepfl/dotty/blob/master/compiler/src/dotty/tools/dotc/transform/ElimRepeated.scala which already takes care of adding varargs bridge to scala methods that override java methods.

alright! can I try to implement it there?

What about @deprecated, which should add the deprecated bytecode attribute to the method?

Works great!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ohze picture ohze  Â·  3Comments

odersky picture odersky  Â·  3Comments

NightMachinary picture NightMachinary  Â·  3Comments

deusaquilus picture deusaquilus  Â·  3Comments

fommil picture fommil  Â·  3Comments