Description
Minimized code
(Note: Full code sample available here: https://github.com/deusaquilus/list_inline_bug)
Let's first create some objects to hold something with non-trivial content.
case class Entity(value: String)
case class Input(ent: Entity)
case class Container(ents: List[Entity])
Let's make a macro that pulls out Entity from Input assuming Input was created via the standard .apply function.
inline def container(inline c: Input):Container = ${ containerImpl('c) }
def containerImpl(c: Expr[Input])(using Quotes): Expr[Container] =
import quotes.reflect._
val entExpr = c match
case '{ Input($ent) } => ent
case _ => report.throwError("Cannot Extract Entity from Input")
'{ Container(List($entExpr)) }
Then let's make a second function to take the result of the first, pull out the entity from the container, and then do a ListFromExpr on the list. This involves writing a FromExpr for Entity:
given FromExpr[Entity] with
def unapply(expr: Expr[Entity])(using Quotes) = expr match
case '{ Entity(${Expr(value)}) } => Some(Entity(value))
case _ => None
... and then the actual function that will do list.valueOrError
.
inline def pull(inline c: Container): Entity = ${ pullImpl('c) }
def pullImpl(c: Expr[Container])(using Quotes): Expr[Entity] =
import quotes.reflect._
val inputs = c match
case '{ Container($list) } =>
println("List Value: " + Printer.TreeStructure.show(list.asTerm))
list.valueOrError
case _ => report.throwError("Cannot Extract List from Container")
'{ Entity(${Expr(inputs.head.value)}) }
Now let's run the application:
def makeEnt = Entity("foo")
inline def input = Input(makeEnt)
inline def contained = container(input)
inline def output = pull(contained)
def main(args: Array[String]): Unit = {
println( output )
}
Output
This will fail with the following error
[error] 16 | println( output )
[error] | ^^^^^^
[error] |Expected a known value.
[error] |
[error] |The value of: scala.List.apply[io.getquill.Entity](io.getquill.Test.makeEnt)
[error] |could not be extracted using scala.quoted.FromExpr$ListFromExpr@3b76a5b3
[error] | This location contains code that was inlined from Dsl.scala:17
[error] | This location contains code that was inlined from Test.scala:13
The source of the problem seems to be that there is an Inlined
node in the list apply:
Apply(TypeApply(
Select(Ident("List"), "apply"), List(Inferred())
), List(Typed(Repeated(
List(/* Here */ Inlined(None, Nil, Ident("makeEnt"))), Inferred()), Inferred())))
If I change makeEnt
into an inline def
then the problem will go away.
Expectation
The code should compile and return the Entity
object.
Repository
Repo with full code sample available here:
https://github.com/deusaquilus/list_inline_bug