diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index ad6d080273dd..dcdcba607b54 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -47,8 +47,6 @@ object PickledQuotes { } forceAndCleanArtefacts.transform(unpickled) case expr: TastyTreeExpr[Tree] @unchecked => healOwner(expr.tree) - case expr: FunctionAppliedTo[_] => - functionAppliedTo(quotedExprToTree(expr.f), expr.args.map(arg => quotedExprToTree(arg(new scala.quoted.QuoteContext(ReflectionImpl(ctx))))).toList) } /** Transform the expression into its fully spliced TypeTree */ @@ -127,33 +125,6 @@ object PickledQuotes { tree } - private def functionAppliedTo(fn: Tree, args: List[Tree])(implicit ctx: Context): Tree = { - val (argVals, argRefs) = args.map(arg => arg.tpe match { - case tpe: SingletonType if isIdempotentExpr(arg) => (Nil, arg) - case _ => - val argVal = SyntheticValDef(NameKinds.UniqueName.fresh("x".toTermName), arg).withSpan(arg.span) - (argVal :: Nil, ref(argVal.symbol)) - }).unzip - def rec(fn: Tree): Tree = fn match { - case Inlined(call, bindings, expansion) => - // this case must go before closureDef to avoid dropping the inline node - cpy.Inlined(fn)(call, bindings, rec(expansion)) - case closureDef(ddef) => - val paramSyms = ddef.vparamss.head.map(param => param.symbol) - val paramToVals = paramSyms.zip(argRefs).toMap - new TreeTypeMap( - oldOwners = ddef.symbol :: Nil, - newOwners = ctx.owner :: Nil, - treeMap = tree => paramToVals.get(tree.symbol).map(_.withSpan(tree.span)).getOrElse(tree) - ).transform(ddef.rhs) - case Block(stats, expr) => - seq(stats, rec(expr)).withSpan(fn.span) - case _ => - fn.select(nme.apply).appliedToArgs(argRefs).withSpan(fn.span) - } - seq(argVals.flatten, rec(fn)) - } - /** Make sure that the owner of this tree is `ctx.owner` */ private def healOwner(tree: Tree)(implicit ctx: Context): Tree = { val getCurrentOwner = new TreeAccumulator[Option[Symbol]] { diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala index 1a46d787dc72..75bc5738c1bf 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala @@ -1,9 +1,8 @@ package dotty.tools.dotc package tastyreflect -import dotty.tools.dotc.ast.Trees.SeqLiteral -import dotty.tools.dotc.ast.{Trees, tpd, untpd} -import dotty.tools.dotc.ast.tpd.TreeOps +import dotty.tools.dotc.ast.Trees._ +import dotty.tools.dotc.ast.{TreeTypeMap, Trees, tpd, untpd} import dotty.tools.dotc.typer.{Implicits, Typer} import dotty.tools.dotc.core._ import dotty.tools.dotc.core.Flags._ @@ -11,6 +10,7 @@ import dotty.tools.dotc.core.StdNames.nme import dotty.tools.dotc.core.quoted.PickledQuotes import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.Types.SingletonType import dotty.tools.dotc.tastyreflect.FromSymbol.{definitionFromSym, packageDefFromSym} import dotty.tools.dotc.parsing.Parsers.Parser import dotty.tools.dotc.typer.Implicits.{AmbiguousImplicits, DivergingImplicit, NoMatchingImplicits, SearchFailure, SearchFailureType} @@ -19,6 +19,7 @@ import dotty.tools.dotc.util.{SourceFile, SourcePosition, Spans} import scala.tasty.reflect.Kernel class KernelImpl(val rootContext: core.Contexts.Context) extends Kernel { + import tpd._ private implicit def ctx: core.Contexts.Context = rootContext @@ -1894,6 +1895,35 @@ class KernelImpl(val rootContext: core.Contexts.Context) extends Kernel { case _ => None } + def betaReduce(fn: Term, args: List[Term]) given (ctx: Context): Term = { + val (argVals0, argRefs0) = args.foldLeft((List.empty[ValDef], List.empty[Tree])) { case ((acc1, acc2), arg) => arg.tpe match { + case tpe: SingletonType if isIdempotentExpr(arg) => (acc1, arg :: acc2) + case _ => + val argVal = SyntheticValDef(NameKinds.UniqueName.fresh("x".toTermName), arg).withSpan(arg.span) + (argVal :: acc1, ref(argVal.symbol) :: acc2) + }} + val argVals = argVals0.reverse + val argRefs = argRefs0.reverse + def rec(fn: Tree): Tree = fn match { + case Inlined(call, bindings, expansion) => + // this case must go before closureDef to avoid dropping the inline node + cpy.Inlined(fn)(call, bindings, rec(expansion)) + case closureDef(ddef) => + val paramSyms = ddef.vparamss.head.map(param => param.symbol) + val paramToVals = paramSyms.zip(argRefs).toMap + new TreeTypeMap( + oldOwners = ddef.symbol :: Nil, + newOwners = ctx.owner :: Nil, + treeMap = tree => paramToVals.get(tree.symbol).map(_.withSpan(tree.span)).getOrElse(tree) + ).transform(ddef.rhs) + case Block(stats, expr) => + seq(stats, rec(expr)).withSpan(fn.span) + case _ => + fn.select(nme.apply).appliedToArgs(argRefs).withSpan(fn.span) + } + seq(argVals, rec(fn)) + } + // // HELPERS // diff --git a/library/src/scala/quoted/Expr.scala b/library/src/scala/quoted/Expr.scala index 86fcfa0e317d..bfb10d0bdee3 100644 --- a/library/src/scala/quoted/Expr.scala +++ b/library/src/scala/quoted/Expr.scala @@ -35,16 +35,20 @@ package quoted { /** Converts a tuple `(T1, ..., Tn)` to `(Expr[T1], ..., Expr[Tn])` */ type TupleOfExpr[Tup <: Tuple] = Tuple.Map[Tup, [X] =>> given QuoteContext => Expr[X]] - implicit class AsFunction[F, Args <: Tuple, R](f: Expr[F]) given (tf: TupledFunction[F, Args => R]) { + implicit class AsFunction[F, Args <: Tuple, R](f: Expr[F]) given (tf: TupledFunction[F, Args => R], qctx: QuoteContext) { /** Beta-reduces the function appication. Generates the an expression only containing the body of the function */ - def apply[G] given (tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = - tg.untupled(args => new FunctionAppliedTo[R](f, args.toArray.map(_.asInstanceOf[QuoteContext => Expr[_]]))) + def apply[G] given (tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = { + import qctx.tasty._ + tg.untupled(args => qctx.tasty.kernel.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]]) + } } - implicit class AsContextualFunction[F, Args <: Tuple, R](f: Expr[F]) given (tf: TupledFunction[F, given Args => R]) { + implicit class AsContextualFunction[F, Args <: Tuple, R](f: Expr[F]) given (tf: TupledFunction[F, given Args => R], qctx: QuoteContext) { /** Beta-reduces the function appication. Generates the an expression only containing the body of the function */ - def apply[G] given (tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = - tg.untupled(args => new FunctionAppliedTo[R](f, args.toArray.map(_.asInstanceOf[QuoteContext => Expr[_]]))) + def apply[G] given (tg: TupledFunction[G, TupleOfExpr[Args] => Expr[R]]): G = { + import qctx.tasty._ + tg.untupled(args => qctx.tasty.kernel.betaReduce(f.unseal, args.toArray.toList.map(_.asInstanceOf[QuoteContext => Expr[_]](qctx).unseal)).seal.asInstanceOf[Expr[R]]) + } } /** Returns a null expresssion equivalent to `'{null}` */ @@ -93,13 +97,5 @@ package internal { override def toString: String = s"Expr()" } - // TODO Use a List in FunctionAppliedTo(val f: Expr[_], val args: List[Expr[_]]) - // FIXME: Having the List in the code above trigers an assertion error while testing dotty.tools.dotc.reporting.ErrorMessagesTests.i3187 - // This test does redefine `scala.collection`. Further investigation is needed. - /** An Expr representing `'{($f).apply($x1, ..., $xn)}` but it is beta-reduced when the closure is known */ - final class FunctionAppliedTo[+R](val f: Expr[_], val args: Array[QuoteContext => Expr[_]]) extends Expr[R] { - override def toString: String = s"Expr($f ${args.toList})" - } - } } diff --git a/library/src/scala/tasty/reflect/Kernel.scala b/library/src/scala/tasty/reflect/Kernel.scala index 7c802744c5bd..37b0b6c43d0b 100644 --- a/library/src/scala/tasty/reflect/Kernel.scala +++ b/library/src/scala/tasty/reflect/Kernel.scala @@ -1538,4 +1538,9 @@ trait Kernel { */ def searchImplicit(tpe: Type) given (ctx: Context): ImplicitSearchResult + /** Inline fn if it is an explicit closure possibly nested inside the expression of a block. + * Otherwise apply the arguments to the closure. + */ + def betaReduce(f: Term, args: List[Term]) given (ctx: Context): Term + }