diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index b08ee8f961af..74c8aa1d2d3b 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Flags._ import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.NameKinds import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types.Type import dotty.tools.dotc.core.tasty.{TastyPickler, TastyPrinter, TastyString} import scala.quoted.Types._ @@ -31,9 +32,13 @@ object PickledQuotes { } /** Transform the expression into its fully spliced Tree */ - def quotedExprToTree(expr: quoted.Expr[_])(implicit ctx: Context): Tree = expr match { + def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree = expr match { case expr: TastyExpr[_] => unpickleExpr(expr) - case expr: LiftedExpr[_] => Literal(Constant(expr.value)) + case expr: LiftedExpr[T] => + expr.value match { + case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value)) + case value=> Literal(Constant(value)) + } case expr: TreeExpr[Tree] @unchecked => expr.tree case expr: FunctionAppliedTo[_, _] => functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x)) @@ -154,4 +159,27 @@ object PickledQuotes { } Block(x1 :: Nil, rec(f)) } + + private def classToType(clazz: Class[_])(implicit ctx: Context): Type = { + if (clazz.isPrimitive) { + if (clazz == classOf[Boolean]) defn.BooleanType + else if (clazz == classOf[Byte]) defn.ByteType + else if (clazz == classOf[Char]) defn.CharType + else if (clazz == classOf[Short]) defn.ShortType + else if (clazz == classOf[Int]) defn.IntType + else if (clazz == classOf[Long]) defn.LongType + else if (clazz == classOf[Float]) defn.FloatType + else if (clazz == classOf[Double]) defn.DoubleType + else defn.UnitType + } else if (clazz.isArray) { + defn.ArrayType.appliedTo(classToType(clazz.getComponentType)) + } else if (clazz.isMemberClass) { + val name = clazz.getSimpleName.toTypeName + val enclosing = classToType(clazz.getEnclosingClass) + if (enclosing.member(name).exists) enclosing.select(name) + else { + enclosing.classSymbol.companionModule.termRef.select(name) + } + } else ctx.getClassIfDefined(clazz.getCanonicalName).typeRef + } } diff --git a/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala b/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala index 0957fac21a83..81ac31d146d2 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala @@ -29,12 +29,16 @@ object Toolbox { def show(expr: Expr[T]): String = expr match { case expr: LiftedExpr[T] => - implicit val ctx = new QuoteDriver().initCtx - if (showSettings.compilerArgs.contains("-color:never")) - ctx.settings.color.update("never") - val printer = new RefinedPrinter(ctx) - if (expr.value == BoxedUnit.UNIT) "()" - else printer.toText(Literal(Constant(expr.value))).mkString(Int.MaxValue, false) + expr.value match { + case value: Class[_] => s"classOf[${value.getCanonicalName}]" + case value if value == BoxedUnit.UNIT => "()" + case value => + implicit val ctx = new QuoteDriver().initCtx + if (showSettings.compilerArgs.contains("-color:never")) + ctx.settings.color.update("never") + val printer = new RefinedPrinter(ctx) + printer.toText(Literal(Constant(value))).mkString(Int.MaxValue, false) + } case _ => new QuoteDriver().show(expr, showSettings) } diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 7eef2edcdeed..73876125afd3 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -75,8 +75,8 @@ object Splicer { liftArgs(call.symbol.info, allArgs(call, Nil)) } - private def evaluateLambda(lambda: Seq[Any] => Object, args: Seq[Any], pos: Position)(implicit ctx: Context): Option[scala.quoted.Expr[_]] = { - try Some(lambda(args).asInstanceOf[scala.quoted.Expr[_]]) + private def evaluateLambda(lambda: Seq[Any] => Object, args: Seq[Any], pos: Position)(implicit ctx: Context): Option[scala.quoted.Expr[Nothing]] = { + try Some(lambda(args).asInstanceOf[scala.quoted.Expr[Nothing]]) catch { case ex: scala.quoted.QuoteError => ctx.error(ex.getMessage, pos) diff --git a/library/src/scala/quoted/Liftable.scala b/library/src/scala/quoted/Liftable.scala index 56cf3a122dda..ae494e17d33b 100644 --- a/library/src/scala/quoted/Liftable.scala +++ b/library/src/scala/quoted/Liftable.scala @@ -25,4 +25,6 @@ object Liftable { implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => liftedExpr(x) implicit def StringIsLiftable: Liftable[String] = (x: String) => liftedExpr(x) + + implicit def ClassIsLiftable[T]: Liftable[Class[T]] = (x: Class[T]) => liftedExpr(x) } diff --git a/library/src/scala/runtime/quoted/Unpickler.scala b/library/src/scala/runtime/quoted/Unpickler.scala index 3d25bb2ab164..ac4466b35fa5 100644 --- a/library/src/scala/runtime/quoted/Unpickler.scala +++ b/library/src/scala/runtime/quoted/Unpickler.scala @@ -18,7 +18,7 @@ object Unpickler { def unpickleExpr[T](repr: Pickled, args: Seq[Any]): Expr[T] = new TastyExpr[T](repr, args) /** Lift the `value` to an `Expr` tree. - * Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String or Null. + * Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String, Null or Class. */ def liftedExpr[T](value: T): LiftedExpr[T] = new LiftedExpr[T](value) diff --git a/tests/run-with-compiler/i3947.check b/tests/run-with-compiler/i3947.check new file mode 100644 index 000000000000..12e71232cc86 --- /dev/null +++ b/tests/run-with-compiler/i3947.check @@ -0,0 +1,12 @@ + +classOf[Object].getCanonicalName() +java.lang.Object + +classOf[Object].getCanonicalName() +java.lang.Object + +classOf[Object].getCanonicalName() +java.lang.Object + +classOf[Object].getCanonicalName() +java.lang.Object diff --git a/tests/run-with-compiler/i3947.scala b/tests/run-with-compiler/i3947.scala new file mode 100644 index 000000000000..cb387260e232 --- /dev/null +++ b/tests/run-with-compiler/i3947.scala @@ -0,0 +1,24 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + // classOf[Object] + test(classOf[Object]) + test(classOf[Any]) + test(classOf[AnyRef]) + test(classOf[AnyVal]) + } + +} diff --git a/tests/run-with-compiler/i3947b.check b/tests/run-with-compiler/i3947b.check new file mode 100644 index 000000000000..decf57290735 --- /dev/null +++ b/tests/run-with-compiler/i3947b.check @@ -0,0 +1,27 @@ + +classOf[Boolean].getCanonicalName() +boolean + +classOf[Byte].getCanonicalName() +byte + +classOf[Char].getCanonicalName() +char + +classOf[Short].getCanonicalName() +short + +classOf[Int].getCanonicalName() +int + +classOf[Long].getCanonicalName() +long + +classOf[Float].getCanonicalName() +float + +classOf[Double].getCanonicalName() +double + +classOf[Unit].getCanonicalName() +void diff --git a/tests/run-with-compiler/i3947b.scala b/tests/run-with-compiler/i3947b.scala new file mode 100644 index 000000000000..404c7980aba2 --- /dev/null +++ b/tests/run-with-compiler/i3947b.scala @@ -0,0 +1,29 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + // primitives + test(classOf[Boolean]) + test(classOf[Byte]) + test(classOf[Char]) + test(classOf[Short]) + test(classOf[Int]) + test(classOf[Long]) + test(classOf[Float]) + test(classOf[Double]) + test(classOf[Unit]) + } + +} diff --git a/tests/run-with-compiler/i3947c.check b/tests/run-with-compiler/i3947c.check new file mode 100644 index 000000000000..dc02862f409a --- /dev/null +++ b/tests/run-with-compiler/i3947c.check @@ -0,0 +1,9 @@ + +classOf[scala.runtime.Null$].getCanonicalName() +scala.runtime.Null$ + +classOf[scala.runtime.Nothing$].getCanonicalName() +scala.runtime.Nothing$ + +classOf[String].getCanonicalName() +java.lang.String diff --git a/tests/run-with-compiler/i3947c.scala b/tests/run-with-compiler/i3947c.scala new file mode 100644 index 000000000000..0f770bea54be --- /dev/null +++ b/tests/run-with-compiler/i3947c.scala @@ -0,0 +1,23 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + test(classOf[Null]) + test(classOf[Nothing]) + + test(classOf[String]) + } + +} diff --git a/tests/run-with-compiler/i3947d.check b/tests/run-with-compiler/i3947d.check new file mode 100644 index 000000000000..075a44674d87 --- /dev/null +++ b/tests/run-with-compiler/i3947d.check @@ -0,0 +1,18 @@ + +classOf[Foo].getCanonicalName() +Foo + +classOf[Foo#Bar].getCanonicalName() +Foo.Bar + +classOf[Foo.Baz].getCanonicalName() +Foo.Baz + +classOf[foo.Foo].getCanonicalName() +foo.Foo + +classOf[foo.Foo#Bar].getCanonicalName() +foo.Foo.Bar + +classOf[foo.Foo.Baz].getCanonicalName() +foo.Foo.Baz diff --git a/tests/run-with-compiler/i3947d.scala b/tests/run-with-compiler/i3947d.scala new file mode 100644 index 000000000000..74dfd0089a32 --- /dev/null +++ b/tests/run-with-compiler/i3947d.scala @@ -0,0 +1,43 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + test(classOf[Foo]) + test(classOf[Foo#Bar]) + test(classOf[Foo.Baz]) + + test(classOf[foo.Foo]) + test(classOf[foo.Foo#Bar]) + test(classOf[foo.Foo.Baz]) + } + +} + +class Foo { + class Bar +} + +object Foo { + class Baz +} + +package foo { + class Foo { + class Bar + } + object Foo { + class Baz + } +} diff --git a/tests/run-with-compiler/i3947e.check b/tests/run-with-compiler/i3947e.check new file mode 100644 index 000000000000..0d25c46e9bee --- /dev/null +++ b/tests/run-with-compiler/i3947e.check @@ -0,0 +1,9 @@ + +classOf[Object].getCanonicalName() +java.lang.Object + +classOf[Array[Foo]].getCanonicalName() +Foo[] + +classOf[Array[Array[Foo]]].getCanonicalName() +Foo[][] diff --git a/tests/run-with-compiler/i3947e.scala b/tests/run-with-compiler/i3947e.scala new file mode 100644 index 000000000000..08b0548e059e --- /dev/null +++ b/tests/run-with-compiler/i3947e.scala @@ -0,0 +1,29 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + // class Object + test(classOf[Array[_]]) + + // class Array[Foo] + test(classOf[Array[Foo]]) + + // class Array[Array[Foo]] + test(classOf[Array[Array[Foo]]]) + } + +} + +class Foo diff --git a/tests/run-with-compiler/i3947f.check b/tests/run-with-compiler/i3947f.check new file mode 100644 index 000000000000..0b6edad23cb1 --- /dev/null +++ b/tests/run-with-compiler/i3947f.check @@ -0,0 +1,12 @@ + +classOf[Array[Object]].getCanonicalName() +java.lang.Object[] + +classOf[Array[Object]].getCanonicalName() +java.lang.Object[] + +classOf[Array[Object]].getCanonicalName() +java.lang.Object[] + +classOf[Array[Object]].getCanonicalName() +java.lang.Object[] diff --git a/tests/run-with-compiler/i3947f.scala b/tests/run-with-compiler/i3947f.scala new file mode 100644 index 000000000000..1f968f3936a8 --- /dev/null +++ b/tests/run-with-compiler/i3947f.scala @@ -0,0 +1,26 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + // class Array[Object] + test(classOf[Array[Any]]) + test(classOf[Array[AnyVal]]) + test(classOf[Array[AnyRef]]) + test(classOf[Array[Object]]) + } + +} + +class Foo diff --git a/tests/run-with-compiler/i3947g.check b/tests/run-with-compiler/i3947g.check new file mode 100644 index 000000000000..cf538c37ae20 --- /dev/null +++ b/tests/run-with-compiler/i3947g.check @@ -0,0 +1,12 @@ + +classOf[Array[Boolean]].getCanonicalName() +boolean[] + +classOf[Array[Byte]].getCanonicalName() +byte[] + +classOf[Array[Char]].getCanonicalName() +char[] + +classOf[Array[Short]].getCanonicalName() +short[] diff --git a/tests/run-with-compiler/i3947g.scala b/tests/run-with-compiler/i3947g.scala new file mode 100644 index 000000000000..a49e8986b115 --- /dev/null +++ b/tests/run-with-compiler/i3947g.scala @@ -0,0 +1,24 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + // primitive arrays + test(classOf[Array[Boolean]]) + test(classOf[Array[Byte]]) + test(classOf[Array[Char]]) + test(classOf[Array[Short]]) + } + +} diff --git a/tests/run-with-compiler/i3947i.check b/tests/run-with-compiler/i3947i.check new file mode 100644 index 000000000000..141776ab3163 --- /dev/null +++ b/tests/run-with-compiler/i3947i.check @@ -0,0 +1,12 @@ + +classOf[Array[Int]].getCanonicalName() +int[] + +classOf[Array[Long]].getCanonicalName() +long[] + +classOf[Array[Float]].getCanonicalName() +float[] + +classOf[Array[Double]].getCanonicalName() +double[] diff --git a/tests/run-with-compiler/i3947i.scala b/tests/run-with-compiler/i3947i.scala new file mode 100644 index 000000000000..a3bc862ab4b0 --- /dev/null +++ b/tests/run-with-compiler/i3947i.scala @@ -0,0 +1,24 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + // primitive arrays + test(classOf[Array[Int]]) + test(classOf[Array[Long]]) + test(classOf[Array[Float]]) + test(classOf[Array[Double]]) + } + +} diff --git a/tests/run-with-compiler/i3947j.check b/tests/run-with-compiler/i3947j.check new file mode 100644 index 000000000000..ce0048ef7906 --- /dev/null +++ b/tests/run-with-compiler/i3947j.check @@ -0,0 +1,6 @@ + +classOf[Array[Array[Int]]].getCanonicalName() +int[][] + +classOf[Array[Array[Array[Int]]]].getCanonicalName() +int[][][] diff --git a/tests/run-with-compiler/i3947j.scala b/tests/run-with-compiler/i3947j.scala new file mode 100644 index 000000000000..04e75012acb8 --- /dev/null +++ b/tests/run-with-compiler/i3947j.scala @@ -0,0 +1,21 @@ + +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + + def main(args: Array[String]): Unit = { + + def test[T](clazz: java.lang.Class[T]): Unit = { + val lclazz = clazz.toExpr + val name = '{ (~lclazz).getCanonicalName } + println() + println(name.show) + println(name.run) + } + + test(classOf[Array[Array[Int]]]) + test(classOf[Array[Array[Array[Int]]]]) + } + +}