Skip to content

Commit

Permalink
Fix two problems related to match types as array elements
Browse files Browse the repository at this point in the history
 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
  • Loading branch information
odersky committed Jul 8, 2022
1 parent 1724d84 commit 8b66f6e
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 4 deletions.
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ object TypeErasure {
isGenericArrayElement(tp.alias, isScala2)
case tp: TypeBounds =>
!fitsInJVMArray(tp.hi)
case tp: MatchType =>
val alts = tp.alternatives
alts.nonEmpty && !fitsInJVMArray(alts.reduce(OrType(_, _, soft = true)))
case tp: TypeProxy =>
isGenericArrayElement(tp.translucentSuperType, isScala2)
case tp: AndType =>
Expand Down
25 changes: 21 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,28 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
val classTag = ref(defn.ClassTagModule)
val tag =
if defn.SpecialClassTagClasses.contains(sym) then
classTag.select(sym.name.toTermName)
classTag.select(sym.name.toTermName).withSpan(span)
else
val clsOfType = escapeJavaArray(erasure(tp))
classTag.select(nme.apply).appliedToType(tp).appliedTo(clsOf(clsOfType))
withNoErrors(tag.withSpan(span))
def clsOfType(tp: Type): Type =
val tp1 = tp.dealias
if tp1.isMatch then
val matchTp = tp1.underlyingIterator.collect {
case mt: MatchType => mt
}.next
matchTp.alternatives.map(clsOfType) match
case ct1 :: cts if cts.forall(ct1 == _) => ct1
case _ => NoType
else
escapeJavaArray(erasure(tp))
val ctype = clsOfType(tp)
if ctype.exists then
classTag.select(nme.apply)
.appliedToType(tp)
.appliedTo(clsOf(ctype))
.withSpan(span)
else
EmptyTree
withNoErrors(tag)
case tp => EmptyTreeNoError
else EmptyTreeNoError
case _ => EmptyTreeNoError
Expand Down
18 changes: 18 additions & 0 deletions tests/neg/i15618.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
-- Error: tests/neg/i15618.scala:17:44 ---------------------------------------------------------------------------------
17 | def toArray: Array[ScalaType[T]] = Array() // error
| ^
| No ClassTag available for ScalaType[T]
|
| where: T is a type in class Tensor with bounds <: DType
|
|
| Note: a match type could not be fully reduced:
|
| trying to reduce ScalaType[T]
| failed since selector T
| does not match case Float16 => Float
| and cannot be shown to be disjoint from it either.
| Therefore, reduction cannot advance to the remaining cases
|
| case Float32 => Float
| case Int32 => Int
23 changes: 23 additions & 0 deletions tests/neg/i15618.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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() // error

@main
def Test =
val t = Tensor(Float32) // Tensor[Float32]
println(t.toSeq.headOption) // works, Seq[Float]
println(t.toArray.headOption) // ClassCastException
2 changes: 2 additions & 0 deletions tests/run/i15618.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
None
None
24 changes: 24 additions & 0 deletions tests/run/i15618.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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

abstract class Tensor[T <: DType]:
def toArray: Array[ScalaType[T]]

object FloatTensor extends Tensor[Float16]:
def toArray: Array[Float] = Array(1, 2, 3)

@main
def Test =
val t = FloatTensor: Tensor[Float16] // Tensor[Float32]
println(t.toArray.headOption) // was ClassCastException

0 comments on commit 8b66f6e

Please sign in to comment.