From 12bc2264e6dd1d3b9da96c89144a8b54c93cc81c Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 5 Feb 2021 17:02:16 +0100 Subject: [PATCH] Alternative layout for IArray definition --- compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../dotty/tools/dotc/core/Definitions.scala | 4 ++ .../tools/dotc/transform/ArrayApply.scala | 7 +- .../dotc/transform/ArrayConstructors.scala | 2 +- .../tools/backend/jvm/ArrayApplyOptTest.scala | 32 +++++----- library/src/scala/IArray.scala | 64 ++++++++----------- 6 files changed, 52 insertions(+), 59 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index e1fa879df834..3971cc4468be 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -95,7 +95,7 @@ class Compiler { new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method new ParamForwarding, // Add forwarders for aliases of superclass parameters new TupleOptimizations, // Optimize generic operations on tuples - new LetOverApply, // Lift blocks from receivers of applications + new LetOverApply, // Lift blocks from receivers of applications 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 diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 0f96fa4df3fb..ef4005f6c1f1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -551,6 +551,10 @@ class Definitions { @tu lazy val ArrayConstructor: Symbol = ArrayClass.requiredMethod(nme.CONSTRUCTOR) @tu lazy val ArrayModule: Symbol = requiredModule("scala.Array") + def ArrayModuleClass: Symbol = ArrayModule.moduleClass + + @tu lazy val IArrayModule: Symbol = requiredModule("scala.IArray") + def IArrayModuleClass: Symbol = IArrayModule.moduleClass @tu lazy val UnitType: TypeRef = valueTypeRef("scala.Unit", java.lang.Void.TYPE, UnitEnc, nme.specializedTypeNames.Void) def UnitClass(using Context): ClassSymbol = UnitType.symbol.asClass diff --git a/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala b/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala index 314236400084..348bc735bd9c 100644 --- a/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala +++ b/compiler/src/dotty/tools/dotc/transform/ArrayApply.scala @@ -6,6 +6,7 @@ import MegaPhase._ import Contexts._ import Symbols._ import Types._ +import Flags._ import StdNames._ import ast.Trees._ import dotty.tools.dotc.ast.tpd @@ -23,7 +24,7 @@ class ArrayApply extends MiniPhase { override def phaseName: String = "arrayApply" override def transformApply(tree: tpd.Apply)(using Context): tpd.Tree = - if (tree.symbol.name == nme.apply && tree.symbol.owner == defn.ArrayModule.moduleClass) // Is `Array.apply` + if isArrayModuleApply(tree.symbol) then tree.args match { case StripAscription(Apply(wrapRefArrayMeth, (seqLit: tpd.JavaSeqLiteral) :: Nil)) :: ct :: Nil if defn.WrapArrayMethods().contains(wrapRefArrayMeth.symbol) && elideClassTag(ct) => @@ -39,6 +40,10 @@ class ArrayApply extends MiniPhase { else tree + private def isArrayModuleApply(sym: Symbol)(using Context): Boolean = + sym.name == nme.apply + && (sym.owner == defn.ArrayModuleClass || (sym.owner == defn.IArrayModuleClass && !sym.is(Extension))) + /** Only optimize when classtag if it is one of * - `ClassTag.apply(classOf[XYZ])` * - `ClassTag.apply(java.lang.XYZ.Type)` for boxed primitives `XYZ`` diff --git a/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala b/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala index 437ff0490822..301026548444 100644 --- a/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/ArrayConstructors.scala @@ -32,7 +32,7 @@ class ArrayConstructors extends MiniPhase { val TypeApply(tycon, targ :: Nil) = tree.fun expand(targ.tpe, tree.args) } - else if ((tree.fun.symbol.maybeOwner eq defn.ArrayModule.moduleClass) && (tree.fun.symbol.name eq nme.ofDim) && !tree.tpe.isInstanceOf[MethodicType]) { + else if ((tree.fun.symbol.maybeOwner eq defn.ArrayModuleClass) && (tree.fun.symbol.name eq nme.ofDim) && !tree.tpe.isInstanceOf[MethodicType]) { val Apply(Apply(TypeApply(_, List(tp)), _), _) = tree val cs = tp.tpe.classSymbol tree.fun match { diff --git a/compiler/test/dotty/tools/backend/jvm/ArrayApplyOptTest.scala b/compiler/test/dotty/tools/backend/jvm/ArrayApplyOptTest.scala index 7c7c9f36ecca..e7cd20ba98b2 100644 --- a/compiler/test/dotty/tools/backend/jvm/ArrayApplyOptTest.scala +++ b/compiler/test/dotty/tools/backend/jvm/ArrayApplyOptTest.scala @@ -22,18 +22,18 @@ class ArrayApplyOptTest extends DottyBytecodeTest { test("Array[Char]()", newArray0Opcodes(T_CHAR)) test("Array[T]()", newArray0Opcodes(T_INT)) - test("IArray[String]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/String"), TypeOp(CHECKCAST, "[Ljava/lang/String;"), Op(POP), Op(RETURN))) - test("IArray[Unit]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), TypeOp(CHECKCAST, "[Lscala/runtime/BoxedUnit;"), Op(POP), Op(RETURN))) - test("IArray[Object]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/Object"), TypeOp(CHECKCAST, "[Ljava/lang/Object;"), Op(POP), Op(RETURN))) - test("IArray[Boolean]()", newArray0Opcodes(T_BOOLEAN, TypeOp(CHECKCAST, "[Z") :: Nil)) - test("IArray[Byte]()", newArray0Opcodes(T_BYTE, TypeOp(CHECKCAST, "[B") :: Nil)) - test("IArray[Short]()", newArray0Opcodes(T_SHORT, TypeOp(CHECKCAST, "[S") :: Nil)) - test("IArray[Int]()", newArray0Opcodes(T_INT, TypeOp(CHECKCAST, "[I") :: Nil)) - test("IArray[Long]()", newArray0Opcodes(T_LONG, TypeOp(CHECKCAST, "[J") :: Nil)) - test("IArray[Float]()", newArray0Opcodes(T_FLOAT, TypeOp(CHECKCAST, "[F") :: Nil)) - test("IArray[Double]()", newArray0Opcodes(T_DOUBLE, TypeOp(CHECKCAST, "[D") :: Nil)) - test("IArray[Char]()", newArray0Opcodes(T_CHAR, TypeOp(CHECKCAST, "[C") :: Nil)) - test("IArray[T]()", newArray0Opcodes(T_INT, TypeOp(CHECKCAST, "[I") :: Nil)) + test("IArray[String]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/String"), Op(POP), Op(RETURN))) + test("IArray[Unit]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), Op(POP), Op(RETURN))) + test("IArray[Object]()", List(Op(ICONST_0), TypeOp(ANEWARRAY, "java/lang/Object"), Op(POP), Op(RETURN))) + test("IArray[Boolean]()", newArray0Opcodes(T_BOOLEAN)) + test("IArray[Byte]()", newArray0Opcodes(T_BYTE)) + test("IArray[Short]()", newArray0Opcodes(T_SHORT)) + test("IArray[Int]()", newArray0Opcodes(T_INT)) + test("IArray[Long]()", newArray0Opcodes(T_LONG)) + test("IArray[Float]()", newArray0Opcodes(T_FLOAT)) + test("IArray[Double]()", newArray0Opcodes(T_DOUBLE)) + test("IArray[Char]()", newArray0Opcodes(T_CHAR)) + test("IArray[T]()", newArray0Opcodes(T_INT)) } @Test def testArrayGenericApply = { @@ -42,10 +42,8 @@ class ArrayApplyOptTest extends DottyBytecodeTest { test("""Array("a", "b")""", opCodes("java/lang/String")) test("""Array[Object]("a", "b")""", opCodes("java/lang/Object")) - def opCodes2(tpe: String) = - List(Op(ICONST_2), TypeOp(ANEWARRAY, tpe), Op(DUP), Op(ICONST_0), Ldc(LDC, "a"), Op(AASTORE), Op(DUP), Op(ICONST_1), Ldc(LDC, "b"), Op(AASTORE), TypeOp(CHECKCAST, s"[L$tpe;"), Op(POP), Op(RETURN)) - test("""IArray("a", "b")""", opCodes2("java/lang/String")) - test("""IArray[Object]("a", "b")""", opCodes2("java/lang/Object")) + test("""IArray("a", "b")""", opCodes("java/lang/String")) + test("""IArray[Object]("a", "b")""", opCodes("java/lang/Object")) } @Test def testArrayApplyBoolean = { @@ -106,7 +104,7 @@ class ArrayApplyOptTest extends DottyBytecodeTest { Op(ICONST_1), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), Op(POP), Op(RETURN))) test("IArray[Unit]((), ())", List(Op(ICONST_2), TypeOp(ANEWARRAY, "scala/runtime/BoxedUnit"), Op(DUP), Op(ICONST_0), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), Op(DUP), - Op(ICONST_1), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), TypeOp(CHECKCAST, "[Lscala/runtime/BoxedUnit;"), Op(POP), Op(RETURN))) + Op(ICONST_1), Field(GETSTATIC, "scala/runtime/BoxedUnit", "UNIT", "Lscala/runtime/BoxedUnit;"), Op(AASTORE), Op(POP), Op(RETURN))) } @Test def testArrayInlined = test( diff --git a/library/src/scala/IArray.scala b/library/src/scala/IArray.scala index 053d4d43abdc..20753409a315 100644 --- a/library/src/scala/IArray.scala +++ b/library/src/scala/IArray.scala @@ -3,14 +3,12 @@ import reflect.ClassTag import scala.collection.immutable +opaque type IArray[+T] = Array[_ <: T] + /** An immutable array. An `IArray[T]` has the same representation as an `Array[T]`, * but it cannot be updated. Unlike regular arrays, immutable arrays are covariant. */ -object opaques: - opaque type IArray[+T] = Array[_ <: T] - - private[scala] type Sub[A] >: Array[A] <: IArray[A] - private[scala] type Sup[A] >: IArray[A] <: Array[_ <: A] +object IArray: /** The selection operation on an immutable array. * @@ -316,17 +314,6 @@ object opaques: extension (arr: IArray[Unit]) def toSeq: immutable.ArraySeq[Unit] = immutable.ArraySeq.ofUnit(arr.asInstanceOf[Array[Unit]]) -end opaques - -type IArray[+T] = opaques.IArray[T] - -object IArray { - import opaques.Sub - import opaques.Sup - - // A convenience to avoid having to cast everything by hand - private given [A]: Conversion[Array[A], IArray[A]] = identity[Sub[A]] - /** Convert an array into an immutable array without copying, the original array * must _not_ be mutated after this or the guaranteed immutablity of IArray will * be violated. @@ -356,25 +343,25 @@ object IArray { def emptyObjectIArray: IArray[Object] = Array.emptyObjectArray /** An immutable array with given elements. */ - inline def apply[T](inline xs: T*)(using inline ct: ClassTag[T]): IArray[T] = Array(xs: _*).asInstanceOf + def apply[T](xs: T*)(using ct: ClassTag[T]): IArray[T] = Array(xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Boolean, inline xs: Boolean*): IArray[Boolean] = Array(x, xs: _*).asInstanceOf + def apply(x: Boolean, xs: Boolean*): IArray[Boolean] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Byte, inline xs: Byte*): IArray[Byte] = Array(x, xs: _*).asInstanceOf + def apply(x: Byte, xs: Byte*): IArray[Byte] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Short, inline xs: Short*): IArray[Short] = Array(x, xs: _*).asInstanceOf + def apply(x: Short, xs: Short*): IArray[Short] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Char, inline xs: Char*): IArray[Char] = Array(x, xs: _*).asInstanceOf + def apply(x: Char, xs: Char*): IArray[Char] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Int, inline xs: Int*): IArray[Int] = Array(x, xs: _*).asInstanceOf + def apply(x: Int, xs: Int*): IArray[Int] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Long, inline xs: Long*): IArray[Long] = Array(x, xs: _*).asInstanceOf + def apply(x: Long, xs: Long*): IArray[Long] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Float, inline xs: Float*): IArray[Float] = Array(x, xs: _*).asInstanceOf + def apply(x: Float, xs: Float*): IArray[Float] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Double, inline xs: Double*): IArray[Double] = Array(x, xs: _*).asInstanceOf + def apply(x: Double, xs: Double*): IArray[Double] = Array(x, xs: _*) /** An immutable array with given elements. */ - inline def apply(inline x: Unit, inline xs: Unit*): IArray[Unit] = Array(x, xs: _*).asInstanceOf + def apply(x: Unit, xs: Unit*): IArray[Unit] = Array(x, xs: _*) /** Concatenates all arrays into a single immutable array. * @@ -405,7 +392,7 @@ object IArray { */ def fill[T: ClassTag](n1: Int, n2: Int)(elem: => T): IArray[IArray[T]] = // We cannot avoid a cast here as Array.fill creates inner arrays out of our control: - Array.fill(n1, n2)(elem).asInstanceOf + Array.fill(n1, n2)(elem) /** Returns a three-dimensional immutable array that contains the results of some element computation a number * of times. Each element is determined by a separate computation. @@ -416,7 +403,7 @@ object IArray { * @param elem the element computation */ def fill[T: ClassTag](n1: Int, n2: Int, n3: Int)(elem: => T): IArray[IArray[IArray[T]]] = - Array.fill(n1, n2, n3)(elem).asInstanceOf + Array.fill(n1, n2, n3)(elem) /** Returns a four-dimensional immutable array that contains the results of some element computation a number * of times. Each element is determined by a separate computation. @@ -428,7 +415,7 @@ object IArray { * @param elem the element computation */ def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(elem: => T): IArray[IArray[IArray[IArray[T]]]] = - Array.fill(n1, n2, n3, n4)(elem).asInstanceOf + Array.fill(n1, n2, n3, n4)(elem) /** Returns a five-dimensional immutable array that contains the results of some element computation a number * of times. Each element is determined by a separate computation. @@ -441,7 +428,7 @@ object IArray { * @param elem the element computation */ def fill[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(elem: => T): IArray[IArray[IArray[IArray[IArray[T]]]]] = - Array.fill(n1, n2, n3, n4, n5)(elem).asInstanceOf + Array.fill(n1, n2, n3, n4, n5)(elem) /** Returns an immutable array containing values of a given function over a range of integer * values starting from 0. @@ -460,7 +447,7 @@ object IArray { * @param f The function computing element values */ def tabulate[T: ClassTag](n1: Int, n2: Int)(f: (Int, Int) => T): IArray[IArray[T]] = - Array.tabulate(n1, n2)(f).asInstanceOf + Array.tabulate(n1, n2)(f) /** Returns a three-dimensional immutable array containing values of a given function * over ranges of integer values starting from `0`. @@ -471,7 +458,7 @@ object IArray { * @param f The function computing element values */ def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int)(f: (Int, Int, Int) => T): IArray[IArray[IArray[T]]] = - Array.tabulate(n1, n2, n3)(f).asInstanceOf + Array.tabulate(n1, n2, n3)(f) /** Returns a four-dimensional immutable array containing values of a given function * over ranges of integer values starting from `0`. @@ -483,7 +470,7 @@ object IArray { * @param f The function computing element values */ def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int)(f: (Int, Int, Int, Int) => T): IArray[IArray[IArray[IArray[T]]]] = - Array.tabulate(n1, n2, n3, n4)(f).asInstanceOf + Array.tabulate(n1, n2, n3, n4)(f) /** Returns a five-dimensional immutable array containing values of a given function * over ranges of integer values starting from `0`. @@ -496,7 +483,7 @@ object IArray { * @param f The function computing element values */ def tabulate[T: ClassTag](n1: Int, n2: Int, n3: Int, n4: Int, n5: Int)(f: (Int, Int, Int, Int, Int) => T): IArray[IArray[IArray[IArray[IArray[T]]]]] = - Array.tabulate(n1, n2, n3, n4, n5)(f).asInstanceOf + Array.tabulate(n1, n2, n3, n4, n5)(f) /** Returns an immutable array containing a sequence of increasing integers in a range. * @@ -531,8 +518,7 @@ object IArray { * @param x the selector value * @return sequence wrapped in a [[scala.Some]], if `x` is a Seq, otherwise `None` */ - def unapplySeq[T](x: IArray[T]) = - // The double type ascription is currently needed, - // for some reason (see: https://scastie.scala-lang.org/sSsmOhKxSKym405MgNRKqQ) - Array.unapplySeq((x: Sup[T]): Array[_ <: T]) -} + def unapplySeq[T](x: IArray[T]): Array.UnapplySeqWrapper[_ <: T] = + Array.unapplySeq(x) + +end IArray \ No newline at end of file