scala3
scala3 copied to clipboard
Inline given with using parameter isn't properly inlined
Note: related to #14282. I am not sure if this change is published in 3.1.3/3.2.0-RC3
Compiler version
Tested in 3.1.3 and 3.2.0-RC3
Minimized code & Output
Inline methods in given instances with using parameters do not always get inlined properly, making them unusable with Expr#value (and similar) in a macro. This behaviour being tricky (works sometimes, even with using parameters), I'm struggling to minmize my example. Here a shrinked code from my current project:
Constraint.scala
trait Constraint[T, C]:
inline def test(value: T): Boolean
inline def message: String
macros.scala
import scala.quoted.*
object macros:
inline def assertCondition(inline cond: Boolean, inline message: String): Unit = ${assertConditionImpl('cond, 'message)}
private def assertConditionImpl(cond: Expr[Boolean], message: Expr[String])(using Quotes): Expr[Unit] =
val report = quotes.reflect.report
val condValue = cond.valueOrAbort //.getOrElse(report.errorAndAbort(s"Expected a known value. Got ${cond.show}"))
val messageValue = message.value.getOrElse("<Unknown message>")
if !condValue then report.errorAndAbort(messageValue)
else '{()}
end macros
Two example Constraint instances:
final class Greater[V <: Int]
inline given [V <: Int]: Constraint[Int, Greater[V]] with
override inline def test(value: Int): Boolean = value > compiletime.constValue[V]
override inline def message: String = "Should be greater"
final class DescribedAs[C, V <: String]
inline given [T, C, Impl <: Constraint[T, C], V <: String](using Impl): Constraint[T, DescribedAs[C, V]] with
override inline def test(value: T): Boolean = summonInline[Impl].test(value)
override inline def message: String = constValue[V]
This works:
macros.assertCondition(summonInline[Constraint[Int, Greater[0]]].test(1), "test") //OK
But this doesn't:
macros.assertCondition(summonInline[Constraint[Int, Greater[0] DescribedAs "pos"]].test(1), "test")
with the following compilation error:
[error] 21 | macros.assertCondition(summonInline[Constraint[Int, Greater[0] DescribedAs "pos"]].test(1), "test")
[error] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] |Expected a known value.
[error] |
[error] |The value of: {
[error] | val x$1$proxy2: io.github.iltotore.iron.Main.given_Constraint_Int_Greater[0] = (new io.github.iltotore.iron.Main.given_Constraint_Int_Greater[0](): io.github.iltotore.iron.Main.g
iven_Constraint_Int_Greater[0])
[error] |
[error] | ((false: scala.Boolean): scala.Boolean)
[error] |}
[error] |could not be extracted using scala.quoted.FromExpr$PrimitiveFromExpr@6ff6baf0
[error] one error found
As shown in the error, an unexpected leftover variable remains after inlining the method which prevents Expr#value to work. I guess this is because the using param do not get inlined in the given instance (and given-with disallows inline parameters).
For instance, it works when dropping the given ... with syntax:
class DescribedAsConstraint[T, C, Impl <: Constraint[T, C], V <: String](using Impl)
extends Constraint[T, DescribedAs[C, V]]:
override inline def test(value: T): Boolean = summonInline[Impl].test(value)
override inline def message: String = constValue[V]
transparent inline given [T, C, Impl <: Constraint[T, C], V <: String](using inline constraint: Impl): Constraint[T, DescribedAs[C, V]] = new DescribedAsConstraint
macros.assertCondition(summonInline[Constraint[Int, Greater[0] DescribedAs "pos"]].test(1), "test") //OK
The potential solution (IMO) is to allow inline parameters in given-with statements and make them desugar like this:
trait Test:
inline def foo: Boolean
From
inline given (using inline dep: Foo): Test with
override inline def foo: Boolean = dep.booleanValue
to
//Or another class name
class Test$1(using inline dep: Foo): //Meaning inline must also be allowed in constructors like this to work
override inline def foo: Boolean = dep.booleanValue
inline given (using inline dep: Foo): Test = new Test$1
Note: Nicolas Stucki commented in the previous issue that inline isn't required for given didn't test without but I will edit this issue once done.