Skip to content

Seal quoted.Expr and quoted.Type #3883

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 8 commits into from
Feb 21, 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
46 changes: 29 additions & 17 deletions compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.NameKinds
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.NameKinds
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.tasty.{TastyPickler, TastyPrinter, TastyString}
import dotty.tools.dotc.interpreter.RawQuoted

import scala.quoted.Types._
import scala.quoted.Exprs._

import scala.reflect.ClassTag

Expand All @@ -29,27 +31,37 @@ object PickledQuotes {
}

/** Transform the expression into its fully spliced Tree */
def quotedToTree(expr: quoted.Quoted)(implicit ctx: Context): Tree = expr match {
case expr: quoted.TastyQuoted =>
unpickleQuote(expr)
case expr: quoted.Liftable.ConstantExpr[_] =>
Literal(Constant(expr.value))
case expr: quoted.Expr.FunctionAppliedTo[_, _] =>
functionAppliedTo(quotedToTree(expr.f), quotedToTree(expr.x))
case expr: quoted.Type.TaggedPrimitive[_] =>
classTagToTypeTree(expr.ct)
case expr: RawQuoted =>
expr.tree
def quotedExprToTree(expr: quoted.Expr[_])(implicit ctx: Context): Tree = expr match {
case expr: TastyExpr[_] => unpickleExpr(expr)
case expr: ValueExpr[_] => Literal(Constant(expr.value))
case expr: TreeExpr[Tree] @unchecked => expr.tree
case expr: FunctionAppliedTo[_, _] =>
functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x))
}

/** Transform the expression into its fully spliced TypeTree */
def quotedTypeToTree(expr: quoted.Type[_])(implicit ctx: Context): Tree = expr match {
case expr: TastyType[_] => unpickleType(expr)
case expr: TaggedType[_] => classTagToTypeTree(expr.ct)
case expr: TreeType[Tree] @unchecked => expr.tree
}

/** Unpickle the tree contained in the TastyQuoted */
private def unpickleQuote(expr: quoted.TastyQuoted)(implicit ctx: Context): Tree = {
/** Unpickle the tree contained in the TastyExpr */
private def unpickleExpr(expr: TastyExpr[_])(implicit ctx: Context): Tree = {
val tastyBytes = TastyString.unpickle(expr.tasty)
val unpickled = unpickle(tastyBytes, expr.args)
unpickled match {
case PackageDef(_, (vdef: ValDef) :: Nil) => vdef.rhs
}
}

/** Unpickle the tree contained in the TastyType */
private def unpickleType(ttpe: TastyType[_])(implicit ctx: Context): Tree = {
val tastyBytes = TastyString.unpickle(ttpe.tasty)
val unpickled = unpickle(tastyBytes, ttpe.args)
unpickled match {
case PackageDef(_, (vdef: ValDef) :: Nil) =>
if (vdef.name == "$quote".toTermName) vdef.rhs
else vdef.rhs.asInstanceOf[TypeApply].args.head
vdef.rhs.asInstanceOf[TypeApply].args.head
}
}

Expand Down
26 changes: 18 additions & 8 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import config.Printers.pickling
import typer.Checking
import config.Config
import dotty.tools.dotc.core.quoted.PickledQuotes
import dotty.tools.dotc.interpreter.RawQuoted
import scala.quoted
import scala.quoted.Types.TreeType
import scala.quoted.Exprs.TreeExpr

/** Unpickler for typed trees
* @param reader the reader from which to unpickle
Expand Down Expand Up @@ -296,7 +297,7 @@ class TreeUnpickler(reader: TastyReader,
case ENUMconst =>
ConstantType(Constant(readTermRef().termSymbol))
case HOLE =>
readHole(end).tpe
readHole(end, isType = true).tpe
}
assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}")
result
Expand Down Expand Up @@ -1076,7 +1077,7 @@ class TreeUnpickler(reader: TastyReader,
val hi = if (currentAddr == end) lo else readTpt()
TypeBoundsTree(lo, hi)
case HOLE =>
readHole(end)
readHole(end, isType = false)
case _ =>
readPathTerm()
}
Expand Down Expand Up @@ -1127,14 +1128,23 @@ class TreeUnpickler(reader: TastyReader,
new LazyReader(localReader, op)
}

def readHole(end: Addr)(implicit ctx: Context): Tree = {
def readHole(end: Addr, isType: Boolean)(implicit ctx: Context): Tree = {
val idx = readNat()
val args = until(end)(readTerm())
val splice = splices(idx)
val quotedType =
if (args.isEmpty) splice.asInstanceOf[quoted.Quoted]
else splice.asInstanceOf[Seq[Any] => quoted.Quoted](args.map(RawQuoted.apply))
PickledQuotes.quotedToTree(quotedType)

if (isType) {
val quotedType =
if (args.isEmpty) splice.asInstanceOf[quoted.Type[_]]
else splice.asInstanceOf[Seq[Any] => quoted.Type[_]](args.map(tree => new TreeType(tree)))
PickledQuotes.quotedTypeToTree(quotedType)
} else {
val quotedExpr =
if (args.isEmpty) splice.asInstanceOf[quoted.Expr[_]]
else splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](args.map(tree => new TreeExpr(tree)))
PickledQuotes.quotedExprToTree(quotedExpr)
}

}

// ------ Setting positions ------------------------------------------------
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ class Interpreter(implicit ctx: Context) {
implicit val pos: Position = tree.pos

tree match {
case Quoted(quotedTree) => RawQuoted(quotedTree)
case Quoted(quotedTree) =>
if (tree.isTerm) new scala.quoted.Exprs.TreeExpr(quotedTree)
else new scala.quoted.Types.TreeType(quotedTree)

case Literal(Constant(c)) => c.asInstanceOf[Object]

Expand Down
22 changes: 0 additions & 22 deletions compiler/src/dotty/tools/dotc/interpreter/RawQuoted.scala

This file was deleted.

4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class ExprCompiler(directory: AbstractFile) extends Compiler {
case exprUnit: ExprCompilationUnit =>
val tree =
if (putInClass) inClass(exprUnit.expr)
else PickledQuotes.quotedToTree(exprUnit.expr)
else PickledQuotes.quotedExprToTree(exprUnit.expr)
val source = new SourceFile("", Seq())
CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true)
}
Expand All @@ -80,7 +80,7 @@ class ExprCompiler(directory: AbstractFile) extends Compiler {
cls.enter(ctx.newDefaultConstructor(cls), EmptyScope)
val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered

val quoted = PickledQuotes.quotedToTree(expr)(ctx.withOwner(meth))
val quoted = PickledQuotes.quotedExprToTree(expr)(ctx.withOwner(meth))

val run = DefDef(meth, quoted)
val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil)
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/quoted/Runners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import dotty.tools.dotc.core.Constants._
import dotty.tools.dotc.printing.RefinedPrinter

import scala.quoted.Expr
import scala.quoted.Liftable.ConstantExpr
import scala.runtime.BoxedUnit
import scala.quoted.Exprs.ValueExpr
import scala.runtime.quoted._

/** Default runners for quoted expressions */
Expand All @@ -31,20 +31,20 @@ object Runners {
case _ => None
}
expr match {
case expr: ConstantExpr[T] => Some(expr.value)
case expr: ValueExpr[T] => Some(expr.value)
case _ => new QuoteDriver().withTree(expr, (tree, _) => toConstantOpt(tree), Settings.run())
}
}

}

