Skip to content

Question : using derived FromExpr to get the value of annotations ?  #7

@Baccata

Description

@Baccata

Hi there 👋

When reading the Readme, I got excited at the idea that maybe Quotidian would help solve the problem of reading the value of static annotations at compile time. Reduced to a simple form, the problem is essentially this :

given this static annotation:

case class Check(value: Int) extends StaticAnnotation derives FromExpr

I want to write a macro inline def check[T] : Unit to fail compilation if a type T is annotated with a Check that holds a value lower than zero.

So I've tried the following :

//> using lib "io.github.kitlangton::quotidian:0.0.14"
import scala.quoted.*
import quotidian.*
import scala.compiletime.*
import scala.annotation.StaticAnnotation

case class Check(x: Int) extends StaticAnnotation derives ToExpr, FromExpr

inline def check[T]: Unit = ${ checkMacro[T] }

private def checkMacro[T: Type](using Quotes): Expr[Unit] = {
  import quotes.reflect.*
  val sym = TypeRepr.of[T].typeSymbol
  val constraintType = TypeRepr.of[Check]
  sym.annotations.find(_.tpe =:= constraintType) match {
    case None => '{ error("couldn't find Check annotation") }
    case Some(term) =>
      term.asExprOf[Check].valueOrAbort match
        case value =>
          if (value.x < 0) '{ error("Check should be positive") }
          else '{ () }
  }
}

and when attempting to call the macro, it appears that the FromExpr is not able to derive the value from the annotation :

@Check(1)
case class Bar()

def test() = {
  check[Bar]
}

fails with :

Compiling project (Scala 3.3.0, JVM)
[error] ./test.scala:5:3
[error] Expected a known value. 
[error] 
[error] The value of: new Check(1)
[error] could not be extracted using Check$$anon$2@16b5188f
[error]   check[Bar]
[error]   ^^^^^^^^^^

So I take it the derived FromExpr is probably not able to "parse" the new T expression, and returns None as a result.
Now I'm only novice when it comes to Scala 3 macros, but a quick read through the internals of quotidian makes me think that maybe the macro is looking at the generated apply invocations for case-classes, and omits the new invocations ? Am I even remotely correct ? Is the support of new could potentially be supported by the library ?

Collateral question : I've also noticed that trying to invoke the FromExpr derivation from Scala 3.3.3 fails with : Illegal reference to scala.quoted.runtime.Expr, which may explain why the FromExpr/ToExpr` round-trip tests are commented in this repo. Have some recent changes in the compiler made this library prematurely obsolete ? 😕

Thank you in advance for reading

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions