Skip to content

Cleanup code in DynamicTuple #8326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Apply {

@Benchmark
def tupleApply(): Any = {
DynamicTuple.dynamicApply(tuple, index)
DynamicTuple.apply(tuple, index)
}

@Benchmark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ class Concat {

@Benchmark
def tupleConcat(): Tuple = {
DynamicTuple.dynamicConcat(tuple1, tuple2)
DynamicTuple.concat(tuple1, tuple2)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Cons {

@Benchmark
def tupleCons(): Tuple = {
DynamicTuple.dynamicCons("elem", tuple)
DynamicTuple.cons("elem", tuple)
}

@Benchmark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,22 @@ class Conversions {

@Benchmark
def tupleToArray(): Array[Object] = {
DynamicTuple.dynamicToArray(tuple)
DynamicTuple.toArray(tuple)
}

@Benchmark
def tupleToIArray(): IArray[Object] = {
DynamicTuple.dynamicToIArray(tuple)
DynamicTuple.toIArray(tuple)
}

@Benchmark
def tupleFromArray(): Tuple = {
DynamicTuple.dynamicFromArray(array)
DynamicTuple.fromArray(array)
}

@Benchmark
def tupleFromIArray(): Tuple = {
DynamicTuple.dynamicFromIArray(iarray)
DynamicTuple.fromIArray(iarray)
}

@Benchmark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Map {

@Benchmark
def tupleMap(): Tuple = {
DynamicTuple.dynamicMap[Tuple, Id](tuple, [T] => (x:T) => x.asInstanceOf[String].updated(0, 'a').asInstanceOf[T])
DynamicTuple.map[Id](tuple, [T] => (x:T) => x.asInstanceOf[String].updated(0, 'a').asInstanceOf[T])
}

@Benchmark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Tail {

@Benchmark
def tupleTail(): Tuple = {
DynamicTuple.dynamicTail(tuple)
DynamicTuple.tail(tuple)
}

@Benchmark
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class Zip {

@Benchmark
def tupleZip(): Tuple = {
DynamicTuple.dynamicZip(tuple1, tuple2)
DynamicTuple.zip(tuple1, tuple2)
}

@Benchmark
Expand Down
2 changes: 1 addition & 1 deletion bench-run/src/main/scala/tuples/Drop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Drop {

@Benchmark
def tupleDrop(): Tuple = {
DynamicTuple.dynamicDrop(tuple, half)
DynamicTuple.drop(tuple, half)
}

@Benchmark
Expand Down
2 changes: 1 addition & 1 deletion bench-run/src/main/scala/tuples/Split.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Split {

@Benchmark
def tupleSplit(): (Tuple, Tuple) = {
DynamicTuple.dynamicSplitAt(tuple, half)
DynamicTuple.splitAt(tuple, half)
}

@Benchmark
Expand Down
2 changes: 1 addition & 1 deletion bench-run/src/main/scala/tuples/Take.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Take {

@Benchmark
def tupleTake(): Tuple = {
DynamicTuple.dynamicTake(tuple, half)
DynamicTuple.take(tuple, half)
}

@Benchmark
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -758,12 +758,12 @@ class Definitions {
@tu lazy val DynamicTupleModuleClass: Symbol = DynamicTupleModule.moduleClass
lazy val DynamicTuple_consIterator: Symbol = DynamicTupleModule.requiredMethod("consIterator")
lazy val DynamicTuple_concatIterator: Symbol = DynamicTupleModule.requiredMethod("concatIterator")
lazy val DynamicTuple_dynamicApply: Symbol = DynamicTupleModule.requiredMethod("dynamicApply")
lazy val DynamicTuple_dynamicCons: Symbol = DynamicTupleModule.requiredMethod("dynamicCons")
lazy val DynamicTuple_dynamicSize: Symbol = DynamicTupleModule.requiredMethod("dynamicSize")
lazy val DynamicTuple_dynamicTail: Symbol = DynamicTupleModule.requiredMethod("dynamicTail")
lazy val DynamicTuple_dynamicConcat: Symbol = DynamicTupleModule.requiredMethod("dynamicConcat")
lazy val DynamicTuple_dynamicToArray: Symbol = DynamicTupleModule.requiredMethod("dynamicToArray")
lazy val DynamicTuple_apply: Symbol = DynamicTupleModule.requiredMethod("apply")
lazy val DynamicTuple_cons: Symbol = DynamicTupleModule.requiredMethod("cons")
lazy val DynamicTuple_size: Symbol = DynamicTupleModule.requiredMethod("size")
lazy val DynamicTuple_tail: Symbol = DynamicTupleModule.requiredMethod("tail")
lazy val DynamicTuple_concat: Symbol = DynamicTupleModule.requiredMethod("concat")
lazy val DynamicTuple_toArray: Symbol = DynamicTupleModule.requiredMethod("toArray")
lazy val DynamicTuple_productToArray: Symbol = DynamicTupleModule.requiredMethod("productToArray")

@tu lazy val TupledFunctionTypeRef: TypeRef = ctx.requiredClassRef("scala.TupledFunction")
Expand Down
39 changes: 19 additions & 20 deletions compiler/src/dotty/tools/dotc/transform/TupleOptimizations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {

override def transformApply(tree: tpd.Apply)(implicit ctx: Context): tpd.Tree =
if (!tree.symbol.exists || tree.symbol.owner != defn.DynamicTupleModuleClass) tree
else if (tree.symbol == defn.DynamicTuple_dynamicCons) transformTupleCons(tree)
else if (tree.symbol == defn.DynamicTuple_dynamicTail) transformTupleTail(tree)
else if (tree.symbol == defn.DynamicTuple_dynamicSize) transformTupleSize(tree)
else if (tree.symbol == defn.DynamicTuple_dynamicConcat) transformTupleConcat(tree)
else if (tree.symbol == defn.DynamicTuple_dynamicApply) transformTupleApply(tree)
else if (tree.symbol == defn.DynamicTuple_dynamicToArray) transformTupleToArray(tree)
else if (tree.symbol == defn.DynamicTuple_cons) transformTupleCons(tree)
else if (tree.symbol == defn.DynamicTuple_tail) transformTupleTail(tree)
else if (tree.symbol == defn.DynamicTuple_size) transformTupleSize(tree)
else if (tree.symbol == defn.DynamicTuple_concat) transformTupleConcat(tree)
else if (tree.symbol == defn.DynamicTuple_apply) transformTupleApply(tree)
else if (tree.symbol == defn.DynamicTuple_toArray) transformTupleToArray(tree)
else tree

private def transformTupleCons(tree: tpd.Apply)(implicit ctx: Context): Tree = {
val head :: tail :: Nil = tree.args
defn.tupleTypes(tree.tpe) match {
defn.tupleTypes(tree.tpe.widenTermRefExpr.dealias) match {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move .dealias before . widenTermRefExpr? Can we just use widen instead of widenTermRefExpr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the alias appears after the widening of the term ref. I did not find a case where we needed to completely widen the type. For the case of indices and sizes, this might widen the constant typea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We only widen the term refs that come from inserting val bindings when inlining.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could mark the parameters an inline to avoid it. But I will remove the inline altogether in a future PR as it does not really have a purpose anymore.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the case of indices and sizes, this might widen the constant type.

The type is a tuple type, it thus may not be constant types.

For dealiasing, what I've in mind is type T = a.type. But I guess no programmers will write such code, so it's fine to ignore such cases.

case Some(tpes) =>
// Generate a the tuple directly with TupleN+1.apply
val size = tpes.size
Expand All @@ -56,14 +56,14 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
}
case _ =>
// No optimization, keep:
// DynamicTuple.dynamicCons:(tail, head)
// DynamicTuple.cons(tail, head)
tree
}
}

private def transformTupleTail(tree: tpd.Apply)(implicit ctx: Context): Tree = {
val Apply(TypeApply(_, tpt :: Nil), tup :: Nil) = tree
defn.tupleTypes(tpt.tpe, MaxTupleArity + 1) match {
val Apply(_, tup :: Nil) = tree
defn.tupleTypes(tup.tpe.widenTermRefExpr.dealias, MaxTupleArity + 1) match {
case Some(tpes) =>
// Generate a the tuple directly with TupleN-1.apply
val size = tpes.size
Expand Down Expand Up @@ -93,7 +93,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
tup.asInstance(defn.TupleXXLClass.typeRef).select("tailXXL".toTermName)
case None =>
// No optimization, keep:
// DynamicTuple.dynamicTail(tup)
// DynamicTuple.tail(tup)
tree
}
}
Expand All @@ -105,9 +105,8 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
}

private def transformTupleConcat(tree: tpd.Apply)(implicit ctx: Context): Tree = {
val Apply(TypeApply(_, selfTp :: thatTp :: Nil), self :: that :: Nil) = tree

(defn.tupleTypes(selfTp.tpe), defn.tupleTypes(that.tpe.widenTermRefExpr)) match {
val Apply(_, self :: that :: Nil) = tree
(defn.tupleTypes(self.tpe.widenTermRefExpr.dealias), defn.tupleTypes(that.tpe.widenTermRefExpr.dealias)) match {
case (Some(tpes1), Some(tpes2)) =>
// Generate a the tuple directly with TupleN+M.apply
val n = tpes1.size
Expand Down Expand Up @@ -135,14 +134,14 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
}
case _ =>
// No optimization, keep:
// DynamicTuple.dynamicCons[This, that.type](self, that)
// DynamicTuple.cons(self, that)
tree
}
}

private def transformTupleApply(tree: tpd.Apply)(implicit ctx: Context): Tree = {
val Apply(TypeApply(_, tpt :: nTpt :: Nil), tup :: nTree :: Nil) = tree
(defn.tupleTypes(tpt.tpe), nTpt.tpe) match {
val Apply(_, tup :: nTree :: Nil) = tree
(defn.tupleTypes(tup.tpe.widenTermRefExpr.dealias), nTree.tpe) match {
case (Some(tpes), nTpe: ConstantType) =>
// Get the element directly with TupleM._n+1 or TupleXXL.productElement(n)
val size = tpes.size
Expand All @@ -162,7 +161,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
tree
case _ =>
// No optimization, keep:
// DynamicTuple.dynamicApply(tup, n)
// DynamicTuple.apply(tup, n)
tree
}
}
Expand All @@ -183,7 +182,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {
tup.asInstance(defn.TupleXXLClass.typeRef).select(nme.toArray)
case None =>
// No optimization, keep:
// DynamicTuple.dynamicToArray(tup)
// DynamicTuple.toArray(tup)
tree
}
}
Expand All @@ -205,7 +204,7 @@ class TupleOptimizations extends MiniPhase with IdentityDenotTransformer {

// TODO outline this code for the 22 alternatives (or less, may not need the smallest ones)?
// This would yield smaller bytecode at the cost of an extra (easily JIT inlinable) call.
// def dynamicTupleN(it: Iterator[Any]): TupleN[Any, ..., Any] = Tuple(it.next(), ..., it.next())
// def tupleN(it: Iterator[Any]): TupleN[Any, ..., Any] = Tuple(it.next(), ..., it.next())
val tpes = List.fill(size)(defn.AnyType)
val elements = (0 until size).map(_ => it.select(nme.next)).toList
knownTupleFromElements(tpes, elements)
Expand Down
32 changes: 16 additions & 16 deletions library/src/scala/Tuple.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,27 @@ sealed trait Tuple extends Any {

/** Create a copy this tuple as an Array */
inline def toArray: Array[Object] =
DynamicTuple.dynamicToArray(this)
DynamicTuple.toArray(this)

/** Create a copy this tuple as an IArray */
inline def toIArray: IArray[Object] =
DynamicTuple.dynamicToIArray(this)
DynamicTuple.toIArray(this)

/** Return a new tuple by prepending the element to `this` tuple.
* This opteration is O(this.size)
*/
inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This =
DynamicTuple.dynamicCons[H, This](x, this)
DynamicTuple.cons(x, this).asInstanceOf[H *: This]

/** Return a new tuple by concatenating `this` tuple with `that` tuple.
* This opteration is O(this.size + that.size)
*/
inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] =
DynamicTuple.dynamicConcat[This, that.type](this, that)
DynamicTuple.concat(this, that).asInstanceOf[Concat[This, that.type]]

/** Return the size (or arity) of the tuple */
inline def size[This >: this.type <: Tuple]: Size[This] =
DynamicTuple.dynamicSize(this)
DynamicTuple.size(this).asInstanceOf[Size[This]]

/** Given two tuples, `(a1, ..., an)` and `(a1, ..., an)`, returns a tuple
* `((a1, b1), ..., (an, bn))`. If the two tuples have different sizes,
Expand All @@ -41,35 +41,35 @@ sealed trait Tuple extends Any {
* `(A1, B1) *: ... *: (Ai, Bi) *: Tuple`
*/
inline def zip[This >: this.type <: Tuple, T2 <: Tuple](t2: T2): Zip[This, T2] =
DynamicTuple.dynamicZip(this, t2)
DynamicTuple.zip(this, t2).asInstanceOf[Zip[This, T2]]

/** Called on a tuple `(a1, ..., an)`, returns a new tuple `(f(a1), ..., f(an))`.
* The result is typed as `(F[A1], ..., F[An])` if the tuple type is fully known.
* If the tuple is of the form `a1 *: ... *: Tuple` (that is, the tail is not known
* to be the cons type.
*/
inline def map[F[_]](f: [t] => t => F[t]): Map[this.type, F] =
DynamicTuple.dynamicMap(this, f)
DynamicTuple.map(this, f).asInstanceOf[Map[this.type, F]]

/** Given a tuple `(a1, ..., am)`, returns the tuple `(a1, ..., an)` consisting
* of its first n elements.
*/
inline def take[This >: this.type <: Tuple](n: Int): Take[This, n.type] =
DynamicTuple.dynamicTake[This, n.type](this, n)
DynamicTuple.take(this, n).asInstanceOf[Take[This, n.type]]


/** Given a tuple `(a1, ..., am)`, returns the tuple `(an+1, ..., am)` consisting
* all its elements except the first n ones.
*/
inline def drop[This >: this.type <: Tuple](n: Int): Drop[This, n.type] =
DynamicTuple.dynamicDrop[This, n.type](this, n)
DynamicTuple.drop(this, n).asInstanceOf[Drop[This, n.type]]

/** Given a tuple `(a1, ..., am)`, returns a pair of the tuple `(a1, ..., an)`
* consisting of the first n elements, and the tuple `(an+1, ..., am)` consisting
* of the remaining elements.
*/
inline def splitAt[This >: this.type <: Tuple](n: Int): Split[This, n.type] =
DynamicTuple.dynamicSplitAt[This, n.type](this, n)
DynamicTuple.splitAt(this, n).asInstanceOf[Split[This, n.type]]
}

object Tuple {
Expand Down Expand Up @@ -165,7 +165,7 @@ object Tuple {
case xs: Array[Object] => xs
case xs => xs.map(_.asInstanceOf[Object])
}
DynamicTuple.dynamicFromArray[Tuple](xs2)
DynamicTuple.fromArray(xs2).asInstanceOf[Tuple]
}

/** Convert an immutable array into a tuple of unknown arity and types */
Expand All @@ -176,12 +176,12 @@ object Tuple {
// TODO suport IArray.map
xs.asInstanceOf[Array[T]].map(_.asInstanceOf[Object]).asInstanceOf[IArray[Object]]
}
DynamicTuple.dynamicFromIArray[Tuple](xs2)
DynamicTuple.fromIArray(xs2).asInstanceOf[Tuple]
}

/** Convert a Product into a tuple of unknown arity and types */
def fromProduct(product: Product): Tuple =
runtime.DynamicTuple.dynamicFromProduct[Tuple](product)
runtime.DynamicTuple.fromProduct(product)

def fromProductTyped[P <: Product](p: P)(using m: scala.deriving.Mirror.ProductOf[P]): m.MirroredElemTypes =
Tuple.fromArray(p.productIterator.toArray).asInstanceOf[m.MirroredElemTypes] // TODO use toIArray of Object to avoid double/triple array copy
Expand All @@ -195,17 +195,17 @@ sealed trait NonEmptyTuple extends Tuple {
* Equivalent to productElement but with a precise return type.
*/
inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] =
DynamicTuple.dynamicApply[This, n.type](this, n)
DynamicTuple.apply(this, n).asInstanceOf[Elem[This, n.type]]

/** Get the head of this tuple */
inline def head[This >: this.type <: NonEmptyTuple]: Head[This] =
DynamicTuple.dynamicApply[This, 0](this, 0)
DynamicTuple.apply(this, 0).asInstanceOf[Head[This]]

/** Get the tail of this tuple.
* This opteration is O(this.size)
*/
inline def tail[This >: this.type <: NonEmptyTuple]: Tail[This] =
DynamicTuple.dynamicTail[This](this)
DynamicTuple.tail(this).asInstanceOf[Tail[This]]

}

Expand Down
Loading