-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add reflect Symbol.info and ClassInfo #11664
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -214,7 +214,7 @@ object Extractors { | |||||
case ByNameType(underlying) => | ||||||
this += "ByNameType(" += underlying += ")" | ||||||
case ParamRef(binder, idx) => | ||||||
this += "ParamRef(" += binder += ", " += idx += ")" | ||||||
this += "ParamRef(binder, " += idx += ")" | ||||||
nicolasstucki marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
case ThisType(tp) => | ||||||
this += "ThisType(" += tp += ")" | ||||||
case SuperType(thistpe, supertpe) => | ||||||
|
@@ -228,8 +228,9 @@ object Extractors { | |||||
case PolyType(argNames, argBounds, resType) => | ||||||
this += "PolyType(" ++= argNames += ", " ++= argBounds += ", " += resType += ")" | ||||||
case TypeLambda(argNames, argBounds, resType) => | ||||||
// resType is not printed to avoid cycles | ||||||
this += "TypeLambda(" ++= argNames += ", " ++= argBounds += ", _)" | ||||||
this += "TypeLambda(" ++= argNames += ", " ++= argBounds += ", " += resType += ")" | ||||||
case ClassInfo(prefix, cls, parents, decls, selfInfo) => | ||||||
this += "ClassInfo(" += prefix += ", cls/*" += cls.fullName += "*/, " ++= parents += ", decls, " += selfInfo += ")" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, we want to generate something like |
||||||
case TypeBounds(lo, hi) => | ||||||
this += "TypeBounds(" += lo += ", " += hi += ")" | ||||||
case NoPrefix() => | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -183,6 +183,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => | |
* | | +- PolyType | ||
* | +- TypeLambda | ||
* +- MatchCase | ||
* +- ClassInfo | ||
* +- TypeBounds | ||
* +- NoPrefix | ||
* | ||
|
@@ -2994,6 +2995,40 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => | |
end extension | ||
end MatchCaseMethods | ||
|
||
/** Type of the definition of a type lambda taking a list of type parameters. It's return type may be a TypeLambda. */ | ||
type ClassInfo <: TypeRepr | ||
|
||
/** `TypeTest` that allows testing at runtime in a pattern match if a `TypeRepr` is a `TypeLambda` */ | ||
given ClassInfoTypeTest: TypeTest[TypeRepr, ClassInfo] | ||
|
||
/** Module object of `type ClassInfo` */ | ||
val ClassInfo: ClassInfoModule | ||
|
||
/** Methods of the module object `val ClassInfo` */ | ||
trait ClassInfoModule { this: ClassInfo.type => | ||
def unapply(x: ClassInfo): (TypeRepr, Symbol, List[TypeRepr], List[Symbol], Option[TypeRepr]) | ||
} | ||
|
||
/** Makes extension methods on `ClassInfo` available without any imports */ | ||
given ClassInfoMethods: ClassInfoMethods | ||
|
||
/** Extension methods of `ClassInfo` */ | ||
trait ClassInfoMethods: | ||
extension (self: ClassInfo) | ||
/** The qualifier on which parents, decls, and selfType need to be rebased. */ | ||
def qualifier: TypeRepr | ||
/** The symbols defined directly in this class. */ | ||
def decls: List[Symbol] | ||
/** The parent types of this class. | ||
* These are all normalized to be TypeRefs by moving any refinements | ||
* to be member definitions of the class itself. | ||
* Unlike `parents`, the types are not seen as seen from `prefix`. | ||
*/ | ||
def declaredParents: List[TypeRepr] | ||
/** The type of `this` in this class, if explicitly given, None otherwise. */ | ||
def selfInfo: Option[TypeRepr] | ||
end extension | ||
end ClassInfoMethods | ||
|
||
// ----- TypeBounds ----------------------------------------------- | ||
|
||
|
@@ -3437,6 +3472,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => | |
trait SymbolMethods { | ||
extension (self: Symbol) | ||
|
||
/** TypeRepr of the definitions of this symbol */ | ||
def info: TypeRepr | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we expose There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a document here saying
What about a method like |
||
/** Owner of this symbol. The owner is the symbol in which this symbol is defined. Throws if this symbol does not have an owner. */ | ||
def owner: Symbol | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
a: | ||
scala.Int | ||
TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int") | ||
b: | ||
scala.Int | ||
TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int") | ||
c: | ||
=> scala.Int | ||
ByNameType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")) | ||
f1: | ||
()scala.Int | ||
MethodType(Nil, Nil, TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")) | ||
f2: | ||
(i: scala.Int)scala.Int | ||
MethodType(List(i), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")) | ||
f3: | ||
(i: scala.Int)(j: scala.Int)scala.Int | ||
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"))) | ||
f4: | ||
[T >: scala.Nothing <: scala.Any] => (x: T)scala.Int | ||
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"))) | ||
f5: | ||
(i: scala.Int)(x$2: scala.Int)scala.Int | ||
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"))) | ||
T1: | ||
_ >: scala.Nothing <: scala.Any | ||
TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Any")) | ||
T2: | ||
_ >: scala.Int <: scala.Int | ||
TypeBounds(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")) | ||
T3: | ||
_ >: scala.Nothing <: scala.Int | ||
TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "<root>")), "scala"), "Int")) | ||
T4: | ||
_ >: scala.Nothing <: [X >: scala.Nothing <: scala.Any] => scala.Any | ||
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"))) | ||
A: | ||
A | ||
TypeRef(NoPrefix(), "A$") | ||
A$: | ||
class Test$._$_$A$ extends java.lang.Object { _: A => } | ||
ClassInfo(NoPrefix(), cls/*Test$._$_$A$*/, List(TypeRef(ThisType(TypeRef(NoPrefix(), "lang")), "Object")), decls, Some(TermRef(NoPrefix(), "A"))) | ||
A: | ||
trait Test$._$_$A extends java.lang.Object | ||
ClassInfo(NoPrefix(), cls/*Test$._$_$A*/, List(TypeRef(ThisType(TypeRef(NoPrefix(), "lang")), "Object")), decls, None) | ||
B: | ||
class Test$._$_$B extends java.lang.Object with A | ||
ClassInfo(NoPrefix(), cls/*Test$._$_$B*/, List(TypeRef(ThisType(TypeRef(NoPrefix(), "lang")), "Object"), TypeRef(NoPrefix(), "A")), decls, None) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import quoted.* | ||
|
||
inline def printInfos(inline x: Any): Unit = ${ infoExpr('x) } | ||
|
||
private def infoExpr(x: Expr[Any])(using Quotes): Expr[Unit] = { | ||
import quotes.reflect.* | ||
val sb = StringBuilder() | ||
new TreeTraverser { | ||
override def traverseTree(tree: Tree)(owner: Symbol): Unit = | ||
tree match | ||
case tree: Definition => | ||
sb.append( | ||
s"""${tree.name}: | ||
| ${tree.symbol.info.show} | ||
| ${tree.symbol.info.show(using Printer.TypeReprStructure)} | ||
|""".stripMargin) | ||
case _ => | ||
super.traverseTree(tree)(owner) | ||
}.traverseTree(x.asTerm)(Symbol.spliceOwner) | ||
val infos = Expr(sb.toString) | ||
'{ println($infos) } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
|
||
object Test { | ||
def main(args : Array[String]) : Unit = | ||
printInfos { | ||
val a: Int = 1 | ||
var b: Int = 1 | ||
def c: Int = 1 | ||
def f1(): Int = 1 | ||
def f2(i: Int): Int = 1 | ||
def f3(i: Int)(j: Int): Int = 1 | ||
def f4[T](x: T): Int = 1 | ||
def f5(i: Int)(using Int): Int = 1 | ||
type T1 | ||
type T2 = Int | ||
type T3 <: Int | ||
type T4[X] | ||
object A | ||
trait A | ||
class B[X] extends A | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't expose
declaredParents
as it's just a less precise version ofparents
: https://github.com/lampepfl/dotty/blob/0dbe9705ec6a319af2365de4e00d3f66154fc133/compiler/src/dotty/tools/dotc/core/Types.scala#L4536There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But then we would have to always go to the tree which is the thing we are trying to avoid.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, we should expose Type#parents which doesn't involve trees.