Description
Perhaps I'm missing something about how inline constants and inline params are supposed to work together. Below are a couple examples where compiler's behaviour is confusing. I expected inline val
s to act like compile time constant (so that I could use them to build a DSL for some compile-time code generation), but it seems my intuition is not exactly correct.
Some clarification would be really helpful!
Compiler version
3.0.0-RC1
Simple example
object Str:
inline def concat(inline a: String, inline b: String): String =
${ evalConcat('a, 'b) }
def evalConcat(expra: Expr[String], exprb: Expr[String])(using Quotes): Expr[String] =
val a = expra.valueOrError
val b = exprb.valueOrError
Expr(a ++ b)
// This works just fine
println(Str.concat("Hello, ", "Scala 3"))
inline val str1 = "Hello, "
inline val str2 = "Scala 3"
// While this doesn't compile with
// "Expected a known value". The value of: str1 could not be extracted using scala.quoted.FromExpr$PrimitiveFromExpr@4a8ebad7
println(Str.concat(str1, str2))
I'd expect that inline constant would get inlined at the use-site and both variants would work the same, but it seems that in the 2nd variant FromExpr
sees str1
rather than its constant value "Hello, "
.
More complex example
This is the setup. concat
does compile-time concatenation of Bag
contents.
case class Bag(things: Seq[Char])
object Bag:
given ToExpr[Bag] with
def apply(x: Bag)(using Quotes): Expr[Bag] =
'{ Bag(${Expr(x.things)}) }
given FromExpr[Bag] with
def unapply(x: Expr[Bag])(using Quotes): Option[Bag] =
x match
case '{ Bag(${Expr(things)}) } => Some(Bag(things))
case _ => None
inline def concat(inline a: Bag, inline b: Bag): Bag =
${ evalConcat('a, 'b) }
def evalConcat(expra: Expr[Bag], exprb: Expr[Bag])(using Quotes): Expr[Bag] =
val a = expra.valueOrError
val b = exprb.valueOrError
val things = a.things ++ b.things
Expr(Bag(things))
Then this works just fine
println(Bag.concat(Bag(Seq('a', 'b', 'c')), Bag(Seq('d', 'e')))) // => Bag(ArraySeq(a, b, c, d, e))
However, this doesn't compile:
inline val bagA = Bag(Seq('a', 'b', 'c'))
// inline value must have a literal constant type
This is somewhat confusing -- it's OK for Bag(Seq('a', 'b', 'c'))
to be an argument to an inline parameter, why is it not OK for the same expression to be an RHS for an inline constant?
Furthermore, here the compilation error is no longer about RHS of an inline value, but it now complains about not being able to FromExpr.unapply
argument bagA
of Bag.concat
.
inline val bagA = Bag(Seq('a', 'b', 'c'))
inline val bagB = Bag(Seq('a', 'b', 'c'))
println(Bag.concat(bagA, bagB))
// Error: The value of: bagA could not be extracted using org.scalex.Bag$given_FromExpr_Bag$@59323a86
The fact that the compilation error is not not about inline val but is rather about bagA
argument is misleading.