Skip to content

Commit 4149833

Browse files
Merge pull request #3883 from dotty-staging/clean-quote-implementations
Seal quoted.Expr and quoted.Type
2 parents 2a019f3 + 4f769ea commit 4149833

File tree

18 files changed

+144
-138
lines changed

18 files changed

+144
-138
lines changed

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

+29-17
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import dotty.tools.dotc.core.Constants.Constant
77
import dotty.tools.dotc.core.Contexts._
88
import dotty.tools.dotc.core.Decorators._
99
import dotty.tools.dotc.core.Flags._
10-
import dotty.tools.dotc.core.NameKinds
1110
import dotty.tools.dotc.core.StdNames._
11+
import dotty.tools.dotc.core.NameKinds
1212
import dotty.tools.dotc.core.Symbols._
1313
import dotty.tools.dotc.core.tasty.{TastyPickler, TastyPrinter, TastyString}
14-
import dotty.tools.dotc.interpreter.RawQuoted
14+
15+
import scala.quoted.Types._
16+
import scala.quoted.Exprs._
1517

1618
import scala.reflect.ClassTag
1719

@@ -29,27 +31,37 @@ object PickledQuotes {
2931
}
3032

3133
/** Transform the expression into its fully spliced Tree */
32-
def quotedToTree(expr: quoted.Quoted)(implicit ctx: Context): Tree = expr match {
33-
case expr: quoted.TastyQuoted =>
34-
unpickleQuote(expr)
35-
case expr: quoted.Liftable.ConstantExpr[_] =>
36-
Literal(Constant(expr.value))
37-
case expr: quoted.Expr.FunctionAppliedTo[_, _] =>
38-
functionAppliedTo(quotedToTree(expr.f), quotedToTree(expr.x))
39-
case expr: quoted.Type.TaggedPrimitive[_] =>
40-
classTagToTypeTree(expr.ct)
41-
case expr: RawQuoted =>
42-
expr.tree
34+
def quotedExprToTree(expr: quoted.Expr[_])(implicit ctx: Context): Tree = expr match {
35+
case expr: TastyExpr[_] => unpickleExpr(expr)
36+
case expr: ValueExpr[_] => Literal(Constant(expr.value))
37+
case expr: TreeExpr[Tree] @unchecked => expr.tree
38+
case expr: FunctionAppliedTo[_, _] =>
39+
functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x))
40+
}
41+
42+
/** Transform the expression into its fully spliced TypeTree */
43+
def quotedTypeToTree(expr: quoted.Type[_])(implicit ctx: Context): Tree = expr match {
44+
case expr: TastyType[_] => unpickleType(expr)
45+
case expr: TaggedType[_] => classTagToTypeTree(expr.ct)
46+
case expr: TreeType[Tree] @unchecked => expr.tree
4347
}
4448

45-
/** Unpickle the tree contained in the TastyQuoted */
46-
private def unpickleQuote(expr: quoted.TastyQuoted)(implicit ctx: Context): Tree = {
49+
/** Unpickle the tree contained in the TastyExpr */
50+
private def unpickleExpr(expr: TastyExpr[_])(implicit ctx: Context): Tree = {
4751
val tastyBytes = TastyString.unpickle(expr.tasty)
4852
val unpickled = unpickle(tastyBytes, expr.args)
53+
unpickled match {
54+
case PackageDef(_, (vdef: ValDef) :: Nil) => vdef.rhs
55+
}
56+
}
57+
58+
/** Unpickle the tree contained in the TastyType */
59+
private def unpickleType(ttpe: TastyType[_])(implicit ctx: Context): Tree = {
60+
val tastyBytes = TastyString.unpickle(ttpe.tasty)
61+
val unpickled = unpickle(tastyBytes, ttpe.args)
4962
unpickled match {
5063
case PackageDef(_, (vdef: ValDef) :: Nil) =>
51-
if (vdef.name == "$quote".toTermName) vdef.rhs
52-
else vdef.rhs.asInstanceOf[TypeApply].args.head
64+
vdef.rhs.asInstanceOf[TypeApply].args.head
5365
}
5466
}
5567

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

+18-8
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import config.Printers.pickling
2020
import typer.Checking
2121
import config.Config
2222
import dotty.tools.dotc.core.quoted.PickledQuotes
23-
import dotty.tools.dotc.interpreter.RawQuoted
2423
import scala.quoted
24+
import scala.quoted.Types.TreeType
25+
import scala.quoted.Exprs.TreeExpr
2526

2627
/** Unpickler for typed trees
2728
* @param reader the reader from which to unpickle
@@ -296,7 +297,7 @@ class TreeUnpickler(reader: TastyReader,
296297
case ENUMconst =>
297298
ConstantType(Constant(readTermRef().termSymbol))
298299
case HOLE =>
299-
readHole(end).tpe
300+
readHole(end, isType = true).tpe
300301
}
301302
assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}")
302303
result
@@ -1076,7 +1077,7 @@ class TreeUnpickler(reader: TastyReader,
10761077
val hi = if (currentAddr == end) lo else readTpt()
10771078
TypeBoundsTree(lo, hi)
10781079
case HOLE =>
1079-
readHole(end)
1080+
readHole(end, isType = false)
10801081
case _ =>
10811082
readPathTerm()
10821083
}
@@ -1127,14 +1128,23 @@ class TreeUnpickler(reader: TastyReader,
11271128
new LazyReader(localReader, op)
11281129
}
11291130

1130-
def readHole(end: Addr)(implicit ctx: Context): Tree = {
1131+
def readHole(end: Addr, isType: Boolean)(implicit ctx: Context): Tree = {
11311132
val idx = readNat()
11321133
val args = until(end)(readTerm())
11331134
val splice = splices(idx)
1134-
val quotedType =
1135-
if (args.isEmpty) splice.asInstanceOf[quoted.Quoted]
1136-
else splice.asInstanceOf[Seq[Any] => quoted.Quoted](args.map(RawQuoted.apply))
1137-
PickledQuotes.quotedToTree(quotedType)
1135+
1136+
if (isType) {
1137+
val quotedType =
1138+
if (args.isEmpty) splice.asInstanceOf[quoted.Type[_]]
1139+
else splice.asInstanceOf[Seq[Any] => quoted.Type[_]](args.map(tree => new TreeType(tree)))
1140+
PickledQuotes.quotedTypeToTree(quotedType)
1141+
} else {
1142+
val quotedExpr =
1143+
if (args.isEmpty) splice.asInstanceOf[quoted.Expr[_]]
1144+
else splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](args.map(tree => new TreeExpr(tree)))
1145+
PickledQuotes.quotedExprToTree(quotedExpr)
1146+
}
1147+
11381148
}
11391149

11401150
// ------ Setting positions ------------------------------------------------

compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala

+3-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ class Interpreter(implicit ctx: Context) {
7272
implicit val pos: Position = tree.pos
7373

7474
tree match {
75-
case Quoted(quotedTree) => RawQuoted(quotedTree)
75+
case Quoted(quotedTree) =>
76+
if (tree.isTerm) new scala.quoted.Exprs.TreeExpr(quotedTree)
77+
else new scala.quoted.Types.TreeType(quotedTree)
7678

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

compiler/src/dotty/tools/dotc/interpreter/RawQuoted.scala

-22
This file was deleted.

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class ExprCompiler(directory: AbstractFile) extends Compiler {
6161
case exprUnit: ExprCompilationUnit =>
6262
val tree =
6363
if (putInClass) inClass(exprUnit.expr)
64-
else PickledQuotes.quotedToTree(exprUnit.expr)
64+
else PickledQuotes.quotedExprToTree(exprUnit.expr)
6565
val source = new SourceFile("", Seq())
6666
CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true)
6767
}
@@ -80,7 +80,7 @@ class ExprCompiler(directory: AbstractFile) extends Compiler {
8080
cls.enter(ctx.newDefaultConstructor(cls), EmptyScope)
8181
val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered
8282

83-
val quoted = PickledQuotes.quotedToTree(expr)(ctx.withOwner(meth))
83+
val quoted = PickledQuotes.quotedExprToTree(expr)(ctx.withOwner(meth))
8484

8585
val run = DefDef(meth, quoted)
8686
val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil)

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

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import dotty.tools.dotc.core.Constants._
66
import dotty.tools.dotc.printing.RefinedPrinter
77

88
import scala.quoted.Expr
9-
import scala.quoted.Liftable.ConstantExpr
109
import scala.runtime.BoxedUnit
10+
import scala.quoted.Exprs.ValueExpr
1111
import scala.runtime.quoted._
1212

1313
/** Default runners for quoted expressions */
@@ -31,20 +31,20 @@ object Runners {
3131
case _ => None
3232
}
3333
expr match {
34-
case expr: ConstantExpr[T] => Some(expr.value)
34+
case expr: ValueExpr[T] => Some(expr.value)
3535
case _ => new QuoteDriver().withTree(expr, (tree, _) => toConstantOpt(tree), Settings.run())
3636
}
3737
}
3838

