0

I’m trying to write the following:

import scala.reflect.runtime.universe._

val value: Tree = /* some AST */
val tpe = typeOf(value)    // This should be the result type of the AST.
                           // This is pseudocode. What should
                           // actually go on this line?
q"""
type U = $tpe
val v: U = $value
"""

I need to capture the type of the value represented by the AST value in tpe and assign it to U. How does one do this?

Edit: Giving a type annotation for value and matching on it via quasiquotes isn't an option here. The use case is a Shapeless extensible record, which has complicated types such as String with labelled.KeyTag[1, String] :: Long with labelled.KeyTag[three, Long] :: HNil for something like val ls = (1 ->> "two") :: ("three" ->> 4L) :: HNil. Also, the value AST is programmatically generated and not a literal.

Dan Li
  • 866
  • 1
  • 7
  • 19

2 Answers2

2

Get a ToolBox, use it to typecheck value, and ask the annotated tree for its type.

import scala.runtime.reflect.currentMirror
val toolbox = currentMirror.mkToolBox()
val tpe = TypeTree(toolbox.typecheck(value).tpe)

The code you've written states you're doing this at runtime. The use case you've stated in your comment makes it seem like you're in a compile-time macro. In that case, use the similar typecheck method in your Context. It won't typecheck otherwise; the value would be a Tree from the wrong Universe, representing the fact that the new compiler instance made by ToolBox operates in the context of the current program (which happens to be a compiler), while the reflection represented by the Context is all about the future context of the code manipulated by the containing compiler.

HTNW
  • 27,182
  • 1
  • 32
  • 60
  • Thank you, this works as advertised. I have the final piece of the puzzle in one more question here; I’d be very grateful if you could take a look: https://stackoverflow.com/questions/55821775/dependent-type-seems-to-not-work-when-generated-by-scala-macro – Dan Li Apr 24 '19 at 03:02
  • You helped me 8 months ago with this — I have a follow-up question now that seems like it’s a natural extension of this one, but I can’t for the life of me figure out the answer. If you could take a quick look, I’d be very appreciative: https://stackoverflow.com/questions/59387459/how-to-get-the-runtime-value-of-parameter-passed-to-a-scala-macro – Dan Li Dec 18 '19 at 07:45
0

Try to use $tpt in q"$mods val $tname: $tpt = $expr"

val t: Tree = reify {
  val value: Int = 3
}.tree

val tpe = t match {
  case q"{$mods val $tname: $tpt = $expr; $_}" => tpt
} // Int

https://docs.scala-lang.org/overviews/quasiquotes/syntax-summary.html#definitions

Connected question: How does one use quasiquotes to obtain the type of a value?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • The problem here is that I cannot give a type annotation for `value`. My actual use case is a Shapeless extensible record, which has the complicated type of `String with labelled.KeyTag[1, String] :: Long with labelled.KeyTag[three, Long] :: HNil` for something like `val ls = (1 ->> "two") :: ("three" ->> 4L) :: HNil`. Also, the `value` AST is programmatically generated and not a literal. – Dan Li Apr 22 '19 at 23:59