-
Couldn't load subscription status.
- Fork 1.1k
Issue with transformed tuples in an inline function (possibly compiler bug?) #16607
-
Hi,
I'm forced to use the inline matches that destruct and re-construct the tuples from the mirror. Both functions should be identical, but the bottom one does not compile.
The usage of summonAll works in the first function, but not the second.
As a side note: it's very hard to debug these things - how could I have the compiler tell me the type of ATypes when its inline function is used?
import scala.deriving.* import scala.compiletime.* object Test { class Reader[A]() given Reader[Int] = Reader() given Reader[Double] = Reader() type PutReader[T] = T match { case (a, b) => Reader[b] } inline def test[ A, ](using aMirror: Mirror.ProductOf[A], ): Unit = { inline erasedValue[aMirror.MirroredElemLabels] match { case _: (head *: tail) => type ALabels = head *: tail inline erasedValue[aMirror.MirroredElemTypes] match { case _: (head *: tail) => type ATypes = head *: tail type AMap = Tuple.Zip[ALabels, ATypes] type Readers = Tuple.Map[AMap, PutReader] summonAll[Readers] // works } } } inline def brokenTest[ A, ](using aMirror: Mirror.ProductOf[A], ): Unit = { type AMap = Tuple.Zip[aMirror.MirroredElemLabels, aMirror.MirroredElemTypes] type Readers = Tuple.Map[AMap, PutReader] summonAll[Readers] // doesn't work } case class Foo(a: Int, b: Double) test[Foo] brokenTest[Foo] }
Beta Was this translation helpful? Give feedback.
All reactions
This code works:
import scala.deriving.* import scala.compiletime.* object Test { class Reader[A]() given Reader[Int] = Reader() given Reader[Double] = Reader() type PutReader[T] = T match { case (a, b) => Reader[b] } inline def brokenTest[ A, ](using aMirror: Mirror.ProductOf[A], ): Unit = { type AMap = Tuple.Zip[aMirror.MirroredElemLabels, aMirror.MirroredElemTypes] & NonEmptyTuple type Readers = Tuple.Map[AMap, PutReader] summonAll[Readers] // doesn't work } case class Foo(a: Int, b: Double) brokenTest[Foo] }
I added & NonEmptyTuple at line 20 to explicitly tell the compiler that AMap is a NonEmptyTuple (note that it is unsafe b...
Replies: 2 comments 2 replies
-
This code works:
import scala.deriving.* import scala.compiletime.* object Test { class Reader[A]() given Reader[Int] = Reader() given Reader[Double] = Reader() type PutReader[T] = T match { case (a, b) => Reader[b] } inline def brokenTest[ A, ](using aMirror: Mirror.ProductOf[A], ): Unit = { type AMap = Tuple.Zip[aMirror.MirroredElemLabels, aMirror.MirroredElemTypes] & NonEmptyTuple type Readers = Tuple.Map[AMap, PutReader] summonAll[Readers] // doesn't work } case class Foo(a: Int, b: Double) brokenTest[Foo] }
I added & NonEmptyTuple at line 20 to explicitly tell the compiler that AMap is a NonEmptyTuple (note that it is unsafe because I don't check if the tuple is empty or not. This is only for quick testing purposes).
It seems that the compiler does not type properly AMap (which should be in this example a NonEmptyTuple). Also for unknown (to me) reasons, using AMap & NonEmptyTuple instead at L21 does not work.
The cause seems to be aMirror.MirorredElemLabels and aMirror.MirroredElemTypes not being accurate enough. For instance, if we use @oscar-broman's original code but replacing aMirror.MirorredElemLabels and aMirror.MirroredElemTypes by ("a", "b"), (Int, Double) compiles and works.
Even if it's annoying, it looks "normal" to me since you don't "know" the specific Mirror.ProductOf passed (from a type perspective).
Beta Was this translation helpful? Give feedback.
All reactions
-
Thanks! I applied similar changes in the code I used to create this minimal test case and it works.
As you mention, it is indeed strange that the types of MirroredElemLabels and MirroredElemTypes are not behaving the same way as if they were hardcoded.
What's strange is that I can understand if the function itself failed to compile, but it only fails to compile when used, despite being used on a non-empty tuple.
Beta Was this translation helpful? Give feedback.
All reactions
-
What's strange is that I can understand if the function itself failed to compile, but it only fails to compile when used, despite being used on a non-empty tuple.
AFAIK that's how inline works in Scala: the inlining process only happens when you use your inline function in a non-inline context (here, brokenTest[Foo] in a non-inline body)
Beta Was this translation helpful? Give feedback.
All reactions
-
I guess what I really want is something like inline type X = ... :-)
Beta Was this translation helpful? Give feedback.