Skip to content

Commit 247e913

Browse files
committed
Merge pull request #1188 from dotty-staging/remove-newarray-magic
Fix #1167: Reduce the magic in Arrays.newRefArray. Implement multidimensional arrays
2 parents 8c9a3f7 + 5399fbe commit 247e913

File tree

14 files changed

+171
-117
lines changed

14 files changed

+171
-117
lines changed

project/Build.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ object DottyBuild extends Build {
7777
com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys.withSource := true,
7878

7979
// get libraries onboard
80-
partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20151022-113908-7fb0e653fd",
80+
partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20160322-171045-e19b30b3cd",
8181
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
8282
"org.scala-lang" % "scala-library" % scalaVersion.value % "test"),
8383
libraryDependencies ++= partestDeps.value,

src/dotty/runtime/Arrays.scala

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package dotty.runtime
22

33
import scala.reflect.ClassTag
44

5+
import java.lang.{reflect => jlr}
6+
57
/** All but the first two operations should be short-circuited and implemented specially by
68
* the backend.
79
*/
@@ -22,35 +24,8 @@ object Arrays {
2224
arr
2325
}
2426

25-
/** Create an array of type T. T must be of form Array[E], with
26-
* E being a reference type.
27+
/** Create an array of a reference type T.
2728
*/
28-
def newRefArray[T](length: Int): T = ???
29-
30-
/** Create a Byte[] array */
31-
def newByteArray(length: Int): Array[Byte] = ???
32-
33-
/** Create a Short[] array */
34-
def newShortArray(length: Int): Array[Short] = ???
35-
36-
/** Create a Char[] array */
37-
def newCharArray(length: Int): Array[Char] = ???
38-
39-
/** Create an Int[] array */
40-
def newIntArray(length: Int): Array[Int] = ???
41-
42-
/** Create a Long[] array */
43-
def newLongArray(length: Int): Array[Long] = ???
44-
45-
/** Create a Float[] array */
46-
def newFloatArray(length: Int): Array[Float] = ???
47-
48-
/** Create a Double[] array */
49-
def newDoubleArray(length: Int): Array[Double] = ???
50-
51-
/** Create a Boolean[] array */
52-
def newBooleanArray(length: Int): Array[Boolean] = ???
53-
54-
/** Create a scala.runtime.BoxedUnit[] array */
55-
def newUnitArray(length: Int): Array[Unit] = ???
29+
def newArray[Arr](componentType: Class[_], returnType: Class[Arr], dimensions: Array[Int]): Arr =
30+
jlr.Array.newInstance(componentType, dimensions: _*).asInstanceOf[Arr]
5631
}

src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
153153
}.toMap
154154
def unboxMethods: Map[Symbol, Symbol] = defn.ScalaValueClasses().map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap
155155

156-
private val mkArrayNames: Set[Name] = Set("Byte", "Float", "Char", "Double", "Boolean", "Unit", "Long", "Int", "Short", "Ref").map{ x=>
157-
("new" + x + "Array").toTermName
158-
}
159-
160-
val dottyArraysModuleClass = toDenot(defn.DottyArraysModule).moduleClass.asClass
161-
162-
163156
override def isSyntheticArrayConstructor(s: Symbol) = {
164-
(toDenot(s).maybeOwner eq dottyArraysModuleClass) && mkArrayNames.contains(s.name)
157+
s eq defn.newArrayMethod
165158
}
166159

167160
def isBox(sym: Symbol): Boolean = Erasure.Boxing.isBox(sym)

src/dotty/tools/backend/sjs/JSCodeGen.scala

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -718,9 +718,9 @@ class JSCodeGen()(implicit ctx: Context) {
718718
if (sym.is(Module)) {
719719
assert(!sym.is(Package), "Cannot use package as value: " + tree)
720720
genLoadModule(sym)
721-
} else /*if (sym.isStaticMember) {
722-
genStaticMember(sym)
723-
} else if (paramAccessorLocals contains sym) {
721+
} else if (sym.is(JavaStatic)) {
722+
genLoadStaticField(sym)
723+
} else /*if (paramAccessorLocals contains sym) {
724724
paramAccessorLocals(sym).ref
725725
} else if (isScalaJSDefinedJSClass(sym.owner)) {
726726
val genQual = genExpr(qualifier)
@@ -1036,8 +1036,6 @@ class JSCodeGen()(implicit ctx: Context) {
10361036
genStringConcat(tree, receiver, args)
10371037
else if (code == HASH)
10381038
genScalaHash(tree, receiver)
1039-
else if (isArrayNew(code))
1040-
genArrayNew(tree, code)
10411039
else if (isArrayOp(code))
10421040
genArrayOp(tree, code)
10431041
else if (code == SYNCHRONIZED)
@@ -1409,24 +1407,6 @@ class JSCodeGen()(implicit ctx: Context) {
14091407
List(genExpr(receiver)))
14101408
}
14111409

1412-
/** Gen JS code for a new array operation. */
1413-
private def genArrayNew(tree: Tree, code: Int): js.Tree = {
1414-
import scala.tools.nsc.backend.ScalaPrimitives._
1415-
1416-
implicit val pos: Position = tree.pos
1417-
1418-
val Apply(fun, args) = tree
1419-
val genLength = genExpr(args.head)
1420-
1421-
toIRType(tree.tpe) match {
1422-
case arrayType: jstpe.ArrayType =>
1423-
js.NewArray(arrayType, List(genLength))
1424-
1425-
case irTpe =>
1426-
throw new FatalError(s"ArrayNew $tree must have an array type but was $irTpe")
1427-
}
1428-
}
1429-
14301410
/** Gen JS code for an array operation (get, set or length) */
14311411
private def genArrayOp(tree: Tree, code: Int): js.Tree = {
14321412
import scala.tools.nsc.backend.ScalaPrimitives._
@@ -2328,6 +2308,24 @@ class JSCodeGen()(implicit ctx: Context) {
23282308
}
23292309
}
23302310

2311+
/** Gen JS code for loading a Java static field.
2312+
*/
2313+
private def genLoadStaticField(sym: Symbol)(implicit pos: Position): js.Tree = {
2314+
/* Actually, there is no static member in Scala.js. If we come here, that
2315+
* is because we found the symbol in a Java-emitted .class in the
2316+
* classpath. But the corresponding implementation in Scala.js will
2317+
* actually be a val in the companion module.
2318+
*/
2319+
2320+
if (sym == defn.BoxedUnit_UNIT) {
2321+
js.Undefined()
2322+
} else {
2323+
val instance = genLoadModule(sym.owner)
2324+
val method = encodeStaticMemberSym(sym)
2325+
js.Apply(instance, method, Nil)(toIRType(sym.info))
2326+
}
2327+
}
2328+
23312329
/** Gen JS code for loading a module.
23322330
*
23332331
* Can be given either the module symbol, or its module class symbol.

src/dotty/tools/backend/sjs/JSPrimitives.scala

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,6 @@ class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
8080

8181
val jsdefn = JSDefinitions.jsdefn
8282

83-
// For some reason, the JVM primitive set does not register those
84-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newBooleanArray")), NEW_ZARRAY)
85-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newByteArray")), NEW_BARRAY)
86-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newShortArray")), NEW_SARRAY)
87-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newCharArray")), NEW_CARRAY)
88-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newIntArray")), NEW_IARRAY)
89-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newLongArray")), NEW_LARRAY)
90-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newFloatArray")), NEW_FARRAY)
91-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newDoubleArray")), NEW_DARRAY)
92-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newRefArray")), NEW_OARRAY)
93-
addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newUnitArray")), NEW_OARRAY)
94-
9583
addPrimitive(defn.Any_getClass, GETCLASS)
9684

9785
for (i <- 0 to 22)

src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ class Compiler {
6666
new Getters, // Replace non-private vals and vars with getter defs (fields are added later)
6767
new ElimByName, // Expand by-name parameters and arguments
6868
new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings
69-
new ResolveSuper), // Implement super accessors and add forwarders to trait methods
69+
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
70+
new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify.
7071
List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
7172
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types
7273
new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations

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

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
125125
def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral =
126126
ta.assignType(untpd.SeqLiteral(elems, elemtpt), elems, elemtpt)
127127

128-
def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral =
129-
ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt)
128+
def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): JavaSeqLiteral =
129+
ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt).asInstanceOf[JavaSeqLiteral]
130130

131131
def TypeTree(original: Tree)(implicit ctx: Context): TypeTree =
132132
TypeTree(original.tpe, original)
@@ -362,18 +362,16 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
362362
* kind for the given element type in `typeArg`. No type arguments or
363363
* `length` arguments are given.
364364
*/
365-
def newArray(typeArg: Tree, pos: Position)(implicit ctx: Context): Tree = {
366-
val elemType = typeArg.tpe
367-
val elemClass = elemType.classSymbol
368-
def newArr(kind: String) =
369-
ref(defn.DottyArraysModule).select(s"new${kind}Array".toTermName).withPos(pos)
370-
if (TypeErasure.isUnboundedGeneric(elemType))
371-
newArr("Generic").appliedToTypeTrees(typeArg :: Nil)
372-
else if (elemClass.isPrimitiveValueClass)
373-
newArr(elemClass.name.toString)
374-
else
375-
newArr("Ref").appliedToTypeTrees(
376-
TypeTree(defn.ArrayOf(elemType)).withPos(typeArg.pos) :: Nil)
365+
def newArray(elemTpe: Type, returnTpe: Type, pos: Position, dims: JavaSeqLiteral)(implicit ctx: Context): Tree = {
366+
val elemClass = elemTpe.classSymbol
367+
def newArr =
368+
ref(defn.DottyArraysModule).select(defn.newArrayMethod).withPos(pos)
369+
370+
if (!ctx.erasedTypes) {
371+
assert(!TypeErasure.isUnboundedGeneric(elemTpe)) //needs to be done during typer. See Applications.convertNewGenericArray
372+
newArr.appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos)
373+
} else // after erasure
374+
newArr.appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos)
377375
}
378376

379377
// ------ Creating typed equivalents of trees that exist only in untyped form -------
@@ -835,7 +833,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
835833
case tpnme.Float => TYPE(defn.BoxedFloatModule)
836834
case tpnme.Double => TYPE(defn.BoxedDoubleModule)
837835
case tpnme.Unit => TYPE(defn.BoxedUnitModule)
838-
case _ => Literal(Constant(TypeErasure.erasure(tp)))
836+
case _ =>
837+
if(ctx.erasedTypes || !tp.derivesFrom(defn.ArrayClass))
838+
Literal(Constant(TypeErasure.erasure(tp)))
839+
else Literal(Constant(tp))
839840
}
840841
}
841842

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -246,8 +246,8 @@ class Definitions {
246246
def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol
247247
lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays")
248248
def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol
249-
250-
def newRefArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newRefArray")
249+
def newGenericArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newGenericArray")
250+
def newArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newArray")
251251

252252
lazy val NilModuleRef = ctx.requiredModuleRef("scala.collection.immutable.Nil")
253253
def NilModule(implicit ctx: Context) = NilModuleRef.symbol
@@ -279,6 +279,9 @@ class Definitions {
279279
def Array_clone(implicit ctx: Context) = Array_cloneR.symbol
280280
lazy val ArrayConstructorR = ArrayClass.requiredMethodRef(nme.CONSTRUCTOR)
281281
def ArrayConstructor(implicit ctx: Context) = ArrayConstructorR.symbol
282+
lazy val ArrayModuleType = ctx.requiredModuleRef("scala.Array")
283+
def ArrayModule(implicit ctx: Context) = ArrayModuleType.symbol.moduleClass.asClass
284+
282285

283286
lazy val UnitType: TypeRef = valueTypeRef("scala.Unit", BoxedUnitType, java.lang.Void.TYPE, UnitEnc)
284287
def UnitClass(implicit ctx: Context) = UnitType.symbol.asClass
@@ -622,7 +625,7 @@ class Definitions {
622625
lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
623626

624627
def isPolymorphicAfterErasure(sym: Symbol) =
625-
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) || (sym eq newRefArrayMethod)
628+
(sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
626629

627630
def isTupleType(tp: Type)(implicit ctx: Context) = {
628631
val arity = tp.dealias.argInfos.length
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import TreeTransforms._
6+
import Contexts.Context
7+
import Flags._
8+
import SymUtils._
9+
import Symbols._
10+
import SymDenotations._
11+
import Types._
12+
import Decorators._
13+
import DenotTransformers._
14+
import StdNames._
15+
import NameOps._
16+
import ast.Trees._
17+
import dotty.tools.dotc.ast.tpd
18+
import util.Positions._
19+
import Names._
20+
21+
import collection.mutable
22+
import ResolveSuper._
23+
24+
import scala.collection.immutable.::
25+
26+
27+
/** This phase rewrites calls to array constructors to newArray method in Dotty.runtime.Arrays module.
28+
*
29+
* It assummes that generic arrays have already been handled by typer(see Applications.convertNewGenericArray).
30+
* Additionally it optimizes calls to scala.Array.ofDim functions by replacing them with calls to newArray with specific dimensions
31+
*/
32+
class ArrayConstructors extends MiniPhaseTransform { thisTransform =>
33+
import ast.tpd._
34+
35+
override def phaseName: String = "arrayConstructors"
36+
37+
override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
38+
def rewrite(elemType: Type, dims: List[Tree]) =
39+
tpd.newArray(elemType, tree.tpe, tree.pos, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef)))
40+
41+
if (tree.fun.symbol eq defn.ArrayConstructor) {
42+
val TypeApply(tycon, targ :: Nil) = tree.fun
43+
rewrite(targ.tpe, tree.args)
44+
} else if ((tree.fun.symbol.maybeOwner eq defn.ArrayModule) && (tree.fun.symbol.name eq nme.ofDim) && !tree.tpe.isInstanceOf[MethodicType]) {
45+
46+
tree.fun match {
47+
case Apply(TypeApply(t: Ident, targ), dims) if !TypeErasure.isUnboundedGeneric(targ.head.tpe) =>
48+
rewrite(targ.head.tpe, dims)
49+
case Apply(TypeApply(t: Select, targ), dims) if !TypeErasure.isUnboundedGeneric(targ.head.tpe) =>
50+
Block(t.qualifier :: Nil, rewrite(targ.head.tpe, dims))
51+
case _ => tree
52+
}
53+
54+
} else tree
55+
}
56+
}
57+

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

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import core.Decorators._
2121
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
2222
import ast.Trees._
2323
import scala.collection.mutable.ListBuffer
24-
import dotty.tools.dotc.core.Flags
24+
import dotty.tools.dotc.core.{Constants, Flags}
2525
import ValueClasses._
2626
import TypeUtils._
2727
import ExplicitOuter._
@@ -299,8 +299,9 @@ object Erasure extends TypeTestsCasts{
299299
assignType(untpd.cpy.Typed(tree)(expr1, tpt1), tpt1)
300300
}
301301

302-
override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal =
302+
override def typedLiteral(tree: untpd.Literal)(implicit ctx: Context): Literal =
303303
if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt)
304+
else if (tree.const.tag == Constants.ClazzTag) Literal(Constant(erasure(tree.const.typeValue)))
304305
else super.typedLiteral(tree)
305306

306307
/** Type check select nodes, applying the following rewritings exhaustively
@@ -467,28 +468,18 @@ object Erasure extends TypeTestsCasts{
467468
tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym)
468469

469470
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = {
470-
var effectiveSym = sym
471-
if (sym == defn.newRefArrayMethod) {
472-
// newRefArray is treated specially: It's the only source-defined method
473-
// that has a polymorphic type after erasure. But treating its (dummy) definition
474-
// with a polymorphic type at and after erasure is an awkward special case.
475-
// We therefore rewrite the method definition with a new Symbol of type
476-
// (length: Int)Object
477-
val MethodType(pnames, ptypes) = sym.info.resultType
478-
effectiveSym = sym.copy(info = MethodType(pnames, ptypes, defn.ObjectType))
479-
}
480471
val restpe =
481-
if (effectiveSym.isConstructor) defn.UnitType
482-
else effectiveSym.info.resultType
472+
if (sym.isConstructor) defn.UnitType
473+
else sym.info.resultType
483474
val ddef1 = untpd.cpy.DefDef(ddef)(
484475
tparams = Nil,
485-
vparamss = (outer.paramDefs(effectiveSym) ::: ddef.vparamss.flatten) :: Nil,
476+
vparamss = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil,
486477
tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)),
487478
rhs = ddef.rhs match {
488479
case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe))
489480
case _ => ddef.rhs
490481
})
491-
super.typedDefDef(ddef1, effectiveSym)
482+
super.typedDefDef(ddef1, sym)
492483
}
493484

494485
/** After erasure, we may have to replace the closure method by a bridge.

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,8 +359,7 @@ class TreeChecker extends Phase with SymTransformer {
359359
def isNonMagicalMethod(x: Symbol) =
360360
x.is(Method) &&
361361
!x.isCompanionMethod &&
362-
!x.isValueClassConvertMethod &&
363-
x != defn.newRefArrayMethod
362+
!x.isValueClassConvertMethod
364363

365364
val symbolsNotDefined = cls.classInfo.decls.toSet.filter(isNonMagicalMethod) -- impl.body.map(_.symbol) - constr.symbol
366365

0 commit comments

Comments
 (0)