Skip to content

Fix #3947: Support lifting of java.lang.Class #4193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand All @@ -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))
Expand Down Expand Up @@ -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
}
}
16 changes: 10 additions & 6 deletions compiler/src/dotty/tools/dotc/quoted/Toolbox.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions library/src/scala/quoted/Liftable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
2 changes: 1 addition & 1 deletion library/src/scala/runtime/quoted/Unpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
12 changes: 12 additions & 0 deletions tests/run-with-compiler/i3947.check
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions tests/run-with-compiler/i3947.scala
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of lclazz in the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lift a class and then splice it in some quote that will run in next stage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see. I lifted it directly in the quote. I will remove it or use it instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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])
}

}
27 changes: 27 additions & 0 deletions tests/run-with-compiler/i3947b.check
Original file line number Diff line number Diff line change
@@ -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
29 changes: 29 additions & 0 deletions tests/run-with-compiler/i3947b.scala
Original file line number Diff line number Diff line change
@@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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])
}

}
9 changes: 9 additions & 0 deletions tests/run-with-compiler/i3947c.check
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions tests/run-with-compiler/i3947c.scala
Original file line number Diff line number Diff line change
@@ -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])
}

}
18 changes: 18 additions & 0 deletions tests/run-with-compiler/i3947d.check
Original file line number Diff line number Diff line change
@@ -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
43 changes: 43 additions & 0 deletions tests/run-with-compiler/i3947d.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
9 changes: 9 additions & 0 deletions tests/run-with-compiler/i3947e.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

classOf[Object].getCanonicalName()
java.lang.Object

classOf[Array[Foo]].getCanonicalName()
Foo[]

classOf[Array[Array[Foo]]].getCanonicalName()
Foo[][]
29 changes: 29 additions & 0 deletions tests/run-with-compiler/i3947e.scala
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions tests/run-with-compiler/i3947f.check
Original file line number Diff line number Diff line change
@@ -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[]
26 changes: 26 additions & 0 deletions tests/run-with-compiler/i3947f.scala
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions tests/run-with-compiler/i3947g.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

classOf[Array[Boolean]].getCanonicalName()
boolean[]

classOf[Array[Byte]].getCanonicalName()
byte[]

classOf[Array[Char]].getCanonicalName()
char[]

classOf[Array[Short]].getCanonicalName()
short[]
Loading