scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.88k stars 1.06k forks source link

NoSuchMethodError calling operator names from non-scala classes #4534

Open Glavo opened 6 years ago

Glavo commented 6 years ago

Here's a Kotlin class:

class C {
  fun `+`() = 10
}

When I use it in repl, the completion can suggest the + method:

scala> val c = new C 
val c: C = C@1bc425e7
scala> c. 
eq            isInstanceOf  hashCode      ==            getClass      !=            equals        ne
+             toString      wait          notify        ##            synchronized  notifyAll     asInstanceOf

However, problems occur when calling:

scala> c.+ 
java.lang.NoSuchMethodError: C.$plus()I
    at rs$line$2$.<init>(rs$line$2:1)
    at rs$line$2$.<clinit>(rs$line$2)
    at rs$line$2.res0Show(rs$line$2)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at dotty.tools.repl.Rendering.valueOf(Rendering.scala:58)
    at dotty.tools.repl.Rendering.renderVal(Rendering.scala:79)
    at dotty.tools.repl.ReplDriver.displayMembers$3$$anonfun$3(ReplDriver.scala:284)
    at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
    at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:59)
    at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:52)
    at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
    at scala.collection.TraversableLike.map(TraversableLike.scala:234)
    at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
    at scala.collection.AbstractTraversable.map(Traversable.scala:104)
    at dotty.tools.repl.ReplDriver.displayMembers$7(ReplDriver.scala:284)
    at dotty.tools.repl.ReplDriver.displayDefinitions$$anonfun$3$$anonfun$2(ReplDriver.scala:311)
    at scala.Option.map(Option.scala:146)
    at dotty.tools.repl.ReplDriver.displayDefinitions$$anonfun$1(ReplDriver.scala:311)
    at dotty.tools.dotc.core.Periods.atPhase(Periods.scala:26)
    at dotty.tools.dotc.core.Phases.atPhase(Phases.scala:36)
    at dotty.tools.repl.ReplDriver.displayDefinitions(ReplDriver.scala:316)
    at dotty.tools.repl.ReplDriver.compile$$anonfun$2(ReplDriver.scala:241)
    at scala.util.Either.fold(Either.scala:188)
    at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:242)
    at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:202)
    at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:150)
    at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:154)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput$$anonfun$2$$anonfun$1(ReplDriver.scala:163)
    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
    at scala.Console$.withErr(Console.scala:192)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput$$anonfun$1(ReplDriver.scala:163)
    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
    at scala.Console$.withOut(Console.scala:163)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:163)
    at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:154)
    at dotty.tools.repl.Main$.main(Main.scala:6)
    at dotty.tools.repl.Main.main(Main.scala)

scala> c.`+` 
java.lang.NoSuchMethodError: C.$plus()I
    at rs$line$3$.<init>(rs$line$3:1)
    at rs$line$3$.<clinit>(rs$line$3)
    at rs$line$3.res1Show(rs$line$3)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at dotty.tools.repl.Rendering.valueOf(Rendering.scala:58)
    at dotty.tools.repl.Rendering.renderVal(Rendering.scala:79)
    at dotty.tools.repl.ReplDriver.displayMembers$3$$anonfun$3(ReplDriver.scala:284)
    at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
    at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:59)
    at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:52)
    at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:48)
    at scala.collection.TraversableLike.map(TraversableLike.scala:234)
    at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
    at scala.collection.AbstractTraversable.map(Traversable.scala:104)
    at dotty.tools.repl.ReplDriver.displayMembers$7(ReplDriver.scala:284)
    at dotty.tools.repl.ReplDriver.displayDefinitions$$anonfun$3$$anonfun$2(ReplDriver.scala:311)
    at scala.Option.map(Option.scala:146)
    at dotty.tools.repl.ReplDriver.displayDefinitions$$anonfun$1(ReplDriver.scala:311)
    at dotty.tools.dotc.core.Periods.atPhase(Periods.scala:26)
    at dotty.tools.dotc.core.Phases.atPhase(Phases.scala:36)
    at dotty.tools.repl.ReplDriver.displayDefinitions(ReplDriver.scala:316)
    at dotty.tools.repl.ReplDriver.compile$$anonfun$2(ReplDriver.scala:241)
    at scala.util.Either.fold(Either.scala:188)
    at dotty.tools.repl.ReplDriver.compile(ReplDriver.scala:242)
    at dotty.tools.repl.ReplDriver.interpret(ReplDriver.scala:202)
    at dotty.tools.repl.ReplDriver.loop$1(ReplDriver.scala:150)
    at dotty.tools.repl.ReplDriver.runUntilQuit$$anonfun$1(ReplDriver.scala:154)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput$$anonfun$2$$anonfun$1(ReplDriver.scala:163)
    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
    at scala.Console$.withErr(Console.scala:192)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput$$anonfun$1(ReplDriver.scala:163)
    at scala.util.DynamicVariable.withValue(DynamicVariable.scala:58)
    at scala.Console$.withOut(Console.scala:163)
    at dotty.tools.repl.ReplDriver.withRedirectedOutput(ReplDriver.scala:163)
    at dotty.tools.repl.ReplDriver.runUntilQuit(ReplDriver.scala:154)
    at dotty.tools.repl.Main$.main(Main.scala:6)
    at dotty.tools.repl.Main.main(Main.scala)
Glavo commented 6 years ago

This Scala object can be compiled:

object Main {
  def main(args: Array[String]): Unit = {
    val c = new C
    println(c.+)
    println(c.`+`)
  }
}

However, problems occur at runtime:

glavo@glavo:~/Document/Test$ dotr -classpath .:/home/glavo/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/181.4668.68/plugins/Kotlin/lib/kotlin-stdlib.jar Main
Exception in thread "main" java.lang.NoSuchMethodError: C.$plus()I
    at Main$.main(Main.scala:4)
    at Main.main(Main.scala)
Glavo commented 6 years ago

I haven't tested it in the IDE, but I think that the IDE's completion and error hints cause similar problems.

Blaisorblade commented 6 years ago

Can Scalac call that function? Right now Scala interoperates with Java and Scala, and AFAIK neither can produce such classes, so we can safely assume we can mangle symbolic names. Changing that assumption risks being pretty invasive...

Glavo commented 6 years ago

@Blaisorblade Scalac seems to ignore this method.

glavo@glavo:~/文档/Test$ scalac Main.scala 
Main.scala:4: error: missing argument list for method + in class any2stringadd
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `+ _` or `+(_)` instead of `+`.
    println(c.+)
              ^
Main.scala:5: error: missing argument list for method + in class any2stringadd
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `+ _` or `+(_)` instead of `+`.
    println(c.`+`)
              ^
two errors found
allanrenucci commented 6 years ago

I guess the sensible thing to do is to ignore class files that don't originate from eitheir a Java source file or a Scala source file

smarter commented 6 years ago

I guess the sensible thing to do is to ignore class files that don't originate from eitheir a Java source file or a Scala source file

That's way too extreme. First, it's impossible to know how a classfile was produced in general. Second, it's possible to produce Kotlin classes that are usable from Scala, since it's possible to make them usable from Java.

allanrenucci commented 6 years ago

A less extreme solution would be to not encode names that don't come from scala