3939
}
4040

4141
def run[T](expr: Expr[T], settings: Settings[Run]): T = expr match {
42-
case expr: ConstantExpr[T] => expr.value
42+
case expr: ValueExpr[T] => expr.value
4343
case _ => new QuoteDriver().run(expr, settings)
4444
}
4545

4646
def show[T](expr: Expr[T], settings: Settings[Show]): String = expr match {
47-
case expr: ConstantExpr[T] =>
47+
case expr: ValueExpr[T] =>
4848
implicit val ctx = new QuoteDriver().initCtx
4949
if (settings.compilerArgs.contains("-color:never"))
5050
ctx.settings.color.update("never")

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ object Splicer {
2424
/** Splice the Tree for a Quoted expression which is constructed via a reflective call to the given method */
2525
private def reflectiveSplice(tree: Tree)(implicit ctx: Context): Tree = {
2626
val interpreter = new Interpreter
27-
interpreter.interpretTree[scala.quoted.Expr[_]](tree).map(PickledQuotes.quotedToTree(_)).getOrElse(tree)
27+
interpreter.interpretTree[scala.quoted.Expr[_]](tree).map(PickledQuotes.quotedExprToTree).getOrElse(tree)
2828
}
2929

3030
}

compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala

+12
Original file line numberDiff line numberDiff line change
@@ -1294,4 +1294,16 @@ class ErrorMessagesTests extends ErrorMessagesTest {
12941294
val JavaSymbolIsNotAValue(symbol) = messages.head
12951295
assertEquals(symbol.show, "package p")
12961296
}
1297+
1298+
@Test def i3187 =
1299+
checkMessagesAfter("genBCode") {
1300+
"""
1301+
|package scala
1302+
|object collection
1303+
""".stripMargin
1304+
}.expect { (itcx, messages) =>
1305+
implicit val ctx: Context = itcx
1306+
1307+
assert(ctx.reporter.hasErrors)
1308+
}
12971309
}

docs/docs/reference/principled-meta-programming.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ to be executed at a later stage. To run that code, there is another method
445445
in class `Expr` called `run`. Note that `~` and `run` both map from `Expr[T]`
446446
to `T` but only `~` is subject to the PCP, whereas `run` is just a normal method.
447447

