Skip to content

Unneeded use of a binding fixes tuple-matching macro that should work without it #15971

Closed
@deusaquilus

Description

@deusaquilus

Compiler version

3.2.0 (also 3.1.3)

Minimized code

Let's say we write a macro that takes apart a tuple into h *: t and puts this into a specialized class HeadTail in order to make it easier to match.

trait HeadTail
object HeadTail:
  case class Pair[H, T <: Tuple](h: H, t: T) extends HeadTail
  object Nil extends HeadTail

object MatchHeadTail {
  transparent inline def apply[A <: Tuple](inline tup: A): HeadTail = ${ applyImpl('tup) }
  def applyImpl[A <: Tuple](tup: Expr[A])(using q: Quotes, tpe: Type[A]): Expr[HeadTail] = {
    import quotes.reflect._
    tpe match
      case '[EmptyTuple] =>
        '{ HeadTail.Nil }
      case '[h *: t] =>
        '{ HeadTail.Pair[h, t]($tup.productElement(0).asInstanceOf[h], $tup.drop(1).asInstanceOf[t]) }
  }
}

Then we attempt to use it like so:

object HeadTailUse {

  inline def concat[T <: Tuple](inline args: T): String =
    inline MatchHeadTail(args) match
      case HeadTail.Nil =>
        ""
      case HeadTail.Pair(head, tail): HeadTail.Pair[h, t] =>
        head.toString + concat(tail)

  def main(args: Array[String]) = {
    println(concat("Hello" *: 42 *: EmptyTuple))
  }
}

An error will occur:

[error] -- Error: /home/alexi/git/tuple-example/src/main/scala/org/deusaquilus/HeadTailUse.scala:13:18 
[error] 13 |    println(concat("Hello" *: 42 *: EmptyTuple))
[error]    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error]    |cannot reduce inline match with
[error]    | scrutinee:  org.deusaquilus.HeadTail.Pair.apply[String, Int *: EmptyTuple.type](
[error]    |  {
[error]    |    val Tuple_this: EmptyTuple.type = EmptyTuple
[error]    |    {
[error]    |      val Tuple_this: (Int *: EmptyTuple.type) = 
[error]    |        (
[error]    |          runtime.Tuples.cons(42, Tuple_this).asInstanceOf[
[error]    |            *:[Int, EmptyTuple.type]
[error]    |          ]
[error]    |        :*:[Int, EmptyTuple.type])
[error]    |      (
[error]    |        runtime.Tuples.cons("Hello", Tuple_this).asInstanceOf[
[error]    |          *:[String, Int *: EmptyTuple.type]
[error]    |        ]
[error]    |      :*:[String, Int *: EmptyTuple.type])
[error]    |    }
[error]    |  }.productElement(0).asInstanceOf[String]
...
[error] one error found

However if we just add a bogus binding e.g. bogusBinding @ then the whole thing will work:

  inline def concat[T <: Tuple](inline args: T): String =
    inline MatchHeadTail(args) match
      case HeadTail.Nil =>
        ""
      case bogusBinding @ HeadTail.Pair(head, tail): HeadTail.Pair[h, t] =>
        head.toString + concat(tail)

// runMain org.deusaquilus.HeadTailUse
// Hello42

Expectation

The same thing should happen in both cases (i.e. the sample should work)

Repo

Code can be found here:
https://github.com/deusaquilus/headtail-macro-fail

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions