diff options
author | Dmitry Petrashko <dark@d-d.me> | 2016-04-18 15:56:09 +0200 |
---|---|---|
committer | Dmitry Petrashko <dark@d-d.me> | 2016-04-18 15:56:09 +0200 |
commit | 247e91365f06cd482bf5fc456bd643d89acca157 (patch) | |
tree | 30ea8adc3cf6d1e0f20edb3aedb7c7ffa774ee2c | |
parent | 8c9a3f7ce5fa7548f9611dc2a14900a5bd42c105 (diff) | |
parent | 5399fbed6d33dca132ca27314f2eba414aea4415 (diff) | |
download | dotty-247e91365f06cd482bf5fc456bd643d89acca157.tar.gz dotty-247e91365f06cd482bf5fc456bd643d89acca157.tar.bz2 dotty-247e91365f06cd482bf5fc456bd643d89acca157.zip |
Merge pull request #1188 from dotty-staging/remove-newarray-magic
Fix #1167: Reduce the magic in Arrays.newRefArray. Implement multidimensional arrays
-rw-r--r-- | project/Build.scala | 2 | ||||
-rw-r--r-- | src/dotty/runtime/Arrays.scala | 35 | ||||
-rw-r--r-- | src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSCodeGen.scala | 44 | ||||
-rw-r--r-- | src/dotty/tools/backend/sjs/JSPrimitives.scala | 12 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/ast/tpd.scala | 31 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 9 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/ArrayConstructors.scala | 57 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/Erasure.scala | 23 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/TreeChecker.scala | 3 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Applications.scala | 21 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 2 | ||||
-rw-r--r-- | tests/run/MultiArr.scala | 37 |
14 files changed, 171 insertions, 117 deletions
diff --git a/project/Build.scala b/project/Build.scala index f6141844d..f64dd0f33 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/runtime/Arrays.scala b/src/dotty/runtime/Arrays.scala index 4469dced7..9ec5512ad 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,8 @@ 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 = ??? - - /** Create a Byte[] array */ - def newByteArray(length: Int): Array[Byte] = ??? - - /** Create a Short[] array */ - def newShortArray(length: Int): Array[Short] = ??? - - /** Create a Char[] array */ - def newCharArray(length: Int): Array[Char] = ??? - - /** Create an Int[] array */ - def newIntArray(length: Int): Array[Int] = ??? - - /** Create a Long[] array */ - def newLongArray(length: Int): Array[Long] = ??? - - /** Create a Float[] array */ - def newFloatArray(length: Int): Array[Float] = ??? - - /** Create a Double[] array */ - def newDoubleArray(length: Int): Array[Double] = ??? - - /** Create a Boolean[] array */ - def newBooleanArray(length: Int): Array[Boolean] = ??? - - /** Create a scala.runtime.BoxedUnit[] array */ - def newUnitArray(length: Int): 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/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index ef8e4997f..a64ce5900 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) diff --git a/src/dotty/tools/backend/sjs/JSCodeGen.scala b/src/dotty/tools/backend/sjs/JSCodeGen.scala index ec75a1c4d..401e01784 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) @@ -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._ @@ -2328,6 +2308,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. 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/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index fe48ac30e..b63e0236d 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 8d21953ae..5e5c842a8 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) @@ -362,18 +362,16 @@ 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 - newArr("Ref").appliedToTypeTrees( - TypeTree(defn.ArrayOf(elemType)).withPos(typeArg.pos) :: Nil) + def newArray(elemTpe: Type, returnTpe: Type, pos: Position, dims: JavaSeqLiteral)(implicit ctx: Context): Tree = { + val elemClass = elemTpe.classSymbol + 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.appliedToTypeTrees(TypeTree(returnTpe) :: Nil).appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) + } else // after erasure + newArr.appliedToArgs(clsOf(elemTpe) :: clsOf(returnTpe) :: dims :: Nil).withPos(pos) } // ------ Creating typed equivalents of trees that exist only in untyped form ------- @@ -835,7 +833,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/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 6625cff3f..5376ed591 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -246,8 +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 newRefArrayMethod(implicit ctx: Context) = DottyArraysModule.requiredMethod("newRefArray") + 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 @@ -279,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 @@ -622,7 +625,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/ArrayConstructors.scala b/src/dotty/tools/dotc/transform/ArrayConstructors.scala new file mode 100644 index 000000000..ec3bfa47f --- /dev/null +++ b/src/dotty/tools/dotc/transform/ArrayConstructors.scala @@ -0,0 +1,57 @@ +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._ + +import scala.collection.immutable.:: + + +/** 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._ + + 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))) + + if (tree.fun.symbol eq defn.ArrayConstructor) { + 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 { + 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) if !TypeErasure.isUnboundedGeneric(targ.head.tpe) => + Block(t.qualifier :: Nil, rewrite(targ.head.tpe, dims)) + case _ => tree + } + + } else tree + } +} + diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 7acb14af4..0b3a07f65 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 @@ -467,28 +468,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 diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index ec2508580..37a9f0ba0 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,11 +636,22 @@ 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 => + /** 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. + */ + 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) - tpd.cpy.Apply(tree)(newArray(targ, tree.pos), args) + + 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 2575dbec2..53296f9c9 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 => diff --git a/tests/run/MultiArr.scala b/tests/run/MultiArr.scala new file mode 100644 index 000000000..b98e512c7 --- /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."<init>":()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 + } +} + |