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

Array parameterized with match type causes ClassCastException at runtime #15618

Closed
sbrunk opened this issue Jul 8, 2022 · 3 comments · Fixed by #15625
Closed

Array parameterized with match type causes ClassCastException at runtime #15618

sbrunk opened this issue Jul 8, 2022 · 3 comments · Fixed by #15625

Comments

@sbrunk
Copy link

sbrunk commented Jul 8, 2022

It looks like the array is instantiated as Array[Any] / Object[], even though i.e. ScalaType[Float32] resolves to Float.

Compiler version

3.1.3

Minimized code

sealed abstract class DType
sealed class Float16 extends DType
sealed class Float32 extends DType
sealed class Int32 extends DType

object Float16 extends Float16
object Float32 extends Float32
object Int32 extends Int32

type ScalaType[U <: DType] <: Int | Float = U match
  case Float16 => Float
  case Float32 => Float
  case Int32 => Int

class Tensor[T <: DType](dtype: T):
  def toSeq: Seq[ScalaType[T]] = Seq()
  def toArray: Array[ScalaType[T]] = Array()

@main
def main = 
  val t = Tensor(Float32) // Tensor[Float32]
  println(t.toSeq.headOption) // works, Seq[Float]
  println(t.toArray.headOption) // ClassCastException

Output

java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class [F ([Ljava.lang.Object; and [F are in module java.base of loader 'bootstrap')

Expectation

toArray should return an Array[Float]

@sbrunk sbrunk added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 8, 2022
@smarter
Copy link
Member

smarter commented Jul 8, 2022

The erased type of def toArray: Array[ScalaType[T]] is Object[] instead of Object (which is the only common superclass of int[] and float[]) which is wrong. By contrast, Array[? <: Int | Float] gets the correct erasure.

@smarter smarter added area:erasure prio:blocker and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Jul 8, 2022
@smarter smarter added this to the 3.2.0 backports milestone Jul 8, 2022
@nicolasstucki
Copy link
Contributor

The issue started in ae83f76 of #15606

@smarter
Copy link
Member

smarter commented Jul 8, 2022

Are you sure? This issue also exists in 3.1.3 which doesn't include this PR.

odersky added a commit to dotty-staging/dotty that referenced this issue Jul 8, 2022
 1. The erasure of an array of matchtypes should sometimes be Object
    instead of Object[]
 2. Classtags of matchtypes can be created only if all alternatives
    produce the same classtag.

About 1: If a matchtype with alternative types A_1, ... A_n is an array element,
it should be treated in the same way as the type ? <: A_1 | ... | A_n. It's an
_unknown_ subtype of A_1 | ... | A_n. That can cause the erasure of the underlying
array to be Object.

Fixes scala#15618
Kordyjan pushed a commit to dotty-staging/dotty that referenced this issue Jul 26, 2022
 1. The erasure of an array of matchtypes should sometimes be Object
    instead of Object[]
 2. Classtags of matchtypes can be created only if all alternatives
    produce the same classtag.

About 1: If a matchtype with alternative types A_1, ... A_n is an array element,
it should be treated in the same way as the type ? <: A_1 | ... | A_n. It's an
_unknown_ subtype of A_1 | ... | A_n. That can cause the erasure of the underlying
array to be Object.

Fixes scala#15618
bishabosha pushed a commit to dotty-staging/dotty that referenced this issue Oct 18, 2022
 1. The erasure of an array of matchtypes should sometimes be Object
    instead of Object[]
 2. Classtags of matchtypes can be created only if all alternatives
    produce the same classtag.

About 1: If a matchtype with alternative types A_1, ... A_n is an array element,
it should be treated in the same way as the type ? <: A_1 | ... | A_n. It's an
_unknown_ subtype of A_1 | ... | A_n. That can cause the erasure of the underlying
array to be Object.

Fixes scala#15618
odersky added a commit to dotty-staging/dotty that referenced this issue Jan 17, 2023
The previous check, meant to address scala#15618, tested whether all alternatives
of a match type had the same class tag and only then permitted a classtag
for the match type. This was the wrong test. It did not work for recursive
match types, because it could lead to stack overflow for them. And it did
not take into account that match types could be reduced.

A better test is to simply declare that match types themselves don't have a stable
erasure, just like TypeBounds don't have a stable erasure. If we find an applied
type with a match type as definition, we proceed to its translucent superytype,
which will try a match type reduction. If that succeeds we produce the classtag
of the redux. If not, we end up with an unreduced matchtype and refuse to generate
a classtag for it.

Fixes scala#16706
Fixes scala#16707
odersky added a commit to dotty-staging/dotty that referenced this issue Jan 17, 2023
The previous check, meant to address scala#15618, tested whether all alternatives
of a match type had the same class tag and only then permitted a classtag
for the match type. This was the wrong test. It did not work for recursive
match types, because it could lead to stack overflow for them. And it did
not take into account that match types could be reduced.

A better test is to simply declare that match types themselves don't have a stable
erasure, just like TypeBounds don't have a stable erasure. If we find an applied
type with a match type as definition, we proceed to its translucent superytype,
which will try a match type reduction. If that succeeds we produce the classtag
of the redux. If not, we end up with an unreduced matchtype and refuse to generate
a classtag for it.

Fixes scala#16706
Fixes scala#16707
odersky added a commit that referenced this issue Jan 23, 2023
The previous check, meant to address #15618, tested whether all
alternatives of a match type had the same classtag and only then
permitted a classtag for the match type. This was the wrong test. It did
not work for recursive match types, because it could lead to stack
overflow for them. And it did not take into account that match types
could be reduced.

A better test is to simply declare that match types themselves don't
have a stable erasure, just like TypeBounds don't have a stable erasure.
If we find an applied type with a match type as definition, we proceed
to its translucent supertype, which will try a match type reduction. If
that succeeds we produce the classtag of the redux. If not, we end up
with an unreduced matchtype and refuse to generate a classtag for it.

Fixes #16706
Fixes #16707
@Kordyjan Kordyjan modified the milestones: 3.2.0 backports, 3.2.1 Aug 1, 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.

4 participants