Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Issue with transformed tuples in an inline function (possibly compiler bug?) #16607

Discussion options

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]
}
You must be logged in to vote

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

Comment options

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).

You must be logged in to vote
0 replies
Answer selected by oscar-broman
Comment options

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.

You must be logged in to vote
2 replies
Comment options

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)

Comment options

I guess what I really want is something like inline type X = ... :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /