diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 2e0aa50e6c2a..b4a065a473fd 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -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 @@ -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 } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 7debff863d65..3e38a9119fbe 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -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 @@ -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 @@ -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() } @@ -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 ------------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala b/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala index 5fa447af602d..5be936d71ee1 100644 --- a/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala +++ b/compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala @@ -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] diff --git a/compiler/src/dotty/tools/dotc/interpreter/RawQuoted.scala b/compiler/src/dotty/tools/dotc/interpreter/RawQuoted.scala deleted file mode 100644 index db669d0c5f5e..000000000000 --- a/compiler/src/dotty/tools/dotc/interpreter/RawQuoted.scala +++ /dev/null @@ -1,22 +0,0 @@ -package dotty.tools.dotc.interpreter - -import dotty.tools.dotc.ast.tpd - -/** Quoted `quoted.Quoted` for which its internal representation is its tree. - * - Used for trees that cannot be serialized, such as references to local symbols that will be spliced in. - * - Used for trees that do not need to be serialized to avoid the overhead of serialization/deserialization. - */ -trait RawQuoted extends quoted.Quoted { - def tree: tpd.Tree - override def toString: String = s"${this.getClass.getName}(${tree.toString})" -} - -object RawQuoted { - - def apply(tree: tpd.Tree): RawQuoted = - if (tree.isTerm) new RawExpr(tree) - else new RawType(tree) - - private final class RawExpr(val tree: tpd.Tree) extends quoted.Expr[Any] with RawQuoted - private final class RawType(val tree: tpd.Tree) extends quoted.Type[Any] with RawQuoted -} diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala index 5bbc9bfd157d..fb782c031543 100644 --- a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala +++ b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala @@ -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) } @@ -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) diff --git a/compiler/src/dotty/tools/dotc/quoted/Runners.scala b/compiler/src/dotty/tools/dotc/quoted/Runners.scala index 1bd66050f515..661d636e68e8 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Runners.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Runners.scala @@ -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 */ @@ -31,7 +31,7 @@ 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()) } } @@ -39,12 +39,12 @@ object Runners { } 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") diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 6e05b9d85873..b24357a934bd 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -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) } } diff --git a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala index 9d950953f550..de80777ea241 100644 --- a/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala +++ b/compiler/test/dotty/tools/dotc/reporting/ErrorMessagesTests.scala @@ -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) + } } diff --git a/docs/docs/reference/principled-meta-programming.md b/docs/docs/reference/principled-meta-programming.md index 1adc3f8ce89d..ea11ac33c3fc 100644 --- a/docs/docs/reference/principled-meta-programming.md +++ b/docs/docs/reference/principled-meta-programming.md @@ -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 diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index c8cc488e0f29..0f724c9257ca 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -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) @@ -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()" + } + + /** 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()" + } + + /** 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 $x)" + } } diff --git a/library/src/scala/quoted/Liftable.scala b/library/src/scala/quoted/Liftable.scala index a802dda0bd22..f1f01a60bd93 100644 --- a/library/src/scala/quoted/Liftable.scala +++ b/library/src/scala/quoted/Liftable.scala @@ -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. */ @@ -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) } diff --git a/library/src/scala/quoted/Quoted.scala b/library/src/scala/quoted/Quoted.scala deleted file mode 100644 index 537dd6221d51..000000000000 --- a/library/src/scala/quoted/Quoted.scala +++ /dev/null @@ -1,4 +0,0 @@ -package scala.quoted - -/** Common superclass of Expr and Type */ -abstract class Quoted diff --git a/library/src/scala/quoted/TastyExpr.scala b/library/src/scala/quoted/TastyExpr.scala deleted file mode 100644 index 219ea5d924d7..000000000000 --- a/library/src/scala/quoted/TastyExpr.scala +++ /dev/null @@ -1,8 +0,0 @@ -package scala.quoted - -import scala.runtime.quoted.Unpickler.Pickled - -/** An Expr backed by a pickled TASTY tree */ -final class TastyExpr[T](val tasty: Pickled, val args: Seq[Any]) extends Expr[T] with TastyQuoted { - override def toString(): String = s"Expr()" -} diff --git a/library/src/scala/quoted/TastyQuoted.scala b/library/src/scala/quoted/TastyQuoted.scala deleted file mode 100644 index 729fedd9a998..000000000000 --- a/library/src/scala/quoted/TastyQuoted.scala +++ /dev/null @@ -1,9 +0,0 @@ -package scala.quoted - -import scala.runtime.quoted.Unpickler.Pickled - -/** A quote backed by a pickled TASTY tree */ -trait TastyQuoted extends Quoted { - def tasty: Pickled - def args: Seq[Any] -} diff --git a/library/src/scala/quoted/TastyType.scala b/library/src/scala/quoted/TastyType.scala deleted file mode 100644 index 2301466e9a9d..000000000000 --- a/library/src/scala/quoted/TastyType.scala +++ /dev/null @@ -1,8 +0,0 @@ -package scala.quoted - -import scala.runtime.quoted.Unpickler.Pickled - -/** A Type backed by a pickled TASTY tree */ -final class TastyType[T](val tasty: Pickled, val args: Seq[Any]) extends Type[T] with TastyQuoted { - override def toString(): String = s"Type()" -} diff --git a/library/src/scala/quoted/Type.scala b/library/src/scala/quoted/Type.scala index 068df6478d96..66ace17f1bee 100644 --- a/library/src/scala/quoted/Type.scala +++ b/library/src/scala/quoted/Type.scala @@ -1,25 +1,42 @@ package scala.quoted +import scala.quoted.Types.TaggedType import scala.reflect.ClassTag +import scala.runtime.quoted.Unpickler.Pickled -abstract class Type[T] extends Quoted { +sealed abstract class Type[T] { type unary_~ = T } /** Some basic type tags, currently incomplete */ object Type { + implicit def UnitTag: Type[Unit] = new TaggedType[Unit] + implicit def BooleanTag: Type[Boolean] = new TaggedType[Boolean] + implicit def ByteTag: Type[Byte] = new TaggedType[Byte] + implicit def CharTag: Type[Char] = new TaggedType[Char] + implicit def ShortTag: Type[Short] = new TaggedType[Short] + implicit def IntTag: Type[Int] = new TaggedType[Int] + implicit def LongTag: Type[Long] = new TaggedType[Long] + implicit def FloatTag: Type[Float] = new TaggedType[Float] + implicit def DoubleTag: Type[Double] = new TaggedType[Double] +} + +/** All implementations of Type[T]. + * These should never be used directly. + */ +object Types { + /** A Type backed by a pickled TASTY tree */ + final class TastyType[T](val tasty: Pickled, val args: Seq[Any]) extends Type[T] { + override def toString(): String = s"Type()" + } - final class TaggedPrimitive[T] private[Type] (implicit val ct: ClassTag[T]) extends Type[T] { + /** An Type backed by a value */ + final class TaggedType[T](implicit val ct: ClassTag[T]) extends Type[T] { override def toString: String = s"Type($ct)" } - implicit def UnitTag: Type[Unit] = new TaggedPrimitive[Unit] - implicit def BooleanTag: Type[Boolean] = new TaggedPrimitive[Boolean] - implicit def ByteTag: Type[Byte] = new TaggedPrimitive[Byte] - implicit def CharTag: Type[Char] = new TaggedPrimitive[Char] - implicit def ShortTag: Type[Short] = new TaggedPrimitive[Short] - implicit def IntTag: Type[Int] = new TaggedPrimitive[Int] - implicit def LongTag: Type[Long] = new TaggedPrimitive[Long] - implicit def FloatTag: Type[Float] = new TaggedPrimitive[Float] - implicit def DoubleTag: Type[Double] = new TaggedPrimitive[Double] -} + /** An Type backed by a tree */ + final class TreeType[Tree](val tree: Tree) extends quoted.Type[Any] { + override def toString: String = s"Type()" + } +} \ No newline at end of file diff --git a/library/src/scala/runtime/quoted/Unpickler.scala b/library/src/scala/runtime/quoted/Unpickler.scala index 5bd07b140cd2..4cf1e854f8f0 100644 --- a/library/src/scala/runtime/quoted/Unpickler.scala +++ b/library/src/scala/runtime/quoted/Unpickler.scala @@ -1,6 +1,8 @@ package scala.runtime.quoted -import scala.quoted._ +import scala.quoted.Types.TastyType +import scala.quoted.Exprs.TastyExpr +import scala.quoted.{Expr, Type} /** Provides methods to unpickle `Expr` and `Type` trees. */ object Unpickler { diff --git a/tests/neg/i3187.scala b/tests/neg/i3187.scala deleted file mode 100644 index 25e098326bd1..000000000000 --- a/tests/neg/i3187.scala +++ /dev/null @@ -1,23 +0,0 @@ -package scala - -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error - -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error -// nopos-error - -object collection