Skip to content

Interplay between inline constants and inline parameters #11854

Closed
@artempyanykh

Description

@artempyanykh

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions