diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index d23e40796034..1d3aebe5d401 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -1695,6 +1695,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler dotc.core.Types.decorateTypeApplications(self).appliedTo(targ) def appliedTo(targs: List[TypeRepr]): TypeRepr = dotc.core.Types.decorateTypeApplications(self).appliedTo(targs) + def asSeenFrom(pre: TypeRepr, clazz: Symbol): TypeRepr = + self.asSeenFrom(pre, clazz) end extension end TypeReprMethods @@ -2130,6 +2132,32 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler end extension end MatchCaseMethods + type ClassInfo = dotc.core.Types.ClassInfo + + given ClassInfoTypeTest: TypeTest[TypeRepr, ClassInfo] with + def unapply(x: TypeRepr): Option[ClassInfo & x.type] = x match + case x: (Types.ClassInfo & x.type) => Some(x) + case _ => None + end ClassInfoTypeTest + + object ClassInfo extends ClassInfoModule: + def unapply(x: ClassInfo): (TypeRepr, Symbol, List[TypeRepr], List[Symbol], Option[TypeRepr]) = + (x.prefix, x.cls, x.declaredParents, ClassInfoMethods.decls(x), ClassInfoMethods.selfInfo(x)) + end ClassInfo + + given ClassInfoMethods: ClassInfoMethods with + extension (self: ClassInfo) + def qualifier: TypeRepr = self.prefix + def decls: List[Symbol] = self.decls.toList + def declaredParents: List[TypeRepr] = self.declaredParents + def selfInfo: Option[TypeRepr] = + self.selfInfo match + case dotc.core.Types.NoType => None + case info: dotc.core.Types.Type => Some(info) + case sym: dotc.core.Symbols.Symbol => Some(sym.typeRef) + end extension + end ClassInfoMethods + type TypeBounds = dotc.core.Types.TypeBounds object TypeBoundsTypeTest extends TypeTest[TypeRepr, TypeBounds]: @@ -2408,6 +2436,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler given SymbolMethods: SymbolMethods with extension (self: Symbol) + def info: TypeRepr = self.denot.info def owner: Symbol = self.denot.owner def maybeOwner: Symbol = self.denot.maybeOwner def flags: Flags = self.denot.flags diff --git a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala index 2e9890228a73..a65824764245 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/Extractors.scala @@ -214,7 +214,7 @@ object Extractors { case ByNameType(underlying) => this += "ByNameType(" += underlying += ")" case ParamRef(binder, idx) => - this += "ParamRef(" += binder += ", " += idx += ")" + this += "ParamRef(binder, " += idx += ")" 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 += ")" case TypeBounds(lo, hi) => this += "TypeBounds(" += lo += ", " += hi += ")" case NoPrefix() => diff --git a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala index 3cb5a19d980e..b7bb7a00dbcd 100644 --- a/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala +++ b/compiler/src/scala/quoted/runtime/impl/printers/SourceCode.scala @@ -1227,6 +1227,16 @@ object SourceCode { this += "] => " printType(tpe.resType) + case ClassInfo(prefix, cls, parents, decls, selfInfo) => + this += (if cls.flags.is(Flags.Trait) then "trait " else "class ") + this += cls.fullName += " extends " + printList(parents, " with ", printType) + for info <- selfInfo do + this += " { _: " + printType(info) + this += " => }" + this + case tpe@TypeBounds(lo, hi) => this += "_ >: " printType(lo) diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 542381fb95a0..8bbdd75fef13 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -183,6 +183,7 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => * | | +- PolyType * | +- TypeLambda * +- MatchCase + * +- ClassInfo * +- TypeBounds * +- NoPrefix * @@ -2447,6 +2448,19 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** The current type applied to given type arguments: `this[targ0, ..., targN]` */ def appliedTo(targs: List[TypeRepr]): TypeRepr + /** This type as seen from prefix `pre` and class `clazz`. This means: + * Replace all thistypes of `clazz` or one of its subclasses + * by `pre` and instantiate all parameters by arguments of `pre`. + * Proceed analogously for thistypes referring to outer classes. + * + * Example: + * class D[T] { def m: T } + * class C extends p.D[Int] + * T.asSeenFrom(ThisType(C), D) (where D is owner of m) + * = Int + */ + def asSeenFrom(pre: TypeRepr, clazz: Symbol): TypeRepr + end extension } @@ -2994,6 +3008,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 +3485,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => trait SymbolMethods { extension (self: Symbol) + /** TypeRepr of the definitions of this symbol */ + def info: TypeRepr + /** 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 diff --git a/tests/run-macros/symbol-info.check b/tests/run-macros/symbol-info.check new file mode 100644 index 000000000000..99dbc80274bb --- /dev/null +++ b/tests/run-macros/symbol-info.check @@ -0,0 +1,49 @@ +a: + scala.Int + TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int") +b: + scala.Int + TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int") +c: + => scala.Int + ByNameType(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")) +f1: + ()scala.Int + MethodType(Nil, Nil, TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")) +f2: + (i: scala.Int)scala.Int + MethodType(List(i), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")) +f3: + (i: scala.Int)(j: scala.Int)scala.Int + MethodType(List(i), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")), MethodType(List(j), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "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(), "")), "scala"), "Int"))) +f5: + (i: scala.Int)(x$2: scala.Int)scala.Int + MethodType(List(i), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")), MethodType(List(x$2), List(TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "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(), "")), "scala"), "Int"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "scala"), "Int")) +T3: + _ >: scala.Nothing <: scala.Int + TypeBounds(TypeRef(ThisType(TypeRef(NoPrefix(), "scala")), "Nothing"), TypeRef(TermRef(ThisType(TypeRef(NoPrefix(), "")), "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) + diff --git a/tests/run-macros/symbol-info/Macro_1.scala b/tests/run-macros/symbol-info/Macro_1.scala new file mode 100644 index 000000000000..c2d4de18f658 --- /dev/null +++ b/tests/run-macros/symbol-info/Macro_1.scala @@ -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) } +} diff --git a/tests/run-macros/symbol-info/Test_2.scala b/tests/run-macros/symbol-info/Test_2.scala new file mode 100644 index 000000000000..2fd2ed69abb3 --- /dev/null +++ b/tests/run-macros/symbol-info/Test_2.scala @@ -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 + } +} +