Skip to content

Commit 91c61e4

Browse files
committed
Previous scheme was buggy; leaked Array types to backend.
Now: All new Array[T] methods are translated to calls of the form dotty.Arrays.newXYZArray ...
1 parent b18ce86 commit 91c61e4

File tree

12 files changed

+121
-15
lines changed

12 files changed

+121
-15
lines changed

src/dotty/runtime/Arrays.scala

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package dotty.runtime
2+
3+
import scala.reflect.ClassTag
4+
5+
/** All but the first operation should be short-circuited and implemented specially by
6+
* the backend.
7+
*/
8+
object Arrays {
9+
10+
/** Creates an array of some element type determined by the given `ClassTag`
11+
* argument. The erased type of applications of this method is `Object`.
12+
*/
13+
def newGenericArray[T](length: Int)(implicit tag: ClassTag[T]): Array[T] =
14+
tag.newArray(length)
15+
16+
/** Create an array of type T. T must be of form Array[E], with
17+
* E being a reference type.
18+
*/
19+
def newRefArray[T](length: Int): T = ???
20+
21+
/** Create a Byte[] array */
22+
def newByteArray(length: Int): Array[Byte] = ???
23+
24+
/** Create a Short[] array */
25+
def newShortArray(length: Int): Array[Short] = ???
26+
27+
/** Create a Char[] array */
28+
def newCharArray(length: Int): Array[Char] = ???
29+
30+
/** Create an Int[] array */
31+
def newIntArray(length: Int): Array[Int] = ???
32+
33+
/** Create a Long[] array */
34+
def newLongArray(length: Int): Array[Long] = ???
35+
36+
/** Create a Float[] array */
37+
def newFloatArray(length: Int): Array[Float] = ???
38+
39+
/** Create a Double[] array */
40+
def newDoubleArray(length: Int): Array[Double] = ???
41+
42+
/** Create a Boolean[] array */
43+
def newBooleanArray(length: Int): Array[Boolean] = ???
44+
45+
/** Create a scala.runtime.BoxedUnit[] array */
46+
def newUnitArray(length: Int): Array[Unit] = ???
47+
}

src/dotty/tools/dotc/TypeErasure.scala

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ object TypeErasure {
142142
tp.derivedPolyType(
143143
tp.paramNames, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType)
144144

145-
if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
145+
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf[PolyType])
146146
else if (sym.isAbstractType) TypeAlias(WildcardType)
147147
else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
148148
else eraseInfo(tp)(erasureCtx) match {
@@ -153,10 +153,24 @@ object TypeErasure {
153153
}
154154
}
155155

156-
def isUnboundedGeneric(tp: Type)(implicit ctx: Context) = !(
157-
(tp derivesFrom defn.ObjectClass) ||
158-
tp.classSymbol.isPrimitiveValueClass ||
159-
(tp.typeSymbol is JavaDefined))
156+
/** Is `tp` an abstract type or polymorphic type parameter that has `Any`
157+
* as upper bound and that is not Java defined? Arrays of such types are
158+
* erased to `Object` instead of `ObjectArray`.
159+
*/
160+
def isUnboundedGeneric(tp: Type)(implicit ctx: Context): Boolean = tp match {
161+
case tp: TypeRef =>
162+
tp.symbol.isAbstractType &&
163+
!tp.derivesFrom(defn.ObjectClass) &&
164+
!tp.typeSymbol.is(JavaDefined)
165+
case tp: PolyParam =>
166+
!tp.derivesFrom(defn.ObjectClass) &&
167+
!tp.binder.resultType.isInstanceOf[JavaMethodType]
168+
case tp: TypeProxy => isUnboundedGeneric(tp.underlying)
169+
case tp: AndType => isUnboundedGeneric(tp.tp1) || isUnboundedGeneric(tp.tp2)
170+
case tp: OrType => isUnboundedGeneric(tp.tp1) && isUnboundedGeneric(tp.tp2)
171+
case _ => false
172+
}
173+
160174

