From 8cccaff7ae2d9de6fae1c1824acf730b5b745cc5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 21 Jan 2018 11:25:57 +0100 Subject: [PATCH 1/4] Fix #3876: Implement Expr.AsFunction --- .../dotc/core/quoted/PickledQuotes.scala | 29 +++++++++++++- library/src/scala/quoted/Expr.scala | 4 +- tests/run-with-compiler/i3876.check | 39 +++++++++++++++++++ tests/run-with-compiler/i3876.scala | 33 ++++++++++++++++ 4 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 tests/run-with-compiler/i3876.check create mode 100644 tests/run-with-compiler/i3876.scala diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index bc0569f7c75a..c2fea2825076 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -1,12 +1,13 @@ package dotty.tools.dotc.core.quoted import dotty.tools.dotc.ast.Trees._ -import dotty.tools.dotc.ast.{tpd, untpd} +import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.config.Printers._ 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.Symbols._ import dotty.tools.dotc.core.tasty.{TastyPickler, TastyPrinter, TastyString} @@ -35,6 +36,8 @@ 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.Expr.FunctionAppliedTo[_, _] => + functionAppliedTo(quotedToTree(expr.f), quotedToTree(expr.x)) case expr: quoted.Type.TaggedPrimitive[_] => val tpe = expr.ct match { case ClassTag.Unit => defn.UnitType @@ -111,4 +114,28 @@ object PickledQuotes { } tree } + + private def functionAppliedTo(f: Tree, x: Tree)(implicit ctx: Context): Tree = { + val x1 = SyntheticValDef(NameKinds.UniqueName.fresh("x".toTermName), x) + def x1Ref() = ref(x1.symbol) + def rec(f: Tree): Tree = f match { + case Block((ddef: DefDef) :: Nil, _: Closure) => + new TreeMap() { + private val paramSym = ddef.vparamss.head.head.symbol + override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { + case tree: Ident if tree.symbol == paramSym => x1Ref().withPos(tree.pos) + case _ => super.transform(tree) + } + }.transform(ddef.rhs) + case Block(stats, expr) => + val applied = rec(expr) + if (stats.isEmpty) applied + else Block(stats, applied) + case Inlined(call, bindings, expansion) => + Inlined(call, bindings, rec(expansion)) + case _ => + f.select(nme.apply).appliedTo(x1Ref()) + } + Block(x1 :: Nil, rec(f)) + } } diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 07f2e6e91751..c8cc488e0f29 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -13,6 +13,8 @@ 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] = ??? + def apply(x: Expr[T]): Expr[U] = new FunctionAppliedTo[T, U](f, x) } + + final class FunctionAppliedTo[T, U] private[Expr](val f: Expr[T => U], val x: Expr[T]) extends Expr[U] } diff --git a/tests/run-with-compiler/i3876.check b/tests/run-with-compiler/i3876.check new file mode 100644 index 000000000000..f1e0e510f1f1 --- /dev/null +++ b/tests/run-with-compiler/i3876.check @@ -0,0 +1,39 @@ +6 +{ + val x$1: Int = 3 + { + x$1.+(x$1) + } +} +6 +{ + val x$1: Int = 3 + { + def f(x: Int): Int = x.+(x) + f(x$1) + } +} +6 +{ + val x$1: Int = 3 + { + val f: + Function1[Int, Int] + { + def apply(x: Int): Int + } + = + { + (x: Int) => x.+(x) + } + (f: (x: Int) => Int).apply(x$1) + } +} +6 +{ + val x$1: Int = 3 + /* inlined from Test*/ + { + x$1.+(x$1) + } +} diff --git a/tests/run-with-compiler/i3876.scala b/tests/run-with-compiler/i3876.scala new file mode 100644 index 000000000000..84c40c615598 --- /dev/null +++ b/tests/run-with-compiler/i3876.scala @@ -0,0 +1,33 @@ +import dotty.tools.dotc.quoted.Runners._ +import scala.quoted._ +object Test { + def main(args: Array[String]): Unit = { + val x: Expr[Int] = '(3) + + val f: Expr[Int => Int] = '{ (x: Int) => x + x } + println(f(x).run) + println(f(x).show) + + val f2: Expr[Int => Int] = '{ + def f(x: Int): Int = x + x + f + } + println(f2(x).run) + println(f2(x).show) + + val f3: Expr[Int => Int] = '{ + val f: (x: Int) => Int = x => x + x + f + } + println(f3(x).run) + println(f3(x).show) // TODO improve printer + + val f4: Expr[Int => Int] = '{ + inlineLambda + } + println(f4(x).run) + println(f4(x).show) + } + + inline def inlineLambda: Int => Int = x => x + x +} \ No newline at end of file From 0bc78271968b5a34d6961b793907d04261c0412b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 21 Jan 2018 11:37:57 +0100 Subject: [PATCH 2/4] Factor out code --- .../dotc/core/quoted/PickledQuotes.scala | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index c2fea2825076..55bd19ea8294 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -34,23 +34,16 @@ 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.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[_] => - 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 + classTagToTypeTree(expr.ct) + case expr: RawQuoted => + expr.tree } /** Unpickle the tree contained in the TastyQuoted */ @@ -115,6 +108,20 @@ object PickledQuotes { tree } + private def classTagToTypeTree(ct: ClassTag[_])(implicit ctx: Context): TypeTree = { + val tpe = 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) + } + private def functionAppliedTo(f: Tree, x: Tree)(implicit ctx: Context): Tree = { val x1 = SyntheticValDef(NameKinds.UniqueName.fresh("x".toTermName), x) def x1Ref() = ref(x1.symbol) From 52c445f280c2904ce177e7ce5fe2b17152537b32 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 21 Jan 2018 20:10:33 +0100 Subject: [PATCH 3/4] Create def with closure extractor in TreeInfo --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 11 ++++++++++- .../dotty/tools/dotc/core/quoted/PickledQuotes.scala | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 09e36950b8d4..a89ab72cd312 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -534,9 +534,18 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } } + /** An extractor for closures with their def contained in a block. */ + object closureWithDef { + def unapply(tree: Tree): Option[(DefDef, Closure)] = tree match { + case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) => + Some(meth, closure) + case _ => None + } + } + /** If tree is a closure, its body, otherwise tree itself */ def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match { - case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs + case closureWithDef(meth, _) => meth.rhs case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 55bd19ea8294..25bd032754fd 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -126,7 +126,7 @@ object PickledQuotes { val x1 = SyntheticValDef(NameKinds.UniqueName.fresh("x".toTermName), x) def x1Ref() = ref(x1.symbol) def rec(f: Tree): Tree = f match { - case Block((ddef: DefDef) :: Nil, _: Closure) => + case closureWithDef(ddef, _) => new TreeMap() { private val paramSym = ddef.vparamss.head.head.symbol override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { From 2dc8bfafce27b89efd2b41f959a66f8862ef686f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Mon, 22 Jan 2018 10:16:39 +0100 Subject: [PATCH 4/4] Change closureWithDef to closureDef --- compiler/src/dotty/tools/dotc/ast/TreeInfo.scala | 10 +++++----- .../dotty/tools/dotc/core/quoted/PickledQuotes.scala | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index a89ab72cd312..f9d76d083cbd 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -534,18 +534,18 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } } - /** An extractor for closures with their def contained in a block. */ - object closureWithDef { - def unapply(tree: Tree): Option[(DefDef, Closure)] = tree match { + /** An extractor for def of a closure contained the block of the closure. */ + object closureDef { + def unapply(tree: Tree): Option[DefDef] = tree match { case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, closure: Closure) => - Some(meth, closure) + Some(meth) case _ => None } } /** If tree is a closure, its body, otherwise tree itself */ def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match { - case closureWithDef(meth, _) => meth.rhs + case closureDef(meth) => meth.rhs case _ => tree } diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 25bd032754fd..afeb44bb153a 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -126,7 +126,7 @@ object PickledQuotes { val x1 = SyntheticValDef(NameKinds.UniqueName.fresh("x".toTermName), x) def x1Ref() = ref(x1.symbol) def rec(f: Tree): Tree = f match { - case closureWithDef(ddef, _) => + case closureDef(ddef) => new TreeMap() { private val paramSym = ddef.vparamss.head.head.symbol override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {