Consider the following simple macro fragment:
val lstConstructor = weakTypeTag[List[String]].tpe.typeConstructor
println(c.typecheck(tq"$lstConstructor[String]", mode = c.TYPEmode))
This seemed to be pretty straightforward piece of code fails with the following no that informative exception:
[error] scala.reflect.macros.TypecheckException: Any does not take type parameters
[error] at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$3(Typers.scala:44)
[error] at scala.reflect.macros.contexts.Typers.$anonfun$typecheck$2(Typers.scala:38)
[error] at scala.reflect.macros.contexts.Typers.doTypecheck$1(Typers.scala:37)
[error] at scala.reflect.macros.contexts.Typers.typecheck(Typers.scala:51)
[error] at scala.reflect.macros.contexts.Typers.typecheck$(Typers.scala:32)
[error] at scala.reflect.macros.contexts.Context.typecheck(Context.scala:18)
[error] at scala.reflect.macros.contexts.Context.typecheck(Context.scala:18)
What is the right way to aoply a type argument to a type constructor?
UPD:
Manually specifying hardcoded name of the type constructor as
println(c.typecheck(tq"List[String]", mode = c.TYPEmode))
works perfectly fine
Welcome to undocumented land of untyped compiler guts.
The quasiquote tq"List[String]"
makes the following tree:
AppliedTypeTree(Ident(TypeName("List")), List(Ident(TypeName("String"))))
Notice that the first parameter is not a Type
, it's an Ident
. So your quasiquote, tq"$lstConstructor[String]"
, silently replaces the unappropriately typed tree with an empty TypeTree()
:
AppliedTypeTree(TypeTree(), List(Ident(TypeName("String"))))
I'm pretty sure that is equivalent to Any
, so you get the error.
Now, to get an Ident
out of a Type
, you can use .typeSymbol
method (though idents don't have type param info, it doesn't matter whether you did .typeConstructor
before or not). This should work:
c.typecheck(tq"${lstConstructor.typeSymbol}[String]", mode = c.TYPEmode)
Code on Scastie (using runtime universe) here.