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")
}
}
}
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
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?
Sure! For @deprecated there's a TODO in the backend indeed: https://github.com/lampepfl/dotty/blob/baba3f0a610bf971045d69a29d7f9e9c1ce2c8ff/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala#L325
Works great!
Most helpful comment
Works great!