Skip to content

Invalid Java generic signatures for polymorphic value classes #12283

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

Open
smarter opened this issue Dec 12, 2020 · 5 comments
Open

Invalid Java generic signatures for polymorphic value classes #12283

smarter opened this issue Dec 12, 2020 · 5 comments
Labels
backend erasure fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) java interop minimized runtime crash valueclass
Milestone

Comments

@smarter
Copy link
Member

smarter commented Dec 12, 2020

reproduction steps

using Scala 2.13.4,

class Poly[A](val value: A) extends AnyVal

class Arr[A](val value: Array[A]) extends AnyVal

class ArrRef[A <: AnyRef](val value: Array[A]) extends AnyVal

class Test {
  def poly1(x: Poly[Int]): Poly[Int] =
    new Poly(x.value)

  def poly2(x: Poly[String]): Poly[String] = // correct signature
    new Poly(x.value)

  def poly3(x: Poly[Array[Int]]): Poly[Array[Int]] = // correct signature
    new Poly(x.value)

  def poly4(x: Poly[Array[String]]): Poly[Array[String]] = // correct signature
    new Poly(x.value)

  def arr1(x: Arr[Int]): Arr[Int] =
    new Arr(x.value)

  def arr2(x: Arr[String]): Arr[String] =
    new Arr(x.value)

  def arr3(x: Arr[Array[Int]]): Arr[Array[Int]] =
    new Arr(x.value)

  def arr4(x: Arr[Array[String]]): Arr[Array[String]] =
    new Arr(x.value)

  def arrRef1(x: ArrRef[Integer]): ArrRef[Integer] =
    new ArrRef(x.value)

  def arrRef2(x: ArrRef[String]): ArrRef[String] =
    new ArrRef(x.value)

  def arrRef3(x: ArrRef[Array[Int]]): ArrRef[Array[Int]] =
    new ArrRef(x.value)

  def arrRef4(x: ArrRef[Array[String]]): ArrRef[Array[String]] =
    new ArrRef(x.value)
}

problem

When using javap we can observe that the generic signature of these methods (the one that javap displays first and that comes from the Signature attribute) does not match their descriptor (which is the actual signature the JVM cares about); except for the ones I marked // correct signature:

% javap -p -v Test|grep -B1 descriptor
...
--
  public int poly1(int);
    descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer;
--
  public java.lang.String poly2(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
--
  public int[] poly3(int[]);
    descriptor: ([I)[I
--
  public java.lang.String[] poly4(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)[Ljava/lang/String;
--
  public int[] arr1(int[]);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
--
  public java.lang.String[] arr2(java.lang.String[]);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
--
  public int[][] arr3(int[][]);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
--
  public java.lang.String[][] arr4(java.lang.String[][]);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
--
  public java.lang.Integer[] arrRef1(java.lang.Integer[]);
    descriptor: ([Ljava/lang/Object;)[Ljava/lang/Object;
--
  public java.lang.String[] arrRef2(java.lang.String[]);
    descriptor: ([Ljava/lang/Object;)[Ljava/lang/Object;
--
  public int[][] arrRef3(int[][]);
    descriptor: ([Ljava/lang/Object;)[Ljava/lang/Object;
--
  public java.lang.String[][] arrRef4(java.lang.String[][]);
    descriptor: ([Ljava/lang/Object;)[Ljava/lang/Object;
--
...

This can only end in disaster when Java tries to call these methods:

public class J {
  public static void main(String[] args) {
    Test test = new Test();
    test.poly1(1);
  }
}
% javac J.java
% java J
Exception in thread "main" java.lang.NoSuchMethodError: Test.poly1(I)I
        at J.main(J.java:4)
@smarter smarter added java interop valueclass backend minimized runtime crash erasure fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) labels Dec 12, 2020
@smarter smarter changed the title Invalid Java generic signatures for polymorphic value clases Invalid Java generic signatures for polymorphic value classes Dec 12, 2020
@smarter
Copy link
Member Author

smarter commented Dec 12, 2020

(it's unfortunate that Poly[Int] erases to Integer and not Int, but it's too late to change that now, and I'm adapting dotty to match that behavior for binary compat).

@Jasper-M
Copy link

Related to #11321 ?

@smarter
Copy link
Member Author

smarter commented Dec 12, 2020

scala/scala3#10765 contains testcases you could reuse as well as reverse-engineered rules for erasure of polymorphic value classes.

@bishabosha
Copy link
Member

bishabosha commented Dec 12, 2020

is this the same problem as #12259?

object Values {
  def reify[I <: Int with Singleton](implicit I: ValueOf[I]): I = valueOf[I]
}

reify has the descriptor (Ljava/lang/Integer;)I on scala 2 but (Ljava/lang/Object;)I in dotty

@smarter
Copy link
Member Author

smarter commented Dec 12, 2020

No, #12259 is fixed by scala/scala3#10765, this issue is about the Java generic signatures which are emitted by both Scala 2 and 3 but only read by javac.

@dwijnand dwijnand added this to the Backlog milestone Jan 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend erasure fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) java interop minimized runtime crash valueclass
Projects
None yet
Development

No branches or pull requests

4 participants