Skip to content

Commit b6dbe42

Browse files
Merge pull request #4193 from dotty-staging/lift-class
Fix #3947: Support lifting of java.lang.Class
2 parents ca84f3f + 2d7653a commit b6dbe42

23 files changed

+405
-11
lines changed

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

Lines changed: 30 additions & 2 deletions
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._
@@ -31,9 +32,13 @@ object PickledQuotes {
3132
}
3233

3334
/** Transform the expression into its fully spliced Tree */
34-
def quotedExprToTree(expr: quoted.Expr[_])(implicit ctx: Context): Tree = expr match {
35+
def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree = expr match {
3536
case expr: TastyExpr[_] => unpickleExpr(expr)
36-
case expr: LiftedExpr[_] => Literal(Constant(expr.value))
37+
case expr: LiftedExpr[T] =>
38+
expr.value match {
39+
case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value))
40+
case value=> Literal(Constant(value))
41+
}
3742
case expr: TreeExpr[Tree] @unchecked => expr.tree
3843
case expr: FunctionAppliedTo[_, _] =>
3944
functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x))
@@ -154,4 +159,27 @@ object PickledQuotes {
154159
}
155160
Block(x1 :: Nil, rec(f))
156161
}
162+
163+
private def classToType(clazz: Class[_])(implicit ctx: Context): Type = {
164+
if (clazz.isPrimitive) {
165+
if (clazz == classOf[Boolean]) defn.BooleanType
166+
else if (clazz == classOf[Byte]) defn.ByteType
167+
else if (clazz == classOf[Char]) defn.CharType
168+
else if (clazz == classOf[Short]) defn.ShortType
169+
else if (clazz == classOf[Int]) defn.IntType
170+
else if (clazz == classOf[Long]) defn.LongType
171+
else if (clazz == classOf[Float]) defn.FloatType
172+
else if (clazz == classOf[Double]) defn.DoubleType
173+
else defn.UnitType
174+
} else if (clazz.isArray) {
175+
defn.ArrayType.appliedTo(classToType(clazz.getComponentType))
176+
} else if (clazz.isMemberClass) {
177+
val name = clazz.getSimpleName.toTypeName
178+
val enclosing = classToType(clazz.getEnclosingClass)
179+
if (enclosing.member(name).exists) enclosing.select(name)
180+
else {
181+
enclosing.classSymbol.companionModule.termRef.select(name)
182+
}
183+
} else ctx.getClassIfDefined(clazz.getCanonicalName).typeRef
184+
}
157185
}

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

compiler/src/dotty/tools/dotc/transform/Splicer.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ object Splicer {
7575
liftArgs(call.symbol.info, allArgs(call, Nil))
7676
}
7777

78-
private def evaluateLambda(lambda: Seq[Any] => Object, args: Seq[Any], pos: Position)(implicit ctx: Context): Option[scala.quoted.Expr[_]] = {
79-
try Some(lambda(args).asInstanceOf[scala.quoted.Expr[_]])
78+
private def evaluateLambda(lambda: Seq[Any] => Object, args: Seq[Any], pos: Position)(implicit ctx: Context): Option[scala.quoted.Expr[Nothing]] = {
79+
try Some(lambda(args).asInstanceOf[scala.quoted.Expr[Nothing]])
8080
catch {
8181
case ex: scala.quoted.QuoteError =>
8282
ctx.error(ex.getMessage, pos)

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 = '{ (~lclazz).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 = '{ (~lclazz).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 = '{ (~lclazz).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 = '{ (~lclazz).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+
}

tests/run-with-compiler/i3947e.check

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

tests/run-with-compiler/i3947e.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 = '{ (~lclazz).getCanonicalName }
12+
println()
13+
println(name.show)
14+
println(name.run)
15+
}
16+
17+
// class Object
18+
test(classOf[Array[_]])
19+
20+
// class Array[Foo]
21+
test(classOf[Array[Foo]])
22+
23+
// class Array[Array[Foo]]
24+
test(classOf[Array[Array[Foo]]])
25+
}
26+
27+
}
28+
29+
class Foo

tests/run-with-compiler/i3947f.check

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

tests/run-with-compiler/i3947f.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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 = '{ (~lclazz).getCanonicalName }
12+
println()
13+
println(name.show)
14+
println(name.run)
15+
}
16+
17+
// class Array[Object]
18+
test(classOf[Array[Any]])
19+
test(classOf[Array[AnyVal]])
20+
test(classOf[Array[AnyRef]])
21+
test(classOf[Array[Object]])
22+
}
23+
24+
}
25+
26+
class Foo

tests/run-with-compiler/i3947g.check

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
classOf[Array[Boolean]].getCanonicalName()
3+
boolean[]
4+
5+
classOf[Array[Byte]].getCanonicalName()
6+
byte[]
7+
8+
classOf[Array[Char]].getCanonicalName()
9+
char[]
10+
11+
classOf[Array[Short]].getCanonicalName()
12+
short[]

0 commit comments

Comments
 (0)