Skip to content
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

Constructors are unnecessarily given Java generic signatures #10834

Closed
dwijnand opened this issue Dec 17, 2020 · 8 comments · Fixed by #13047
Closed

Constructors are unnecessarily given Java generic signatures #10834

dwijnand opened this issue Dec 17, 2020 · 8 comments · Fixed by #13047

Comments

@dwijnand
Copy link
Member

Minimized code

class Foo[T]

Output

  public <T extends java.lang.Object> Foo();
    descriptor: ()V
[..]
    Signature: #9                           // <T:Ljava/lang/Object;>()V

Expectation

No Signature on the constructor. Compare against the equivalent Java:

$ echo 'class Foo<T> {}' > Foo.java && javac Foo.java && javap -v Foo
Classfile /d/mima/Foo.class
  Last modified 17 Dec 2020; size 243 bytes
  MD5 checksum 33fcdfd618f6e8ce83505acaa1f16ed2
  Compiled from "Foo.java"
class Foo<T extends java.lang.Object> extends java.lang.Object
  minor version: 0
  major version: 55
  flags: (0x0020) ACC_SUPER
  this_class: #2                          // Foo
  super_class: #3                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 1, attributes: 2
Constant pool:
   #1 = Methodref          #3.#12         // java/lang/Object."<init>":()V
   #2 = Class              #13            // Foo
   #3 = Class              #14            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               Signature
   #9 = Utf8               <T:Ljava/lang/Object;>Ljava/lang/Object;
  #10 = Utf8               SourceFile
  #11 = Utf8               Foo.java
  #12 = NameAndType        #4:#5          // "<init>":()V
  #13 = Utf8               Foo
  #14 = Utf8               java/lang/Object
{
  Foo();
    descriptor: ()V
    flags: (0x0000)
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0
}
Signature: #9                           // <T:Ljava/lang/Object;>Ljava/lang/Object;
SourceFile: "Foo.java"

(Signature #9 is the class Signature.) Scala 2 doesn't emit this, which I perceive as correct.

This came up in the context of trialing MiMa on different versions of a library compiled with the Scala 3 compiler and some changes are resulting in additional, unexpected constructor signature changes - however signature checking is an opt-in feature of MiMa, so this wouldn't affect all MiMa users.

@dwijnand
Copy link
Member Author

Signature is Java's generic method signature, so it's only impactful for Javac users (or MiMa users).

@xuwei-k
Copy link
Contributor

xuwei-k commented Jul 7, 2021

I think some guice DI does not work due to this issue 😢

Main.scala

package example

import com.google.inject.AbstractModule
import com.google.inject.Guice
import com.google.inject.Key
import com.google.inject.TypeLiteral
import javax.inject.Inject

class B[X] @Inject() (val a: A[X])

class A[X](x: X)

class MyModule extends AbstractModule {
  override def configure(): Unit = {
    bind(new TypeLiteral[A[String]]() {}).toInstance(new A[String]("x"))
  }
}

object Main {
  def main(args: Array[String]): Unit = {
    val injector = Guice.createInjector(new MyModule())
    injector.getInstance(
      Key.get(new TypeLiteral[B[String]]() {})
    )
    println("success") // success with Scala 2.13.6
  }
}

sbt run log with Scala 3

[error] (run-main-a) com.google.inject.ConfigurationException: Guice configuration errors:
[error] 
[error] 1) example.A<X> cannot be used as a key; It is not fully specified.
[error]   at example.B.<init>(A.scala:9)
[error]   while locating example.B<java.lang.String>
[error] 
[error] 1 error
[error] com.google.inject.ConfigurationException: Guice configuration errors:
[error] 
[error] 1) example.A<X> cannot be used as a key; It is not fully specified.
[error]   at example.B.<init>(A.scala:9)
[error]   while locating example.B<java.lang.String>
[error] 
[error] 1 error
[error] 	at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1120)
[error] 	at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1126)
[error] 	at example.Main$.main(A.scala:23)
[error] 	at example.Main.main(A.scala)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] 	at java.lang.reflect.Method.invoke(Method.java:498)
[error] stack trace is suppressed; run last Compile / bgRun for the full output
[error] Nonzero exit code: 1
[error] (Compile / run) Nonzero exit code: 

build.sbt

scalaVersion := "3.0.2-RC1-bin-20210706-6011847-NIGHTLY"

libraryDependencies += "com.google.inject" % "guice" % "4.2.3"

@smarter
Copy link
Member

smarter commented Jul 7, 2021

It seems that we're missing https://github.com/scala/scala/pull/7975/files#diff-271d576b3a80359cbc5db9e1a8dd2629c2cb1d99bf8a30854c64742f98c7a6b0R78-R87 which ensures that we do not generate signatures for constructors whose parameters don't refer to a class type parameter, but I don't know if this is related to @xuwei-k's issue since in his case the type parameter is used so it's normal to have a signature on the constructor.

@smarter smarter changed the title Constructors are incorrectly been given a Signature Constructors are unnecessarily given Java generic signatures Jul 7, 2021
@smarter
Copy link
Member

smarter commented Jul 7, 2021

@xuwei-k
Copy link
Contributor

xuwei-k commented Jul 7, 2021

another Java Interop problem

Main.java

package example;

public class Main {
  public static void main(String[] args) {
    A<Integer> a = new A<>("x");
    int x = a.x();
  }
}

A.scala

package example

class A[X](val x: X)

Scala 2.13.6

compile error

Main.java:5:1: incompatible types: cannot infer type arguments for example.A<>
[error]     reason: inference variable X has incompatible bounds
[error]       equality constraints: java.lang.Integer
[error]       lower bounds: java.lang.String
[error] A<>
[error] (Compile / compileIncremental) javac returned non-zero exit code

Scala 3.0.2-RC1-bin-20210706-6011847-NIGHTLY

runtime error

[info] running example.Main 
[error] (run-main-0) java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
[error] java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
[error] 	at example.Main.main(Main.java:6)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[error] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[error] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[error] 	at java.lang.reflect.Method.invoke(Method.java:498)
[error] stack trace is suppressed; run last Compile / bgRunMain for the full output
[error] Nonzero exit code: 1
[error] (Compile / runMain) Nonzero exit code: 1

@dwijnand
Copy link
Member Author

dwijnand commented Jul 7, 2021

Nice example, it looks like javac takes the wrong signatures as building a A<Integer> will invoking new A<String>("x") so doesn't fail to compile.

@xuwei-k
Copy link
Contributor

xuwei-k commented Jul 11, 2021

#13047

@Kordyjan Kordyjan added this to the 3.0.2 milestone Aug 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants