Skip to content

Commit 7f96001

Browse files
committed
Add reflect Symbol.info and ClassInfo
Also fix TypeReprStructure printer for Binder to avoid cycles and remove workaround in TypeLambda. Fixes #11657
1 parent 93718bd commit 7f96001

File tree

7 files changed

+172
-3
lines changed

7 files changed

+172
-3
lines changed

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,32 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
21302130
end extension
21312131
end MatchCaseMethods
21322132

2133+
type ClassInfo = dotc.core.Types.ClassInfo
2134+
2135+
given ClassInfoTypeTest: TypeTest[TypeRepr, ClassInfo] with
2136+
def unapply(x: TypeRepr): Option[ClassInfo & x.type] = x match
2137+
case x: (Types.ClassInfo & x.type) => Some(x)
2138+
case _ => None
2139+
end ClassInfoTypeTest
2140+
2141+
object ClassInfo extends ClassInfoModule:
2142+
def unapply(x: ClassInfo): (TypeRepr, Symbol, List[TypeRepr], List[Symbol], Option[TypeRepr]) =
2143+
(x.prefix, x.cls, x.declaredParents, ClassInfoMethods.decls(x), ClassInfoMethods.selfInfo(x))
2144+
end ClassInfo
2145+
2146+
given ClassInfoMethods: ClassInfoMethods with
2147+
extension (self: ClassInfo)
2148+
def qualifier: TypeRepr = self.prefix
2149+
def decls: List[Symbol] = self.decls.toList
2150+
def declaredParents: List[TypeRepr] = self.declaredParents
2151+
def selfInfo: Option[TypeRepr] =
2152+
self.selfInfo match
2153+
case dotc.core.Types.NoType => None
2154+
case info: dotc.core.Types.Type => Some(info)
2155+
case sym: dotc.core.Symbols.Symbol => Some(sym.typeRef)
2156+
end extension
2157+
end ClassInfoMethods
2158+
21332159
type TypeBounds = dotc.core.Types.TypeBounds
21342160

21352161
object TypeBoundsTypeTest extends TypeTest[TypeRepr, TypeBounds]:
@@ -2408,6 +2434,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
24082434

24092435
given SymbolMethods: SymbolMethods with
24102436
extension (self: Symbol)
2437+
def info: TypeRepr = self.denot.info
24112438
def owner: Symbol = self.denot.owner
24122439
def maybeOwner: Symbol = self.denot.maybeOwner
24132440
def flags: Flags = self.denot.flags

compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ object Extractors {
214214
case ByNameType(underlying) =>
215215
this += "ByNameType(" += underlying += ")"
216216
case ParamRef(binder, idx) =>
217-
this += "ParamRef(" += binder += ", " += idx += ")"
217+
this += "ParamRef(binder, " += idx += ")"
218218
case ThisType(tp) =>
219219
this += "ThisType(" += tp += ")"
220220
case SuperType(thistpe, supertpe) =>
@@ -228,8 +228,9 @@ object Extractors {
228228
case PolyType(argNames, argBounds, resType) =>
229229
this += "PolyType(" ++= argNames += ", " ++= argBounds += ", " += resType += ")"
230230
case TypeLambda(argNames, argBounds, resType) =>
231-
// resType is not printed to avoid cycles
232-
this += "TypeLambda(" ++= argNames += ", " ++= argBounds += ", _)"
231+
this += "TypeLambda(" ++= argNames += ", " ++= argBounds += ", " += resType += ")"
232+
case ClassInfo(prefix, cls, parents, decls, selfInfo) =>
233+
this += "ClassInfo(" += prefix += ", cls/*" += cls.fullName += "*/, " ++= parents += ", decls, " += selfInfo += ")"
233234
case TypeBounds(lo, hi) =>
234235
this += "TypeBounds(" += lo += ", " += hi += ")"
235236
case NoPrefix() =>

compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,16 @@ object SourceCode {
12271227
this += "] => "
12281228
printType(tpe.resType)
12291229

1230+
case ClassInfo(prefix, cls, parents, decls, selfInfo) =>
1231+
this += (if cls.flags.is(Flags.Trait) then "trait " else "class ")
1232+
this += cls.fullName += " extends "
1233+
printList(parents, " with ", printType)
1234+
for info <- selfInfo do
1235+
this += " { _: "
1236+
printType(info)
1237+
this += " => }"
1238+
this
1239+
12301240
case tpe@TypeBounds(lo, hi) =>
12311241
this += "_ >: "
12321242
printType(lo)

library/src/scala/quoted/Quotes.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
183183
* | | +- PolyType
184184
* | +- TypeLambda
185185
* +- MatchCase
186+
* +- ClassInfo
186187
* +- TypeBounds
187188
* +- NoPrefix
188189
*
@@ -2994,6 +2995,40 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
29942995
end extension
29952996
end MatchCaseMethods
29962997

2998+
/** Type of the definition of a type lambda taking a list of type parameters. It's return type may be a TypeLambda. */
2999+
type ClassInfo <: TypeRepr
3000+
3001+
/** `TypeTest` that allows testing at runtime in a pattern match if a `TypeRepr` is a `TypeLambda` */
3002+
given ClassInfoTypeTest: TypeTest[TypeRepr, ClassInfo]
3003+
3004+
/** Module object of `type ClassInfo` */
3005+
val ClassInfo: ClassInfoModule
3006+
3007+
/** Methods of the module object `val ClassInfo` */
3008+
trait ClassInfoModule { this: ClassInfo.type =>
3009+
def unapply(x: ClassInfo): (TypeRepr, Symbol, List[TypeRepr], List[Symbol], Option[TypeRepr])
3010+
}
3011+
3012+
/** Makes extension methods on `ClassInfo` available without any imports */
3013+
given ClassInfoMethods: ClassInfoMethods
3014+
3015+
/** Extension methods of `ClassInfo` */
3016+
trait ClassInfoMethods:
3017+
extension (self: ClassInfo)
3018+
/** The qualifier on which parents, decls, and selfType need to be rebased. */
3019+
def qualifier: TypeRepr
3020+
/** The symbols defined directly in this class. */
3021+
def decls: List[Symbol]
3022+
/** The parent types of this class.
3023+
* These are all normalized to be TypeRefs by moving any refinements
3024+
* to be member definitions of the class itself.
3025+
* Unlike `parents`, the types are not seen as seen from `prefix`.
3026+
*/
3027+
def declaredParents: List[TypeRepr]
3028+
/** The type of `this` in this class, if explicitly given, None otherwise. */
3029+
def selfInfo: Option[TypeRepr]
3030+
end extension
3031+
end ClassInfoMethods
29973032

29983033
// ----- TypeBounds -----------------------------------------------
29993034

@@ -3437,6 +3472,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
34373472
trait SymbolMethods {
34383473
extension (self: Symbol)
34393474

3475+
/** TypeRepr of the definitions of this symbol */
3476+
def info: TypeRepr
3477+
34403478
/** Owner of this symbol. The owner is the symbol in which this symbol is defined. Throws if this symbol does not have an owner. */
34413479
def owner: Symbol
34423480

tests/run-macros/symbol-info.check

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
a:
2+
scala.Int
3+
TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")
4+
b:
5+
scala.Int
6+
TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")
7+
c:
8+
=> scala.Int
9+
ByNameType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"))
10+
f1:
11+
()scala.Int
12+
MethodType(Nil, Nil, TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"))
13+
f2:
14+
(i: scala.Int)scala.Int
15+
MethodType(List(i), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"))
16+
f3:
17+
(i: scala.Int)(j: scala.Int)scala.Int
18+
MethodType(List(i), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")), MethodType(List(j), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")))
19+
f4:
20+
[T >: scala.Nothing <: scala.Any] => (x: T)scala.Int
21+
PolyType(List(T), List(TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any"))), MethodType(List(x), List(ParamRef(binder, 0)), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")))
22+
f5:
23+
(i: scala.Int)(x$2: scala.Int)scala.Int
24+
MethodType(List(i), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")), MethodType(List(x$2), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")))
25+
T1:
26+
_ >: scala.Nothing <: scala.Any
27+
TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any"))
28+
T2:
29+
_ >: scala.Int <: scala.Int
30+
TypeBounds(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"))
31+
T3:
32+
_ >: scala.Nothing <: scala.Int
33+
TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"))
34+
T4:
35+
_ >: scala.Nothing <: [X >: scala.Nothing <: scala.Any] => scala.Any
36+
TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeLambda(List(X), List(TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any"))), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any")))
37+
A:
38+
A
39+
TypeRef(NoPrefix(), "A$")
40+
A$:
41+
class Test$._$_$A$ extends java.lang.Object { _: A => }
42+
ClassInfo(NoPrefix(), cls/*Test$._$_$A$*/, List(TypeRef(ThisType(TypeRef(NoPrefix(), "lang")), "Object")), decls, Some(TermRef(NoPrefix(), "A")))
43+
A:
44+
trait Test$._$_$A extends java.lang.Object
45+
ClassInfo(NoPrefix(), cls/*Test$._$_$A*/, List(TypeRef(ThisType(TypeRef(NoPrefix(), "lang")), "Object")), decls, None)
46+
B:
47+
class Test$._$_$B extends java.lang.Object with A
48+
ClassInfo(NoPrefix(), cls/*Test$._$_$B*/, List(TypeRef(ThisType(TypeRef(NoPrefix(), "lang")), "Object"), TypeRef(NoPrefix(), "A")), decls, None)
49+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import quoted.*
2+
3+
inline def printInfos(inline x: Any): Unit = ${ infoExpr('x) }
4+
5+
private def infoExpr(x: Expr[Any])(using Quotes): Expr[Unit] = {
6+
import quotes.reflect.*
7+
val sb = StringBuilder()
8+
new TreeTraverser {
9+
override def traverseTree(tree: Tree)(owner: Symbol): Unit =
10+
tree match
11+
case tree: Definition =>
12+
sb.append(
13+
s"""${tree.name}:
14+
| ${tree.symbol.info.show}
15+
| ${tree.symbol.info.show(using Printer.TypeReprStructure)}
16+
|""".stripMargin)
17+
case _ =>
18+
super.traverseTree(tree)(owner)
19+
}.traverseTree(x.asTerm)(Symbol.spliceOwner)
20+
val infos = Expr(sb.toString)
21+
'{ println($infos) }
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
object Test {
3+
def main(args : Array[String]) : Unit =
4+
printInfos {
5+
val a: Int = 1
6+
var b: Int = 1
7+
def c: Int = 1
8+
def f1(): Int = 1
9+
def f2(i: Int): Int = 1
10+
def f3(i: Int)(j: Int): Int = 1
11+
def f4[T](x: T): Int = 1
12+
def f5(i: Int)(using Int): Int = 1
13+
type T1
14+
type T2 = Int
15+
type T3 <: Int
16+
type T4[X]
17+
object A
18+
trait A
19+
class B[X] extends A
20+
}
21+
}
22+

0 commit comments

Comments
 (0)