Skip to content

Make failing pc tests work again in non-bootstrapped mode #21707

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 1 commit into from
Oct 5, 2024
Merged
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
228 changes: 228 additions & 0 deletions library/src-non-bootstrapped/scala/NamedTuple.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
package scala
import scala.language.experimental.clauseInterleaving
import annotation.experimental
import compiletime.ops.boolean.*

@experimental
object NamedTuple:

/** The type to which named tuples get mapped to. For instance,
* (name: String, age: Int)
* gets mapped to
* NamedTuple[("name", "age"), (String, Int)]
*/
opaque type NamedTuple[N <: Tuple, +V <: Tuple] >: V <: AnyNamedTuple = V

/** A type which is a supertype of all named tuples */
opaque type AnyNamedTuple = Any

def apply[N <: Tuple, V <: Tuple](x: V): NamedTuple[N, V] = x

def unapply[N <: Tuple, V <: Tuple](x: NamedTuple[N, V]): Some[V] = Some(x)

/** A named tuple expression will desugar to a call to `build`. For instance,
* `(name = "Lyra", age = 23)` will desugar to `build[("name", "age")]()(("Lyra", 23))`.
*/
inline def build[N <: Tuple]()[V <: Tuple](x: V): NamedTuple[N, V] = x

extension [V <: Tuple](x: V)
inline def withNames[N <: Tuple]: NamedTuple[N, V] = x

export NamedTupleDecomposition.{
Names, DropNames,
apply, size, init, head, last, tail, take, drop, splitAt, ++, map, reverse, zip, toList, toArray, toIArray
}

extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V])

// ALL METHODS DEPENDING ON `toTuple` MUST BE EXPORTED FROM `NamedTupleDecomposition`
/** The underlying tuple without the names */
inline def toTuple: V = x

// This intentionally works for empty named tuples as well. I think NonEmptyTuple is a dead end
// and should be reverted, just like NonEmptyList is also appealing at first, but a bad idea
// in the end.

// inline def :* [L] (x: L): NamedTuple[Append[N, ???], Append[V, L] = ???
// inline def *: [H] (x: H): NamedTuple[??? *: N], H *: V] = ???

end extension

/** The size of a named tuple, represented as a literal constant subtype of Int */
type Size[X <: AnyNamedTuple] = Tuple.Size[DropNames[X]]

/** The type of the element value at position N in the named tuple X */
type Elem[X <: AnyNamedTuple, N <: Int] = Tuple.Elem[DropNames[X], N]

/** The type of the first element value of a named tuple */
type Head[X <: AnyNamedTuple] = Elem[X, 0]

/** The type of the last element value of a named tuple */
type Last[X <: AnyNamedTuple] = Tuple.Last[DropNames[X]]

/** The type of a named tuple consisting of all elements of named tuple X except the first one */
type Tail[X <: AnyNamedTuple] = Drop[X, 1]

/** The type of the initial part of a named tuple without its last element */
type Init[X <: AnyNamedTuple] =
NamedTuple[Tuple.Init[Names[X]], Tuple.Init[DropNames[X]]]

/** The type of the named tuple consisting of the first `N` elements of `X`,
* or all elements if `N` exceeds `Size[X]`.
*/
type Take[X <: AnyNamedTuple, N <: Int] =
NamedTuple[Tuple.Take[Names[X], N], Tuple.Take[DropNames[X], N]]

/** The type of the named tuple consisting of all elements of `X` except the first `N` ones,
* or no elements if `N` exceeds `Size[X]`.
*/
type Drop[X <: AnyNamedTuple, N <: Int] =
NamedTuple[Tuple.Drop[Names[X], N], Tuple.Drop[DropNames[X], N]]

/** The pair type `(Take(X, N), Drop[X, N]). */
type Split[X <: AnyNamedTuple, N <: Int] = (Take[X, N], Drop[X, N])

/** Type of the concatenation of two tuples `X` and `Y` */
type Concat[X <: AnyNamedTuple, Y <: AnyNamedTuple] =
NamedTuple[Tuple.Concat[Names[X], Names[Y]], Tuple.Concat[DropNames[X], DropNames[Y]]]

/** The type of the named tuple `X` mapped with the type-level function `F`.
* If `X = (n1 : T1, ..., ni : Ti)` then `Map[X, F] = `(n1 : F[T1], ..., ni : F[Ti])`.
*/
type Map[X <: AnyNamedTuple, F[_ <: Tuple.Union[DropNames[X]]]] =
NamedTuple[Names[X], Tuple.Map[DropNames[X], F]]

/** A named tuple with the elements of tuple `X` in reversed order */
type Reverse[X <: AnyNamedTuple] =
NamedTuple[Tuple.Reverse[Names[X]], Tuple.Reverse[DropNames[X]]]

/** The type of the named tuple consisting of all element values of
* named tuple `X` zipped with corresponding element values of
* named tuple `Y`. If the two tuples have different sizes,
* the extra elements of the larger tuple will be disregarded.
* The names of `X` and `Y` at the same index must be the same.
* The result tuple keeps the same names as the operand tuples.
* For example, if
* ```
* X = (n1 : S1, ..., ni : Si)
* Y = (n1 : T1, ..., nj : Tj) where j >= i
* ```
* then
* ```
* Zip[X, Y] = (n1 : (S1, T1), ..., ni: (Si, Ti))
* ```
* @syntax markdown
*/
type Zip[X <: AnyNamedTuple, Y <: AnyNamedTuple] =
Names[X] match
case Names[Y] =>
NamedTuple[Names[X], Tuple.Zip[DropNames[X], DropNames[Y]]]

/** A type specially treated by the compiler to represent all fields of a
* class argument `T` as a named tuple. Or, if `T` is already a named tuple,
* `From[T]` is the same as `T`.
*/
type From[T] <: AnyNamedTuple

/** The type of the empty named tuple */
type Empty = NamedTuple[EmptyTuple, EmptyTuple]

/** The empty named tuple */
val Empty: Empty = EmptyTuple

end NamedTuple

/** Separate from NamedTuple object so that we can match on the opaque type NamedTuple. */
@experimental
object NamedTupleDecomposition:
import NamedTuple.*
extension [N <: Tuple, V <: Tuple](x: NamedTuple[N, V])
/** The value (without the name) at index `n` of this tuple */
inline def apply(n: Int): Tuple.Elem[V, n.type] =
inline x.toTuple match
case tup: NonEmptyTuple => tup(n).asInstanceOf[Tuple.Elem[V, n.type]]
case tup => tup.productElement(n).asInstanceOf[Tuple.Elem[V, n.type]]

/** The number of elements in this tuple */
inline def size: Tuple.Size[V] = x.toTuple.size

/** The first element value of this tuple */
inline def head: Tuple.Elem[V, 0] = apply(0)

/** The last element value of this tuple */
inline def last: Tuple.Last[V] = apply(size - 1).asInstanceOf[Tuple.Last[V]]

/** The tuple consisting of all elements of this tuple except the last one */
inline def init: NamedTuple[Tuple.Init[N], Tuple.Init[V]] =
x.toTuple.take(size - 1).asInstanceOf[NamedTuple[Tuple.Init[N], Tuple.Init[V]]]

/** The tuple consisting of all elements of this tuple except the first one */
inline def tail: NamedTuple[Tuple.Tail[N], Tuple.Tail[V]] =
x.toTuple.drop(1).asInstanceOf[NamedTuple[Tuple.Tail[N], Tuple.Tail[V]]]

/** The tuple consisting of the first `n` elements of this tuple, or all
* elements if `n` exceeds `size`.
*/
inline def take(n: Int): NamedTuple[Tuple.Take[N, n.type], Tuple.Take[V, n.type]] =
x.toTuple.take(n)

/** The tuple consisting of all elements of this tuple except the first `n` ones,
* or no elements if `n` exceeds `size`.
*/
inline def drop(n: Int): NamedTuple[Tuple.Drop[N, n.type], Tuple.Drop[V, n.type]] =
x.toTuple.drop(n)

/** The tuple `(x.take(n), x.drop(n))` */
inline def splitAt(n: Int):
(NamedTuple[Tuple.Take[N, n.type], Tuple.Take[V, n.type]],
NamedTuple[Tuple.Drop[N, n.type], Tuple.Drop[V, n.type]]) =
// would be nice if this could have type `Split[NamedTuple[N, V]]` instead, but
// we get a type error then. Similar for other methods here.
x.toTuple.splitAt(n)

/** The tuple consisting of all elements of this tuple followed by all elements
* of tuple `that`. The names of the two tuples must be disjoint.
*/
inline def ++ [N2 <: Tuple, V2 <: Tuple](that: NamedTuple[N2, V2])(using Tuple.Disjoint[N, N2] =:= true)
: NamedTuple[Tuple.Concat[N, N2], Tuple.Concat[V, V2]]
= x.toTuple ++ that.toTuple

/** The named tuple consisting of all element values of this tuple mapped by
* the polymorphic mapping function `f`. The names of elements are preserved.
* If `x = (n1 = v1, ..., ni = vi)` then `x.map(f) = `(n1 = f(v1), ..., ni = f(vi))`.
*/
inline def map[F[_]](f: [t] => t => F[t]): NamedTuple[N, Tuple.Map[V, F]] =
x.toTuple.map(f).asInstanceOf[NamedTuple[N, Tuple.Map[V, F]]]

/** The named tuple consisting of all elements of this tuple in reverse */
inline def reverse: NamedTuple[Tuple.Reverse[N], Tuple.Reverse[V]] =
x.toTuple.reverse

/** The named tuple consisting of all elements values of this tuple zipped
* with corresponding element values in named tuple `that`.
* If the two tuples have different sizes,
* the extra elements of the larger tuple will be disregarded.
* The names of `x` and `that` at the same index must be the same.
* The result tuple keeps the same names as the operand tuples.
*/
inline def zip[V2 <: Tuple](that: NamedTuple[N, V2]): NamedTuple[N, Tuple.Zip[V, V2]] =
x.toTuple.zip(that.toTuple)

/** A list consisting of all element values */
inline def toList: List[Tuple.Union[V]] = x.toTuple.toList.asInstanceOf[List[Tuple.Union[V]]]

/** An array consisting of all element values */
inline def toArray: Array[Object] = x.toTuple.toArray

/** An immutable array consisting of all element values */
inline def toIArray: IArray[Object] = x.toTuple.toIArray

end extension

/** The names of a named tuple, represented as a tuple of literal string values. */
type Names[X <: AnyNamedTuple] <: Tuple = X match
case NamedTuple[n, _] => n

/** The value types of a named tuple represented as a regular tuple. */
type DropNames[NT <: AnyNamedTuple] <: Tuple = NT match
case NamedTuple[_, x] => x