Skip to content
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
29 changes: 0 additions & 29 deletions compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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]] {
Expand Down
36 changes: 33 additions & 3 deletions compiler/src/dotty/tools/dotc/tastyreflect/KernelImpl.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
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._
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}
Expand All @@ -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

Expand Down Expand Up @@ -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
//
Expand Down
24 changes: 10 additions & 14 deletions library/src/scala/quoted/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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}` */
Expand Down Expand Up @@ -93,13 +97,5 @@ package internal {
override def toString: String = s"Expr(<tasty tree>)"
}

// 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 <applied to> ${args.toList})"
}

}
}
5 changes: 5 additions & 0 deletions library/src/scala/tasty/reflect/Kernel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

}