def run[T](expr: Expr[T], settings: Settings[Run]): T = expr match {
case expr: ConstantExpr[T] => expr.value
case expr: ValueExpr[T] => expr.value
case _ => new QuoteDriver().run(expr, settings)
}

def show[T](expr: Expr[T], settings: Settings[Show]): String = expr match {
case expr: ConstantExpr[T] =>
case expr: ValueExpr[T] =>
implicit val ctx = new QuoteDriver().initCtx
if (settings.compilerArgs.contains("-color:never"))
ctx.settings.color.update("never")
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Splicer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ object Splicer {
/** Splice the Tree for a Quoted expression which is constructed via a reflective call to the given method */
private def reflectiveSplice(tree: Tree)(implicit ctx: Context): Tree = {
val interpreter = new Interpreter
interpreter.interpretTree[scala.quoted.Expr[_]](tree).map(PickledQuotes.quotedToTree(_)).getOrElse(tree)
interpreter.interpretTree[scala.quoted.Expr[_]](tree).map(PickledQuotes.quotedExprToTree).getOrElse(tree)
}

}
12 changes: 12 additions & 0 deletions compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1294,4 +1294,16 @@ class ErrorMessagesTests extends ErrorMessagesTest {
val JavaSymbolIsNotAValue(symbol) = messages.head
assertEquals(symbol.show, "package p")
}

@Test def i3187 =
checkMessagesAfter("genBCode") {
"""
|package scala
|object collection
""".stripMargin
}.expect { (itcx, messages) =>
implicit val ctx: Context = itcx

assert(ctx.reporter.hasErrors)
}
}
2 changes: 1 addition & 1 deletion docs/docs/reference/principled-meta-programming.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ to be executed at a later stage. To run that code, there is another method
in class `Expr` called `run`. Note that `~` and `run` both map from `Expr[T]`
to `T` but only `~` is subject to the PCP, whereas `run` is just a normal method.

