Skip to content

Macro fails to match on more specific case: HList example #10358

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

Closed
hmf opened this issue Nov 17, 2020 · 3 comments
Closed

Macro fails to match on more specific case: HList example #10358

hmf opened this issue Nov 17, 2020 · 3 comments
Assignees
Labels
area:metaprogramming:quotes Issues related to quotes and splices

Comments

@hmf
Copy link

hmf commented Nov 17, 2020

Minimized code

  sealed trait HList
  case class HCons[+HD, TL <: HList](hd: HD, tl: TL) extends HList
  case object HNil extends HList

  private def flattenImpl[Xs <: HList](e: Expr[Xs])(using xt:Type[Xs], qctx:QuoteContext):
  Expr[_ <:HList] = {
    import qctx.reflect._

    e match {
      case '{HNil} =>
        '{HNil}

      //case '{HCons($h, $_)} =>
      case '{HCons($h, HCons($_,$_))} =>
        '{???}

      case _ =>
        report.error(s"flattenImpl ???????  ${e.show}", e); '{???}
    }
  }


  transparent inline def flattenM[Xs <: HList](inline xs: Xs): Any = {
    ${flattenImpl('xs)}
  }

  val ffl00: HCons[Int, HCons[Int, HNil.type]] = flattenM( HCons(0, HCons(1, HNil)) )

Output

[error] -- Error: HyperParameters.scala:448:66 
[error] 448 |    val ffl00: HCons[Int, HCons[Int, HNil.type]] = flattenM( HCons(0, HCons(1, HNil)) )
[error]     |                                                             ^^^^^^^^^^^^^^^^^^^^^^^^
[error]     |flattenImpl ???????  automl.Recorder.HCons.apply[scala.Int, automl.Recorder.HCons[scala.Int, automl.Recorder.HNil]](0, automl.Recorder.HCons.apply[scala.Int, automl.Recorder.HNil.type](1, automl.Recorder.HNil))
[error]     | This location contains code that was inlined from HyperParameters.scala:448

Expectation

I expected the match to succeed. If I use the more general case that is commented out, the match occurs.

@prolativ
Copy link
Contributor

prolativ commented Nov 17, 2020

It looks like the problem is with the second parameter of HCons. Let's take a slightly simpler example:

import scala.quoted._

sealed trait HList
case class HCons[HD](hd: HD, tl: HList) extends HList
case object HNil extends HList

def showFirstTwoImpl(e: Expr[HList])(using Quotes): Expr[String] = {
  e match {
      case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
      case _ => '{""}
  }
}

transparent inline def showFirstTwo(inline xs: HList) = ${ showFirstTwoImpl('xs) }
object Main:
  @main
  def run(): Unit = 
    println(showFirstTwo(HCons(123, HCons("abc", HNil))))

Running the code above prints "123abc" but if you redefine HCons to use 2 type parameters like this

case class HCons[HD, TL](hd: HD, tl: TL) extends HList

then it prints an empty string

@nicolasstucki
Copy link
Contributor

Using

case class HCons[HD, TL](hd: HD, tl: TL) extends HList

we can make it match by changing

-      case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
+      case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString}

or

-      case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
+      case '{HCons[hd, HCons[sd, tl]]($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}

The issue with the original one is that we infer

case '{HCons[Any, HCons[Any, Any]]($h1, HCons[Any, Any]($h2, $_))} => ...

That will not match because the type parameters of HCons are invariant.

@nicolasstucki nicolasstucki added area:metaprogramming:quotes Issues related to quotes and splices and removed area:metaprogramming labels Jun 2, 2022
@hmf
Copy link
Author

hmf commented Dec 13, 2022

@nicolasstucki

I have gotten bitten by this again. On reviewing this example, the solution:

-      case '{HCons($h1, HCons($h2, $_))} => '{$h1.toString ++ $h2.toString}
+      case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => '{$h1.toString ++ $h2.toString}

now fails to compile in Scala version 3.2.1 with the following error:

Found:    tl$given1.Underlying
Required: data.Macros3.HList

The second solution seems to work. Is this a regression issue or need I do something else?

Compiler explanation
[error] 777 |      case '{HCons($h1: hd1, HCons($h2: hd2, $_ : tl))} => 
[error]     |                                             ^^^^^^^
[error]     |                                        Found:    tl$given1.Underlying
[error]     |                                        Required: data.Macros3.HList
[error]     |---------------------------------------------------------------------------
[error]     | Explanation (enabled by `-explain`)
[error]     |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[error]     |
[error]     | Tree: ${_}:(tl$given1 @ _).Underlying
[error]     | I tried to show that
[error]     |   tl$given1.Underlying
[error]     | conforms to
[error]     |   TL
[error]     | but the comparison trace ended with `false`:
[error]     |
[error]     |   ==> tl$given1.Underlying  <:  TL
[error]     |     ==> tl$given1.Underlying  <:  TL
[error]     |       ==> tl  <:  TL
[error]     |         ==> tl  <:  TL in frozen constraint
[error]     |           ==> tl  <:  Nothing in frozen constraint
[error]     |             ==> Any  <:  Nothing (left is approximated) in frozen constraint
[error]     |             <== Any  <:  Nothing (left is approximated) in frozen constraint = false
[error]     |           <== tl  <:  Nothing in frozen constraint = false
[error]     |           ==> Any  <:  TL (left is approximated) in frozen constraint
[error]     |             ==> Any  <:  Nothing (left is approximated) in frozen constraint
[error]     |             <== Any  <:  Nothing (left is approximated) in frozen constraint = false
[error]     |           <== Any  <:  TL (left is approximated) in frozen constraint = false
[error]     |         <== tl  <:  TL in frozen constraint = false
[error]     |         ==> add constraint TL >: tl , constraint =  uninstantiated variables: TL, HD, TL, HD, TL, HD, TL, HD, TL  constrained types:    [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL] , [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL],    [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL] , [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL],    [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL]  bounds:       HD := Any      TL >: data.Macros3.HCons[Any, data.Macros3.HList] <: data.Macros3.HList      HD      TL <: data.Macros3.HList      HD >: hd1      TL >: data.Macros3.HCons[HD, TL] <: data.Macros3.HList      HD      TL <: data.Macros3.HList      HD >: hd2      TL <: data.Macros3.HList  ordering: 
[error]     |           ==> lub(Nothing, tl, canConstrain=false, isSoft=true)
[error]     |           <== lub(Nothing, tl, canConstrain=false, isSoft=true) = tl
[error]     |           ==> tl  <:  data.Macros3.HList
[error]     |             ==> Any  <:  data.Macros3.HList (left is approximated)
[error]     |             <== Any  <:  data.Macros3.HList (left is approximated) = false
[error]     |           <== tl  <:  data.Macros3.HList = false
[error]     |         <== add constraint TL >: tl , constraint =  uninstantiated variables: TL, HD, TL, HD, TL, HD, TL, HD, TL  constrained types:    [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL] , [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL],    [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL] , [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL],    [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL]  bounds:       HD := Any      TL >: data.Macros3.HCons[Any, data.Macros3.HList] <: data.Macros3.HList      HD      TL <: data.Macros3.HList      HD >: hd1      TL >: data.Macros3.HCons[HD, TL] <: data.Macros3.HList      HD      TL <: data.Macros3.HList      HD >: hd2      TL <: data.Macros3.HList  ordering:  = false
[error]     |       <== tl  <:  TL = false
[error]     |     <== tl$given1.Underlying  <:  TL = false
[error]     |   <== tl$given1.Underlying  <:  TL = false
[error]     |
[error]     | The tests were made under a constraint with:
[error]     |  uninstantiated variables: TL, HD, TL, HD, TL, HD, TL, HD, TL
[error]     |  constrained types: 
[error]     |   [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL]
[error]     | , [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL], 
[error]     |   [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL]
[error]     | , [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL], 
[error]     |   [HD, TL <: data.Macros3.HList](hd: HD, tl: TL): data.Macros3.HCons[HD, TL]
[error]     |  bounds: 
[error]     |      HD := Any
[error]     |      TL >: data.Macros3.HCons[Any, data.Macros3.HList] <: data.Macros3.HList
[error]     |      HD
[error]     |      TL <: data.Macros3.HList
[error]     |      HD >: hd1
[error]     |      TL >: data.Macros3.HCons[HD, TL] <: data.Macros3.HList
[error]     |      HD
[error]     |      TL <: data.Macros3.HList
[error]     |      HD >: hd2
[error]     |      TL <: data.Macros3.HList
[error]     |  ordering: 
[error]      ---------------------------------------------------------------------------

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:metaprogramming:quotes Issues related to quotes and splices
Projects
None yet
Development

No branches or pull requests

3 participants