-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Kotlin interop: reporting wrong type for inner classes appearing in Kotlin method types #12086
Comments
We need a way (ideally minimized) to reproduce the problem, otherwise there's not much we can do about it. |
Foo.java as: package foo;
import foo.Foo.Bar1;
import java.util.List;
public abstract class Foo {
public static final class Bar1 extends Foo {}
public static final class Bar2 extends Foo {}
}
class Baz {
public void bars(List<Bar1> bs) {
bs.clear();
}
} FooScala.scala as: package foo
import Foo._
import scala.jdk.CollectionConverters._
object FooScala {
def doThings(foo: Foo): Unit =
foo match {
case b: Foo.Bar1 => new Baz().bars(List(b).asJava)
case _: Foo.Bar2 => ()
}
} will recreate the issue |
Thank you for the minimization, I just tried it but I can't reproduce the issue (the code compiles fine), can you list the steps you use to reproduce the problem and the version of javac you have? |
Running |
I don't have java 14 here, but I can't reproduce with either java 8 or 15 locally, could you try using a different version? |
oh I'm an idiot, I was getting my errors all mixed up (sorry, end of the day here in UK). My minimized example isn't causing the error for me 🤦 I'm stuck for a way to reproduce this for you besides having you try it with the library I am using |
I know that feeling :). if you can minimize it to a scala file that depends on a publicly available library that would be useful already. |
Finally got something for you! 😁 https://scastie.scala-lang.org/e4TAeoEISaeDFNE4HqbrnQ |
Thank you! This can be reproduced using coursier with: scalac -classpath "$(cs fetch -p au.com.dius.pact:provider:4.2.0)" Test.scala @prolativ You've worked on inner classes recently, so you might be able to figure out what's going on here |
Minimized: Result.ktsealed class Result {
class Failed : Result()
}
class Verifier {
fun displayFailure(failure: Result.Failed) {}
} Test.scalaobject Test {
def foo(verifier: Verifier, failed: Result.Failed): Unit =
verifier.displayFailure(failed)
}
I tried translating the Kotlin to Java but was unable to reproduce the issue, compilation was successful. |
Very interesting, could you paste the output of |
@lrytz are you aware of any special handling of inner classes in kotlin that required changes in scalac? |
Kotlin Result.class
Java Result.class
which was compiled from public abstract class Result {
public static final class Failed extends Result {}
} by |
Hmm that looks identical, could you do the same for |
Kotlin Result$Failed.class
More unprintable chars at Java Result$Failed.class
|
Oh wait, the issue is probably in the Verifier class, I just found https://youtrack.jetbrains.com/issue/KT-27936 which indicates that kotlin isn't writing InnerClasses attributes for types that it references. So if I'm correct we need to figure out how Scala 2 bypasses the need for this (I guess it assumes everything with a |
Found it: scala/scala#5822 |
Yes, the Java version of Verifier.class has
which is missing from the Kotlin version. |
hi, is there any update on this? I can't cross-build my library to scala 3 without it 🙁 |
Hi @Kordyjan - I just confirmed this is still an issue in recent versions of Scala and Kotlin (3.0.2 and 1.5.30 respectively). This is a bit out of my depth but if you are bogged down I can take a crack at it, since the relevant fix in Scala 2 has already been pointed out, any pointers on where to start in the dotty codebase would probably be a big help. |
Thanks for stepping up!
|
@smarter thanks! One question regarding tests for this: as there are currently neither any |
If you look at the scala 2 pr, it doesn't add any kt or class file, instead it adds java files that mimic the behavior of kotlin. |
One question regarding the test classes, in the Scala file we have: val c = new C
assert(Test_2.acceptD(Test_1.mD(new c.D)) == 2) But,
If we instead change the argument from
So my current major point of confusion, which may not be very related to the issue at hand, is just how |
If I'm understanding things correctly, it doesn't: in Scala 3 currently it only accepts the toplevel |
In Scala 2.13.5, perplexingly I seem to get the same error as in dotty:
But, this is in the |
The _X suffixes are used for separate compilation, so Test_1 should be compiled with javac, then Test_2 with a separate call to javac, then Test_3 should be compiled by scalac with the result of the previous steps on the classpath. This is what the test framework does. |
Thanks! baby steps 👶 I added a bit of logging to try to understand the situation in the dotty code a bit better: def debugPrint(name: Name, info: String): Unit =
if (name.toString == "C" || name.toString == "D" || name.toString == "C.D" || name.toString == "C$D" || name.toString == "c.D" ) {
System.err.println(info)
}
/** Return the class symbol of the given name. */
def classNameToSymbol(name: Name)(using Context): Symbol = innerClasses.get(name.toString) match {
case Some(entry) =>
debugPrint(name, s"classNameToSymbol (with inner) name=$name has symbol ${innerClasses.classSymbol(entry)}")
innerClasses.classSymbol(entry)
case None =>
debugPrint(name, s"classNameToSymbol (no inner) name=$name has symbol ${requiredClass(name)}")
requiredClass(name)
}
For the line
So if I understand right, the two lines:
should actually be reading
? Thanks again |
Ooops, no, I think it should actually be this: Current:
Desired:
|
The toString on a class will print just the class name regardless of where it's defined, so it'll be "class D", not "class C.D", and it's the one whose currently interpreted as "class C$D" that needs to be seen as D inside C instead. |
OK, getting a bit better grasp on this but wanted to check on my planned approach for manipulating the
|
I don't think you need to use mapParts, you can directly go from a String to a Name via |
I have a branch with a couple of commits. The first commit attempted to demonstrate a very basic fix for the specific case of Primarily, on the following line, if I remove the case _ => requiredClass(localizedName).copy(owner = owner) // instanceScope.lookup(innerName) As it is, there are still errors for some of the more heavily nested cases:
|
This is a port of scala/scala#5822 which works around a bug in Kotlin (https://youtrack.jetbrains.com/issue/KT-27936). Fixes scala#12086. Co-Authored-By: Lukas Rytz <lukas.rytz@gmail.com> Co-Authored-By: Brandon Barker <beb82@cornell.edu>
This is a port of scala/scala#5822 which works around a bug in Kotlin (https://youtrack.jetbrains.com/issue/KT-27936). Fixes scala#12086. Co-Authored-By: Lukas Rytz <lukas.rytz@gmail.com> Co-Authored-By: Brandon Barker <beb82@cornell.edu>
Thank you @smarter ! |
Compiler version
3.0.0-RC2
Minimized code
I'm working with a library that is written in kotlin, and running into a compilation error on 3.0.0-RC2 that doesn't happen on 2.13.5. The decompiled .class file looks like:
and then there is a method on another class like:
I then get this compiler error when calling the method:
Output
Expectation
Should compile.
The text was updated successfully, but these errors were encountered: