aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Doeraene <sjrdoeraene@gmail.com>2016-03-23 11:51:25 +0100
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2016-04-18 14:44:23 +0200
commit27deb1e0d41a4dfc24a4cac8a311c30471d454e5 (patch)
treeef9001d9b1bf141f4e4887d1525315f8d20e404b
parent88baa04f2bc6b89d3e65226973d7b72fdf715095 (diff)
downloaddotty-27deb1e0d41a4dfc24a4cac8a311c30471d454e5.tar.gz
dotty-27deb1e0d41a4dfc24a4cac8a311c30471d454e5.tar.bz2
dotty-27deb1e0d41a4dfc24a4cac8a311c30471d454e5.zip
Fix #1167: Remove the magic from Arrays.newRefArray.
Previously, the method `Arrays.newRefArray` was one of the only 3 methods that are kept generic after erasure. This commit removes this magic, by making it take an actual `j.l.Class[T]` as parameter. Moreover, the methods `newXArray` all receive an actual body, implemented on top of Java reflection, which means that a back-end does not *have to* special-case those methods for correctness. It might still be required for performance, though, depending on the back-end. The JVM back-end is made non-optimal in this commit, precisely because it does not specialize that method anymore. Doing so requires modifying the fork of scalac that we use, which should be done separately. The JS back-end is adapted simply by doing nothing at all on any of the newXArray methods. It will normally call the user-space implementations which use reflection. The Scala.js optimizer will inline and intrinsify the reflective calls, producing optimal code, at the end of the day.
-rw-r--r--src/dotty/runtime/Arrays.scala35
-rw-r--r--src/dotty/tools/backend/jvm/DottyBackendInterface.scala2
-rw-r--r--src/dotty/tools/backend/sjs/JSCodeGen.scala20
-rw-r--r--src/dotty/tools/backend/sjs/JSPrimitives.scala12
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala9
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala4
-rw-r--r--src/dotty/tools/dotc/transform/Erasure.scala18
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala3
8 files changed, 36 insertions, 67 deletions
diff --git a/src/dotty/runtime/Arrays.scala b/src/dotty/runtime/Arrays.scala
index 4469dced7..2771df66b 100644
--- a/src/dotty/runtime/Arrays.scala
+++ b/src/dotty/runtime/Arrays.scala
@@ -2,6 +2,8 @@ package dotty.runtime
import scala.reflect.ClassTag
+import java.lang.{reflect => jlr}
+
/** All but the first two operations should be short-circuited and implemented specially by
* the backend.
*/
@@ -22,35 +24,44 @@ object Arrays {
arr
}
- /** Create an array of type T. T must be of form Array[E], with
- * E being a reference type.
+ /** Create an array of a reference type T.
*/
- def newRefArray[T](length: Int): T = ???
+ def newRefArray[T](componentType: Class[T])(length: Int): Array[T] =
+ jlr.Array.newInstance(componentType, length).asInstanceOf[Array[T]]
/** Create a Byte[] array */
- def newByteArray(length: Int): Array[Byte] = ???
+ def newByteArray(length: Int): Array[Byte] =
+ jlr.Array.newInstance(classOf[Byte], length).asInstanceOf[Array[Byte]]
/** Create a Short[] array */
- def newShortArray(length: Int): Array[Short] = ???
+ def newShortArray(length: Int): Array[Short] =
+ jlr.Array.newInstance(classOf[Short], length).asInstanceOf[Array[Short]]
/** Create a Char[] array */
- def newCharArray(length: Int): Array[Char] = ???
+ def newCharArray(length: Int): Array[Char] =
+ jlr.Array.newInstance(classOf[Char], length).asInstanceOf[Array[Char]]
/** Create an Int[] array */
- def newIntArray(length: Int): Array[Int] = ???
+ def newIntArray(length: Int): Array[Int] =
+ jlr.Array.newInstance(classOf[Int], length).asInstanceOf[Array[Int]]
/** Create a Long[] array */
- def newLongArray(length: Int): Array[Long] = ???
+ def newLongArray(length: Int): Array[Long] =
+ jlr.Array.newInstance(classOf[Long], length).asInstanceOf[Array[Long]]
/** Create a Float[] array */
- def newFloatArray(length: Int): Array[Float] = ???
+ def newFloatArray(length: Int): Array[Float] =
+ jlr.Array.newInstance(classOf[Float], length).asInstanceOf[Array[Float]]
/** Create a Double[] array */
- def newDoubleArray(length: Int): Array[Double] = ???
+ def newDoubleArray(length: Int): Array[Double] =
+ jlr.Array.newInstance(classOf[Double], length).asInstanceOf[Array[Double]]
/** Create a Boolean[] array */
- def newBooleanArray(length: Int): Array[Boolean] = ???
+ def newBooleanArray(length: Int): Array[Boolean] =
+ jlr.Array.newInstance(classOf[Boolean], length).asInstanceOf[Array[Boolean]]
/** Create a scala.runtime.BoxedUnit[] array */
- def newUnitArray(length: Int): Array[Unit] = ???
+ def newUnitArray(length: Int): Array[Unit] =
+ jlr.Array.newInstance(classOf[scala.runtime.BoxedUnit], length).asInstanceOf[Array[Unit]]
}
diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
index ef8e4997f..6e4431b1a 100644
--- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
+++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
@@ -153,7 +153,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
}.toMap
def unboxMethods: Map[Symbol, Symbol] = defn.ScalaValueClasses().map(x => (x, Erasure.Boxing.unboxMethod(x.asClass))).toMap
- private val mkArrayNames: Set[Name] = Set("Byte", "Float", "Char", "Double", "Boolean", "Unit", "Long", "Int", "Short", "Ref").map{ x=>
+ private val mkArrayNames: Set[Name] = Set("Byte", "Float", "Char", "Double", "Boolean", "Unit", "Long", "Int", "Short"/*, "Ref"*/).map{ x=>
("new" + x + "Array").toTermName
}
diff --git a/src/dotty/tools/backend/sjs/JSCodeGen.scala b/src/dotty/tools/backend/sjs/JSCodeGen.scala
index 70c5af1e7..401e01784 100644
--- a/src/dotty/tools/backend/sjs/JSCodeGen.scala
+++ b/src/dotty/tools/backend/sjs/JSCodeGen.scala
@@ -1036,8 +1036,6 @@ class JSCodeGen()(implicit ctx: Context) {
genStringConcat(tree, receiver, args)
else if (code == HASH)
genScalaHash(tree, receiver)
- else if (isArrayNew(code))
- genArrayNew(tree, code)
else if (isArrayOp(code))
genArrayOp(tree, code)
else if (code == SYNCHRONIZED)
@@ -1409,24 +1407,6 @@ class JSCodeGen()(implicit ctx: Context) {
List(genExpr(receiver)))
}
- /** Gen JS code for a new array operation. */
- private def genArrayNew(tree: Tree, code: Int): js.Tree = {
- import scala.tools.nsc.backend.ScalaPrimitives._
-
- implicit val pos: Position = tree.pos
-
- val Apply(fun, args) = tree
- val genLength = genExpr(args.head)
-
- toIRType(tree.tpe) match {
- case arrayType: jstpe.ArrayType =>
- js.NewArray(arrayType, List(genLength))
-
- case irTpe =>
- throw new FatalError(s"ArrayNew $tree must have an array type but was $irTpe")
- }
- }
-
/** Gen JS code for an array operation (get, set or length) */
private def genArrayOp(tree: Tree, code: Int): js.Tree = {
import scala.tools.nsc.backend.ScalaPrimitives._
diff --git a/src/dotty/tools/backend/sjs/JSPrimitives.scala b/src/dotty/tools/backend/sjs/JSPrimitives.scala
index 52b5dc4b9..6c3c5715c 100644
--- a/src/dotty/tools/backend/sjs/JSPrimitives.scala
+++ b/src/dotty/tools/backend/sjs/JSPrimitives.scala
@@ -80,18 +80,6 @@ class JSPrimitives(ctx: Context) extends DottyPrimitives(ctx) {
val jsdefn = JSDefinitions.jsdefn
- // For some reason, the JVM primitive set does not register those
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newBooleanArray")), NEW_ZARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newByteArray")), NEW_BARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newShortArray")), NEW_SARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newCharArray")), NEW_CARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newIntArray")), NEW_IARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newLongArray")), NEW_LARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newFloatArray")), NEW_FARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newDoubleArray")), NEW_DARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newRefArray")), NEW_OARRAY)
- addPrimitive(defn.DottyArraysModule.requiredMethod(Names.termName("newUnitArray")), NEW_OARRAY)
-
addPrimitive(defn.Any_getClass, GETCLASS)
for (i <- 0 to 22)
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index 8d21953ae..a58ff7f51 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -371,9 +371,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
newArr("Generic").appliedToTypeTrees(typeArg :: Nil)
else if (elemClass.isPrimitiveValueClass)
newArr(elemClass.name.toString)
- else
- newArr("Ref").appliedToTypeTrees(
- TypeTree(defn.ArrayOf(elemType)).withPos(typeArg.pos) :: Nil)
+ else {
+ val typeApplied = newArr("Ref").appliedToTypeTrees(typeArg :: Nil)
+ val classOfArg =
+ ref(defn.Predef_classOf).appliedToTypeTrees(typeArg :: Nil)
+ Apply(typeApplied, classOfArg :: Nil).withPos(typeArg.pos)
+ }
}
// ------ Creating typed equivalents of trees that exist only in untyped form -------
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 6625cff3f..fb71fa467 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -247,8 +247,6 @@ class Definitions {
lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays")
def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol
- def newRefArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newRefArray")
-
lazy val NilModuleRef = ctx.requiredModuleRef("scala.collection.immutable.Nil")
def NilModule(implicit ctx: Context) = NilModuleRef.symbol
@@ -622,7 +620,7 @@ class Definitions {
lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass)
def isPolymorphicAfterErasure(sym: Symbol) =
- (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf) || (sym eq newRefArrayMethod)
+ (sym eq Any_isInstanceOf) || (sym eq Any_asInstanceOf)
def isTupleType(tp: Type)(implicit ctx: Context) = {
val arity = tp.dealias.argInfos.length
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index 7acb14af4..4e7bcffd4 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -467,28 +467,18 @@ object Erasure extends TypeTestsCasts{
tpt = untpd.TypedSplice(TypeTree(sym.info).withPos(vdef.tpt.pos))), sym)
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = {
- var effectiveSym = sym
- if (sym == defn.newRefArrayMethod) {
- // newRefArray is treated specially: It's the only source-defined method
- // that has a polymorphic type after erasure. But treating its (dummy) definition
- // with a polymorphic type at and after erasure is an awkward special case.
- // We therefore rewrite the method definition with a new Symbol of type
- // (length: Int)Object
- val MethodType(pnames, ptypes) = sym.info.resultType
- effectiveSym = sym.copy(info = MethodType(pnames, ptypes, defn.ObjectType))
- }
val restpe =
- if (effectiveSym.isConstructor) defn.UnitType
- else effectiveSym.info.resultType
+ if (sym.isConstructor) defn.UnitType
+ else sym.info.resultType
val ddef1 = untpd.cpy.DefDef(ddef)(
tparams = Nil,
- vparamss = (outer.paramDefs(effectiveSym) ::: ddef.vparamss.flatten) :: Nil,
+ vparamss = (outer.paramDefs(sym) ::: ddef.vparamss.flatten) :: Nil,
tpt = untpd.TypedSplice(TypeTree(restpe).withPos(ddef.tpt.pos)),
rhs = ddef.rhs match {
case id @ Ident(nme.WILDCARD) => untpd.TypedSplice(id.withType(restpe))
case _ => ddef.rhs
})
- super.typedDefDef(ddef1, effectiveSym)
+ super.typedDefDef(ddef1, sym)
}
/** After erasure, we may have to replace the closure method by a bridge.
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index 007869e06..f11789c9a 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -359,8 +359,7 @@ class TreeChecker extends Phase with SymTransformer {
def isNonMagicalMethod(x: Symbol) =
x.is(Method) &&
!x.isCompanionMethod &&
- !x.isValueClassConvertMethod &&
- x != defn.newRefArrayMethod
+ !x.isValueClassConvertMethod
val symbolsNotDefined = cls.classInfo.decls.toSet.filter(isNonMagicalMethod) -- impl.body.map(_.symbol) - constr.symbol