Description
dotty.runtime.Arrays
has a bunch of magic methods, including the very magic newRefArray
, which is one of the 3 methods that stay generic after erasure.
It is very easy to get rid of this complexity, by given an actual parameter of type Class[T]
to newRefArray
:
def newRefArray[T <: AnyRef](componentType: Class[T], length: Int): Array[T]
The back-end still needs to know about this magic method, and "assume" that componentType
is a Literal(Constant(tpe))
(i.e., crash if it isn't). But then the method does not need to be generic anymore after erasure, which is a substantial win, IMO.
We can remove even more magic by given actual bodies to all the newXArray
method, leveraging java.lang.reflect.Array.newInstance
:
def newRefArray[T <: AnyRef](componentType: Class[T], length: Int): Array[T] =
java.lang.reflect.Array.newInstance(componentType, length).asInstanceOf[Array[T]]
def newByteArray(length: Int): Array[Byte] =
java.lang.reflect.Array.newInstance(classOf[Byte], length).asInstanceOf[Array[Byte]]
...
def newUnitArray(length: Int): Array[Unit] =
java.lang.reflect.Array.newInstance(classOf[BoxedUnit], length).asInstanceOf[Array[Unit]]
and now the back-end does not need to treat these methods specially. Their user-space implementation is perfectly correct.
Of course, on the JVM, that implementation is inefficient, so the JVM back-end would still specialize them. But other back-ends need not have the same problem. For example, Scala.js does not need to specialize those to get full speed, because Array.newInstance
is intrisified by the optimizer when the componentType
is a literal classOf
.