Skip to content

Commit 96629a0

Browse files
committed
Fix #3947: Support lifting of java.lang.Class
1 parent 1183d0d commit 96629a0

File tree

12 files changed

+224
-8
lines changed

12 files changed

+224
-8
lines changed

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Flags._
1010
import dotty.tools.dotc.core.StdNames._
1111
import dotty.tools.dotc.core.NameKinds
1212
import dotty.tools.dotc.core.Symbols._
13+
import dotty.tools.dotc.core.Types.Type
1314
import dotty.tools.dotc.core.tasty.{TastyPickler, TastyPrinter, TastyString}
1415

1516
import scala.quoted.Types._
@@ -33,7 +34,10 @@ object PickledQuotes {
3334
/** Transform the expression into its fully spliced Tree */
3435
def quotedExprToTree(expr: quoted.Expr[_])(implicit ctx: Context): Tree = expr match {
3536
case expr: TastyExpr[_] => unpickleExpr(expr)
36-
case expr: LiftedExpr[_] => Literal(Constant(expr.value))
37+
case expr: LiftedExpr[_] =>
38+
if (expr.value.isInstanceOf[Class[_]]) // Should be a pattern match after #4198 is fixed
39+
ref(defn.Predef_classOf).appliedToType(classToType(expr.value.asInstanceOf[Class[_]]))
40+
else Literal(Constant(expr.value))
3741
case expr: TreeExpr[Tree] @unchecked => expr.tree
3842
case expr: FunctionAppliedTo[_, _] =>
3943
functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x))
@@ -154,4 +158,25 @@ object PickledQuotes {
154158
}
155159
Block(x1 :: Nil, rec(f))
156160
}
161+
162+
private def classToType(clazz: Class[_])(implicit ctx: Context): Type = {
163+
if (clazz == classOf[Boolean]) defn.BooleanType
164+
else if (clazz == classOf[Byte]) defn.ByteType
165+
else if (clazz == classOf[Char]) defn.CharType
166+
else if (clazz == classOf[Short]) defn.ShortType
167+
else if (clazz == classOf[Int]) defn.IntType
168+
else if (clazz == classOf[Long]) defn.LongType
169+
else if (clazz == classOf[Float]) defn.FloatType
170+
else if (clazz == classOf[Double]) defn.DoubleType
171+
else if (clazz == classOf[Unit]) defn.UnitType
172+
else if (!clazz.isMemberClass) ctx.getClassIfDefined(clazz.getCanonicalName).typeRef
173+
else {
174+
val name = clazz.getSimpleName.toTypeName
175+
val enclosing = classToType(clazz.getEnclosingClass)
176+
if (enclosing.member(name).exists) enclosing.select(name)
177+
else {
178+
enclosing.classSymbol.companionModule.termRef.select(name)
179+
}
180+
}
181+
}
157182
}

