Skip to content

Commit 0d4a118

Browse files
committed
Base Tuple computations on types
Avoids rewrites of possibly very large terms
1 parent d924ef1 commit 0d4a118

File tree

2 files changed

+101
-29
lines changed

2 files changed

+101
-29
lines changed

library/src-scala3/scala/Tuple.scala

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import typelevel._
44

55
sealed trait Tuple extends Any {
66
import Tuple._
7-
rewrite def toArray: Array[Object] = rewrite _size(this) match {
7+
8+
rewrite def toArray: Array[Object] = rewrite constValue[BoundedSize[this.type]] match {
89
case 0 =>
910
$emptyArray
1011
case 1 =>
@@ -47,39 +48,39 @@ sealed trait Tuple extends Any {
4748
}
4849

4950
rewrite def ++(that: Tuple): Tuple = {
50-
erased val resTpe = Typed(_concat(this, that))
51-
rewrite _size(this) match {
51+
type Result = Concat[this.type, that.type]
52+
rewrite constValue[BoundedSize[this.type]] match {
5253
case 0 =>
5354
that
5455
case 1 =>
55-
if (_size(that) == 0) this
56-
else (asInstanceOf[Tuple1[_]]._1 *: that).asInstanceOf[resTpe.Type]
56+
if (constValue[BoundedSize[that.type]] == 0) this
57+
else (asInstanceOf[Tuple1[_]]._1 *: that).asInstanceOf[Result]
5758
case 2 =>
5859
val t = asInstanceOf[Tuple2[_, _]]
59-
rewrite _size(that) match {
60+
rewrite constValue[BoundedSize[that.type]] match {
6061
case 0 => this
6162
case 1 =>
6263
val u = that.asInstanceOf[Tuple1[_]]
63-
Tuple3(t._1, t._2, u._1).asInstanceOf[resTpe.Type]
64+
Tuple3(t._1, t._2, u._1).asInstanceOf[Result]
6465
case 2 =>
6566
val u = that.asInstanceOf[Tuple2[_, _]]
66-
Tuple4(t._1, t._2, u._1, u._2).asInstanceOf[resTpe.Type]
67+
Tuple4(t._1, t._2, u._1, u._2).asInstanceOf[Result]
6768
case _ =>
68-
genericConcat[resTpe.Type](this, that)
69+
genericConcat[Result](this, that)
6970
}
7071
case 3 =>
7172
val t = asInstanceOf[Tuple3[_, _, _]]
72-
rewrite _size(that) match {
73+
rewrite constValue[BoundedSize[that.type]] match {
7374
case 0 => this
7475
case 1 =>
7576
val u = that.asInstanceOf[Tuple1[_]]
76-
Tuple4(t._1, t._2, t._3, u._1).asInstanceOf[resTpe.Type]
77+
Tuple4(t._1, t._2, t._3, u._1).asInstanceOf[Result]
7778
case _ =>
78-
genericConcat[resTpe.Type](this, that)
79+
genericConcat[Result](this, that)
7980
}
8081
case _ =>
81-
if (_size(that) == 0) this
82-
else genericConcat[resTpe.Type](this, that)
82+
if (constValue[BoundedSize[that.type]] == 0) this
83+
else genericConcat[Result](this, that)
8384
}
8485
}
8586

@@ -89,6 +90,37 @@ sealed trait Tuple extends Any {
8990

9091
object Tuple {
9192
transparent val $MaxSpecialized = 22
93+
transparent private val XXL = $MaxSpecialized + 1
94+
95+
type Concat[X <: Tuple, Y <: Tuple] <: Tuple = X match {
96+
case Unit => Y
97+
case x1 *: xs1 => x1 *: Concat[xs1, Y]
98+
}
99+
100+
type Elem[X <: Tuple, N] = (X, N) match {
101+
case (x *: xs, 0) => x
102+
case (x *: xs, S[n1]) => Elem[xs, n1]
103+
}
104+
105+
type Size[X] <: Int = X match {
106+
case Unit => 0
107+
case x *: xs => S[Size[xs]]
108+
}
109+
110+
private type XXL = S[$MaxSpecialized.type]
111+
112+
private type BoundedS[N <: Int] = N match {
113+
case XXL => XXL
114+
case _ => S[N]
115+
}
116+
117+
private[scala] type BoundedSize[X] <: Int = X match {
118+
case Unit => 0
119+
case x *: xs => BoundedSize[xs] match {
120+
case XXL => XXL
121+
case _ => S[BoundedSize[xs]]
122+
}
123+
}
92124

93125
val $emptyArray = Array[Object]()
94126

@@ -138,7 +170,7 @@ object Tuple {
138170
}
139171

140172
rewrite def fromArray[T <: Tuple](xs: Array[Object]): T =
141-
rewrite _size(erasedValue[T]) match {
173+
rewrite constValue[BoundedSize[T]] match {
142174
case 0 => ().asInstanceOf[T]
143175
case 1 => Tuple1(xs(0)).asInstanceOf[T]
144176
case 2 => Tuple2(xs(0), xs(1)).asInstanceOf[T]
@@ -216,38 +248,38 @@ abstract sealed class NonEmptyTuple extends Tuple {
216248
}
217249

218250
rewrite def apply(n: Int): Any = {
219-
erased val resTpe = Typed(_index(this, n))
220-
rewrite _size(this) match {
251+
type Result = Elem[this.type, n.type]
252+
rewrite constValue[BoundedSize[this.type]] match {
221253
case 1 =>
222254
val t = asInstanceOf[Tuple1[_]]
223255
rewrite n match {
224-
case 0 => t._1.asInstanceOf[resTpe.Type]
256+
case 0 => t._1.asInstanceOf[Result]
225257
}
226258
case 2 =>
227259
val t = asInstanceOf[Tuple2[_, _]]
228260
rewrite n match {
229-
case 0 => t._1.asInstanceOf[resTpe.Type]
230-
case 1 => t._2.asInstanceOf[resTpe.Type]
261+
case 0 => t._1.asInstanceOf[Result]
262+
case 1 => t._2.asInstanceOf[Result]
231263
}
232264
case 3 =>
233265
val t = asInstanceOf[Tuple3[_, _, _]]
234266
rewrite n match {
235-
case 0 => t._1.asInstanceOf[resTpe.Type]
236-
case 1 => t._2.asInstanceOf[resTpe.Type]
237-
case 2 => t._3.asInstanceOf[resTpe.Type]
267+
case 0 => t._1.asInstanceOf[Result]
268+
case 1 => t._2.asInstanceOf[Result]
269+
case 2 => t._3.asInstanceOf[Result]
238270
}
239271
case 4 =>
240272
val t = asInstanceOf[Tuple4[_, _, _, _]]
241273
rewrite n match {
242-
case 0 => t._1.asInstanceOf[resTpe.Type]
243-
case 1 => t._2.asInstanceOf[resTpe.Type]
244-
case 2 => t._3.asInstanceOf[resTpe.Type]
245-
case 3 => t._4.asInstanceOf[resTpe.Type]
274+
case 0 => t._1.asInstanceOf[Result]
275+
case 1 => t._2.asInstanceOf[Result]
276+
case 2 => t._3.asInstanceOf[Result]
277+
case 3 => t._4.asInstanceOf[Result]
246278
}
247279
case s if s > 4 && s <= $MaxSpecialized && n >= 0 && n < s =>
248-
asInstanceOf[Product].productElement(n).asInstanceOf[resTpe.Type]
280+
asInstanceOf[Product].productElement(n).asInstanceOf[Result]
249281
case s if s > $MaxSpecialized && n >= 0 && n < s =>
250-
asInstanceOf[TupleXXL].elems(n).asInstanceOf[resTpe.Type]
282+
asInstanceOf[TupleXXL].elems(n).asInstanceOf[Result]
251283
}
252284
}
253285
}

tests/run/tuples2.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
object Test extends App {
2+
val xs0 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
3+
assert(xs0(15) == 16)
4+
// 2.733s
5+
6+
val xs1 = xs0 ++ xs0
7+
assert(xs1(31) == 16)
8+
// 3.089s
9+
10+
val xs2 = xs1 ++ xs1
11+
assert(xs2(63) == 16)
12+
// 3.329s
13+
14+
val xs3 = xs2 ++ xs2
15+
assert(xs3(127) == 16)
16+
// 3.416s
17+
18+
val xs4 = xs3 ++ xs3
19+
assert(xs4(255) == 16)
20+
// 3.765s
21+
22+
val xs5a = xs3 ++ xs4
23+
assert(xs5a(383) == 16)
24+
// 3.804s
25+
26+
/* The following operations exhaust the standard 2MB stack, but succeed with -Xs10m:
27+
28+
val xs5 = xs4 ++ xs4
29+
assert(xs5(511) == 16)
30+
// 3.866s
31+
32+
val xs6 = xs5 ++ xs5
33+
assert(xs6(1023) == 16)
34+
// 4.115s
35+
36+
val xs7 = xs6 ++ xs6
37+
assert(xs7(2047) == 16)
38+
// 4.846s
39+
*/
40+
}

0 commit comments

Comments
 (0)