Skip to content

Impossible cast from Long when Java varargs are involved #13645

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

Closed
jlprat opened this issue Sep 30, 2021 · 0 comments · Fixed by #13718
Closed

Impossible cast from Long when Java varargs are involved #13645

jlprat opened this issue Sep 30, 2021 · 0 comments · Fixed by #13718

Comments

@jlprat
Copy link

jlprat commented Sep 30, 2021

When interacting with some java code involving parametrized methods and varargs of the same type and a Scala Long is used, a ClassCastException is thrown at runtime as primitive long can't be casted to Object.

Compiler version

Scala 3.0.2 and 3.1.0-RC2

Minimized code

You can find a repo with the code here
Given this Java class

public class TypedVarargs<V> {

    public TypedVarargs<V> varArgs(V thing, V... things) {
        return this;
    }
}

And this Scala class:

object ImpossibleCast {
 
    val x = new TypedVarargs[Long]()
    val y = x.varArgs(1L)
    def main(args: Array[String]): Unit = {
        println("runs")
    }
}

Output

When running ImpossibleCast we get the following exception:

[error] java.lang.ExceptionInInitializerError
[error] 	at ImpossibleCast.main(ImpossibleCast.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] Caused by: java.lang.ClassCastException: [J cannot be cast to [Ljava.lang.Object;
[error] 	at ImpossibleCast$.<clinit>(ImpossibleCast.scala:4)
[error] 	at ImpossibleCast.main(ImpossibleCast.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)

Expectation

The code should work (it does under Scala 2.13.6) and print:

runs

Workaround

object ImpossibleCast {
 
    val x = new TypedVarargs[java.lang.Long]()
    val y = x.varArgs(1L)
    def main(args: Array[String]): Unit = {
        println("runs")
    }
}

Notice that if we instantiate the TypedVarargs class with java.lang.Long instead of Scala's Long the code works as expected.

@smarter smarter self-assigned this Sep 30, 2021
@smarter smarter changed the title Impossible cast from Long when Java vargargs are involved Impossible cast from Long when Java varargs are involved Oct 1, 2021
smarter added a commit to dotty-staging/dotty that referenced this issue Oct 8, 2021
Previously, ElimRepeated correctly handled Java varargs of the
form `Object...` and `T...` where `T` is a method parameter, in both
cases we erased them to `Array[? <: Object]` in the denotation
transformer and handled any adaptation in the tree transformer of
ElimRepeated.

Unfortunately, the denotation transformer logic failed to account for
class type parameters, and fixing it introduced issues in override
checking (RefChecks happens after ElimRepeated). So this commit gives up
and delay the adaptation of primitive arrays into reference arrays until
Erasure. This is less efficient in some situations but is closer to what
Scala 2 does so hopefully means we won't run into more pitfalls.

Fixes scala#13645.
smarter added a commit to dotty-staging/dotty that referenced this issue Oct 8, 2021
Previously, ElimRepeated correctly handled Java varargs of the
form `Object...` and `T...` where `T` is a method parameter, in both
cases we erased them to `Array[? <: Object]` in the denotation
transformer and handled any adaptation in the tree transformer of
ElimRepeated.

Unfortunately, the denotation transformer logic failed to account for
class type parameters, and fixing it introduced issues in override
checking (RefChecks happens after ElimRepeated). So this commit gives up
and delay the adaptation of primitive arrays into reference arrays until
Erasure. This is less efficient in some situations but is closer to what
Scala 2 does so hopefully means we won't run into more pitfalls.

Fixes scala#13645.
smarter added a commit to dotty-staging/dotty that referenced this issue Oct 8, 2021
Previously, ElimRepeated correctly handled Java varargs of the
form `Object...` and `T...` where `T` is a method parameter, in both
cases we erased them to `Array[? <: Object]` in the denotation
transformer and handled any adaptation in the tree transformer of
ElimRepeated.

Unfortunately, the denotation transformer logic failed to account for
class type parameters, and fixing it introduced issues in override
checking (RefChecks happens after ElimRepeated). So this commit gives up
and delay the adaptation of primitive arrays into reference arrays until
Erasure. This is less efficient in some situations but is closer to what
Scala 2 does so hopefully means we won't run into more pitfalls.

Fixes scala#13645.
smarter added a commit to dotty-staging/dotty that referenced this issue Oct 8, 2021
Previously, ElimRepeated correctly handled Java varargs of the
form `Object...` and `T...` where `T` is a method parameter, in both
cases we erased them to `Array[? <: Object]` in the denotation
transformer and handled any adaptation in the tree transformer of
ElimRepeated.

Unfortunately, the denotation transformer logic failed to account for
class type parameters, and fixing it introduced issues in override
checking (RefChecks happens after ElimRepeated). So this commit gives up
and delay the adaptation of primitive arrays into reference arrays until
Erasure. This is less efficient in some situations but is closer to what
Scala 2 does so hopefully means we won't run into more pitfalls.

Fixes scala#13645.
olsdavis pushed a commit to olsdavis/dotty that referenced this issue Apr 4, 2022
Previously, ElimRepeated correctly handled Java varargs of the
form `Object...` and `T...` where `T` is a method parameter, in both
cases we erased them to `Array[? <: Object]` in the denotation
transformer and handled any adaptation in the tree transformer of
ElimRepeated.

Unfortunately, the denotation transformer logic failed to account for
class type parameters, and fixing it introduced issues in override
checking (RefChecks happens after ElimRepeated). So this commit gives up
and delay the adaptation of primitive arrays into reference arrays until
Erasure. This is less efficient in some situations but is closer to what
Scala 2 does so hopefully means we won't run into more pitfalls.

Fixes scala#13645.
@Kordyjan Kordyjan added this to the 3.1.1 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.

3 participants