abstract class Expr[T] {
sealed abstract class Expr[T] {
def unary_~: T
def run(implicit runner: Runner[T]): T // run staged code
def show(implicit runner: Runner[T]): String // show staged code
Expand Down
33 changes: 30 additions & 3 deletions library/src/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package scala.quoted

import scala.runtime.quoted.Runner
import scala.runtime.quoted.Unpickler.Pickled

abstract class Expr[T] extends Quoted {
sealed abstract class Expr[T] {
final def unary_~ : T = throw new Error("~ should have been compiled away")
final def run(implicit runner: Runner[T]): T = runner.run(this)
final def show(implicit runner: Runner[T]): String = runner.show(this)
Expand All @@ -13,8 +14,34 @@ object Expr {
ev.toExpr(x)

implicit class AsFunction[T, U](private val f: Expr[T => U]) extends AnyVal {
def apply(x: Expr[T]): Expr[U] = new FunctionAppliedTo[T, U](f, x)
def apply(x: Expr[T]): Expr[U] = new Exprs.FunctionAppliedTo[T, U](f, x)
}

final class FunctionAppliedTo[T, U] private[Expr](val f: Expr[T => U], val x: Expr[T]) extends Expr[U]
}

/** All implementations of Expr[T].
* These should never be used directly.
*/
object Exprs {
/** An Expr backed by a pickled TASTY tree */
final class TastyExpr[T](val tasty: Pickled, val args: Seq[Any]) extends Expr[T] {
override def toString(): String = s"Expr(<pickled>)"
}

/** An Expr backed by a value.
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String or Null.
*/
final class ValueExpr[T](val value: T) extends Expr[T] {
override def toString: String = s"Expr($value)"
}

/** An Expr backed by a tree. Only the current compiler trees are allowed. */
final class TreeExpr[Tree](val tree: Tree) extends quoted.Expr[Any] {
override def toString: String = s"Expr(<raw>)"
}

/** An Expr representing `'{(~f).apply(~x)}` but it is beta-reduced when the closure is known */
final class FunctionAppliedTo[T, U](val f: Expr[T => U], val x: Expr[T]) extends Expr[U] {
override def toString: String = s"Expr($f <applied to> $x)"
}
}
26 changes: 12 additions & 14 deletions library/src/scala/quoted/Liftable.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package scala.quoted

import scala.quoted.Exprs.ValueExpr

/** A typeclass for types that can be turned to `quoted.Expr[T]`
* without going through an explicit `'(...)` operation.
*/
Expand All @@ -18,19 +20,15 @@ object Liftable {
def toExpr(implicit liftable: Liftable[T]): Expr[T] = liftable.toExpr(x)
}

final class ConstantExpr[T] private[Liftable](val value: T) extends Expr[T] {
override def toString: String = s"Expr($value)"
}

implicit def UnitIsLiftable: Liftable[Unit] = (x: Unit) => new ConstantExpr(x)
implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => new ConstantExpr(x)
implicit def ByteLiftable: Liftable[Byte] = (x: Byte) => new ConstantExpr(x)
implicit def CharIsLiftable: Liftable[Char] = (x: Char) => new ConstantExpr(x)
implicit def ShortIsLiftable: Liftable[Short] = (x: Short) => new ConstantExpr(x)
implicit def IntIsLiftable: Liftable[Int] = (x: Int) => new ConstantExpr(x)
implicit def LongIsLiftable: Liftable[Long] = (x: Long) => new ConstantExpr(x)
implicit def FloatIsLiftable: Liftable[Float] = (x: Float) => new ConstantExpr(x)
implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => new ConstantExpr(x)
implicit def UnitIsLiftable: Liftable[Unit] = (x: Unit) => new ValueExpr(x)
implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => new ValueExpr(x)
implicit def ByteLiftable: Liftable[Byte] = (x: Byte) => new ValueExpr(x)
implicit def CharIsLiftable: Liftable[Char] = (x: Char) => new ValueExpr(x)
implicit def ShortIsLiftable: Liftable[Short] = (x: Short) => new ValueExpr(x)
implicit def IntIsLiftable: Liftable[Int] = (x: Int) => new ValueExpr(x)
implicit def LongIsLiftable: Liftable[Long] = (x: Long) => new ValueExpr(x)
implicit def FloatIsLiftable: Liftable[Float] = (x: Float) => new ValueExpr(x)
implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => new ValueExpr(x)

implicit def StringIsLiftable: Liftable[String] = (x: String) => new ConstantExpr(x)
implicit def StringIsLiftable: Liftable[String] = (x: String) => new ValueExpr(x)
}
4 changes: 0 additions & 4 deletions library/src/scala/quoted/Quoted.scala

This file was deleted.

8 changes: 0 additions & 8 deletions library/src/scala/quoted/TastyExpr.scala

This file was deleted.

9 changes: 0 additions & 9 deletions library/src/scala/quoted/TastyQuoted.scala

This file was deleted.

8 changes: 0 additions & 8 deletions library/src/scala/quoted/TastyType.scala

This file was deleted.

Loading