161175
/** The erased least upper bound is computed as follows
162176
* - if both argument are arrays, an array of the lub of the element types

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,24 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
298298
case ConstantType(value) => Literal(value)
299299
}
300300

301+
/** A tree representing a `newXYZArray` operation of the right
302+
* kind for the given element type in `typeArg`. No type arguments or
303+
* `length` arguments are given.
304+
*/
305+
def newArray(typeArg: Tree, pos: Position)(implicit ctx: Context): Tree = {
306+
val elemType = typeArg.tpe
307+
val elemClass = elemType.classSymbol
308+
def newArr(kind: String) =
309+
ref(defn.DottyArraysModule).select(s"new${kind}Array".toTermName).withPos(pos)
310+
if (TypeErasure.isUnboundedGeneric(elemType))
311+
newArr("Generic").appliedToTypeTrees(typeArg :: Nil)
312+
else if (elemClass.isPrimitiveValueClass)
313+
newArr(elemClass.name.toString)
314+
else
315+
newArr("Ref").appliedToTypeTrees(
316+
TypeTree(defn.ArrayType(elemType)).withPos(typeArg.pos) :: Nil)
317+
}
318+
301319
// ------ Creating typed equivalents of trees that exist only in untyped form -------
302320

303321
/** new C(args), calling the primary constructor of C */
@@ -678,7 +696,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
678696
Throw(New(defn.ClassCastExceptionClass.typeRef, Nil)) withPos tree.pos
679697
}
680698
}
681-
699+
682700
def applyOverloaded(receiver: Tree, method: TermName, args: List[Tree], targs: List[Type], expectedType: Type, isAnnotConstructor: Boolean = false)(implicit ctx: Context): Tree = {
683701
val typer = ctx.typer
684702
val proto = new FunProtoTyped(args, expectedType, typer)

src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ object Contexts {
120120
protected def scope_=(scope: Scope) = _scope = scope
121121
def scope: Scope = _scope
122122

123-
/** The current type assigner ot typer */
123+
/** The current type assigner or typer */
124124
private[this] var _typeAssigner: TypeAssigner = _
125125
protected def typeAssigner_=(typeAssigner: TypeAssigner) = _typeAssigner = typeAssigner
126126
def typeAssigner: TypeAssigner = _typeAssigner

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,10 @@ class Definitions {
193193
def staticsMethod(name: PreName) = ctx.requiredMethod(ScalaStaticsClass, name)
194194

195195
lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef")
196+
lazy val DottyArraysModule = ctx.requiredModule("dotty.runtime.Arrays")
197+
198+
def newRefArrayMethod = ctx.requiredMethod(DottyArraysModule.moduleClass.asClass, "newRefArray")
199+
196200
lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil")
197201
lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms)
198202

@@ -211,6 +215,7 @@ class Definitions {
211215
lazy val Array_update = ctx.requiredMethod(ArrayClass, nme.update)
212216
lazy val Array_length = ctx.requiredMethod(ArrayClass, nme.length)
213217
lazy val Array_clone = ctx.requiredMethod(ArrayClass, nme.clone_)
218+
lazy val ArrayConstructor = ctx.requiredMethod(ArrayClass, nme.CONSTRUCTOR)
214219
lazy val traversableDropMethod = ctx.requiredMethod(ScalaRuntimeClass, nme.drop)
215220
lazy val uncheckedStableClass: ClassSymbol = ctx.requiredClass("scala.annotation.unchecked.uncheckedStable")
216221

@@ -428,6 +433,8 @@ class Definitions {
428433

429434
lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
430435

436+
lazy val isPolymorphicAfterErasure = Set[Symbol](Any_isInstanceOf, Any_asInstanceOf, newRefArrayMethod)
437+
431438
lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule)
432439

433440
lazy val overriddenBySynthetic = Set[Symbol](Any_equals, Any_hashCode, Any_toString, Product_canEqual)

src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ object NameOps {
204204
case nme.length => nme.primitive.arrayLength
205205
case nme.update => nme.primitive.arrayUpdate
206206
case nme.clone_ => nme.clone_
207-
case nme.CONSTRUCTOR => nme.primitive.arrayConstructor
208207
}
209208

210209
/** If name length exceeds allowable limit, replace part of it by hash */

src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,6 @@ object StdNames {
433433
val moduleClass : N = "moduleClass"
434434
val name: N = "name"
435435
val ne: N = "ne"
436-
val newArray: N = "newArray"
437436
val newFreeTerm: N = "newFreeTerm"
438437
val newFreeType: N = "newFreeType"
439438
val newNestedSymbol: N = "newNestedSymbol"
@@ -691,8 +690,7 @@ object StdNames {
691690
val arrayApply: TermName = "[]apply"
692691
val arrayUpdate: TermName = "[]update"
693692
val arrayLength: TermName = "[]length"
694-
val arrayConstructor: TermName = "[]<init>"
695-
val names: Set[Name] = Set(arrayApply, arrayUpdate, arrayLength, arrayConstructor)
693+
val names: Set[Name] = Set(arrayApply, arrayUpdate, arrayLength)
696694
}
697695

698696
def isPrimitiveName(name: Name) = primitive.names.contains(name)

src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
8989
*/
9090
def assertErased(tree: tpd.Tree)(implicit ctx: Context): Unit = {
9191
assertErased(tree.typeOpt, tree)
92-
if (!(tree.symbol == defn.Any_isInstanceOf || tree.symbol == defn.Any_asInstanceOf))
92+
if (!defn.isPolymorphicAfterErasure(tree.symbol))
9393
assertErased(tree.typeOpt.widen, tree)
9494
if (ctx.mode.isExpr)
9595
tree.tpe match {

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,16 @@ trait Applications extends Compatibility { self: Typer =>
596596
checkBounds(typedArgs, pt)
597597
case _ =>
598598
}
599-
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs)
599+
convertNewArray(
600+
assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs))
601+
}
602+
603+
/** Rewrite `new Array[T]` trees to calls of newXYZArray methods. */
604+
def convertNewArray(tree: Tree)(implicit ctx: Context): Tree = tree match {
605+
case TypeApply(tycon, targs) if tycon.symbol == defn.ArrayConstructor =>
606+
newArray(targs.head, tree.pos)
607+
case _ =>
608+
tree
600609
}
601610

602611
def typedUnApply(tree: untpd.Apply, selType: Type)(implicit ctx: Context): Tree = track("typedUnApply") {

src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,6 @@ trait TypeAssigner {
208208
case p.arrayApply => MethodType(defn.IntType :: Nil, arrayElemType)
209209
case p.arrayUpdate => MethodType(defn.IntType :: arrayElemType :: Nil, defn.UnitType)
210210
case p.arrayLength => MethodType(Nil, defn.IntType)
211-
case p.arrayConstructor => MethodType(defn.IntType :: Nil, qualType)
212211
case nme.clone_ if qualType.isInstanceOf[JavaArrayType] => MethodType(Nil, qualType)
213212
case _ => accessibleSelectionType(tree, qual)
214213
}

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1326,7 +1326,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
13261326
if (pt.isInstanceOf[PolyProto]) tree
13271327
else {
13281328
val (_, tvars) = constrained(poly, tree)
1329-
adaptInterpolated(tree appliedToTypes tvars, pt, original)
1329+
convertNewArray(
1330+
adaptInterpolated(tree.appliedToTypes(tvars), pt, original))
13301331
}
13311332
case wtp =>
13321333
pt match {

tests/pos/new-array.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object Test {
2+
val w = new Array[String](10)
3+
val x = new Array[Int](10)
4+
def f[T: reflect.ClassTag] = new Array[T](10)
5+
val y = new Array[Any](10)
6+
val z = new Array[Unit](10)
7+
}
8+
object Test2 {
9+
val w: Array[String] = new Array(10)
10+
val x: Array[Int] = new Array(10)
11+
def f[T: reflect.ClassTag]: Array[T] = new Array(10)
12+
val y: Array[Any] = new Array(10)
13+
val z: Array[Unit] = new Array(10)
14+
}

0 commit comments

Comments
 (0)