Skip to content

Commit f82952f

Browse files
committed
wip
1 parent 067c250 commit f82952f

File tree

5 files changed

+327
-3
lines changed

5 files changed

+327
-3
lines changed

Foo.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Tuple._
2+
class Foo {
3+
4+
// (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)
5+
// 36.169 ±(99.9%) 0.655 ms/op [Average]
6+
7+
8+
Tuple2(1, 2).tail
9+
// 419.855 ±(99.9%) 44.090 ms/op [Average]
10+
// 254.921 ±(99.9%) 3.946 ms/op [Average]
11+
// 237.878 ±(99.9%) 2.846 ms/op [Average]
12+
13+
// (21 *: 22 *: ())
14+
}

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,8 @@ class Definitions {
791791
def Tuple_cons: Symbol = Tuple_consR.symbol
792792
lazy val NonEmptyTupleTypeRef: TypeRef = ctx.requiredClassRef("scala.NonEmptyTuple")
793793
def NonEmptyTupleClass(implicit ctx: Context): ClassSymbol = NonEmptyTupleTypeRef.symbol.asClass
794+
lazy val NonEmptyTuple_tailR: TermRef = NonEmptyTupleClass.requiredMethod("tail").termRef
795+
def NonEmptyTuple_tail: Symbol = NonEmptyTuple_tailR.symbol
794796

795797
lazy val PairType: TypeRef = ctx.requiredClassRef("scala.*:")
796798
def PairClass(implicit ctx: Context): ClassSymbol = PairType.symbol.asClass
@@ -804,6 +806,7 @@ class Definitions {
804806

805807
lazy val DynamicTupleModule: Symbol = ctx.requiredModule("scala.runtime.DynamicTuple")
806808
lazy val DynamicTuple_consIterator: Symbol = DynamicTupleModule.requiredMethod("consIterator")
809+
lazy val DynamicTuple_dynamicTail: Symbol = DynamicTupleModule.requiredMethod("dynamicTail")
807810

808811
// Annotation base classes
809812
lazy val AnnotationType: TypeRef = ctx.requiredClassRef("scala.annotation.Annotation")

compiler/src/dotty/tools/dotc/transform/GenericTuples.scala

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,19 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer {
3030
else super.transformApply(tree)
3131
}
3232

33+
override def transformTypeApply(tree: tpd.TypeApply)(implicit ctx: Context): tpd.Tree = {
34+
if (tree.symbol == defn.NonEmptyTuple_tail) transformTupleTail(tree)
35+
else super.transformTypeApply(tree)
36+
}
37+
3338
private def transformTupleCons(tree: tpd.Apply)(implicit ctx: Context): Tree = {
3439
val TypeApply(Select(qual, _), headType :: tailType :: Nil) = tree.fun
3540
tupleTypes(tree.tpe) match {
3641
case Some(tpes) =>
3742
val size = tpes.size
3843
if (size <= 5) {
44+
// val t = tail.asInstanceOf[TupleN[...]]
45+
// TupleN+1(head, t._1, ..., t._n)
3946
val tailType =
4047
if (size == 1) defn.UnitType
4148
else defn.TupleType(size - 1).appliedTo(tpes.tail)
@@ -44,16 +51,48 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer {
4451
knownTupleFromElements(tpes, elements)
4552
}
4653
} else {
54+
// val it = Iterator.single(head) ++ tail.asInstanceOf[Product].productIterator
55+
// TupleN(it.next(), ..., it.next())
4756
val fullIterator = ref(defn.DynamicTuple_consIterator).appliedToArgs(tree.args.head :: qual :: Nil)
4857
evalOnce(fullIterator) { it =>
4958
knownTupleFromIterator(tpes.length, it).asInstance(tree.tpe)
5059
}
5160
}
5261
case _ =>
62+
// DynamicTuple.dynamic_*:(tail, head)
5363
ref(defn.DynamicTupleModule).select("dynamic_*:".toTermName).appliedToTypeTrees(tailType :: headType :: Nil).appliedToArgs(qual :: tree.args).asInstance(tree.tpe)
5464
}
5565
}
5666

67+
private def transformTupleTail(tree: tpd.TypeApply)(implicit ctx: Context): Tree = {
68+
val Select(tup, _) = tree.fun
69+
tupleTypes(tree.args.head.tpe) match { // TODO tupleBoundedTypes
70+
case Some(tpes) =>
71+
val size = tpes.size
72+
if (size <= 5) {
73+
// val t = tup.asInstanceOf[TupleN[...]]
74+
// TupleN-1(t._2, ..., t._n)
75+
evalOnce(tup.asInstance(defn.TupleType(size).appliedTo(tpes))) { tup =>
76+
val elements = (1 until size).map(i => tup.select(nme.selectorName(i))).toList
77+
knownTupleFromElements(tpes.tail, elements)
78+
}
79+
} else {
80+
// val it = this.asInstanceOf[Product].productIterator
81+
// it.next()
82+
// TupleN(it.next(), ..., it.next())
83+
evalOnce(tup.asInstance(defn.ProductType).select(nme.productIterator)) { it =>
84+
Block(
85+
it.select(nme.next).ensureApplied :: Nil,
86+
knownTupleFromIterator(size - 1, it).asInstance(tree.tpe)
87+
)
88+
}
89+
}
90+
case None =>
91+
// DynamicTuple.dynamicTail(tup)
92+
ref(defn.DynamicTuple_dynamicTail).appliedToType(tree.tpe).appliedToArgs(tup :: Nil)
93+
}
94+
}
95+
5796
/** Create a TupleN (1 <= N < 23) from the elements */
5897
private def knownTupleFromElements(tpes: List[Type], elements: List[Tree])(implicit ctx: Context) = {
5998
val size = elements.size
@@ -63,22 +102,29 @@ class GenericTuples extends MiniPhase with IdentityDenotTransformer {
63102
}
64103

65104
private def knownTupleFromIterator(size: Int, it: Tree)(implicit ctx: Context): Tree = {
66-
if (size == 0) Literal(Constant(())) // TODO should this code be here? Or assert(size > specializedSize)
105+
if (size == 0) {
106+
// Unit for empty tuple
107+
Literal(Constant(())) // TODO should this code be here? Or assert(size > specializedSize)
108+
}
67109
else if (size <= Definitions.MaxTupleArity) {
110+
// TupleN(it.next(), ..., it.next())
111+
68112
// TODO outline this code for the 22 alternatives (or less, may not need the smallest ones)?
69113
// This would yield smaller bytecode at the cost of an extra (easily JIT inlinable) call.
70114
// def dynamicTupleN(it: Iterator[Any]): TupleN[Any, ..., Any] = Tuple(it.next(), ..., it.next())
71115
val tpes = List.fill(size)(defn.AnyType)
72116
val elements = (0 until size).map(_ => it.select(nme.next)).toList
73117
knownTupleFromElements(tpes, elements)
74118
} else {
119+
// TupleXXL.fromIterator(it)
75120
ref(defn.TupleXXL_fromIterator).appliedTo(it)
76121
}
77122
}
78123

79124
private def tupleTypes(tp: Type)(implicit ctx: Context): Option[List[Type]] = {
80125
@tailrec def rec(tp: Type, acc: List[Type]): Option[List[Type]] = tp match {
81126
case tp: AppliedType if defn.PairClass == tp.classSymbol => rec(tp.args(1), tp.args(0) :: acc)
127+
case tp: AppliedType if defn.isTupleClass(tp.tycon.classSymbol) => Some(acc.reverse ::: tp.args)
82128
case tp if tp.classSymbol == defn.UnitClass => Some(acc.reverse)
83129
case _ => None
84130
}
Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
package scala
2+
import annotation.showAsInfix
3+
import compiletime._
4+
import internal._
5+
6+
import scala.runtime.DynamicTuple
7+
8+
sealed trait Tuple extends Any {
9+
import Tuple._
10+
11+
inline def toArray: Array[Object] =
12+
inline constValueOpt[BoundedSize[this.type]] match {
13+
case Some(0) =>
14+
DynamicTuple.empty$Array
15+
case Some(1) =>
16+
val t = asInstanceOf[Tuple1[Object]]
17+
Array(t._1)
18+
case Some(2) =>
19+
val t = asInstanceOf[Tuple2[Object, Object]]
20+
Array(t._1, t._2)
21+
case Some(3) =>
22+
val t = asInstanceOf[Tuple3[Object, Object, Object]]
23+
Array(t._1, t._2, t._3)
24+
case Some(4) =>
25+
val t = asInstanceOf[Tuple4[Object, Object, Object, Object]]
26+
Array(t._1, t._2, t._3, t._4)
27+
case Some(n) if n <= DynamicTuple.MaxSpecialized =>
28+
DynamicTuple.to$Array(this, n)
29+
case Some(n) =>
30+
asInstanceOf[TupleXXL].elems
31+
case None =>
32+
DynamicTuple.dynamicToArray(this)
33+
}
34+
35+
def *: [H, This >: this.type <: Tuple] (x: H): H *: This = ???
36+
37+
inline def ++ [This >: this.type <: Tuple](that: Tuple): Concat[This, that.type] = {
38+
type Result = Concat[This, that.type]
39+
inline constValueOpt[BoundedSize[this.type]] match {
40+
case Some(0) =>
41+
that.asInstanceOf[Result]
42+
case Some(1) =>
43+
if (constValue[BoundedSize[that.type]] == 0) this.asInstanceOf[Result]
44+
else (asInstanceOf[Tuple1[_]]._1 *: that).asInstanceOf[Result]
45+
case Some(2) =>
46+
val t = asInstanceOf[Tuple2[_, _]]
47+
inline constValue[BoundedSize[that.type]] match {
48+
case 0 => this.asInstanceOf[Result]
49+
case 1 =>
50+
val u = that.asInstanceOf[Tuple1[_]]
51+
Tuple3(t._1, t._2, u._1).asInstanceOf[Result]
52+
case 2 =>
53+
val u = that.asInstanceOf[Tuple2[_, _]]
54+
Tuple4(t._1, t._2, u._1, u._2).asInstanceOf[Result]
55+
case m =>
56+
knownTupleFromItrator[Result](2 + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator)
57+
}
58+
case Some(3) =>
59+
val t = asInstanceOf[Tuple3[_, _, _]]
60+
inline constValue[BoundedSize[that.type]] match {
61+
case 0 => this.asInstanceOf[Result]
62+
case 1 =>
63+
val u = that.asInstanceOf[Tuple1[_]]
64+
Tuple4(t._1, t._2, t._3, u._1).asInstanceOf[Result]
65+
case m =>
66+
knownTupleFromItrator[Result](3 + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator)
67+
}
68+
case Some(n) =>
69+
inline constValue[BoundedSize[that.type]] match {
70+
case 0 => this.asInstanceOf[Result]
71+
case m => knownTupleFromItrator[Result](n + m, this.asInstanceOf[Product].productIterator ++ that.asInstanceOf[Product].productIterator)
72+
}
73+
case None =>
74+
DynamicTuple.dynamic_++[This, that.type](this, that)
75+
}
76+
}
77+
78+
inline def size[This >: this.type <: Tuple]: Size[This] = {
79+
type Result = Size[This]
80+
inline constValueOpt[BoundedSize[this.type]] match {
81+
case Some(n) => n.asInstanceOf[Result]
82+
case _ => DynamicTuple.dynamicSize(this)
83+
}
84+
}
85+
86+
}
87+
88+
object Tuple {
89+
90+
type Head[X <: NonEmptyTuple] = X match {
91+
case x *: _ => x
92+
}
93+
94+
type Tail[X <: NonEmptyTuple] <: Tuple = X match {
95+
case _ *: xs => xs
96+
}
97+
98+
type Concat[X <: Tuple, +Y <: Tuple] <: Tuple = X match {
99+
case Unit => Y
100+
case x1 *: xs1 => x1 *: Concat[xs1, Y]
101+
}
102+
103+
type Elem[X <: Tuple, N] = X match {
104+
case x *: xs =>
105+
N match {
106+
case 0 => x
107+
case S[n1] => Elem[xs, n1]
108+
}
109+
}
110+
111+
type Size[X] <: Int = X match {
112+
case Unit => 0
113+
case x *: xs => S[Size[xs]]
114+
}
115+
116+
private[scala] type BoundedSizeRecur[X, L <: Int] <: Int = X match {
117+
case Unit => 0
118+
case x *: xs =>
119+
L match {
120+
case 0 => 0
121+
case S[n] => S[BoundedSizeRecur[xs, n]]
122+
}
123+
}
124+
125+
private[scala] type BoundedSize[X] = BoundedSizeRecur[X, 24]
126+
127+
private[scala] inline def knownTupleFromItrator[T <: Tuple](n: Int, it: Iterator[Any]): T =
128+
inline n match {
129+
case 0 => ().asInstanceOf[T]
130+
case 1 => Tuple1(it.next()).asInstanceOf[T]
131+
case 2 => Tuple2(it.next(), it.next()).asInstanceOf[T]
132+
case 3 => Tuple3(it.next(), it.next(), it.next()).asInstanceOf[T]
133+
case 4 => Tuple4(it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
134+
case 5 => Tuple5(it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
135+
case 6 => Tuple6(it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
136+
case 7 => Tuple7(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
137+
case 8 => Tuple8(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
138+
case 9 => Tuple9(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
139+
case 10 => Tuple10(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
140+
case 11 => Tuple11(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
141+
case 12 => Tuple12(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
142+
case 13 => Tuple13(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
143+
case 14 => Tuple14(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
144+
case 15 => Tuple15(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
145+
case 16 => Tuple16(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
146+
case 17 => Tuple17(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
147+
case 18 => Tuple18(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
148+
case 19 => Tuple19(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
149+
case 20 => Tuple20(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
150+
case 21 => Tuple21(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
151+
case 22 => Tuple22(it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next(), it.next()).asInstanceOf[T]
152+
case _ => TupleXXL(it.asInstanceOf[Iterator[Object]].toArray).asInstanceOf[T]
153+
}
154+
155+
def fromArray[T](xs: Array[T]): Tuple = {
156+
val xs2 = xs match {
157+
case xs: Array[Object] => xs
158+
case xs => xs.map(_.asInstanceOf[Object])
159+
}
160+
DynamicTuple.dynamicFromArray[Tuple](xs2)
161+
}
162+
163+
def fromProduct(product: Product): Tuple =
164+
runtime.DynamicTuple.dynamicFromProduct[Tuple](product)
165+
166+
}
167+
168+
sealed trait NonEmptyTuple extends Tuple {
169+
import Tuple._
170+
171+
inline def head[This >: this.type <: NonEmptyTuple]: Head[This] = {
172+
type Result = Head[This]
173+
val resVal = inline constValueOpt[BoundedSize[this.type]] match {
174+
case Some(1) =>
175+
val t = asInstanceOf[Tuple1[_]]
176+
t._1
177+
case Some(2) =>
178+
val t = asInstanceOf[Tuple2[_, _]]
179+
t._1
180+
case Some(3) =>
181+
val t = asInstanceOf[Tuple3[_, _, _]]
182+
t._1
183+
case Some(4) =>
184+
val t = asInstanceOf[Tuple4[_, _, _, _]]
185+
t._1
186+
case Some(n) if n > 4 && n <= DynamicTuple.MaxSpecialized =>
187+
asInstanceOf[Product].productElement(0)
188+
case Some(n) if n > DynamicTuple.MaxSpecialized =>
189+
val t = asInstanceOf[TupleXXL]
190+
t.elems(0)
191+
case None =>
192+
DynamicTuple.dynamicHead[this.type](this)
193+
}
194+
resVal.asInstanceOf[Result]
195+
}
196+
197+
def tail[This >: this.type <: NonEmptyTuple]: Tail[This]
198+
199+
inline def fallbackApply(n: Int) =
200+
inline constValueOpt[n.type] match {
201+
case Some(n: Int) => error("index out of bounds: ", n)
202+
case None => DynamicTuple.dynamicApply[this.type, n.type](this, n)
203+
}
204+
205+
inline def apply[This >: this.type <: NonEmptyTuple](n: Int): Elem[This, n.type] = {
206+
type Result = Elem[This, n.type]
207+
inline constValueOpt[Size[this.type]] match {
208+
case Some(1) =>
209+
val t = asInstanceOf[Tuple1[_]]
210+
inline constValueOpt[n.type] match {
211+
case Some(0) => t._1.asInstanceOf[Result]
212+
case _ => fallbackApply(n).asInstanceOf[Result]
213+
}
214+
case Some(2) =>
215+
val t = asInstanceOf[Tuple2[_, _]]
216+
inline constValueOpt[n.type] match {
217+
case Some(0) => t._1.asInstanceOf[Result]
218+
case Some(1) => t._2.asInstanceOf[Result]
219+
case _ => fallbackApply(n).asInstanceOf[Result]
220+
}
221+
case Some(3) =>
222+
val t = asInstanceOf[Tuple3[_, _, _]]
223+
inline constValueOpt[n.type] match {
224+
case Some(0) => t._1.asInstanceOf[Result]
225+
case Some(1) => t._2.asInstanceOf[Result]
226+
case Some(2) => t._3.asInstanceOf[Result]
227+
case _ => fallbackApply(n).asInstanceOf[Result]
228+
}
229+
case Some(4) =>
230+
val t = asInstanceOf[Tuple4[_, _, _, _]]
231+
inline constValueOpt[n.type] match {
232+
case Some(0) => t._1.asInstanceOf[Result]
233+
case Some(1) => t._2.asInstanceOf[Result]
234+
case Some(2) => t._3.asInstanceOf[Result]
235+
case Some(3) => t._4.asInstanceOf[Result]
236+
case _ => fallbackApply(n).asInstanceOf[Result]
237+
}
238+
case Some(s) if s > 4 && s <= DynamicTuple.MaxSpecialized =>
239+
val t = asInstanceOf[Product]
240+
inline constValueOpt[n.type] match {
241+
case Some(n) if n >= 0 && n < s => t.productElement(n).asInstanceOf[Result]
242+
case _ => fallbackApply(n).asInstanceOf[Result]
243+
}
244+
case Some(s) if s > DynamicTuple.MaxSpecialized =>
245+
val t = asInstanceOf[TupleXXL]
246+
inline constValueOpt[n.type] match {
247+
case Some(n) if n >= 0 && n < s => t.elems(n).asInstanceOf[Result]
248+
case _ => fallbackApply(n).asInstanceOf[Result]
249+
}
250+
case _ => fallbackApply(n).asInstanceOf[Result]
251+
}
252+
}
253+
254+
}
255+
256+
@showAsInfix
257+
sealed abstract class *:[+H, +T <: Tuple] extends NonEmptyTuple
258+
259+
object *: {
260+
inline def unapply[H, T <: Tuple](x: H *: T) = (x.head, x.tail)
261+
}

0 commit comments

Comments
 (0)