Skip to content

Commit d53b3fa

Browse files
committed
Allow generic tuple operations to be dynamic
Rewrite to specialized version if static, fall back to generic runtime version otherwise.
1 parent 3657799 commit d53b3fa

File tree

7 files changed

+220
-44
lines changed

7 files changed

+220
-44
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/StandardDefinitions.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ trait StandardDefinitions extends scala.tasty.reflect.StandardDefinitions {
5959
defn.FunctionClass(arity, isImplicit, isErased).asClass
6060
def TupleClass(arity: Int): Symbol = defn.TupleType(arity).classSymbol.asClass
6161

62-
6362
def ScalaPrimitiveValueClasses: List[Symbol] =
6463
UnitClass :: BooleanClass :: ScalaNumericValueClasses
6564
def ScalaNumericValueClasses: List[Symbol] =

library/src-scala3/scala/Tuple.scala

Lines changed: 176 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ sealed trait Tuple extends Any {
2525
case Some(n) =>
2626
asInstanceOf[TupleXXL].elems
2727
case None =>
28-
error(".toArray cannot be applied to tuple of unknown size")
28+
dynamicToArray(this)
2929
}
3030

3131
rewrite def *: [H] (x: H): H *: this.type = {
@@ -47,7 +47,7 @@ sealed trait Tuple extends Any {
4747
case Some(n) =>
4848
fromArray[Result]($consArray(x, toArray))
4949
case _ =>
50-
error("*: cannot be applied to tuple of unknown size")
50+
dynamic_*:[this.type, H](this, x)
5151
}
5252
}
5353

@@ -86,12 +86,20 @@ sealed trait Tuple extends Any {
8686
if (constValue[BoundedSize[that.type]] == 0) this.asInstanceOf[Result]
8787
else genericConcat[Result](this, that).asInstanceOf[Result]
8888
case None =>
89-
error("++ cannot be applied to tuple of unknown size")
89+
dynamic_++[this.type, that.type](this, that)
9090
}
9191
}
9292

9393
rewrite def genericConcat[T <: Tuple](xs: Tuple, ys: Tuple): Tuple =
9494
fromArray[T](xs.toArray ++ ys.toArray)
95+
96+
rewrite def size: Size[this.type] = {
97+
type Result = Size[this.type]
98+
rewrite constValueOpt[BoundedSize[this.type]] match {
99+
case Some(n) => n.asInstanceOf[Result]
100+
case _ => dynamicSize(this).asInstanceOf[Result]
101+
}
102+
}
95103
}
96104

97105
object Tuple {
@@ -183,10 +191,98 @@ object Tuple {
183191
case 22 => Tuple22(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18), xs(19), xs(20), xs(21)).asInstanceOf[T]
184192
case _ => TupleXXL(xs).asInstanceOf[T]
185193
}
194+
195+
def dynamicFromArray[T <: Tuple](xs: Array[Object]): T = xs.length match {
196+
case 0 => ().asInstanceOf[T]
197+
case 1 => Tuple1(xs(0)).asInstanceOf[T]
198+
case 2 => Tuple2(xs(0), xs(1)).asInstanceOf[T]
199+
case 3 => Tuple3(xs(0), xs(1), xs(2)).asInstanceOf[T]
200+
case 4 => Tuple4(xs(0), xs(1), xs(2), xs(3)).asInstanceOf[T]
201+
case 5 => Tuple5(xs(0), xs(1), xs(2), xs(3), xs(4)).asInstanceOf[T]
202+
case 6 => Tuple6(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5)).asInstanceOf[T]
203+
case 7 => Tuple7(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6)).asInstanceOf[T]
204+
case 8 => Tuple8(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7)).asInstanceOf[T]
205+
case 9 => Tuple9(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8)).asInstanceOf[T]
206+
case 10 => Tuple10(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9)).asInstanceOf[T]
207+
case 11 => Tuple11(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10)).asInstanceOf[T]
208+
case 12 => Tuple12(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11)).asInstanceOf[T]
209+
case 13 => Tuple13(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12)).asInstanceOf[T]
210+
case 14 => Tuple14(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13)).asInstanceOf[T]
211+
case 15 => Tuple15(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14)).asInstanceOf[T]
212+
case 16 => Tuple16(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15)).asInstanceOf[T]
213+
case 17 => Tuple17(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16)).asInstanceOf[T]
214+
case 18 => Tuple18(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17)).asInstanceOf[T]
215+
case 19 => Tuple19(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18)).asInstanceOf[T]
216+
case 20 => Tuple20(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18), xs(19)).asInstanceOf[T]
217+
case 21 => Tuple21(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18), xs(19), xs(20)).asInstanceOf[T]
218+
case 22 => Tuple22(xs(0), xs(1), xs(2), xs(3), xs(4), xs(5), xs(6), xs(7), xs(8), xs(9), xs(10), xs(11), xs(12), xs(13), xs(14), xs(15), xs(16), xs(17), xs(18), xs(19), xs(20), xs(21)).asInstanceOf[T]
219+
case _ => TupleXXL(xs).asInstanceOf[T]
220+
}
221+
222+
def dynamicToArray(self: Tuple): Array[Object] = (self: Any) match {
223+
case self: Unit =>
224+
$emptyArray
225+
case self: Tuple1[_] =>
226+
val t = self.asInstanceOf[Tuple1[Object]]
227+
Array(t._1)
228+
case self: Tuple2[_, _] =>
229+
val t = self.asInstanceOf[Tuple2[Object, Object]]
230+
Array(t._1, t._2)
231+
case self: Tuple3[_, _, _] =>
232+
val t = self.asInstanceOf[Tuple3[Object, Object, Object]]
233+
Array(t._1, t._2, t._3)
234+
case self: Tuple4[_, _, _, _] =>
235+
val t = self.asInstanceOf[Tuple4[Object, Object, Object, Object]]
236+
Array(t._1, t._2, t._3, t._4)
237+
case self: TupleXXL =>
238+
asInstanceOf[TupleXXL].elems
239+
case self: Product =>
240+
val arr = new Array[Object](self.productArity)
241+
for (i <- 0 until arr.length) arr(i) = self.productElement(i).asInstanceOf[Object]
242+
arr
243+
}
244+
245+
def dynamic_*: [This <: Tuple, H] (self: Tuple, x: H): H *: This = {
246+
type Result = H *: This
247+
(self: Any) match {
248+
case Unit =>
249+
Tuple1(x).asInstanceOf[Result]
250+
case self: Tuple1[_] =>
251+
Tuple2(x, self._1).asInstanceOf[Result]
252+
case self: Tuple2[_, _] =>
253+
Tuple3(x, self._1, self._2).asInstanceOf[Result]
254+
case self: Tuple3[_, _, _] =>
255+
Tuple4(x, self._1, self._2, self._3).asInstanceOf[Result]
256+
case self: Tuple4[_, _, _, _] =>
257+
Tuple5(x, self._1, self._2, self._3, self._4).asInstanceOf[Result]
258+
case _ =>
259+
dynamicFromArray[Result]($consArray(x, dynamicToArray(self)))
260+
}
261+
}
262+
263+
def dynamic_++[This <: Tuple, That <: Tuple](self: This, that: That): Concat[This, That] = {
264+
type Result = Concat[This, That]
265+
(this: Any) match {
266+
case self: Unit => return self.asInstanceOf[Result]
267+
case _ =>
268+
}
269+
(that: Any) match {
270+
case that: Unit => return self.asInstanceOf[Result]
271+
case _ =>
272+
}
273+
dynamicFromArray[Result](dynamicToArray(self) ++ dynamicToArray(that))
274+
}
275+
276+
def dynamicSize[This <: Tuple](self: This) = (self: Any) match {
277+
case self: Unit => 0
278+
case self: TupleXXL => self.elems.length
279+
case self: Product => self.productArity
280+
}
186281
}
187282

188283
abstract sealed class NonEmptyTuple extends Tuple {
189284
import Tuple._
285+
import NonEmptyTuple._
190286

191287
rewrite def head: Head[this.type] = {
192288
type Result = Head[this.type]
@@ -209,7 +305,7 @@ abstract sealed class NonEmptyTuple extends Tuple {
209305
val t = asInstanceOf[TupleXXL]
210306
t.elems(0)
211307
case None =>
212-
error(".head cannot be applied to tuple of unknown size")
308+
dynamicHead[this.type](this)
213309
}
214310
resVal.asInstanceOf[Result]
215311
}
@@ -234,54 +330,101 @@ abstract sealed class NonEmptyTuple extends Tuple {
234330
case Some(n) if n > 5 =>
235331
fromArray[Result](toArray.tail)
236332
case None =>
237-
error(".tail cannot be applied to tuple of unknown size")
333+
dynamicTail[this.type](this)
238334
}
239335
}
240336

241-
rewrite def indexOutOfBounds = error("index out of bounds")
337+
rewrite def fallbackApply(n: Int) =
338+
rewrite constValueOpt[n.type] match {
339+
case Some(n: Int) => error("index out of bounds", n)
340+
case None => dynamicApply[this.type](this, n)
341+
}
242342

