From 88baa04f2bc6b89d3e65226973d7b72fdf715095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 23 Mar 2016 12:19:14 +0100 Subject: [PATCH 01/13] Implement loading static fields in the Scala.js back-end. --- src/dotty/tools/backend/sjs/JSCodeGen.scala | 24 ++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/backend/sjs/JSCodeGen.scala b/src/dotty/tools/backend/sjs/JSCodeGen.scala index ec75a1c4d59f..70c5af1e7e34 100644 --- a/src/dotty/tools/backend/sjs/JSCodeGen.scala +++ b/src/dotty/tools/backend/sjs/JSCodeGen.scala @@ -718,9 +718,9 @@ class JSCodeGen()(implicit ctx: Context) { if (sym.is(Module)) { assert(!sym.is(Package), "Cannot use package as value: " + tree) genLoadModule(sym) - } else /*if (sym.isStaticMember) { - genStaticMember(sym) - } else if (paramAccessorLocals contains sym) { + } else if (sym.is(JavaStatic)) { + genLoadStaticField(sym) + } else /*if (paramAccessorLocals contains sym) { paramAccessorLocals(sym).ref } else if (isScalaJSDefinedJSClass(sym.owner)) { val genQual = genExpr(qualifier) @@ -2328,6 +2328,24 @@ class JSCodeGen()(implicit ctx: Context) { } } + /** Gen JS code for loading a Java static field. + */ + private def genLoadStaticField(sym: Symbol)(implicit pos: Position): js.Tree = { + /* Actually, there is no static member in Scala.js. If we come here, that + * is because we found the symbol in a Java-emitted .class in the + * classpath. But the corresponding implementation in Scala.js will + * actually be a val in the companion module. + */ + + if (sym == defn.BoxedUnit_UNIT) { + js.Undefined() + } else { + val instance = genLoadModule(sym.owner) + val method = encodeStaticMemberSym(sym) + js.Apply(instance, method, Nil)(toIRType(sym.info)) + } + } + /** Gen JS code for loading a module. * * Can be given either the module symbol, or its module class symbol. From 27deb1e0d41a4dfc24a4cac8a311c30471d454e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Doeraene?= Date: Wed, 23 Mar 2016 11:51:25 +0100 Subject: [PATCH 02/13] 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. --- src/dotty/runtime/Arrays.scala | 35 ++++++++++++------- .../backend/jvm/DottyBackendInterface.scala | 2 +- src/dotty/tools/backend/sjs/JSCodeGen.scala | 20 ----------- .../tools/backend/sjs/JSPrimitives.scala | 12 ------- src/dotty/tools/dotc/ast/tpd.scala | 9 +++-- src/dotty/tools/dotc/core/Definitions.scala | 4 +-- src/dotty/tools/dotc/transform/Erasure.scala | 18 +++------- .../tools/dotc/transform/TreeChecker.scala | 3 +- 8 files changed, 36 insertions(+), 67 deletions(-) diff --git a/src/dotty/runtime/Arrays.scala b/src/dotty/runtime/Arrays.scala index 4469dced7ff1..2771df66ba3d 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 ef8e4997f2ab..6e4431b1a097 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 70c5af1e7e34..401e01784777 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 52b5dc4b964b..6c3c5715cac9 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 8d21953ae564..a58ff7f51c7e 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 6625cff3f7fd..fb71fa467a59 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 7acb14af462d..4e7bcffd4aab 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 007869e06ae3..f11789c9a2c9 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 From 0211e56d104934188007e3c02eac131b81460100 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 23 Mar 2016 18:36:38 +0100 Subject: [PATCH 03/13] Get rid of all new***Array methods expect one. This one is able to encode creation of array of any type and any dimension. Note, it does not handle value classes. --- src/dotty/runtime/Arrays.scala | 40 ++------------------- src/dotty/tools/dotc/ast/tpd.scala | 30 ++++++++-------- src/dotty/tools/dotc/core/Definitions.scala | 5 +++ 3 files changed, 22 insertions(+), 53 deletions(-) diff --git a/src/dotty/runtime/Arrays.scala b/src/dotty/runtime/Arrays.scala index 2771df66ba3d..9ec5512ad86b 100644 --- a/src/dotty/runtime/Arrays.scala +++ b/src/dotty/runtime/Arrays.scala @@ -26,42 +26,6 @@ object Arrays { /** Create an array of a reference type 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] = - jlr.Array.newInstance(classOf[Byte], length).asInstanceOf[Array[Byte]] - - /** Create a Short[] array */ - 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] = - jlr.Array.newInstance(classOf[Char], length).asInstanceOf[Array[Char]] - - /** Create an Int[] array */ - 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] = - jlr.Array.newInstance(classOf[Long], length).asInstanceOf[Array[Long]] - - /** Create a Float[] array */ - 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] = - jlr.Array.newInstance(classOf[Double], length).asInstanceOf[Array[Double]] - - /** Create a Boolean[] array */ - 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] = - jlr.Array.newInstance(classOf[scala.runtime.BoxedUnit], length).asInstanceOf[Array[Unit]] + def newArray[Arr](componentType: Class[_], returnType: Class[Arr], dimensions: Array[Int]): Arr = + jlr.Array.newInstance(componentType, dimensions: _*).asInstanceOf[Arr] } diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index a58ff7f51c7e..528b5fa29889 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -362,21 +362,21 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { * kind for the given element type in `typeArg`. No type arguments or * `length` arguments are given. */ - def newArray(typeArg: Tree, pos: Position)(implicit ctx: Context): Tree = { - val elemType = typeArg.tpe - val elemClass = elemType.classSymbol - def newArr(kind: String) = - ref(defn.DottyArraysModule).select(s"new${kind}Array".toTermName).withPos(pos) - if (TypeErasure.isUnboundedGeneric(elemType)) - newArr("Generic").appliedToTypeTrees(typeArg :: Nil) - else if (elemClass.isPrimitiveValueClass) - newArr(elemClass.name.toString) - else { - val typeApplied = newArr("Ref").appliedToTypeTrees(typeArg :: Nil) - val classOfArg = - ref(defn.Predef_classOf).appliedToTypeTrees(typeArg :: Nil) - Apply(typeApplied, classOfArg :: Nil).withPos(typeArg.pos) - } + def newArray(elemTpe: Type, returnTpe: Type, pos: Position, dims: JavaSeqLiteral)(implicit ctx: Context): Tree = { + val elemClass = elemTpe.classSymbol + def newArr(symbol: TermSymbol) = + ref(defn.DottyArraysModule).select(symbol).withPos(pos) + + if (!ctx.erasedTypes) + if (TypeErasure.isUnboundedGeneric(elemTpe)) { + //exists only before erasure + assert(dims.elems.tail.isEmpty) + newArr(defn.newGenericArrayMethod).appliedToType(elemTpe).appliedTo(dims.elems.head) + } + else + newArr(defn.newArrayMethod).appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) + else // after erasure + newArr(defn.newArrayMethod).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(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 fb71fa467a59..5376ed591160 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -246,6 +246,8 @@ class Definitions { def DottyPredefModule(implicit ctx: Context) = DottyPredefModuleRef.symbol lazy val DottyArraysModuleRef = ctx.requiredModuleRef("dotty.runtime.Arrays") def DottyArraysModule(implicit ctx: Context) = DottyArraysModuleRef.symbol + def newGenericArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newGenericArray") + def newArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newArray") lazy val NilModuleRef = ctx.requiredModuleRef("scala.collection.immutable.Nil") def NilModule(implicit ctx: Context) = NilModuleRef.symbol @@ -277,6 +279,9 @@ class Definitions { def Array_clone(implicit ctx: Context) = Array_cloneR.symbol lazy val ArrayConstructorR = ArrayClass.requiredMethodRef(nme.CONSTRUCTOR) def ArrayConstructor(implicit ctx: Context) = ArrayConstructorR.symbol + lazy val ArrayModuleType = ctx.requiredModuleRef("scala.Array") + def ArrayModule(implicit ctx: Context) = ArrayModuleType.symbol.moduleClass.asClass + lazy val UnitType: TypeRef = valueTypeRef("scala.Unit", BoxedUnitType, java.lang.Void.TYPE, UnitEnc) def UnitClass(implicit ctx: Context) = UnitType.symbol.asClass From 7e2352ac7a5968958156c5fdab54b3e9e9f2808f Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 23 Mar 2016 18:37:30 +0100 Subject: [PATCH 04/13] Decouple handling array constructors from typer. It's done in a separate ArrayConstructors phase now. --- src/dotty/tools/dotc/Compiler.scala | 3 +- src/dotty/tools/dotc/ast/tpd.scala | 5 +- .../dotc/transform/ArrayConstructors.scala | 59 +++++++++++++++++++ src/dotty/tools/dotc/typer/Applications.scala | 11 +--- src/dotty/tools/dotc/typer/Typer.scala | 3 +- 5 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/ArrayConstructors.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index fe48ac30e12f..f1a3c526be2e 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -66,7 +66,8 @@ class Compiler { new Getters, // Replace non-private vals and vars with getter defs (fields are added later) new ElimByName, // Expand by-name parameters and arguments new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings - new ResolveSuper), // Implement super accessors and add forwarders to trait methods + new ResolveSuper, // Implement super accessors and add forwarders to trait methods + new ArrayConstructors) // Intercept creation of (non-generic) arrays and intrinsify. List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 528b5fa29889..01b1919c9be3 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -838,7 +838,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case tpnme.Float => TYPE(defn.BoxedFloatModule) case tpnme.Double => TYPE(defn.BoxedDoubleModule) case tpnme.Unit => TYPE(defn.BoxedUnitModule) - case _ => Literal(Constant(TypeErasure.erasure(tp))) + case _ => + if(ctx.erasedTypes || !tp.derivesFrom(defn.ArrayClass)) + Literal(Constant(TypeErasure.erasure(tp))) + else Literal(Constant(tp)) } } diff --git a/src/dotty/tools/dotc/transform/ArrayConstructors.scala b/src/dotty/tools/dotc/transform/ArrayConstructors.scala new file mode 100644 index 000000000000..e9541fce08df --- /dev/null +++ b/src/dotty/tools/dotc/transform/ArrayConstructors.scala @@ -0,0 +1,59 @@ +package dotty.tools.dotc +package transform + +import core._ +import TreeTransforms._ +import Contexts.Context +import Flags._ +import SymUtils._ +import Symbols._ +import SymDenotations._ +import Types._ +import Decorators._ +import DenotTransformers._ +import StdNames._ +import NameOps._ +import ast.Trees._ +import dotty.tools.dotc.ast.tpd +import util.Positions._ +import Names._ +import collection.mutable +import ResolveSuper._ + + +/** This phase rewrites calls to array constructors to newArray and newGenericArray methods + * in Dotty.runtime.Arrays module. + * + * Additionally it optimizes calls to scala.Array.ofDim functions + * + */ +class ArrayConstructors extends MiniPhaseTransform { thisTransform => + import ast.tpd._ + + override def phaseName: String = "arrayConstructors" + + override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { + def rewrite(elemType: Type, dims: List[Tree]) = + tpd.newArray(elemType, tree.tpe, tree.pos, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef)).asInstanceOf[JavaSeqLiteral]) + + if (tree.fun.symbol eq defn.ArrayConstructor) { + tree.fun match { + case TypeApply(tycon, targ :: Nil) => + rewrite(targ.tpe, tree.args) + case _ => + ??? + } + } else if ((tree.fun.symbol.maybeOwner eq defn.ArrayModule) && (tree.fun.symbol.name eq nme.ofDim) && !tree.tpe.isInstanceOf[MethodicType]) { + + tree.fun match { + case Apply(TypeApply(t: Ident, targ), dims) => + rewrite(targ.head.tpe, dims) + case Apply(TypeApply(t: Select, targ), dims) => + Block(t.qualifier :: Nil, rewrite(targ.head.tpe, dims)) + case _ => tree + } + + } else tree + } +} + diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index ec2508580bf0..c0664e0d6200 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -560,7 +560,7 @@ trait Applications extends Compatibility { self: Typer => if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) else new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree)) val result = app.result - convertNewArray(ConstFold(result)) + ConstFold(result) } { (failedVal, failedState) => val fun2 = tryInsertImplicitOnQualifier(fun1, proto) if (fun1 eq fun2) { @@ -636,15 +636,6 @@ trait Applications extends Compatibility { self: Typer => def adaptTypeArg(tree: tpd.Tree, bound: Type)(implicit ctx: Context): tpd.Tree = tree.withType(tree.tpe.etaExpandIfHK(bound)) - /** Rewrite `new Array[T](....)` trees to calls of newXYZArray methods. */ - def convertNewArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { - case Apply(TypeApply(tycon, targ :: Nil), args) if tycon.symbol == defn.ArrayConstructor => - fullyDefinedType(tree.tpe, "array", tree.pos) - tpd.cpy.Apply(tree)(newArray(targ, tree.pos), args) - case _ => - tree - } - def typedUnApply(tree: untpd.Apply, selType: Type)(implicit ctx: Context): Tree = track("typedUnApply") { val Apply(qual, args) = tree diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 2575dbec2e8e..6a8e93f86799 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1684,8 +1684,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => Nil } if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2 - convertNewArray( - adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original)) + adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original) } case wtp => pt match { From 3f41bd6a4c525eb7c40fd96aba7997c4dcfb8b86 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 23 Mar 2016 18:40:10 +0100 Subject: [PATCH 05/13] Update to new version of DottyBackendInterface. That knows that there exists only single magical array method. --- project/Build.scala | 2 +- src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index f6141844d768..f64dd0f338f9 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -77,7 +77,7 @@ object DottyBuild extends Build { com.typesafe.sbteclipse.plugin.EclipsePlugin.EclipseKeys.withSource := true, // get libraries onboard - partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20151022-113908-7fb0e653fd", + partestDeps := Seq("me.d-d" % "scala-compiler" % "2.11.5-20160322-171045-e19b30b3cd", "org.scala-lang" % "scala-reflect" % scalaVersion.value, "org.scala-lang" % "scala-library" % scalaVersion.value % "test"), libraryDependencies ++= partestDeps.value, diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 6e4431b1a097..a64ce5900fc8 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -153,15 +153,8 @@ 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=> - ("new" + x + "Array").toTermName - } - - val dottyArraysModuleClass = toDenot(defn.DottyArraysModule).moduleClass.asClass - - override def isSyntheticArrayConstructor(s: Symbol) = { - (toDenot(s).maybeOwner eq dottyArraysModuleClass) && mkArrayNames.contains(s.name) + s eq defn.newArrayMethod } def isBox(sym: Symbol): Boolean = Erasure.Boxing.isBox(sym) From 57fcea6ee648164d7fd7597b34d50709ddaf4952 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 23 Mar 2016 18:41:00 +0100 Subject: [PATCH 06/13] Erasure: Erase constant literals. The problem comes from JavaArrayTypes. They are invalid before erasure, and cannot be pickled, while Array[T] is invalid after erasure and should be erased. --- src/dotty/tools/dotc/transform/Erasure.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 4e7bcffd4aab..0b3a07f65833 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -21,7 +21,7 @@ import core.Decorators._ import dotty.tools.dotc.ast.{Trees, tpd, untpd} import ast.Trees._ import scala.collection.mutable.ListBuffer -import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.{Constants, Flags} import ValueClasses._ import TypeUtils._ import ExplicitOuter._ @@ -299,8 +299,9 @@ object Erasure extends TypeTestsCasts{ assignType(untpd.cpy.Typed(tree)(expr1, tpt1), tpt1) } - override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = + override def typedLiteral(tree: untpd.Literal)(implicit ctx: Context): Literal = if (tree.typeOpt.isRef(defn.UnitClass)) tree.withType(tree.typeOpt) + else if (tree.const.tag == Constants.ClazzTag) Literal(Constant(erasure(tree.const.typeValue))) else super.typedLiteral(tree) /** Type check select nodes, applying the following rewritings exhaustively From 3fc2b6e022a9ca38f93aa778999516d8e67576cf Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 23 Mar 2016 21:10:43 +0100 Subject: [PATCH 07/13] Add a test to check multidimenstionall arrays. --- tests/run/MultiArr.scala | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/run/MultiArr.scala diff --git a/tests/run/MultiArr.scala b/tests/run/MultiArr.scala new file mode 100644 index 000000000000..b98e512c704d --- /dev/null +++ b/tests/run/MultiArr.scala @@ -0,0 +1,37 @@ +class Arr { + val s = new Array[Int](1) + val s2 = new Array[Array[Int]](2) + val s3: Array[Array[Int]] = Array.ofDim(2,3) + /* +public Arr(); + Code: + 0: aload_0 + 1: invokespecial #14 // Method java/lang/Object."":()V + 4: aload_0 + 5: iconst_1 + 6: newarray int + 8: checkcast #15 // class "[I" + 11: putfield #17 // Field s$$local:[I + 14: aload_0 + 15: iconst_2 + 16: anewarray #15 // class "[I" + 19: checkcast #18 // class "[[I" + 22: putfield #20 // Field s2$$local:[[I + 25: aload_0 + 26: getstatic #26 // Field scala/Array$.MODULE$:Lscala/Array$; + 29: pop + 30: iconst_2 + 31: iconst_3 + 32: multianewarray #18, 2 // class "[[I" + 36: checkcast #18 // class "[[I" + 39: putfield #28 // Field s3$$local:[[I + 42: return +*/ +} + +object Test{ + def main(args: Array[String]): Unit = { + new Arr + } +} + From 2c370bdec8d2a8a2c4b339d4e7f18e6215b03244 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 30 Mar 2016 17:07:11 +0200 Subject: [PATCH 08/13] make tpd.JavaSeqLiteral return JavaSeqLiteral --- src/dotty/tools/dotc/ast/tpd.scala | 5 +++-- src/dotty/tools/dotc/transform/ArrayConstructors.scala | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 01b1919c9be3..de9d2165c28d 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -125,8 +125,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = ta.assignType(untpd.SeqLiteral(elems, elemtpt), elems, elemtpt) - def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): SeqLiteral = - ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt) + def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit ctx: Context): JavaSeqLiteral = + ta.assignType(new untpd.JavaSeqLiteral(elems, elemtpt), elems, elemtpt).asInstanceOf[JavaSeqLiteral] def TypeTree(original: Tree)(implicit ctx: Context): TypeTree = TypeTree(original.tpe, original) @@ -371,6 +371,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { if (TypeErasure.isUnboundedGeneric(elemTpe)) { //exists only before erasure assert(dims.elems.tail.isEmpty) + assert(!ctx.isAfterTyper) // needs to infer an implicit newArr(defn.newGenericArrayMethod).appliedToType(elemTpe).appliedTo(dims.elems.head) } else diff --git a/src/dotty/tools/dotc/transform/ArrayConstructors.scala b/src/dotty/tools/dotc/transform/ArrayConstructors.scala index e9541fce08df..fbb5bb9d7fb6 100644 --- a/src/dotty/tools/dotc/transform/ArrayConstructors.scala +++ b/src/dotty/tools/dotc/transform/ArrayConstructors.scala @@ -34,7 +34,7 @@ class ArrayConstructors extends MiniPhaseTransform { thisTransform => override def transformApply(tree: tpd.Apply)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = { def rewrite(elemType: Type, dims: List[Tree]) = - tpd.newArray(elemType, tree.tpe, tree.pos, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef)).asInstanceOf[JavaSeqLiteral]) + tpd.newArray(elemType, tree.tpe, tree.pos, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef))) if (tree.fun.symbol eq defn.ArrayConstructor) { tree.fun match { From 9d7db0c2fff12420f7ef37119746860622e40387 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 30 Mar 2016 17:12:16 +0200 Subject: [PATCH 09/13] Bring back convertNewArray into typer. It's needed in order to create calls to newGenricArray as it needs to infer the ClassTag. --- src/dotty/tools/dotc/typer/Applications.scala | 14 +++++++++++++- src/dotty/tools/dotc/typer/Typer.scala | 3 ++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index c0664e0d6200..7fb71b7fb8af 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -560,7 +560,7 @@ trait Applications extends Compatibility { self: Typer => if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) else new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree)) val result = app.result - ConstFold(result) + convertNewArray(ConstFold(result)) } { (failedVal, failedState) => val fun2 = tryInsertImplicitOnQualifier(fun1, proto) if (fun1 eq fun2) { @@ -636,6 +636,18 @@ trait Applications extends Compatibility { self: Typer => def adaptTypeArg(tree: tpd.Tree, bound: Type)(implicit ctx: Context): tpd.Tree = tree.withType(tree.tpe.etaExpandIfHK(bound)) + /** Rewrite `new Array[T](....)` trees to calls of newXYZArray methods. + * It is performed during typer as creation of generic arrays needs a classTag. + * we rely on implicit search to find one + */ + def convertNewArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + case Apply(TypeApply(tycon, targ :: Nil), args) if tycon.symbol == defn.ArrayConstructor => + fullyDefinedType(tree.tpe, "array", tree.pos) + newArray(targ.tpe, tree.tpe, tree.pos, JavaSeqLiteral(args, TypeTree(defn.IntClass.typeRef))) + case _ => + tree + } + def typedUnApply(tree: untpd.Apply, selType: Type)(implicit ctx: Context): Tree = track("typedUnApply") { val Apply(qual, args) = tree diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 6a8e93f86799..2575dbec2e8e 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1684,7 +1684,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => Nil } if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2 - adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original) + convertNewArray( + adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original)) } case wtp => pt match { From c74bb42251eb233c858810d83300ba78876a2b2c Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 30 Mar 2016 17:27:41 +0200 Subject: [PATCH 10/13] Separate handling of genericArray creation from normal ones. This allowed to simplify the code in both Applications and tpd.newArray. Now, only creation of generic arrays is handled by typer. All other arrays are handled in ArrayConstructors phase. --- src/dotty/tools/dotc/ast/tpd.scala | 16 +++++----------- src/dotty/tools/dotc/typer/Applications.scala | 18 ++++++++++++------ src/dotty/tools/dotc/typer/Typer.scala | 2 +- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index de9d2165c28d..84747aecd6b9 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -367,16 +367,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def newArr(symbol: TermSymbol) = ref(defn.DottyArraysModule).select(symbol).withPos(pos) - if (!ctx.erasedTypes) - if (TypeErasure.isUnboundedGeneric(elemTpe)) { - //exists only before erasure - assert(dims.elems.tail.isEmpty) - assert(!ctx.isAfterTyper) // needs to infer an implicit - newArr(defn.newGenericArrayMethod).appliedToType(elemTpe).appliedTo(dims.elems.head) - } - else - newArr(defn.newArrayMethod).appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) - else // after erasure + if (!ctx.erasedTypes) { + assert(!TypeErasure.isUnboundedGeneric(elemTpe)) //needs to be done during typer. See Applications.convertNewGenericArray + newArr(defn.newArrayMethod).appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) + } else // after erasure newArr(defn.newArrayMethod).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) } @@ -754,7 +748,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** If inititializer tree is `_', the default value of its type, * otherwise the tree itself. - */ + * */ def wildcardToDefault(implicit ctx: Context) = if (isWildcardArg(tree)) defaultValue(tree.tpe) else tree diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 7fb71b7fb8af..2cec4bf08cd1 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -560,7 +560,7 @@ trait Applications extends Compatibility { self: Typer => if (proto.argsAreTyped) new ApplyToTyped(tree, fun1, funRef, proto.typedArgs, pt) else new ApplyToUntyped(tree, fun1, funRef, proto, pt)(argCtx(tree)) val result = app.result - convertNewArray(ConstFold(result)) + convertNewGenericArray(ConstFold(result)) } { (failedVal, failedState) => val fun2 = tryInsertImplicitOnQualifier(fun1, proto) if (fun1 eq fun2) { @@ -636,14 +636,20 @@ trait Applications extends Compatibility { self: Typer => def adaptTypeArg(tree: tpd.Tree, bound: Type)(implicit ctx: Context): tpd.Tree = tree.withType(tree.tpe.etaExpandIfHK(bound)) - /** Rewrite `new Array[T](....)` trees to calls of newXYZArray methods. + /** Rewrite `new Array[T](....)` if T is an unbounded generic to calls to newGenericArray. * It is performed during typer as creation of generic arrays needs a classTag. - * we rely on implicit search to find one + * we rely on implicit search to find one. */ - def convertNewArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { - case Apply(TypeApply(tycon, targ :: Nil), args) if tycon.symbol == defn.ArrayConstructor => + def convertNewGenericArray(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + case Apply(TypeApply(tycon, targs@(targ :: Nil)), args) if tycon.symbol == defn.ArrayConstructor => fullyDefinedType(tree.tpe, "array", tree.pos) - newArray(targ.tpe, tree.tpe, tree.pos, JavaSeqLiteral(args, TypeTree(defn.IntClass.typeRef))) + + def newGenericArrayCall = + ref(defn.DottyArraysModule).select(defn.newGenericArrayMethod).withPos(tree.pos).appliedToTypeTrees(targs).appliedToArgs(args) + + if (TypeErasure.isUnboundedGeneric(targ.tpe)) + newGenericArrayCall + else tree case _ => tree } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 2575dbec2e8e..53296f9c91a4 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1684,7 +1684,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => Nil } if (typeArgs.isEmpty) typeArgs = constrained(poly, tree)._2 - convertNewArray( + convertNewGenericArray( adaptInterpolated(tree.appliedToTypes(typeArgs), pt, original)) } case wtp => From 55e2ae676e32eb21c9e0c2eabc2867704c93d34c Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 30 Mar 2016 18:20:29 +0200 Subject: [PATCH 11/13] ArrayConstructors: don't optimise creating of multi-dim generic arrays. They need to be created through their class tag. --- .../tools/dotc/transform/ArrayConstructors.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/transform/ArrayConstructors.scala b/src/dotty/tools/dotc/transform/ArrayConstructors.scala index fbb5bb9d7fb6..fe79627eac33 100644 --- a/src/dotty/tools/dotc/transform/ArrayConstructors.scala +++ b/src/dotty/tools/dotc/transform/ArrayConstructors.scala @@ -21,12 +21,11 @@ import collection.mutable import ResolveSuper._ -/** This phase rewrites calls to array constructors to newArray and newGenericArray methods - * in Dotty.runtime.Arrays module. - * - * Additionally it optimizes calls to scala.Array.ofDim functions - * - */ +/** This phase rewrites calls to array constructors to newArray method in Dotty.runtime.Arrays module. + * + * It assummes that generic arrays have already been handled by typer(see Applications.convertNewGenericArray). + * Additionally it optimizes calls to scala.Array.ofDim functions by replacing them with calls to newArray with specific dimensions + */ class ArrayConstructors extends MiniPhaseTransform { thisTransform => import ast.tpd._ @@ -46,9 +45,9 @@ class ArrayConstructors extends MiniPhaseTransform { thisTransform => } else if ((tree.fun.symbol.maybeOwner eq defn.ArrayModule) && (tree.fun.symbol.name eq nme.ofDim) && !tree.tpe.isInstanceOf[MethodicType]) { tree.fun match { - case Apply(TypeApply(t: Ident, targ), dims) => + case Apply(TypeApply(t: Ident, targ), dims) if !TypeErasure.isUnboundedGeneric(targ.head.tpe) => rewrite(targ.head.tpe, dims) - case Apply(TypeApply(t: Select, targ), dims) => + case Apply(TypeApply(t: Select, targ), dims) if !TypeErasure.isUnboundedGeneric(targ.head.tpe) => Block(t.qualifier :: Nil, rewrite(targ.head.tpe, dims)) case _ => tree } From 0b1ca2d823ba0eac5558c3bcd1750c84a250cd18 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Thu, 31 Mar 2016 15:30:16 +0200 Subject: [PATCH 12/13] Address review comments. https://github.com/lampepfl/dotty/pull/1188 --- src/dotty/tools/dotc/ast/tpd.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 84747aecd6b9..5e5c842a8193 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -364,14 +364,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { */ def newArray(elemTpe: Type, returnTpe: Type, pos: Position, dims: JavaSeqLiteral)(implicit ctx: Context): Tree = { val elemClass = elemTpe.classSymbol - def newArr(symbol: TermSymbol) = - ref(defn.DottyArraysModule).select(symbol).withPos(pos) + def newArr = + ref(defn.DottyArraysModule).select(defn.newArrayMethod).withPos(pos) if (!ctx.erasedTypes) { assert(!TypeErasure.isUnboundedGeneric(elemTpe)) //needs to be done during typer. See Applications.convertNewGenericArray - newArr(defn.newArrayMethod).appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) + newArr.appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) } else // after erasure - newArr(defn.newArrayMethod).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) + newArr.appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) } // ------ Creating typed equivalents of trees that exist only in untyped form ------- @@ -748,7 +748,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { /** If inititializer tree is `_', the default value of its type, * otherwise the tree itself. - * */ + */ def wildcardToDefault(implicit ctx: Context) = if (isWildcardArg(tree)) defaultValue(tree.tpe) else tree From 5399fbed6d33dca132ca27314f2eba414aea4415 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 14:49:18 +0200 Subject: [PATCH 13/13] Address review comments of #1188 --- src/dotty/tools/dotc/Compiler.scala | 4 ++-- .../tools/dotc/transform/ArrayConstructors.scala | 11 +++++------ src/dotty/tools/dotc/typer/Applications.scala | 4 +++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index f1a3c526be2e..b63e0236df00 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -66,8 +66,8 @@ class Compiler { new Getters, // Replace non-private vals and vars with getter defs (fields are added later) new ElimByName, // Expand by-name parameters and arguments new AugmentScala2Traits, // Expand traits defined in Scala 2.11 to simulate old-style rewritings - new ResolveSuper, // Implement super accessors and add forwarders to trait methods - new ArrayConstructors) // Intercept creation of (non-generic) arrays and intrinsify. + new ResolveSuper, // Implement super accessors and add forwarders to trait methods + new ArrayConstructors), // Intercept creation of (non-generic) arrays and intrinsify. List(new Erasure), // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements. List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations diff --git a/src/dotty/tools/dotc/transform/ArrayConstructors.scala b/src/dotty/tools/dotc/transform/ArrayConstructors.scala index fe79627eac33..ec3bfa47f28b 100644 --- a/src/dotty/tools/dotc/transform/ArrayConstructors.scala +++ b/src/dotty/tools/dotc/transform/ArrayConstructors.scala @@ -17,9 +17,12 @@ import ast.Trees._ import dotty.tools.dotc.ast.tpd import util.Positions._ import Names._ + import collection.mutable import ResolveSuper._ +import scala.collection.immutable.:: + /** This phase rewrites calls to array constructors to newArray method in Dotty.runtime.Arrays module. * @@ -36,12 +39,8 @@ class ArrayConstructors extends MiniPhaseTransform { thisTransform => tpd.newArray(elemType, tree.tpe, tree.pos, JavaSeqLiteral(dims, TypeTree(defn.IntClass.typeRef))) if (tree.fun.symbol eq defn.ArrayConstructor) { - tree.fun match { - case TypeApply(tycon, targ :: Nil) => - rewrite(targ.tpe, tree.args) - case _ => - ??? - } + val TypeApply(tycon, targ :: Nil) = tree.fun + rewrite(targ.tpe, tree.args) } else if ((tree.fun.symbol.maybeOwner eq defn.ArrayModule) && (tree.fun.symbol.name eq nme.ofDim) && !tree.tpe.isInstanceOf[MethodicType]) { tree.fun match { diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 2cec4bf08cd1..37a9f0ba008f 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -645,7 +645,9 @@ trait Applications extends Compatibility { self: Typer => fullyDefinedType(tree.tpe, "array", tree.pos) def newGenericArrayCall = - ref(defn.DottyArraysModule).select(defn.newGenericArrayMethod).withPos(tree.pos).appliedToTypeTrees(targs).appliedToArgs(args) + ref(defn.DottyArraysModule) + .select(defn.newGenericArrayMethod).withPos(tree.pos) + .appliedToTypeTrees(targs).appliedToArgs(args) if (TypeErasure.isUnboundedGeneric(targ.tpe)) newGenericArrayCall