Skip to content

Commit 8b66f6e

Browse files
committed
Fix two problems related to match types as array elements
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 #15618
1 parent 1724d84 commit 8b66f6e

File tree

6 files changed

+91
-4
lines changed

6 files changed

+91
-4
lines changed

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,9 @@ object TypeErasure {
328328
isGenericArrayElement(tp.alias, isScala2)
329329
case tp: TypeBounds =>
330330
!fitsInJVMArray(tp.hi)
331+
case tp: MatchType =>
332+
val alts = tp.alternatives
333+
alts.nonEmpty && !fitsInJVMArray(alts.reduce(OrType(_, _, soft = true)))
331334
case tp: TypeProxy =>
332335
isGenericArrayElement(tp.translucentSuperType, isScala2)
333336
case tp: AndType =>

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,28 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
4040
val classTag = ref(defn.ClassTagModule)
4141
val tag =
4242
if defn.SpecialClassTagClasses.contains(sym) then
43-
classTag.select(sym.name.toTermName)
43+
classTag.select(sym.name.toTermName).withSpan(span)
4444
else
45-
val clsOfType = escapeJavaArray(erasure(tp))
46-
classTag.select(nme.apply).appliedToType(tp).appliedTo(clsOf(clsOfType))
47-
withNoErrors(tag.withSpan(span))
45+
def clsOfType(tp: Type): Type =
46+
val tp1 = tp.dealias
47+
if tp1.isMatch then
48+
val matchTp = tp1.underlyingIterator.collect {
49+
case mt: MatchType => mt
50+
}.next
51+
matchTp.alternatives.map(clsOfType) match
52+
case ct1 :: cts if cts.forall(ct1 == _) => ct1
53+
case _ => NoType
54+
else
55+
escapeJavaArray(erasure(tp))
56+
val ctype = clsOfType(tp)
57+
if ctype.exists then
58+
classTag.select(nme.apply)
59+
.appliedToType(tp)
60+
.appliedTo(clsOf(ctype))
61+
.withSpan(span)
62+
else
63+
EmptyTree
64+
withNoErrors(tag)
4865
case tp => EmptyTreeNoError
4966
else EmptyTreeNoError
5067
case _ => EmptyTreeNoError

tests/neg/i15618.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
-- Error: tests/neg/i15618.scala:17:44 ---------------------------------------------------------------------------------
2+
17 | def toArray: Array[ScalaType[T]] = Array() // error
3+
| ^
4+
| No ClassTag available for ScalaType[T]
5+
|
6+
| where: T is a type in class Tensor with bounds <: DType
7+
|
8+
|
9+
| Note: a match type could not be fully reduced:
10+
|
11+
| trying to reduce ScalaType[T]
12+
| failed since selector T
13+
| does not match case Float16 => Float
14+
| and cannot be shown to be disjoint from it either.
15+
| Therefore, reduction cannot advance to the remaining cases
16+
|
17+
| case Float32 => Float
18+
| case Int32 => Int

tests/neg/i15618.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
sealed abstract class DType
2+
sealed class Float16 extends DType
3+
sealed class Float32 extends DType
4+
sealed class Int32 extends DType
5+
6+
object Float16 extends Float16
7+
object Float32 extends Float32
8+
object Int32 extends Int32
9+
10+
type ScalaType[U <: DType] <: Int | Float = U match
11+
case Float16 => Float
12+
case Float32 => Float
13+
case Int32 => Int
14+
15+
class Tensor[T <: DType](dtype: T):
16+
def toSeq: Seq[ScalaType[T]] = Seq()
17+
def toArray: Array[ScalaType[T]] = Array() // error
18+
19+
@main
20+
def Test =
21+
val t = Tensor(Float32) // Tensor[Float32]
22+
println(t.toSeq.headOption) // works, Seq[Float]
23+
println(t.toArray.headOption) // ClassCastException

tests/run/i15618.check

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
None
2+
None

tests/run/i15618.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
sealed abstract class DType
2+
sealed class Float16 extends DType
3+
sealed class Float32 extends DType
4+
sealed class Int32 extends DType
5+
6+
object Float16 extends Float16
7+
object Float32 extends Float32
8+
object Int32 extends Int32
9+
10+
type ScalaType[U <: DType] <: Int | Float = U match
11+
case Float16 => Float
12+
case Float32 => Float
13+
case Int32 => Int
14+
15+
abstract class Tensor[T <: DType]:
16+
def toArray: Array[ScalaType[T]]
17+
18+
object FloatTensor extends Tensor[Float16]:
19+
def toArray: Array[Float] = Array(1, 2, 3)
20+
21+
@main
22+
def Test =
23+
val t = FloatTensor: Tensor[Float16] // Tensor[Float32]
24+
println(t.toArray.headOption) // was ClassCastException

0 commit comments

Comments
 (0)