diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 5966d6b817ee..f771983799aa 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -197,18 +197,18 @@ object PickledQuotes { } def reifyTypeHoleArgs(args: List[Tree])(using Context): List[scala.quoted.Type[?]] = - args.map(arg => new TypeImpl(arg, SpliceScope.getCurrent)) + args.map(arg => new TypeImpl(arg)) def reifyExprHoleV1Args(args: List[Tree])(using Context): List[ExprHole.ArgV1] = args.map { arg => - if arg.isTerm then (q: Quotes) ?=> new ExprImpl(arg, SpliceScope.getCurrent) - else new TypeImpl(arg, SpliceScope.getCurrent) + if arg.isTerm then (q: Quotes) ?=> new ExprImpl(arg) + else new TypeImpl(arg) } def reifyExprHoleV2Args(args: List[Tree])(using Context): List[ExprHole.ArgV2] = args.map { arg => - if arg.isTerm then new ExprImpl(arg, SpliceScope.getCurrent) - else new TypeImpl(arg, SpliceScope.getCurrent) + if arg.isTerm then new ExprImpl(arg) + else new TypeImpl(arg) } // TASTY picklingtests/pos/quoteTest.scala diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index b936afb73dc8..41e7d50b1af1 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -237,11 +237,11 @@ object Splicer { case Inlined(EmptyTree, _, quoted) => quoted case _ => quoted } - new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(quoted1, ctx.owner)).withSpan(quoted1.span), SpliceScope.getCurrent) + new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(quoted1, ctx.owner)).withSpan(quoted1.span)) // Interpret level -1 `Type.of[T]` case Apply(TypeApply(fn, quoted :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of => - new TypeImpl(QuoteUtils.changeOwnerOfTree(quoted, ctx.owner), SpliceScope.getCurrent) + new TypeImpl(QuoteUtils.changeOwnerOfTree(quoted, ctx.owner)) case _ => super.interpretTree(tree) diff --git a/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala b/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala index b33ba14b9e70..17e483ba6fd7 100644 --- a/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/ExprImpl.scala @@ -2,6 +2,7 @@ package scala.quoted package runtime.impl import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context /** An Expr backed by a tree. Only the current compiler trees are allowed. * @@ -10,7 +11,10 @@ import dotty.tools.dotc.ast.tpd * * May contain references to code defined outside this Expr instance. */ -final class ExprImpl(val tree: tpd.Tree, val scope: Scope) extends Expr[Any] { +final class ExprImpl(val tree: tpd.Tree)(using val ctx: Context) extends Expr[Any]: + + def scope: Scope = SpliceScope.getCurrent + override def equals(that: Any): Boolean = that match { case that: ExprImpl => // Expr are wrappers around trees, therefore they are equals if their trees are equal. @@ -21,5 +25,4 @@ final class ExprImpl(val tree: tpd.Tree, val scope: Scope) extends Expr[Any] { override def hashCode(): Int = tree.hashCode() - override def toString: String = "'{ ... }" -} + override def toString(): String = QuotesImpl.showDecompiledTree(tree) diff --git a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala index d85d92de5455..33bc44601b5f 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuoteMatcher.scala @@ -462,7 +462,7 @@ object QuoteMatcher { val matched: Matching = Some(Tuple()) def matched(tree: Tree)(using Context): Matching = - Some(Tuple1(new ExprImpl(tree, SpliceScope.getCurrent))) + Some(Tuple1(new ExprImpl(tree))) extension (self: Matching) def asOptionOfTuple: Option[Tuple] = self diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 7a3b33c4a9f4..81c01b122b45 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -105,7 +105,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler case _ => false def asExpr: scala.quoted.Expr[Any] = if self.isExpr then - new ExprImpl(self, SpliceScope.getCurrent) + new ExprImpl(self) else self match case TermTypeTest(self) => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.") case _ => throw new Exception("Expected a Term but was: " + Printer.TreeStructure.show(self)) @@ -377,11 +377,11 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler given TermMethods: TermMethods with extension (self: Term) def seal: scala.quoted.Expr[Any] = - if self.isExpr then new ExprImpl(self, SpliceScope.getCurrent) + if self.isExpr then new ExprImpl(self) else throw new Exception("Cannot seal a partially applied Term. Try eta-expanding the term first.") def sealOpt: Option[scala.quoted.Expr[Any]] = - if self.isExpr then Some(new ExprImpl(self, SpliceScope.getCurrent)) + if self.isExpr then Some(new ExprImpl(self)) else None def tpe: TypeRepr = self.tpe.widenSkolem @@ -1722,7 +1722,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def seal: scala.quoted.Type[_] = self.asType def asType: scala.quoted.Type[?] = - new TypeImpl(Inferred(self), SpliceScope.getCurrent) + new TypeImpl(Inferred(self)) def =:=(that: TypeRepr): Boolean = self =:= that def <:<(that: TypeRepr): Boolean = self <:< that @@ -3048,19 +3048,19 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def unpickleExpr[T](pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?]): scala.quoted.Expr[T] = val tree = PickledQuotes.unpickleTerm(pickled, PickledQuotes.TypeHole.V1(typeHole), PickledQuotes.ExprHole.V1(termHole)) - new ExprImpl(tree, SpliceScope.getCurrent).asInstanceOf[scala.quoted.Expr[T]] + new ExprImpl(tree).asInstanceOf[scala.quoted.Expr[T]] def unpickleExprV2[T](pickled: String | List[String], types: Seq[Type[?]], termHole: Null | ((Int, Seq[Type[?] | Expr[Any]], Quotes) => Expr[?])): scala.quoted.Expr[T] = val tree = PickledQuotes.unpickleTerm(pickled, PickledQuotes.TypeHole.V2(types), PickledQuotes.ExprHole.V2(termHole)) - new ExprImpl(tree, SpliceScope.getCurrent).asInstanceOf[scala.quoted.Expr[T]] + new ExprImpl(tree).asInstanceOf[scala.quoted.Expr[T]] def unpickleType[T <: AnyKind](pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?]): scala.quoted.Type[T] = val tree = PickledQuotes.unpickleTypeTree(pickled, PickledQuotes.TypeHole.V1(typeHole)) - new TypeImpl(tree, SpliceScope.getCurrent).asInstanceOf[scala.quoted.Type[T]] + new TypeImpl(tree).asInstanceOf[scala.quoted.Type[T]] def unpickleTypeV2[T <: AnyKind](pickled: String | List[String], types: Seq[Type[?]]): scala.quoted.Type[T] = val tree = PickledQuotes.unpickleTypeTree(pickled, PickledQuotes.TypeHole.V2(types)) - new TypeImpl(tree, SpliceScope.getCurrent).asInstanceOf[scala.quoted.Type[T]] + new TypeImpl(tree).asInstanceOf[scala.quoted.Type[T]] object ExprMatch extends ExprMatchModule: def unapply[TypeBindings <: Tuple, Tup <: Tuple](scrutinee: scala.quoted.Expr[Any])(using pattern: scala.quoted.Expr[Any]): Option[Tup] = diff --git a/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala b/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala index 36da30e112c8..088a3ead9e6d 100644 --- a/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/TypeImpl.scala @@ -2,9 +2,13 @@ package scala.quoted package runtime.impl import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context /** Quoted type (or kind) `T` backed by a tree */ -final class TypeImpl(val typeTree: tpd.Tree, val scope: Scope) extends Type[?] { +final class TypeImpl(val typeTree: tpd.Tree)(using val ctx: Context) extends Type[?]: + + def scope: Scope = SpliceScope.getCurrent + override def equals(that: Any): Boolean = that match { case that: TypeImpl => typeTree == // TastyTreeExpr are wrappers around trees, therefore they are equals if their trees are equal. @@ -15,5 +19,4 @@ final class TypeImpl(val typeTree: tpd.Tree, val scope: Scope) extends Type[?] { override def hashCode(): Int = typeTree.hashCode() - override def toString: String = "Type.of[...]" -} + override def toString: String = QuotesImpl.showDecompiledTree(typeTree) diff --git a/tests/pos-with-compiler-cc/dotc/quoted/PickledQuotes.scala b/tests/pos-with-compiler-cc/dotc/quoted/PickledQuotes.scala index a124cbb03d03..5e297ca24f66 100644 --- a/tests/pos-with-compiler-cc/dotc/quoted/PickledQuotes.scala +++ b/tests/pos-with-compiler-cc/dotc/quoted/PickledQuotes.scala @@ -196,18 +196,18 @@ object PickledQuotes { } def reifyTypeHoleArgs(args: List[Tree])(using Context): List[scala.quoted.Type[?]] = - args.map(arg => new TypeImpl(arg, SpliceScope.getCurrent)) + args.map(arg => new TypeImpl(arg)(using ctx.detach)) def reifyExprHoleV1Args(args: List[Tree])(using Context): List[ExprHole.ArgV1] = args.map { arg => - if arg.isTerm then (q: Quotes) ?=> new ExprImpl(arg, SpliceScope.getCurrent) - else new TypeImpl(arg, SpliceScope.getCurrent) + if arg.isTerm then (q: Quotes) ?=> new ExprImpl(arg)(using ctx.detach) + else new TypeImpl(arg)(using ctx.detach) } def reifyExprHoleV2Args(args: List[Tree])(using Context): List[ExprHole.ArgV2] = args.map { arg => - if arg.isTerm then new ExprImpl(arg, SpliceScope.getCurrent) - else new TypeImpl(arg, SpliceScope.getCurrent) + if arg.isTerm then new ExprImpl(arg)(using ctx.detach) + else new TypeImpl(arg)(using ctx.detach) } // TASTY picklingtests/pos/quoteTest.scala diff --git a/tests/pos-with-compiler-cc/dotc/transform/Splicer.scala b/tests/pos-with-compiler-cc/dotc/transform/Splicer.scala index 09c820169179..495df537e151 100644 --- a/tests/pos-with-compiler-cc/dotc/transform/Splicer.scala +++ b/tests/pos-with-compiler-cc/dotc/transform/Splicer.scala @@ -241,11 +241,11 @@ object Splicer { case Inlined(EmptyTree, _, quoted) => quoted case _ => quoted } - new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(quoted1, ctx.owner)).withSpan(quoted1.span), SpliceScope.getCurrent) + new ExprImpl(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(quoted1, ctx.owner)).withSpan(quoted1.span))(using ctx.detach) // Interpret level -1 `Type.of[T]` case Apply(TypeApply(fn, quoted :: Nil), _) if fn.symbol == defn.QuotedTypeModule_of => - new TypeImpl(QuoteUtils.changeOwnerOfTree(quoted, ctx.owner), SpliceScope.getCurrent) + new TypeImpl(QuoteUtils.changeOwnerOfTree(quoted, ctx.owner))(using ctx.detach) case _ => super.interpretTree(tree) diff --git a/tests/run-macros/Expr-toString.check b/tests/run-macros/Expr-toString.check new file mode 100644 index 000000000000..9df31eb73fdd --- /dev/null +++ b/tests/run-macros/Expr-toString.check @@ -0,0 +1,4 @@ +1 +"abc" +scala.Predef.println("abc") +while (true) () diff --git a/tests/run-macros/Expr-toString/Macros_1.scala b/tests/run-macros/Expr-toString/Macros_1.scala new file mode 100644 index 000000000000..443ecd7881d2 --- /dev/null +++ b/tests/run-macros/Expr-toString/Macros_1.scala @@ -0,0 +1,6 @@ +import scala.quoted.* + +inline def showToString(inline x: Any): String = ${ macroImpl('x) } + +private def macroImpl(x: Expr[Any])(using Quotes): Expr[String] = + Expr(x.toString) diff --git a/tests/run-macros/Expr-toString/Test_2.scala b/tests/run-macros/Expr-toString/Test_2.scala new file mode 100644 index 000000000000..4f7eccfdcdf1 --- /dev/null +++ b/tests/run-macros/Expr-toString/Test_2.scala @@ -0,0 +1,5 @@ +@main def Test() = + println(showToString(1)) + println(showToString("abc")) + println(showToString(println("abc"))) + println(showToString { while true do () } ) diff --git a/tests/run-macros/Type-toString.check b/tests/run-macros/Type-toString.check new file mode 100644 index 000000000000..81ecbfaca1fe --- /dev/null +++ b/tests/run-macros/Type-toString.check @@ -0,0 +1,7 @@ +1 +"abc" +scala.Int +scala.collection.immutable.List[scala.Int] +java.lang.Object { + def foo: scala.Int +} diff --git a/tests/run-macros/Type-toString/Macros_1.scala b/tests/run-macros/Type-toString/Macros_1.scala new file mode 100644 index 000000000000..f1740def48ff --- /dev/null +++ b/tests/run-macros/Type-toString/Macros_1.scala @@ -0,0 +1,6 @@ +import scala.quoted.* + +inline def showToString[T]: String = ${ macroImpl[T] } + +private def macroImpl[T: Type](using Quotes): Expr[String] = + Expr(Type.of[T].toString) diff --git a/tests/run-macros/Type-toString/Test_2.scala b/tests/run-macros/Type-toString/Test_2.scala new file mode 100644 index 000000000000..ce5933d81b05 --- /dev/null +++ b/tests/run-macros/Type-toString/Test_2.scala @@ -0,0 +1,6 @@ +@main def Test() = + println(showToString[1]) + println(showToString["abc"]) + println(showToString[Int]) + println(showToString[List[Int]]) + println(showToString[Object { def foo: Int }])