From 98a65a14685ff1f68c5bc5a2e5720d2562e14d92 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 6 Feb 2019 14:21:41 +0100 Subject: [PATCH 1/8] Support show on Type[T] --- .../dotty/tools/dotc/quoted/QuoteDriver.scala | 24 +++++++++++-------- .../dotty/tools/dotc/quoted/ToolboxImpl.scala | 3 ++- .../scala/quoted/Type.scala | 3 +++ library/src/scala/quoted/Toolbox.scala | 3 ++- .../src/scala/tasty/reflect/Printers.scala | 22 +++++++++++++---- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala index 61d1ff0adb9a..e3b5a98cc884 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala @@ -41,17 +41,21 @@ class QuoteDriver extends Driver { method.invoke(inst).asInstanceOf[T] } - def show(expr: Expr[_], settings: Toolbox.Settings): String = { - def show(tree: Tree, ctx: Context): String = { - implicit val c: Context = ctx - val tree1 = - if (ctx.settings.YshowRawQuoteTrees.value) tree - else (new TreeCleaner).transform(tree) - ReflectionImpl.showTree(tree1) - } - withTree(expr, show, settings) + private def doShow(tree: Tree, ctx: Context): String = { + implicit val c: Context = ctx + val tree1 = + if (ctx.settings.YshowRawQuoteTrees.value) tree + else (new TreeCleaner).transform(tree) + ReflectionImpl.showTree(tree1) } + def show(expr: Expr[_], settings: Toolbox.Settings): String = + withTree(expr, doShow, settings) + + def show(tpe: Type[_], settings: Toolbox.Settings): String = + withTypeTree(tpe, doShow, settings) + + def withTree[T](expr: Expr[_], f: (Tree, Context) => T, settings: Toolbox.Settings): T = { val ctx = setToolboxSettings(setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)._2.fresh, settings) @@ -65,7 +69,7 @@ class QuoteDriver extends Driver { } def withTypeTree[T](tpe: Type[_], f: (TypTree, Context) => T, settings: Toolbox.Settings): T = { - val (_, ctx: Context) = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh) + val ctx = setToolboxSettings(setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)._2.fresh, settings) var output: Option[T] = None def registerTree(tree: tpd.Tree)(ctx: Context): Unit = { diff --git a/compiler/src/dotty/tools/dotc/quoted/ToolboxImpl.scala b/compiler/src/dotty/tools/dotc/quoted/ToolboxImpl.scala index 1067caf89a9a..3e48fd0bd649 100644 --- a/compiler/src/dotty/tools/dotc/quoted/ToolboxImpl.scala +++ b/compiler/src/dotty/tools/dotc/quoted/ToolboxImpl.scala @@ -2,7 +2,7 @@ package dotty.tools.dotc.quoted import dotty.tools.dotc.ast.tpd -import scala.quoted.Expr +import scala.quoted._ import scala.quoted.Exprs.{LiftedExpr, TastyTreeExpr} /** Default runners for quoted expressions */ @@ -24,5 +24,6 @@ object ToolboxImpl { def show[T](expr: Expr[T]): String = synchronized(driver.show(expr, settings)) + def show[T](tpe: Type[T]): String = synchronized(driver.show(tpe, settings)) } } diff --git a/library/src-non-bootstrapped/scala/quoted/Type.scala b/library/src-non-bootstrapped/scala/quoted/Type.scala index 90f56da01712..0dfc0f944f16 100644 --- a/library/src-non-bootstrapped/scala/quoted/Type.scala +++ b/library/src-non-bootstrapped/scala/quoted/Type.scala @@ -6,6 +6,9 @@ import scala.runtime.quoted.Unpickler.Pickled sealed abstract class Type[T] { type `$splice` = T + + /** Show a source code like representation of this type */ + final def show(implicit toolbox: Toolbox): String = toolbox.show(this) } /** Some basic type tags, currently incomplete */ diff --git a/library/src/scala/quoted/Toolbox.scala b/library/src/scala/quoted/Toolbox.scala index 7fab42b308de..6834d58df3be 100644 --- a/library/src/scala/quoted/Toolbox.scala +++ b/library/src/scala/quoted/Toolbox.scala @@ -6,6 +6,7 @@ import scala.annotation.implicitNotFound trait Toolbox { def run[T](expr: Expr[T]): T def show[T](expr: Expr[T]): String + def show[T](tpe: Type[T]): String } object Toolbox { @@ -32,7 +33,7 @@ object Toolbox { } /** Setting of the Toolbox instance. */ - class Settings private (val outDir: Option[String], val showRawTree: Boolean, val compilerArgs: List[String], val color: Boolean) + case class Settings private (val outDir: Option[String], val showRawTree: Boolean, val compilerArgs: List[String], val color: Boolean) object Settings { diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 81c5e3057e17..3f5849088154 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -1360,6 +1360,9 @@ trait Printers case tpe @ Type.SymRef(IsClassDefSymbol(sym), _) if sym.name.endsWith("$") => printType(tpe) this += ".type" + case tpe @ Type.SymRef(sym, _) if !IsTypeSymbol.unapply(sym).nonEmpty => + printType(tpe) + this += ".type" case tpe => printType(tpe) } printTypeAndAnnots(tree.tpe) @@ -1442,7 +1445,7 @@ trait Printers case Type.ConstantType(const) => printConstant(const) - case Type.SymRef(sym, prefix) => + case Type.SymRef(sym, prefix) if IsTypeSymbol.unapply(sym).nonEmpty => prefix match { case Type.ThisType(Types.EmptyPackage() | Types.RootPackage()) => case NoPrefix() => @@ -1456,13 +1459,22 @@ trait Printers printType(prefix) this += "#" case IsType(prefix) => - if (!sym.flags.is(Flags.Local)) { - printType(prefix) - this += "." - } + printType(prefix) + this += "." } this += highlightTypeDef(sym.name.stripSuffix("$"), color) + case Type.SymRef(sym, prefix) if !IsTypeSymbol.unapply(sym).nonEmpty => + prefix match { + case NoPrefix() => + this += highlightTypeDef(sym.name, color) + case _ => + printTypeOrBound(prefix) + if (sym.name != "package") + this += "." += highlightTypeDef(sym.name, color) + this + } + case Type.TermRef(name, prefix) => prefix match { case Type.ThisType(Types.EmptyPackage()) => From 7b7838dc0012e8c4d6de49f2944c324c0d50c659 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 6 Feb 2019 14:24:48 +0100 Subject: [PATCH 2/8] Add test for Type[T].show --- tests/run/type-show/Macro_1.scala | 11 +++++++++++ tests/run/type-show/Test_2.scala | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 tests/run/type-show/Macro_1.scala create mode 100644 tests/run/type-show/Test_2.scala diff --git a/tests/run/type-show/Macro_1.scala b/tests/run/type-show/Macro_1.scala new file mode 100644 index 000000000000..5df282f3d59d --- /dev/null +++ b/tests/run/type-show/Macro_1.scala @@ -0,0 +1,11 @@ +import scala.quoted._ +import scala.tasty._ + +object TypeToolbox { + inline def show[A]: String = ~showImpl('[A]) + private def showImpl[A, B](a: Type[A])(implicit refl: Reflection): Expr[String] = { + import refl._ + import scala.quoted.Toolbox.Default._ + a.show.toExpr + } +} diff --git a/tests/run/type-show/Test_2.scala b/tests/run/type-show/Test_2.scala new file mode 100644 index 000000000000..54bad6c5fdca --- /dev/null +++ b/tests/run/type-show/Test_2.scala @@ -0,0 +1,21 @@ + +object Test { + import TypeToolbox._ + def main(args: Array[String]): Unit = { + val x = 5 + println(show[Nil.type]) + println(show[Int => Int]) + println(show[(Int, String)]) + println(show[x.type]) + assert(show[x.type] == "x.type") + assert(show[Nil.type] == "scala.Nil.type") + assert(show[Int] == "scala.Int") + assert(show[Int => Int] == "scala.Function1[scala.Int, scala.Int]") + assert(show[(Int, String)] == "scala.Tuple2[scala.Int, scala.Predef.String]") + + // TODO: more complex types: + // - implicit function types + // - dependent function types + // - refinement types + } +} From f1e57a30ae6a19eb1d0ca13d43489b1f9da85f83 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 6 Feb 2019 17:24:23 +0100 Subject: [PATCH 3/8] Address review --- compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala | 4 ++++ library/src/scala/quoted/Toolbox.scala | 2 +- library/src/scala/tasty/reflect/Kernel.scala | 6 +++++- library/src/scala/tasty/reflect/Printers.scala | 6 +++--- library/src/scala/tasty/reflect/SymbolOps.scala | 3 +++ tests/pos/i2104b.decompiled | 6 +++--- tests/pos/simpleCaseClass-3.decompiled | 6 +++--- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index 5ea4b8aa21a7..5edf5106ff7f 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -1427,6 +1427,10 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util. def Symbol_isAbstractType(self: Symbol)(implicit ctx: Context): Boolean = self.isAbstractType def Symbol_isClassConstructor(self: Symbol)(implicit ctx: Context): Boolean = self.isClassConstructor + def Symbol_isType(self: Symbol)(implicit ctx: Context): Boolean = self.isType + + def Symbol_isTerm(self: Symbol)(implicit ctx: Context): Boolean = self.isTerm + type PackageDefSymbol = core.Symbols.Symbol def matchPackageDefSymbol(symbol: Symbol)(implicit ctx: Context): Option[PackageDefSymbol] = diff --git a/library/src/scala/quoted/Toolbox.scala b/library/src/scala/quoted/Toolbox.scala index 6834d58df3be..b57fee97dad7 100644 --- a/library/src/scala/quoted/Toolbox.scala +++ b/library/src/scala/quoted/Toolbox.scala @@ -33,7 +33,7 @@ object Toolbox { } /** Setting of the Toolbox instance. */ - case class Settings private (val outDir: Option[String], val showRawTree: Boolean, val compilerArgs: List[String], val color: Boolean) + case class Settings private (outDir: Option[String], showRawTree: Boolean, compilerArgs: List[String], color: Boolean) object Settings { diff --git a/library/src/scala/tasty/reflect/Kernel.scala b/library/src/scala/tasty/reflect/Kernel.scala index 38c605ccfc6b..b2859cd3c549 100644 --- a/library/src/scala/tasty/reflect/Kernel.scala +++ b/library/src/scala/tasty/reflect/Kernel.scala @@ -699,7 +699,7 @@ trait Kernel { // // PATTERNS // - + /** Pattern tree of the pattern part of a CaseDef */ type Pattern <: AnyRef @@ -1169,6 +1169,10 @@ trait Kernel { def Symbol_isDefinedInCurrentRun(self: Symbol)(implicit ctx: Context): Boolean + def Symbol_isType(self: Symbol)(implicit ctx: Context): Boolean + + def Symbol_isTerm(self: Symbol)(implicit ctx: Context): Boolean + /** Symbol of a package definition */ type PackageDefSymbol <: Symbol diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 3f5849088154..3c418ad88748 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -1360,7 +1360,7 @@ trait Printers case tpe @ Type.SymRef(IsClassDefSymbol(sym), _) if sym.name.endsWith("$") => printType(tpe) this += ".type" - case tpe @ Type.SymRef(sym, _) if !IsTypeSymbol.unapply(sym).nonEmpty => + case tpe @ Type.SymRef(sym, _) if sym.isTerm => printType(tpe) this += ".type" case tpe => printType(tpe) @@ -1445,7 +1445,7 @@ trait Printers case Type.ConstantType(const) => printConstant(const) - case Type.SymRef(sym, prefix) if IsTypeSymbol.unapply(sym).nonEmpty => + case Type.SymRef(sym, prefix) if sym.isType => prefix match { case Type.ThisType(Types.EmptyPackage() | Types.RootPackage()) => case NoPrefix() => @@ -1464,7 +1464,7 @@ trait Printers } this += highlightTypeDef(sym.name.stripSuffix("$"), color) - case Type.SymRef(sym, prefix) if !IsTypeSymbol.unapply(sym).nonEmpty => + case Type.SymRef(sym, prefix) if sym.isTerm => prefix match { case NoPrefix() => this += highlightTypeDef(sym.name, color) diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index 5e34d610c4c8..cbb6300bc593 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -82,6 +82,9 @@ trait SymbolOps extends Core { def isAnonymousFunction(implicit ctx: Context): Boolean = kernel.Symbol_isAnonymousFunction(self) def isAbstractType(implicit ctx: Context): Boolean = kernel.Symbol_isAbstractType(self) def isClassConstructor(implicit ctx: Context): Boolean = kernel.Symbol_isClassConstructor(self) + + def isType(implicit ctx: Context): Boolean = kernel.Symbol_isType(self) + def isTerm(implicit ctx: Context): Boolean = kernel.Symbol_isTerm(self) } // PackageSymbol diff --git a/tests/pos/i2104b.decompiled b/tests/pos/i2104b.decompiled index 2e052fbf1e90..c40ab085ab31 100644 --- a/tests/pos/i2104b.decompiled +++ b/tests/pos/i2104b.decompiled @@ -10,14 +10,14 @@ case class Pair[A, B](_1: A, _2: B) { acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(Pair.this._2)) scala.runtime.Statics.finalizeHash(acc, 2) } - override def equals(x$0: scala.Any): scala.Boolean = Pair.this.eq(x$0.$asInstanceOf$[java.lang.Object]).||(x$0 match { - case x$0: Pair[A, B] @scala.unchecked => + override def equals(x$0: scala.Any): scala.Boolean = Pair.this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match { + case x$0: Pair[Pair.this.A, Pair.this.B] @scala.unchecked => Pair.this._1.==(x$0._1).&&(Pair.this._2.==(x$0._2)) case _ => false }) override def toString(): java.lang.String = scala.runtime.ScalaRunTime._toString(Pair.this) - override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[Pair[A, B] @scala.unchecked] + override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[Pair[Pair.this.A, Pair.this.B] @scala.unchecked] override def productArity: scala.Int = 2 override def productPrefix: java.lang.String = "Pair" override def productElement(n: scala.Int): scala.Any = n match { diff --git a/tests/pos/simpleCaseClass-3.decompiled b/tests/pos/simpleCaseClass-3.decompiled index 43171c07de47..fd404740a52f 100644 --- a/tests/pos/simpleCaseClass-3.decompiled +++ b/tests/pos/simpleCaseClass-3.decompiled @@ -4,14 +4,14 @@ case class A[T](x: T) { acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(A.this.x)) scala.runtime.Statics.finalizeHash(acc, 1) } - override def equals(x$0: scala.Any): scala.Boolean = A.this.eq(x$0.$asInstanceOf$[java.lang.Object]).||(x$0 match { - case x$0: A[T] @scala.unchecked => + override def equals(x$0: scala.Any): scala.Boolean = A.this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match { + case x$0: A[A.this.T] @scala.unchecked => A.this.x.==(x$0.x) case _ => false }) override def toString(): java.lang.String = scala.runtime.ScalaRunTime._toString(A.this) - override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A[T] @scala.unchecked] + override def canEqual(that: scala.Any): scala.Boolean = that.isInstanceOf[A[A.this.T] @scala.unchecked] override def productArity: scala.Int = 1 override def productPrefix: java.lang.String = "A" override def productElement(n: scala.Int): scala.Any = n match { From 5eb8de4b876a8a10dfdc6e519ace0a1045b2364e Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 6 Feb 2019 20:47:19 +0100 Subject: [PATCH 4/8] Elide `this` for types in parent list and self annotations --- .../src/scala/tasty/reflect/Printers.scala | 80 ++++++++++++------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 3c418ad88748..246baf5b85da 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -433,7 +433,7 @@ trait Printers (new Buffer).printPattern(pattern).result() def showTypeOrBounds(tpe: TypeOrBounds)(implicit ctx: Context): String = - (new Buffer).printTypeOrBound(tpe).result() + (new Buffer).printTypeOrBound(tpe)(None).result() def showConstant(const: Constant)(implicit ctx: Context): String = (new Buffer).printConstant(const).result() @@ -516,7 +516,7 @@ trait Printers def lineBreak(): String = "\n" + (" " * indent) def doubleLineBreak(): String = "\n\n" + (" " * indent) - def printTree(tree: Tree): Buffer = tree match { + def printTree(tree: Tree)(implicit elideThis: Option[Symbol] = None): Buffer = tree match { case PackageObject(body)=> printTree(body) // Print package object @@ -582,19 +582,19 @@ trait Printers def printParent(parent: Tree /* Term | TypeTree */, needEmptyParens: Boolean = false): Unit = parent match { case IsTypeTree(parent) => - printTypeTree(parent) + printTypeTree(parent)(Some(cdef.symbol)) case IsTerm(Term.TypeApply(fun, targs)) => printParent(fun) case IsTerm(Term.Apply(fun@Term.Apply(_,_), args)) => printParent(fun, true) if (!args.isEmpty || needEmptyParens) - inParens(printTrees(args, ", ")) + inParens(printTrees(args, ", ")(Some(cdef.symbol))) case IsTerm(Term.Apply(fun, args)) => printParent(fun) if (!args.isEmpty || needEmptyParens) - inParens(printTrees(args, ", ")) + inParens(printTrees(args, ", ")(Some(cdef.symbol))) case IsTerm(Term.Select(Term.IsNew(newTree), _)) => - printType(newTree.tpe) + printType(newTree.tpe)(Some(cdef.symbol)) case IsTerm(parent) => throw new MatchError(parent.show) } @@ -646,7 +646,7 @@ trait Printers indented { val name1 = if (name == "_") "this" else name this += " " += highlightValDef(name1, color) += ": " - printTypeTree(tpt) + printTypeTree(tpt)(Some(cdef.symbol)) this += " =>" } } @@ -971,7 +971,7 @@ trait Printers (flatStats.result(), flatExpr) } - def printFlatBlock(stats: List[Statement], expr: Term): Buffer = { + def printFlatBlock(stats: List[Statement], expr: Term)(implicit elideThis: Option[Symbol]): Buffer = { val (stats1, expr1) = flatBlock(stats, expr) // Remove Term.Lambda nodes, lambdas are printed by their definition val stats2 = stats1.filter { case Term.Lambda(_, _) => false; case _ => true } @@ -992,7 +992,7 @@ trait Printers } } - def printStats(stats: List[Tree], expr: Tree): Unit = { + def printStats(stats: List[Tree], expr: Tree)(implicit elideThis: Option[Symbol]): Unit = { def printSeparator(next: Tree): Unit = { // Avoid accidental application of opening `{` on next line with a double break def rec(next: Tree): Unit = next match { @@ -1039,13 +1039,13 @@ trait Printers this } - def printTrees(trees: List[Tree], sep: String): Buffer = - printList(trees, sep, printTree) + def printTrees(trees: List[Tree], sep: String)(implicit elideThis: Option[Symbol]): Buffer = + printList(trees, sep, (t: Tree) => printTree(t)) - def printTypeTrees(trees: List[TypeTree], sep: String): Buffer = - printList(trees, sep, printTypeTree) + def printTypeTrees(trees: List[TypeTree], sep: String)(implicit elideThis: Option[Symbol] = None): Buffer = + printList(trees, sep, (t: TypeTree) => printTypeTree(t)) - def printTypes(trees: List[Type], sep: String): Buffer = { + def printTypes(trees: List[Type], sep: String)(implicit elideThis: Option[Symbol]): Buffer = { def printSeparated(list: List[Type]): Unit = list match { case Nil => case x :: Nil => printType(x) @@ -1111,7 +1111,7 @@ trait Printers this } - def printTypesOrBounds(types: List[TypeOrBounds], sep: String): Buffer = { + def printTypesOrBounds(types: List[TypeOrBounds], sep: String)(implicit elideThis: Option[Symbol]): Buffer = { def printSeparated(list: List[TypeOrBounds]): Unit = list match { case Nil => case x :: Nil => printTypeOrBound(x) @@ -1124,7 +1124,7 @@ trait Printers this } - def printTargsDefs(targs: List[(TypeDef, TypeDef)], isDef:Boolean = true): Unit = { + def printTargsDefs(targs: List[(TypeDef, TypeDef)], isDef:Boolean = true)(implicit elideThis: Option[Symbol]): Unit = { if (!targs.isEmpty) { def printSeparated(list: List[(TypeDef, TypeDef)]): Unit = list match { case Nil => @@ -1139,7 +1139,7 @@ trait Printers } } - def printTargDef(arg: (TypeDef, TypeDef), isMember: Boolean = false, isDef:Boolean = true): Buffer = { + def printTargDef(arg: (TypeDef, TypeDef), isMember: Boolean = false, isDef:Boolean = true)(implicit elideThis: Option[Symbol]): Buffer = { val (argDef, argCons) = arg if (isDef) { @@ -1189,7 +1189,7 @@ trait Printers } } - def printArgsDefs(args: List[ValDef]): Unit = inParens { + def printArgsDefs(args: List[ValDef])(implicit elideThis: Option[Symbol]): Unit = inParens { args match { case Nil => case arg :: _ => @@ -1209,7 +1209,7 @@ trait Printers printSeparated(args) } - def printAnnotations(trees: List[Term]): Buffer = { + def printAnnotations(trees: List[Term])(implicit elideThis: Option[Symbol]): Buffer = { def printSeparated(list: List[Term]): Unit = list match { case Nil => case x :: Nil => printAnnotation(x) @@ -1222,7 +1222,7 @@ trait Printers this } - def printParamDef(arg: ValDef): Unit = { + def printParamDef(arg: ValDef)(implicit elideThis: Option[Symbol]): Unit = { val name = arg.name arg.symbol.owner match { case IsDefDefSymbol(sym) if sym.name == "" => @@ -1260,7 +1260,7 @@ trait Printers indented { caseDef.rhs match { case Term.Block(stats, expr) => - printStats(stats, expr) + printStats(stats, expr)(None) case body => this += lineBreak() printTree(body) @@ -1334,7 +1334,7 @@ trait Printers this += highlightLiteral("'" + v.name, color) } - def printTypeOrBoundsTree(tpt: Tree /*TypeTree | TypeBoundsTree*/): Buffer = tpt match { + def printTypeOrBoundsTree(tpt: Tree)(implicit elideThis: Option[Symbol] = None): Buffer = tpt match { case TypeBoundsTree(lo, hi) => this += "_ >: " printTypeTree(lo) @@ -1346,7 +1346,15 @@ trait Printers printTypeTree(tpt) } - def printTypeTree(tree: TypeTree): Buffer = tree match { + /** Print type tree + * + * @param elideThis Shoud printing elide `C.this` for the given class `C`? + * None means no eliding. + * + * Self type annotation and types in parent list should elide current class + * prefix `C.this` to avoid type checking errors. + */ + def printTypeTree(tree: TypeTree)(implicit elideThis: Option[Symbol] = None): Buffer = tree match { case TypeTree.Inferred() => // TODO try to move this logic into `printType` def printTypeAndAnnots(tpe: Type): Buffer = tpe match { @@ -1432,7 +1440,7 @@ trait Printers } - def printTypeOrBound(tpe: TypeOrBounds): Buffer = tpe match { + def printTypeOrBound(tpe: TypeOrBounds)(implicit elideThis: Option[Symbol]): Buffer = tpe match { case tpe@TypeBounds(lo, hi) => this += "_ >: " printType(lo) @@ -1441,7 +1449,15 @@ trait Printers case IsType(tpe) => printType(tpe) } - def printType(tpe: Type): Buffer = tpe match { + /** Print type + * + * @param elideThis Shoud printing elide `C.this` for the given class `C`? + * None means no eliding. + * + * Self type annotation and types in parent list should elide current class + * prefix `C.this` to avoid type checking errors. + */ + def printType(tpe: Type)(implicit elideThis: Option[Symbol] = None): Buffer = tpe match { case Type.ConstantType(const) => printConstant(const) @@ -1458,6 +1474,8 @@ trait Printers case IsType(prefix @ Type.SymRef(IsClassDefSymbol(_), _)) => printType(prefix) this += "#" + case IsType(Type.ThisType(Type.SymRef(cdef, _))) + if elideThis.nonEmpty && cdef == elideThis.get => case IsType(prefix) => printType(prefix) this += "." @@ -1590,7 +1608,7 @@ trait Printers case PackageDef(name, _) => this += highlightTypeDef(name, color) } - def printAnnotation(annot: Term): Buffer = { + def printAnnotation(annot: Term)(implicit elideThis: Option[Symbol]): Buffer = { val Annotation(ref, args) = annot this += "@" printTypeTree(ref) @@ -1600,7 +1618,7 @@ trait Printers inParens(printTrees(args, ", ")) } - def printDefAnnotations(definition: Definition): Buffer = { + def printDefAnnotations(definition: Definition)(implicit elideThis: Option[Symbol]): Buffer = { val annots = definition.symbol.annots.filter { case Annotation(annot, _) => annot.tpe match { @@ -1615,7 +1633,7 @@ trait Printers else this } - def printRefinement(tpe: Type): Buffer = { + def printRefinement(tpe: Type)(implicit elideThis: Option[Symbol]): Buffer = { def printMethodicType(tp: TypeOrBounds): Unit = tp match { case tp @ Type.MethodType(paramNames, params, res) => inParens(printMethodicTypeParams(paramNames, params)) @@ -1655,7 +1673,7 @@ trait Printers this += lineBreak() += "}" } - def printMethodicTypeParams(paramNames: List[String], params: List[TypeOrBounds]): Unit = { + def printMethodicTypeParams(paramNames: List[String], params: List[TypeOrBounds])(implicit elideThis: Option[Symbol]): Unit = { def printInfo(info: TypeOrBounds) = info match { case IsTypeBounds(info) => printBounds(info) case IsType(info) => @@ -1676,7 +1694,7 @@ trait Printers printSeparated(paramNames.zip(params)) } - def printBoundsTree(bounds: TypeBoundsTree): Buffer = { + def printBoundsTree(bounds: TypeBoundsTree)(implicit elideThis: Option[Symbol]): Buffer = { bounds.low match { case TypeTree.Inferred() => case low => @@ -1691,7 +1709,7 @@ trait Printers } } - def printBounds(bounds: TypeBounds): Buffer = { + def printBounds(bounds: TypeBounds)(implicit elideThis: Option[Symbol]): Buffer = { this += " >: " printType(bounds.low) this += " <: " From 8ddb8c8defa6c17efac4bbee5ac64eb95a2151b3 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 7 Feb 2019 11:34:35 +0100 Subject: [PATCH 5/8] Fix test for decompilation Otherwise the typer will infer the type `wr.underlying.T`, which is not valid in source: val x: wr.underlying.T = wr.underlying.get() --- tests/pos/i1793.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pos/i1793.scala b/tests/pos/i1793.scala index fed8a6165699..8de9b99eb73e 100644 --- a/tests/pos/i1793.scala +++ b/tests/pos/i1793.scala @@ -1,7 +1,7 @@ object Test { import scala.ref.WeakReference def unapply[T <: AnyRef](wr: WeakReference[T]): Option[T] = { - val x = wr.underlying.get + val x: T = wr.underlying.get if (x != null) Some(x) else None } } From 77c060c214e7af5d55e07fa3ebbe0a7e57d0e976 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 20 Mar 2019 13:37:27 +0100 Subject: [PATCH 6/8] Fix rebase --- library/src-bootstrapped/scala/quoted/Type.scala | 3 +++ library/src/scala/tasty/reflect/Printers.scala | 2 +- tests/run/type-show/Macro_1.scala | 2 +- tests/run/type-show/Test_2.scala | 4 ---- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/library/src-bootstrapped/scala/quoted/Type.scala b/library/src-bootstrapped/scala/quoted/Type.scala index c324e753ff8e..243b63b3889a 100644 --- a/library/src-bootstrapped/scala/quoted/Type.scala +++ b/library/src-bootstrapped/scala/quoted/Type.scala @@ -6,6 +6,9 @@ import scala.runtime.quoted.Unpickler.Pickled sealed abstract class Type[T <: AnyKind] { type `$splice` = T + + /** Show a source code like representation of this type */ + final def show(implicit toolbox: Toolbox): String = toolbox.show(this.asInstanceOf[Type[Any]]) } /** Some basic type tags, currently incomplete */ diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 246baf5b85da..0f34ce7edc84 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -1484,7 +1484,7 @@ trait Printers case Type.SymRef(sym, prefix) if sym.isTerm => prefix match { - case NoPrefix() => + case NoPrefix() | Type.ThisType(Types.EmptyPackage() | Types.RootPackage()) => this += highlightTypeDef(sym.name, color) case _ => printTypeOrBound(prefix) diff --git a/tests/run/type-show/Macro_1.scala b/tests/run/type-show/Macro_1.scala index 5df282f3d59d..87dbe60603c1 100644 --- a/tests/run/type-show/Macro_1.scala +++ b/tests/run/type-show/Macro_1.scala @@ -2,7 +2,7 @@ import scala.quoted._ import scala.tasty._ object TypeToolbox { - inline def show[A]: String = ~showImpl('[A]) + inline def show[A]: String = ${ showImpl('[A]) } private def showImpl[A, B](a: Type[A])(implicit refl: Reflection): Expr[String] = { import refl._ import scala.quoted.Toolbox.Default._ diff --git a/tests/run/type-show/Test_2.scala b/tests/run/type-show/Test_2.scala index 54bad6c5fdca..471d19840338 100644 --- a/tests/run/type-show/Test_2.scala +++ b/tests/run/type-show/Test_2.scala @@ -3,10 +3,6 @@ object Test { import TypeToolbox._ def main(args: Array[String]): Unit = { val x = 5 - println(show[Nil.type]) - println(show[Int => Int]) - println(show[(Int, String)]) - println(show[x.type]) assert(show[x.type] == "x.type") assert(show[Nil.type] == "scala.Nil.type") assert(show[Int] == "scala.Int") From 09a6b778daef962a196c215e4c434122367d74d1 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 20 Mar 2019 14:49:42 +0100 Subject: [PATCH 7/8] Fix CI --- tests/pos/i2104b.decompiled | 4 ++-- tests/pos/simpleCaseClass-3.decompiled | 4 ++-- tests/run/tasty-macro-positions.check | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/pos/i2104b.decompiled b/tests/pos/i2104b.decompiled index c40ab085ab31..620bbc7ab092 100644 --- a/tests/pos/i2104b.decompiled +++ b/tests/pos/i2104b.decompiled @@ -10,7 +10,7 @@ case class Pair[A, B](_1: A, _2: B) { acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(Pair.this._2)) scala.runtime.Statics.finalizeHash(acc, 2) } - override def equals(x$0: scala.Any): scala.Boolean = Pair.this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match { + override def equals(x$0: scala.Any): scala.Boolean = Pair.this.eq(x$0.$asInstanceOf$[java.lang.Object]).||(x$0 match { case x$0: Pair[Pair.this.A, Pair.this.B] @scala.unchecked => Pair.this._1.==(x$0._1).&&(Pair.this._2.==(x$0._2)) case _ => @@ -35,4 +35,4 @@ object Test { case Cons(scala.Some(i), scala.None) => () } -} +} \ No newline at end of file diff --git a/tests/pos/simpleCaseClass-3.decompiled b/tests/pos/simpleCaseClass-3.decompiled index fd404740a52f..a2af5c0c086f 100644 --- a/tests/pos/simpleCaseClass-3.decompiled +++ b/tests/pos/simpleCaseClass-3.decompiled @@ -4,7 +4,7 @@ case class A[T](x: T) { acc = scala.runtime.Statics.mix(acc, scala.runtime.Statics.anyHash(A.this.x)) scala.runtime.Statics.finalizeHash(acc, 1) } - override def equals(x$0: scala.Any): scala.Boolean = A.this.eq(x$0.asInstanceOf[java.lang.Object]).||(x$0 match { + override def equals(x$0: scala.Any): scala.Boolean = A.this.eq(x$0.$asInstanceOf$[java.lang.Object]).||(x$0 match { case x$0: A[A.this.T] @scala.unchecked => A.this.x.==(x$0.x) case _ => @@ -21,4 +21,4 @@ case class A[T](x: T) { throw new java.lang.IndexOutOfBoundsException(n.toString()) } } -object A extends scala.AnyRef +object A extends scala.AnyRef \ No newline at end of file diff --git a/tests/run/tasty-macro-positions.check b/tests/run/tasty-macro-positions.check index 25b2142bc36f..23c6a5fa3ffe 100644 --- a/tests/run/tasty-macro-positions.check +++ b/tests/run/tasty-macro-positions.check @@ -11,7 +11,7 @@ quoted_2.scala:[217..222] quoted_2.scala:[232..245] ("abc": scala.Predef.String) quoted_2.scala:[255..269] -_root_.scala.StringContext.apply(("abc", "": scala.[scala#Predef.String])).s(("def": scala.[scala.Any])) +_root_.scala.StringContext.apply(("abc", "": scala.[scala.Predef.String])).s(("def": scala.[scala.Any])) quoted_2.scala:[281..282] a quoted_2.scala:[293..294] @@ -25,7 +25,7 @@ quoted_2.scala:[329..334] quoted_2.scala:[345..358] ("abc": scala.Predef.String) quoted_2.scala:[369..383] -_root_.scala.StringContext.apply(("abc", "": scala.[scala#Predef.String])).s(("def": scala.[scala.Any])) +_root_.scala.StringContext.apply(("abc", "": scala.[scala.Predef.String])).s(("def": scala.[scala.Any])) quoted_2.scala:[426..427] T quoted_2.scala:[438..444] From 50cdb1e6786e916a3d3f6e18f8239dc1ebb76659 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 21 Mar 2019 10:56:20 +0100 Subject: [PATCH 8/8] Address review --- compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala | 4 ---- library/src/scala/tasty/reflect/Kernel.scala | 4 ---- library/src/scala/tasty/reflect/SymbolOps.scala | 4 ++-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index 5edf5106ff7f..5ea4b8aa21a7 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -1427,10 +1427,6 @@ class KernelImpl(val rootContext: core.Contexts.Context, val rootPosition: util. def Symbol_isAbstractType(self: Symbol)(implicit ctx: Context): Boolean = self.isAbstractType def Symbol_isClassConstructor(self: Symbol)(implicit ctx: Context): Boolean = self.isClassConstructor - def Symbol_isType(self: Symbol)(implicit ctx: Context): Boolean = self.isType - - def Symbol_isTerm(self: Symbol)(implicit ctx: Context): Boolean = self.isTerm - type PackageDefSymbol = core.Symbols.Symbol def matchPackageDefSymbol(symbol: Symbol)(implicit ctx: Context): Option[PackageDefSymbol] = diff --git a/library/src/scala/tasty/reflect/Kernel.scala b/library/src/scala/tasty/reflect/Kernel.scala index b2859cd3c549..1b0144ed9335 100644 --- a/library/src/scala/tasty/reflect/Kernel.scala +++ b/library/src/scala/tasty/reflect/Kernel.scala @@ -1169,10 +1169,6 @@ trait Kernel { def Symbol_isDefinedInCurrentRun(self: Symbol)(implicit ctx: Context): Boolean - def Symbol_isType(self: Symbol)(implicit ctx: Context): Boolean - - def Symbol_isTerm(self: Symbol)(implicit ctx: Context): Boolean - /** Symbol of a package definition */ type PackageDefSymbol <: Symbol diff --git a/library/src/scala/tasty/reflect/SymbolOps.scala b/library/src/scala/tasty/reflect/SymbolOps.scala index cbb6300bc593..49de26d96bc3 100644 --- a/library/src/scala/tasty/reflect/SymbolOps.scala +++ b/library/src/scala/tasty/reflect/SymbolOps.scala @@ -83,8 +83,8 @@ trait SymbolOps extends Core { def isAbstractType(implicit ctx: Context): Boolean = kernel.Symbol_isAbstractType(self) def isClassConstructor(implicit ctx: Context): Boolean = kernel.Symbol_isClassConstructor(self) - def isType(implicit ctx: Context): Boolean = kernel.Symbol_isType(self) - def isTerm(implicit ctx: Context): Boolean = kernel.Symbol_isTerm(self) + def isType(implicit ctx: Context): Boolean = kernel.matchTypeSymbol(self).isDefined + def isTerm(implicit ctx: Context): Boolean = kernel.matchTermSymbol(self).isDefined } // PackageSymbol