Skip to content

Fix #3823: Unpickle type holes #3833

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 4 commits into from
Jan 17, 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
39 changes: 25 additions & 14 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,12 @@ 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.StdNames._
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.tasty.{TastyPickler, TastyPrinter, TastyString}
import dotty.tools.dotc.interpreter.RawQuoted

import scala.runtime.quoted.Unpickler.Pickled
import scala.reflect.ClassTag

object PickledQuotes {
import tpd._
Expand All @@ -34,6 +35,18 @@ object PickledQuotes {
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.Type.TaggedPrimitive[_] =>
val tpe = expr.ct match {
case ClassTag.Unit => defn.UnitType
case ClassTag.Byte => defn.ByteType
case ClassTag.Char => defn.CharType
case ClassTag.Short => defn.ShortType
case ClassTag.Int => defn.IntType
case ClassTag.Long => defn.LongType
case ClassTag.Float => defn.FloatType
case ClassTag.Double => defn.FloatType
}
TypeTree(tpe)
case expr: RawQuoted => expr.tree
}

Expand All @@ -42,26 +55,24 @@ object PickledQuotes {
val tastyBytes = TastyString.unpickle(expr.tasty)
val unpickled = unpickle(tastyBytes, expr.args)
unpickled match {
case PackageDef(_, (vdef: ValDef) :: Nil) => vdef.rhs
case PackageDef(_, (tdef: TypeDef) :: Nil) => tdef.rhs
case PackageDef(_, (vdef: ValDef) :: Nil) =>
if (vdef.name == "$quote".toTermName) vdef.rhs
else vdef.rhs.asInstanceOf[TypeApply].args.head
}
}

/** Encapsulate the tree in a top level `val` or `type`
* `<tree>` ==> `package _root_ { val ': Any = <tree> }`
* `<tree>` ==> `package _root_ { val $quote: Any = <tree> }`
* or
* `<type tree>` ==> `package _root_ { type ' = <tree tree> }`
* `<type tree>` ==> `package _root_ { val $typeQuote: Any = null.asInstanceOf[<tree>] }`
*/
private def encapsulateQuote(tree: Tree)(implicit ctx: Context): Tree = {
def encapsulatedTerm = {
val sym = ctx.newSymbol(ctx.owner, "'".toTermName, Synthetic, defn.AnyType, coord = tree.pos)
ValDef(sym, tree).withPos(tree.pos)
}

def encapsulatedType =
untpd.TypeDef("'".toTypeName, tree).withPos(tree.pos).withType(defn.AnyType)

val quoted = if (tree.isTerm) encapsulatedTerm else encapsulatedType
val name = (if (tree.isTerm) "$quote" else "$typeQuote").toTermName
val sym = ctx.newSymbol(ctx.owner, name, Synthetic, defn.AnyType, coord = tree.pos)
val encoded =
if (tree.isTerm) tree
else Literal(Constant(null)).select(nme.asInstanceOf_).appliedToTypeTrees(tree :: Nil)
val quoted = ValDef(sym, encoded).withPos(tree.pos)
PackageDef(ref(defn.RootPackage).asInstanceOf[Ident], quoted :: Nil).withPos(tree.pos)
}

Expand Down
22 changes: 14 additions & 8 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import typer.Checking
import config.Config
import dotty.tools.dotc.core.quoted.PickledQuotes
import dotty.tools.dotc.interpreter.RawQuoted
import scala.quoted.Expr
import scala.quoted

/** Unpickler for typed trees
* @param reader the reader from which to unpickle
Expand Down Expand Up @@ -287,6 +287,8 @@ class TreeUnpickler(reader: TastyReader,
ConstantType(Constant(readType()))
case ENUMconst =>
ConstantType(Constant(readTermRef().termSymbol))
case HOLE =>
readHole(end).tpe
}
assert(currentAddr == end, s"$start $currentAddr $end ${astTagToString(tag)}")
result
Expand Down Expand Up @@ -1030,13 +1032,7 @@ class TreeUnpickler(reader: TastyReader,
case TYPEBOUNDStpt =>
TypeBoundsTree(readTpt(), readTpt())
case HOLE =>
val idx = readNat()
val args = until(end)(readTerm())
val splice = splices(idx)
val expr =
if (args.isEmpty) splice.asInstanceOf[Expr[_]]
else splice.asInstanceOf[Seq[Any] => Expr[_]](args.map(RawQuoted.apply))
PickledQuotes.quotedToTree(expr)
readHole(end)
case _ =>
readPathTerm()
}
Expand Down Expand Up @@ -1083,6 +1079,16 @@ class TreeUnpickler(reader: TastyReader,
new LazyReader(localReader, op)
}

def readHole(end: Addr)(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)
}

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

/** Pickled position for `addr`. */
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ class ReifyQuotes extends MacroTransform {
else {
val trefs = importedTypes.toList
val typeDefs = for (tref <- trefs) yield {
val tag = New(defn.QuotedTypeType.appliedTo(tref), Nil)
val tag = New(defn.QuotedTypeType.appliedTo(tref), Nil) // FIXME: should be an implicitly inferred defn.QuotedTypeType.appliedTo(tref)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@odersky could you have a look at this one. I did not manage to use the MacroTransformWithImplicits and inferImplicitArg to get the implicit argument. I looked at b466e0a but it might be too outdated.

val rhs = transform(tag.select(tpnme.UNARY_~))
val alias = ctx.typeAssigner.assignType(untpd.TypeBoundsTree(rhs, rhs), rhs, rhs)
val original = tref.symbol.asType
Expand Down
18 changes: 15 additions & 3 deletions library/src/scala/quoted/Type.scala
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
package scala.quoted

class Type[T] extends Quoted {
import scala.reflect.ClassTag

abstract class Type[T] extends Quoted {
type unary_~ = T
}

/** Some basic type tags, currently incomplete */
object Type {
implicit def IntTag: Type[Int] = new Type[Int]
implicit def BooleanTag: Type[Boolean] = new Type[Boolean]

class TaggedPrimitive[T] private[Type] (implicit val ct: ClassTag[T]) extends Type[T]

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]
}
4 changes: 4 additions & 0 deletions tests/run-with-compiler/i3823-b.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
val z: Int = 2
()
}
10 changes: 10 additions & 0 deletions tests/run-with-compiler/i3823-b.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import dotty.tools.dotc.quoted.Runners._
import scala.quoted._
object Test {
def main(args: Array[String]): Unit = {
def f[T](x: Expr[T])(t: Type[T]) = '{
val z: t.unary_~ = ~x
}
println(f('(2))(Type.IntTag).show)
}
}
4 changes: 4 additions & 0 deletions tests/run-with-compiler/i3823-c.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
val z: Int = 2
()
}
12 changes: 12 additions & 0 deletions tests/run-with-compiler/i3823-c.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import dotty.tools.dotc.quoted.Runners._
import scala.quoted._
object Test {
def main(args: Array[String]): Unit = {
def f[T](x: Expr[T])(implicit t: Type[T]) = '{
val z = ~x
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This quote will be pickled as

type T = [[1| ]]
{
  val z: T = [[0| ]]
  ()
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After ReifyQuotes the tree is

package <empty> {
  import dotty.tools.dotc.quoted.Runners._
  import scala.quoted._
  final lazy module val Test: Test$ = new Test$()
  @scala.annotation.internal.SourceFile("Foo.scala") final module class Test$()
     extends
   Object() { this: Test.type => 
    def main(args: Array[String]): Unit = 
      {
        def f[T >: Nothing <: Any](x: quoted.Expr[T])(implicit t: quoted.Type[T]
          )
        : scala.quoted.Expr[Unit] = 
          scala.runtime.quoted.Unpickler.unpickleExpr[Unit](
            scala.collection.immutable.Nil.::[String](
              
                "\\¡«\037\0203\0200\00\00\00\00\00\00\00\00\00\00\00\00\016\02M0£\01\0204ASTs\01\0206_root_\01\0201\'\01\0203Any\01\0205scala\01\0201z\01\0201T\020\0200¢6\0201\0201\0236\0202u\02036\0204\0214\0226\0214\0211\02\0201\0206\02055\0230ÿ\0201\0200\0203\0211\020\0205ÿ\0201\02012\0235\025\025"
              
            )
          , [x,new scala.quoted.Type[T]() : Any])
        println(
          f[Int](
            scala.runtime.quoted.Unpickler.unpickleExpr[Int](
              scala.collection.immutable.Nil.::[String](
                
                  "\\¡«\037\0203\0200\00\00\00\00\00\00\00\00\00\00\00\00\n%fð\0235\01\0204ASTs\01\0206_root_\01\0201\'\01\0203Any\01\0205scala\0200\0216\0200\02146\0201\0201\0210\0202u\02036\0204<\0202\025"
                
              )
            , [ : Any])
          )(quoted.Type.IntTag).show(
            dotty.tools.dotc.quoted.Runners.runner[Unit]
          )
        )
      }
  }
}

Note that the arguments for the holes are x and new scala.quoted.Type[T]() hence when unpickling we get an instance of scala.quoted.Type with an erased T. Instead of new scala.quoted.Type[T]() we should have t.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we have

def f[T](x: Expr[T])(implicit t: Type[T]) = '{
   val z: ~t= ~x
}

then the hole receives the t and works as expected.

// FIXME uncomment next line
// println(f('(2))(Type.IntTag).show)
println("{\n val z: Int = 2\n ()\n}") // TODO remove line
}
}
4 changes: 4 additions & 0 deletions tests/run-with-compiler/i3823.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
val z: Int = 2
()
}
10 changes: 10 additions & 0 deletions tests/run-with-compiler/i3823.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import dotty.tools.dotc.quoted.Runners._
import scala.quoted._
object Test {
def main(args: Array[String]): Unit = {
def f[T](x: Expr[T])(t: Type[T]) = '{
val z: t.unary_~ = ~x
}
println(f('(2))('[Int]).show)
}
}