compiler/src/dotty/tools/dotc/quoted/Toolbox.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@ object Toolbox {
2929

3030
def show(expr: Expr[T]): String = expr match {
3131
case expr: LiftedExpr[T] =>
32-
implicit val ctx = new QuoteDriver().initCtx
33-
if (showSettings.compilerArgs.contains("-color:never"))
34-
ctx.settings.color.update("never")
35-
val printer = new RefinedPrinter(ctx)
36-
if (expr.value == BoxedUnit.UNIT) "()"
37-
else printer.toText(Literal(Constant(expr.value))).mkString(Int.MaxValue, false)
32+
expr.value match {
33+
case value: Class[_] => s"classOf[${value.getCanonicalName}]"
34+
case value if value == BoxedUnit.UNIT => "()"
35+
case value =>
36+
implicit val ctx = new QuoteDriver().initCtx
37+
if (showSettings.compilerArgs.contains("-color:never"))
38+
ctx.settings.color.update("never")
39+
val printer = new RefinedPrinter(ctx)
40+
printer.toText(Literal(Constant(value))).mkString(Int.MaxValue, false)
41+
}
3842
case _ => new QuoteDriver().show(expr, showSettings)
3943
}
4044

library/src/scala/quoted/Liftable.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ object Liftable {
2525
implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => liftedExpr(x)
2626

2727
implicit def StringIsLiftable: Liftable[String] = (x: String) => liftedExpr(x)
28+
29+
implicit def ClassIsLiftable[T]: Liftable[Class[T]] = (x: Class[T]) => liftedExpr(x)
2830
}

library/src/scala/runtime/quoted/Unpickler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ object Unpickler {
1818
def unpickleExpr[T](repr: Pickled, args: Seq[Any]): Expr[T] = new TastyExpr[T](repr, args)
1919

2020
/** Lift the `value` to an `Expr` tree.
21-
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String or Null.
21+
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String, Null or Class.
2222
*/
2323
def liftedExpr[T](value: T): LiftedExpr[T] = new LiftedExpr[T](value)
2424

tests/run-with-compiler/i3947.check

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
classOf[Object].getCanonicalName()
3+
java.lang.Object
4+
5+
classOf[Object].getCanonicalName()
6+
java.lang.Object
7+
8+
classOf[Object].getCanonicalName()
9+
java.lang.Object
10+
11+
classOf[Object].getCanonicalName()
12+
java.lang.Object

tests/run-with-compiler/i3947.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
import scala.quoted._
3+
import dotty.tools.dotc.quoted.Toolbox._
4+
5+
object Test {
6+
7+
def main(args: Array[String]): Unit = {
8+
9+
def test[T](clazz: java.lang.Class[T]): Unit = {
10+
val lclazz = clazz.toExpr
11+
val name = '{ (~clazz.toExpr).getCanonicalName }
12+
println()
13+
println(name.show)
14+
println(name.run)
15+
}
16+
17+
// classOf[Object]
18+
test(classOf[Object])
19+
test(classOf[Any])
20+
test(classOf[AnyRef])
21+
test(classOf[AnyVal])
22+
}
23+
24+
}

tests/run-with-compiler/i3947b.check

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
classOf[Boolean].getCanonicalName()
3+
boolean
4+
5+
classOf[Byte].getCanonicalName()
6+
byte
7+
8+
classOf[Char].getCanonicalName()
9+
char
10+
11+
classOf[Short].getCanonicalName()
12+
short
13+
14+
classOf[Int].getCanonicalName()
15+
int
16+
17+
classOf[Long].getCanonicalName()
18+
long
19+
20+
classOf[Float].getCanonicalName()
21+
float
22+
23+
classOf[Double].getCanonicalName()
24+
double
25+
26+
classOf[Unit].getCanonicalName()
27+
void

tests/run-with-compiler/i3947b.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
import scala.quoted._
3+
import dotty.tools.dotc.quoted.Toolbox._
4+
5+
object Test {
6+
7+
def main(args: Array[String]): Unit = {
8+
9+
def test[T](clazz: java.lang.Class[T]): Unit = {
10+
val lclazz = clazz.toExpr
11+
val name = '{ (~clazz.toExpr).getCanonicalName }
12+
println()
13+
println(name.show)
14+
println(name.run)
15+
}
16+
17+
// primitives
18+
test(classOf[Boolean])
19+
test(classOf[Byte])
20+
test(classOf[Char])
21+
test(classOf[Short])
22+
test(classOf[Int])
23+
test(classOf[Long])
24+
test(classOf[Float])
25+
test(classOf[Double])
26+
test(classOf[Unit])
27+
}
28+
29+
}

tests/run-with-compiler/i3947c.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
classOf[scala.runtime.Null$].getCanonicalName()
3+
scala.runtime.Null$
4+
5+
classOf[scala.runtime.Nothing$].getCanonicalName()
6+
scala.runtime.Nothing$
7+
8+
classOf[String].getCanonicalName()
9+
java.lang.String

tests/run-with-compiler/i3947c.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
import scala.quoted._
3+
import dotty.tools.dotc.quoted.Toolbox._
4+
5+
object Test {
6+
7+
def main(args: Array[String]): Unit = {
8+
9+
def test[T](clazz: java.lang.Class[T]): Unit = {
10+
val lclazz = clazz.toExpr
11+
val name = '{ (~clazz.toExpr).getCanonicalName }
12+
println()
13+
println(name.show)
14+
println(name.run)
15+
}
16+
17+
test(classOf[Null])
18+
test(classOf[Nothing])
19+
20+
test(classOf[String])
21+
}
22+
23+
}

tests/run-with-compiler/i3947d.check

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
classOf[Foo].getCanonicalName()
3+
Foo
4+
5+
classOf[Foo#Bar].getCanonicalName()
6+
Foo.Bar
7+
8+
classOf[Foo.Baz].getCanonicalName()
9+
Foo.Baz
10+
11+
classOf[foo.Foo].getCanonicalName()
12+
foo.Foo
13+
14+
classOf[foo.Foo#Bar].getCanonicalName()
15+
foo.Foo.Bar
16+
17+
classOf[foo.Foo.Baz].getCanonicalName()
18+
foo.Foo.Baz

tests/run-with-compiler/i3947d.scala

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
2+
import scala.quoted._
3+
import dotty.tools.dotc.quoted.Toolbox._
4+
5+
object Test {
6+
7+
def main(args: Array[String]): Unit = {
8+
9+
def test[T](clazz: java.lang.Class[T]): Unit = {
10+
val lclazz = clazz.toExpr
11+
val name = '{ (~clazz.toExpr).getCanonicalName }
12+
println()
13+
println(name.show)
14+
println(name.run)
15+
}
16+
17+
test(classOf[Foo])
18+
test(classOf[Foo#Bar])
19+
test(classOf[Foo.Baz])
20+
21+
test(classOf[foo.Foo])
22+
test(classOf[foo.Foo#Bar])
23+
test(classOf[foo.Foo.Baz])
24+
}
25+
26+
}
27+
28+
class Foo {
29+
class Bar
30+
}
31+
32+
object Foo {
33+
class Baz
34+
}
35+
36+
package foo {
37+
class Foo {
38+
class Bar
39+
}
40+
object Foo {
41+
class Baz
42+
}
43+
}

0 commit comments

Comments
 (0)