448-
abstract class Expr[T] {
448+
sealed abstract class Expr[T] {
449449
def unary_~: T
450450
def run(implicit runner: Runner[T]): T // run staged code
451451
def show(implicit runner: Runner[T]): String // show staged code

library/src/scala/quoted/Expr.scala

+30-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package scala.quoted
22

33
import scala.runtime.quoted.Runner
4+
import scala.runtime.quoted.Unpickler.Pickled
45

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

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

19-
final class FunctionAppliedTo[T, U] private[Expr](val f: Expr[T => U], val x: Expr[T]) extends Expr[U]
20+
}
21+
22+
/** All implementations of Expr[T].
23+
* These should never be used directly.
24+
*/
25+
object Exprs {
26+
/** An Expr backed by a pickled TASTY tree */
27+
final class TastyExpr[T](val tasty: Pickled, val args: Seq[Any]) extends Expr[T] {
28+
override def toString(): String = s"Expr(<pickled>)"
29+
}
30+
31+
/** An Expr backed by a value.
32+
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String or Null.
33+
*/
34+
final class ValueExpr[T](val value: T) extends Expr[T] {
35+
override def toString: String = s"Expr($value)"
36+
}
37+
38+
/** An Expr backed by a tree. Only the current compiler trees are allowed. */
39+
final class TreeExpr[Tree](val tree: Tree) extends quoted.Expr[Any] {
40+
override def toString: String = s"Expr(<raw>)"
41+
}
42+
43+
/** An Expr representing `'{(~f).apply(~x)}` but it is beta-reduced when the closure is known */
44+
final class FunctionAppliedTo[T, U](val f: Expr[T => U], val x: Expr[T]) extends Expr[U] {
45+
override def toString: String = s"Expr($f <applied to> $x)"
46+
}
2047
}
+12-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package scala.quoted
22

3+
import scala.quoted.Exprs.ValueExpr
4+
35
/** A typeclass for types that can be turned to `quoted.Expr[T]`
46
* without going through an explicit `'(...)` operation.
57
*/
@@ -18,19 +20,15 @@ object Liftable {
1820
def toExpr(implicit liftable: Liftable[T]): Expr[T] = liftable.toExpr(x)
1921
}
2022

21-
final class ConstantExpr[T] private[Liftable](val value: T) extends Expr[T] {
22-
override def toString: String = s"Expr($value)"
23-
}
24-
25-
implicit def UnitIsLiftable: Liftable[Unit] = (x: Unit) => new ConstantExpr(x)
26-
implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => new ConstantExpr(x)
27-
implicit def ByteLiftable: Liftable[Byte] = (x: Byte) => new ConstantExpr(x)
28-
implicit def CharIsLiftable: Liftable[Char] = (x: Char) => new ConstantExpr(x)
29-
implicit def ShortIsLiftable: Liftable[Short] = (x: Short) => new ConstantExpr(x)
30-
implicit def IntIsLiftable: Liftable[Int] = (x: Int) => new ConstantExpr(x)
31-
implicit def LongIsLiftable: Liftable[Long] = (x: Long) => new ConstantExpr(x)
32-
implicit def FloatIsLiftable: Liftable[Float] = (x: Float) => new ConstantExpr(x)
33-
implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => new ConstantExpr(x)
23+
implicit def UnitIsLiftable: Liftable[Unit] = (x: Unit) => new ValueExpr(x)
24+
implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => new ValueExpr(x)
25+
implicit def ByteLiftable: Liftable[Byte] = (x: Byte) => new ValueExpr(x)
26+
implicit def CharIsLiftable: Liftable[Char] = (x: Char) => new ValueExpr(x)
27+
implicit def ShortIsLiftable: Liftable[Short] = (x: Short) => new ValueExpr(x)
28+
implicit def IntIsLiftable: Liftable[Int] = (x: Int) => new ValueExpr(x)
29+
implicit def LongIsLiftable: Liftable[Long] = (x: Long) => new ValueExpr(x)
30+
implicit def FloatIsLiftable: Liftable[Float] = (x: Float) => new ValueExpr(x)
31+
implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => new ValueExpr(x)
3432

35-
implicit def StringIsLiftable: Liftable[String] = (x: String) => new ConstantExpr(x)
33+
implicit def StringIsLiftable: Liftable[String] = (x: String) => new ValueExpr(x)
3634
}

library/src/scala/quoted/Quoted.scala

-4
This file was deleted.

library/src/scala/quoted/TastyExpr.scala

-8
This file was deleted.

library/src/scala/quoted/TastyQuoted.scala

-9
This file was deleted.

library/src/scala/quoted/TastyType.scala

-8
This file was deleted.

0 commit comments

Comments
 (0)