diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index 09e36950b8d4..f9d76d083cbd 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 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) + 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 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 bc0569f7c75a..afeb44bb153a 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} @@ -33,21 +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 */ @@ -111,4 +107,42 @@ 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) + def rec(f: Tree): Tree = f match { + 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 { + 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