Open
Description
Compiler version
3.1.3
Minimized code
example.scala:
package izumi.reflect.dottyreflection
@main def main = {
object X {
type Alias = String
opaque type Opaque = String
type Bound <: String
opaque type OpaqueBound <: String = String
}
import X.*
println(s"Alias: ${exampleMacro.typeDefRhs[Alias]}")
println(s"Opaque: ${exampleMacro.typeDefRhs[Opaque]}")
println(s"Bound: ${exampleMacro.typeDefRhs[Bound]}")
println(s"OpaqueBound: ${exampleMacro.typeDefRhs[OpaqueBound]}")
}
exampleMacro.scala:
package izumi.reflect.dottyreflection
import scala.quoted.*
object exampleMacro {
inline def typeDefRhs[T <: AnyKind]: (String, String) = ${ typeDefRhsImpl[T] }
def typeDefRhsImpl[T <: AnyKind: Type](using Quotes): Expr[(String, String)] = {
import quotes.reflect.*
val typeRepr = TypeRepr.of[T]
val typeSymbol = typeRepr.typeSymbol
val defDefRhsTpe: String = typeSymbol.tree match {
case t: TypeDef => t.rhs.asInstanceOf[TypeTree].tpe.show
}
val underlyingTpe: String = {
// TypeRef#underlying(using Context)
val underlyingMethod = typeRepr.getClass.getMethods.collect { case m if m.getName == "underlying" => m }.head
// QuotesImpl#ctx: Context
val quotesImplCtxMethod = quotes.getClass.getMethods.collect { case m if m.getName == "ctx" => m }.head
val underlying = underlyingMethod.invoke(typeRepr, quotesImplCtxMethod.invoke(quotes))
underlying.asInstanceOf[TypeRepr].show
}
'{ (${ Expr(defDefRhsTpe) }, ${ Expr(underlyingTpe) }) }
}
}
Output
Alias: (_ >: scala.Predef.String <: scala.Predef.String,_ >: scala.Predef.String <: scala.Predef.String)
Opaque: (_ >: scala.Nothing <: scala.Any,_ >: scala.Nothing <: scala.Any)
Bound: (_ >: scala.Nothing <: scala.Predef.String,_ >: scala.Nothing <: scala.Predef.String)
OpaqueBound: (_ >: scala.Nothing <: scala.Predef.String,_ >: scala.Nothing <: scala.Predef.String)
Expectation
We need to get the right hand side of type alias / opaque / abstract type to to construct a Type Tag from it, but the only way to do this right now is by using Symbol#tree
in typeRepr.typeSymbol.tree match { case t: TypeDef => t.rhs.asInstanceOf[TypeTree].tpe }
Unfortunately, Symbol#tree
sometimes requires -Yretain-trees
to work, which creates problems for end-users.
However, the right hand side is contained in a TypeRepr's TypeRef#underlying
[1] method. But since the method is not exported in Quotes.scala, the only way to access this right now is through java reflection.
Original issue: zio/izumi-reflect#307