243-
rewrite def apply(transparent n: Int): Elem[this.type, n.type] = {
343+
rewrite def apply(n: Int): Elem[this.type, n.type] = {
244344
type Result = Elem[this.type, n.type]
245345
rewrite constValueOpt[BoundedSize[this.type]] match {
246346
case Some(1) =>
247347
val t = asInstanceOf[Tuple1[_]]
248-
rewrite n match {
249-
case 0 => t._1.asInstanceOf[Result]
250-
case _ => indexOutOfBounds
348+
rewrite constValueOpt[n.type] match {
349+
case Some(0) => t._1.asInstanceOf[Result]
350+
case _ => fallbackApply(n).asInstanceOf[Result]
251351
}
252352
case Some(2) =>
253353
val t = asInstanceOf[Tuple2[_, _]]
254-
rewrite n match {
255-
case 0 => t._1.asInstanceOf[Result]
256-
case 1 => t._2.asInstanceOf[Result]
257-
case _ => indexOutOfBounds
354+
rewrite constValueOpt[n.type] match {
355+
case Some(0) => t._1.asInstanceOf[Result]
356+
case Some(1) => t._2.asInstanceOf[Result]
357+
case _ => fallbackApply(n).asInstanceOf[Result]
258358
}
259359
case Some(3) =>
260360
val t = asInstanceOf[Tuple3[_, _, _]]
261-
rewrite n match {
262-
case 0 => t._1.asInstanceOf[Result]
263-
case 1 => t._2.asInstanceOf[Result]
264-
case 2 => t._3.asInstanceOf[Result]
265-
case _ => indexOutOfBounds
361+
rewrite constValueOpt[n.type] match {
362+
case Some(0) => t._1.asInstanceOf[Result]
363+
case Some(1) => t._2.asInstanceOf[Result]
364+
case Some(2) => t._3.asInstanceOf[Result]
365+
case _ => fallbackApply(n).asInstanceOf[Result]
266366
}
267367
case Some(4) =>
268368
val t = asInstanceOf[Tuple4[_, _, _, _]]
269-
rewrite n match {
270-
case 0 => t._1.asInstanceOf[Result]
271-
case 1 => t._2.asInstanceOf[Result]
272-
case 2 => t._3.asInstanceOf[Result]
273-
case 3 => t._4.asInstanceOf[Result]
274-
case _ => indexOutOfBounds
369+
rewrite constValueOpt[n.type] match {
370+
case Some(0) => t._1.asInstanceOf[Result]
371+
case Some(1) => t._2.asInstanceOf[Result]
372+
case Some(2) => t._3.asInstanceOf[Result]
373+
case Some(3) => t._4.asInstanceOf[Result]
374+
case _ => fallbackApply(n).asInstanceOf[Result]
275375
}
276-
case Some(s) if s > 4 && s <= $MaxSpecialized && n >= 0 && n < s =>
277-
asInstanceOf[Product].productElement(n).asInstanceOf[Result]
278-
case Some(s) if s > $MaxSpecialized && n >= 0 && n < s =>
279-
asInstanceOf[TupleXXL].elems(n).asInstanceOf[Result]
280-
case Some(s) =>
281-
indexOutOfBounds
282-
case None =>
283-
error("selection (...) cannot be applied to tuple of unknown size")
376+
case Some(s) if s > 4 && s <= $MaxSpecialized =>
377+
val t = asInstanceOf[Product]
378+
rewrite constValueOpt[n.type] match {
379+
case Some(n) if n >= 0 && n < s => t.productElement(n).asInstanceOf[Result]
380+
case _ => fallbackApply(n).asInstanceOf[Result]
381+
}
382+
case Some(s) if s > $MaxSpecialized =>
383+
val t = asInstanceOf[TupleXXL]
384+
rewrite constValueOpt[n.type] match {
385+
case Some(n) if n >= 0 && n < s => t.elems(n).asInstanceOf[Result]
386+
case _ => fallbackApply(n).asInstanceOf[Result]
387+
}
388+
case _ => fallbackApply(n).asInstanceOf[Result]
389+
}
390+
}
391+
}
392+
393+
object NonEmptyTuple {
394+
import Tuple._
395+
396+
def dynamicHead[This <: NonEmptyTuple] (self: This): Head[This] = {
397+
type Result = Head[This]
398+
val res = (self: Any) match {
399+
case self: Tuple1[_] => self._1
400+
case self: Tuple2[_, _] => self._1
401+
case self: Tuple3[_, _, _] => self._1
402+
case self: Tuple4[_, _, _, _] => self._1
403+
case self: TupleXXL => self.elems(0)
404+
case self: Product => self.productElement(0)
405+
}
406+
res.asInstanceOf[Result]
407+
}
408+
409+
def dynamicTail[This <: NonEmptyTuple] (self: This): Tail[This] = {
410+
type Result = Tail[This]
411+
val res = (self: Any) match {
412+
case self: Tuple1[_] => self._1
413+
case self: Tuple2[_, _] => Tuple1(self._2)
414+
case self: Tuple3[_, _, _] => Tuple2(self._2, self._3)
415+
case self: Tuple4[_, _, _, _] => Tuple3(self._2, self._3, self._4)
416+
case _ => dynamicFromArray[Result](self.toArray.tail)
417+
}
418+
res.asInstanceOf[Result]
419+
}
420+
421+
def dynamicApply[This <: NonEmptyTuple] (self: This, n: Int): Elem[This, n.type] = {
422+
type Result = Elem[This, n.type]
423+
val res = (self: Any) match {
424+
case self: TupleXXL => self.elems(n)
425+
case self: Product => self.productElement(n)
284426
}
427+
res.asInstanceOf[Result]
285428
}
286429
}
287430

library/src/scala/TupleXXL.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package scala
22
import java.util.Arrays.{deepEquals, deepHashCode}
33

44
final class TupleXXL private (es: Array[Object]) {
5+
assert(es.length > 22)
56
override def toString = elems.mkString("(", ",", ")")
67
override def hashCode = getClass.hashCode * 41 + deepHashCode(elems)
78
override def equals(that: Any) = that match {

tests/neg/tuple-nonconstant.scala

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/neg/tuple-nonconstant2.scala

Lines changed: 0 additions & 3 deletions
This file was deleted.

tests/neg/tuple-oob2.scala

Lines changed: 0 additions & 3 deletions
This file was deleted.

tests/run/tuples1.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,47 @@ object Test extends App {
7272
val xsc: Unit = xs
7373
println(s"$x23 -> $x, $y, $xs")
7474
}
75+
76+
val x3s: 3 = x3.size
77+
val us: 0 = ().size
78+
val x23s: 23 = x23.size
79+
80+
// dynamic operations
81+
82+
def head1(x: NonEmptyTuple): Tuple.Head[x.type] = x.head
83+
def head2[X <: NonEmptyTuple](x: X): Tuple.Head[X] = x.head
84+
85+
val hd1: Int = head1(x3)
86+
val hd2: Int = head2(x3)
87+
88+
def tail1(x: NonEmptyTuple): Tuple.Tail[x.type] = x.tail
89+
def tail2[X <: NonEmptyTuple](x: X): Tuple.Tail[X] = x.tail
90+
91+
val tl1: (String, Int) = tail1(x3)
92+
val tl2: (String, Int) = tail2(x3)
93+
94+
def elem[X <: NonEmptyTuple](x: X, n: Int): Tuple.Elem[X, n.type] = x(n)
95+
val elem1: String = x3(1)
96+
97+
def toArray[X <: Tuple](x: X): Array[Object] = x.toArray
98+
val toArray1 = x3.toArray
99+
100+
def cons[X, Y <: Tuple](x: X, y: Y): X *: Y = x *: y
101+
val cons1: Boolean *: Int *: (String, Int) = cons(true, x3)
102+
103+
def concat[X <: Tuple, Y <: Tuple](x: X, y: Y): Tuple.Concat[X, Y] = x ++ y
104+
def concat0(x: Tuple, y: Tuple): Tuple.Concat[x.type, y.type] = x ++ y
105+
val conc1: String *: Int *: Unit = concat((), tl1)
106+
val conc2: String *: Int *: Unit = concat(tl1, ())
107+
val conc3: String *: Int *: String *: Int *: Unit = concat(tl1, tl1)
108+
val conc4: String *: Int *: Unit = concat0((), tl1)
109+
val conc5: String *: Int *: Unit = concat0(tl1, ())
110+
val conc6: String *: Int *: String *: Int *: Unit = concat0(tl1, tl1)
111+
112+
def size[X <: Tuple](x: X): Tuple.Size[X] = x.size
113+
def size0(x: Tuple): Tuple.Size[x.type] = x.size
114+
val x3s0: 3 = size(x3)
115+
val us0: 0 = size(())
116+
val x3s1: 3 = size0(x3)
117+
val us1: 0 = size0(())
75118
}

0 commit comments

Comments
 (0)