Skip to content

Commit bea5f20

Browse files
committed
Port gestalt TypeToolbox
1 parent 3cfbb6d commit bea5f20

File tree

8 files changed

+404
-0
lines changed

8 files changed

+404
-0
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tastyreflect
33

44
import dotty.tools.dotc.core.Flags
55
import dotty.tools.dotc.core.Symbols._
6+
import dotty.tools.dotc.core.Decorators._
67

78
trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with TastyCoreImpl {
89

@@ -73,6 +74,8 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with TastyCoreImpl {
7374
}
7475
}
7576

77+
def info(implicit ctx: Context): Type = symbol.info
78+
7679
}
7780

7881
object IsPackageSymbol extends IsPackageSymbolExtractor {
@@ -93,13 +96,67 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with TastyCoreImpl {
9396
def tree(implicit ctx: Context): TypeDef = FromSymbol.typeDefFromSym(symbol)
9497
}
9598

99+
object ClassSymbol extends ClassSymbolModule {
100+
def of(fullName: String)(implicit ctx: Context): ClassSymbol = ctx.requiredClass(fullName)
101+
}
102+
96103
object IsClassSymbol extends IsClassSymbolExtractor {
97104
def unapply(symbol: Symbol)(implicit ctx: Context): Option[ClassSymbol] =
98105
if (symbol.isClass) Some(symbol.asClass) else None
99106
}
100107

101108
def ClassSymbolDeco(symbol: ClassSymbol): ClassSymbolAPI = new ClassSymbolAPI {
102109
def tree(implicit ctx: Context): ClassDef = FromSymbol.classDef(symbol)
110+
111+
def fields(implicit ctx: Context): List[Symbol] = {
112+
symbol.unforcedDecls.filter(isField)
113+
}
114+
115+
def field(name: String)(implicit ctx: Context): Option[Symbol] = {
116+
val sym = symbol.unforcedDecls.find(sym => sym.name == name.toTermName)
117+
if (sym.exists && isField(sym)) Some(sym) else None
118+
}
119+
120+
def classMethod(name: String)(implicit ctx: Context): List[DefSymbol] = {
121+
symbol.typeRef.decls.iterator.collect {
122+
case sym if isMethod(sym) && sym.name.toString == name => sym.asTerm
123+
}.toList
124+
}
125+
126+
def classMethods(implicit ctx: Context): List[DefSymbol] = {
127+
symbol.typeRef.decls.iterator.collect {
128+
case sym if isMethod(sym) => sym.asTerm
129+
}.toList
130+
}
131+
132+
def method(name: String)(implicit ctx: Context): List[DefSymbol] = {
133+
symbol.typeRef.allMembers.iterator.map(_.symbol).collect {
134+
case sym if isMethod(sym) && sym.name.toString == name => sym.asTerm
135+
}.toList
136+
}
137+
138+
def methods(implicit ctx: Context): List[DefSymbol] = {
139+
symbol.typeRef.allMembers.iterator.map(_.symbol).collect {
140+
case sym if isMethod(sym) => sym.asTerm
141+
}.toList
142+
}
143+
144+
private def isMethod(sym: Symbol)(implicit ctx: Context): Boolean =
145+
sym.isTerm && sym.is(Flags.Method) && !sym.isConstructor
146+
147+
def caseFields(implicit ctx: Context): List[ValSymbol] = {
148+
if (!symbol.isClass) Nil
149+
else symbol.asClass.paramAccessors.collect {
150+
case sym if sym.is(Flags.CaseAccessor) => sym.asTerm
151+
}
152+
}
153+
154+
def companionClass(implicit ctx: Context): Option[ClassSymbol] = {
155+
val sym = symbol.companionModule.companionClass
156+
if (sym.exists) Some(sym.asClass) else None
157+
}
158+
159+
private def isField(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm && !sym.is(Flags.Method)
103160
}
104161

105162
object IsDefSymbol extends IsDefSymbolExtractor {

compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package dotty.tools.dotc.tastyreflect
22

33
import dotty.tools.dotc.core.{Names, Types}
4+
import dotty.tools.dotc.core.Flags
5+
46

57
trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with TastyCoreImpl {
68

@@ -9,6 +11,14 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with Tasty
911
def TypeDeco(tpe: Type): TypeAPI = new TypeAPI {
1012
def =:=(other: Type)(implicit ctx: Context): Boolean = tpe =:= other
1113
def <:<(other: Type)(implicit ctx: Context): Boolean = tpe <:< other
14+
15+
def widen(implicit ctx: Context): Type = tpe.widen
16+
17+
def typeSymbol(implicit ctx: Context): Option[Symbol] = {
18+
val sym = tpe.typeSymbol
19+
if (sym.exists) Some(sym) else None
20+
}
21+
1222
}
1323

1424
def MethodTypeDeco(tpe: MethodType): MethodTypeAPI = new MethodTypeAPI {

compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ trait TypeOrBoundsTreesOpsImpl extends scala.tasty.reflect.TypeOrBoundsTreeOps w
1919
def TypeTreeDeco(tpt: TypeTree): TypeTreeAPI = new TypeTreeAPI {
2020
def pos(implicit ctx: Context): Position = tpt.pos
2121
def tpe(implicit ctx: Context): Type = tpt.tpe.stripTypeVar
22+
def symbol(implicit ctx: Context): Symbol = {
23+
val sym = tpt.symbol
24+
if (sym.isType) sym else sym.companionClass
25+
}
2226
}
2327

2428
object IsTypeTree extends IsTypeTreeExtractor {

library/src/scala/tasty/reflect/SymbolOps.scala

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ trait SymbolOps extends TastyCore {
1515
def privateWithin(implicit ctx: Context): Option[Type]
1616
def protectedWithin(implicit ctx: Context): Option[Type]
1717

18+
/** Name of the definition */
1819
def name(implicit ctx: Context): String
20+
21+
/** Full name of the definition from the _root_ package */
1922
def fullName(implicit ctx: Context): String
2023

2124
def localContext(implicit ctx: Context): Context
@@ -29,6 +32,8 @@ trait SymbolOps extends TastyCore {
2932

3033
def annots(implicit ctx: Context): List[Term]
3134

35+
def info(implicit ctx: Context): Type
36+
3237
}
3338
implicit def SymbolDeco(symbol: Symbol): SymbolAPI
3439

@@ -51,8 +56,38 @@ trait SymbolOps extends TastyCore {
5156
def unapply(symbol: Symbol)(implicit ctx: Context): Option[ClassSymbol]
5257
}
5358

59+
val ClassSymbol: ClassSymbolModule
60+
abstract class ClassSymbolModule {
61+
/** The ClassSymbol of a global class definition */
62+
def of(fullName: String)(implicit ctx: Context): ClassSymbol
63+
}
64+
5465
trait ClassSymbolAPI {
66+
/** Tree of this class definition */
5567
def tree(implicit ctx: Context): ClassDef
68+
69+
/** Fields directly declared in the class */
70+
def fields(implicit ctx: Context): List[Symbol]
71+
72+
/** Field with the given name directly declared in the class */
73+
def field(name: String)(implicit ctx: Context): Option[Symbol]
74+
75+
/** Get non-private named methods defined directly inside the class */
76+
def classMethod(name: String)(implicit ctx: Context): List[DefSymbol]
77+
78+
/** Get all non-private methods defined directly inside the class, exluding constructors */
79+
def classMethods(implicit ctx: Context): List[DefSymbol]
80+
81+
/** Get named non-private methods declared or inherited */
82+
def method(name: String)(implicit ctx: Context): List[DefSymbol]
83+
84+
/** Get all non-private methods declared or inherited */
85+
def methods(implicit ctx: Context): List[DefSymbol]
86+
87+
/** Fields of a case class type -- only the ones declared in primary constructor */
88+
def caseFields(implicit ctx: Context): List[ValSymbol]
89+
90+
def companionClass(implicit ctx: Context): Option[ClassSymbol]
5691
}
5792
implicit def ClassSymbolDeco(symbol: ClassSymbol): ClassSymbolAPI
5893

library/src/scala/tasty/reflect/TypeOrBoundsOps.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ trait TypeOrBoundsOps extends TastyCore {
88
trait TypeAPI {
99
def =:=(other: Type)(implicit ctx: Context): Boolean
1010
def <:<(other: Type)(implicit ctx: Context): Boolean
11+
12+
def widen(implicit ctx: Context): Type
13+
14+
def typeSymbol(implicit ctx: Context): Option[Symbol]
1115
}
1216
implicit def TypeDeco(tpe: Type): TypeAPI
1317

library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ trait TypeOrBoundsTreeOps extends TastyCore {
1313
trait TypeTreeAPI {
1414
def pos(implicit ctx: Context): Position
1515
def tpe(implicit ctx: Context): Type
16+
def symbol(implicit ctx: Context): Symbol
1617
}
1718
implicit def TypeTreeDeco(tpt: TypeTree): TypeTreeAPI
1819

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/main/scala/gestalt/macros/TypeToolbox.scala
2+
// using staging reflection
3+
4+
import scala.quoted._
5+
import scala.tasty._
6+
7+
object TypeToolbox {
8+
/** are the two types equal? */
9+
inline def =:=[A, B]: Boolean = ~tpEqImpl('[A], '[B])
10+
private def tpEqImpl[A, B](a: Type[A], b: Type[B])(implicit tasty: Tasty): Expr[Boolean] = {
11+
import tasty._
12+
val res = a.toTasty.tpe =:= b.toTasty.tpe
13+
res.toExpr
14+
}
15+
16+
/** is `tp1` a subtype of `tp2` */
17+
inline def <:<[A, B]: Boolean = ~tpLEqImpl('[A], '[B])
18+
private def tpLEqImpl[A, B](a: Type[A], b: Type[B])(implicit tasty: Tasty): Expr[Boolean] = {
19+
import tasty._
20+
val res = a.toTasty.tpe <:< b.toTasty.tpe
21+
res.toExpr
22+
}
23+
24+
/** type associated with the tree */
25+
inline def typeOf[T, Expected](a: T): Boolean = ~typeOfImpl('(a), '[Expected])
26+
private def typeOfImpl(a: Expr[_], expected: Type[_])(implicit tasty: Tasty): Expr[Boolean] = {
27+
import tasty._
28+
val res = a.toTasty.tpe =:= expected.toTasty.tpe
29+
res.toExpr
30+
}
31+
32+
/** does the type refer to a case class? */
33+
inline def isCaseClass[A]: Boolean = ~isCaseClassImpl('[A])
34+
private def isCaseClassImpl(tp: Type[_])(implicit tasty: Tasty): Expr[Boolean] = {
35+
import tasty._
36+
val res = tp.toTasty.symbol match {
37+
case IsClassSymbol(sym) => sym.flags.isCase
38+
case _ => false
39+
}
40+
res.toExpr
41+
}
42+
43+
/** val fields of a case class Type -- only the ones declared in primary constructor */
44+
inline def caseFields[T]: List[String] = ~caseFieldsImpl('[T])
45+
private def caseFieldsImpl(tp: Type[_])(implicit tasty: Tasty): Expr[List[String]] = {
46+
import tasty._
47+
val fields = tp.toTasty.symbol.asClass.caseFields.map(_.name)
48+
fields.toExpr
49+
}
50+
51+
inline def fieldIn[T](inline mem: String): String = ~fieldInImpl('[T], mem)
52+
private def fieldInImpl(t: Type[_], mem: String)(implicit tasty: Tasty): Expr[String] = {
53+
import tasty._
54+
val field = t.toTasty.symbol.asClass.field(mem)
55+
field.map(_.name).getOrElse("").toExpr
56+
}
57+
58+
inline def fieldsIn[T]: Seq[String] = ~fieldsInImpl('[T])
59+
private def fieldsInImpl(t: Type[_])(implicit tasty: Tasty): Expr[Seq[String]] = {
60+
import tasty._
61+
val fields = t.toTasty.symbol.asClass.fields
62+
fields.map(_.name).toList.toExpr
63+
}
64+
65+
inline def methodIn[T](inline mem: String): Seq[String] = ~methodInImpl('[T], mem)
66+
private def methodInImpl(t: Type[_], mem: String)(implicit tasty: Tasty): Expr[Seq[String]] = {
67+
import tasty._
68+
t.toTasty.symbol.asClass.classMethod(mem).map(_.name).toExpr
69+
}
70+
71+
inline def methodsIn[T]: Seq[String] = ~methodsInImpl('[T])
72+
private def methodsInImpl(t: Type[_])(implicit tasty: Tasty): Expr[Seq[String]] = {
73+
import tasty._
74+
t.toTasty.symbol.asClass.classMethods.map(_.name).toExpr
75+
}
76+
77+
inline def method[T](inline mem: String): Seq[String] = ~methodImpl('[T], mem)
78+
private def methodImpl(t: Type[_], mem: String)(implicit tasty: Tasty): Expr[Seq[String]] = {
79+
import tasty._
80+
t.toTasty.symbol.asClass.method(mem).map(_.name).toExpr
81+
}
82+
83+
inline def methods[T]: Seq[String] = ~methodsImpl('[T])
84+
private def methodsImpl(t: Type[_])(implicit tasty: Tasty): Expr[Seq[String]] = {
85+
import tasty._
86+
t.toTasty.symbol.asClass.methods.map(_.name).toExpr
87+
}
88+
89+
inline def typeTag[T](x: T): String = ~typeTagImpl('[T])
90+
private def typeTagImpl(tp: Type[_])(implicit tasty: Tasty): Expr[String] = {
91+
import tasty._
92+
val res = tp.toTasty.tpe.show(rootContext, showSourceCode)
93+
res.toExpr
94+
}
95+
96+
inline def companion[T1, T2]: Boolean = ~companionImpl('[T1], '[T2])
97+
private def companionImpl(t1: Type[_], t2: Type[_])(implicit tasty: Tasty): Expr[Boolean] = {
98+
import tasty._
99+
val res = t1.toTasty.symbol.asClass.companionClass.contains(t2.toTasty.symbol)
100+
res.toExpr
101+
}
102+
103+
inline def companionName[T1]: String = ~companionNameImpl('[T1])
104+
private def companionNameImpl(tp: Type[_])(implicit tasty: Tasty): Expr[String] = {
105+
import tasty._
106+
tp.toTasty.symbol match {
107+
case IsClassSymbol(sym) => sym.companionClass.map(_.fullName).getOrElse("").toExpr
108+
case _ => '("")
109+
}
110+
}
111+
112+
// TODO add to the std lib
113+
private implicit def listIsLiftable[T: Type: Liftable]: Liftable[List[T]] = new Liftable {
114+
def toExpr(list: List[T]): Expr[List[T]] = list match {
115+
case x :: xs => '(~x.toExpr :: ~toExpr(xs))
116+
case Nil => '(Nil)
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)