Skip to content

Fix #4846: Allow transparent parameters at any stage #4856

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 2 commits into from
Jul 31, 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
61 changes: 13 additions & 48 deletions compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
def levelOK(sym: Symbol)(implicit ctx: Context): Boolean = levelOf.get(sym) match {
case Some(l) =>
l == level ||
l == 0 && level == -1 && isStageNegOneValue(sym)
level == -1 && sym == defn.TastyTopLevelSplice_tastyContext
case None =>
!sym.is(Param) || levelOK(sym.owner)
}
Expand Down Expand Up @@ -359,18 +359,12 @@ class ReifyQuotes extends MacroTransformWithImplicits {
}
else body match {
case body: RefTree if isCaptured(body.symbol, level + 1) =>
if (isStageNegOneValue(body.symbol)) {
// Optimization: avoid the full conversion when capturing inlined `x`
// in '{ x } to '{ x$1.toExpr.unary_~ } and go directly to `x$1.toExpr`
liftInlineParamValue(capturers(body.symbol)(body))
} else {
// Optimization: avoid the full conversion when capturing `x`
// in '{ x } to '{ x$1.unary_~ } and go directly to `x$1`
capturers(body.symbol)(body)
}
// Optimization: avoid the full conversion when capturing `x`
// in '{ x } to '{ x$1.unary_~ } and go directly to `x$1`
capturers(body.symbol)(body)
case _=>
val (body1, splices) = nested(isQuote = true).split(body)
if (level >= 0) pickledQuote(body1, splices, body.tpe, isType).withPos(quote.pos)
if (level == 0 && !ctx.inTransparentMethod) pickledQuote(body1, splices, body.tpe, isType).withPos(quote.pos)
Copy link
Contributor

Choose a reason for hiding this comment

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

Small question. We don't split if we are in a transparent method but the level is above or equal zero?
If yes, we can move splitting inside the if now.

Copy link
Contributor Author

@nicolasstucki nicolasstucki Jul 31, 2018

Choose a reason for hiding this comment

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

We do not split, but we still need to perform the checks that split does. We should be able to improve the performance by calling transform directly in another branch. I will try that later, nice catch :)

else {
// In top-level splice in an transparent def. Keep the tree as it is, it will be transformed at inline site.
body
Expand Down Expand Up @@ -464,7 +458,6 @@ class ReifyQuotes extends MacroTransformWithImplicits {
val tpw = tree.tpe.widen
val argTpe =
if (tree.isType) defn.QuotedTypeType.appliedTo(tpw)
else if (isStageNegOneValue(tree.symbol)) tpw
else defn.QuotedExprType.appliedTo(tpw)
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argTpe)
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
Expand All @@ -489,21 +482,17 @@ class ReifyQuotes extends MacroTransformWithImplicits {
val captured = mutable.LinkedHashMap.empty[Symbol, Tree]
val captured2 = capturer(captured)

outer.enteredSyms.foreach(sym => capturers.put(sym, captured2))
outer.enteredSyms.foreach(sym => if (!sym.is(Transparent)) capturers.put(sym, captured2))

val tree2 = transform(tree)
capturers --= outer.enteredSyms

seq(captured.result().valuesIterator.toList, tree2)
}

/** Returns true if this tree will be captured by `makeLambda` */
private def isCaptured(sym: Symbol, level: Int)(implicit ctx: Context): Boolean = {
// Check phase consistency and presence of capturer
( (level == 1 && levelOf.get(sym).contains(1)) ||
(level == 0 && isStageNegOneValue(sym))
) && capturers.contains(sym)
}
/** Returns true if this tree will be captured by `makeLambda`. Checks phase consistency and presence of capturer. */
private def isCaptured(sym: Symbol, level: Int)(implicit ctx: Context): Boolean =
level == 1 && levelOf.get(sym).contains(1) && capturers.contains(sym)

/** Transform `tree` and return the resulting tree and all `embedded` quotes
* or splices as a pair, after performing the `addTags` transform.
Expand Down Expand Up @@ -539,13 +528,11 @@ class ReifyQuotes extends MacroTransformWithImplicits {
splice(ref(splicedType).select(tpnme.UNARY_~).withPos(tree.pos))
case tree: Select if tree.symbol.isSplice =>
splice(tree)
case tree: RefTree if tree.symbol.is(Transparent) && tree.symbol.is(Param) =>
tree
case tree: RefTree if isCaptured(tree.symbol, level) =>
val capturer = capturers(tree.symbol)
def captureAndSplice(t: Tree) =
splice(t.select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~))
if (!isStageNegOneValue(tree.symbol)) captureAndSplice(capturer(tree))
else if (level == 0) capturer(tree)
else captureAndSplice(liftInlineParamValue(capturer(tree)))
val t = capturers(tree.symbol).apply(tree)
splice(t.select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~))
case Block(stats, _) =>
val last = enteredSyms
stats.foreach(markDef)
Expand Down Expand Up @@ -589,28 +576,6 @@ class ReifyQuotes extends MacroTransformWithImplicits {
}
}

/** Takes a reference to an transparent parameter `tree` and lifts it to an Expr */
private def liftInlineParamValue(tree: Tree)(implicit ctx: Context): Tree = {
val tpSym = tree.tpe.widenDealias.classSymbol

val lifter =
if (tpSym eq defn.BooleanClass) defn.QuotedLiftable_BooleanIsLiftable
else if (tpSym eq defn.ByteClass) defn.QuotedLiftable_ByteIsLiftable
else if (tpSym eq defn.CharClass) defn.QuotedLiftable_CharIsLiftable
else if (tpSym eq defn.ShortClass) defn.QuotedLiftable_ShortIsLiftable
else if (tpSym eq defn.IntClass) defn.QuotedLiftable_IntIsLiftable
else if (tpSym eq defn.LongClass) defn.QuotedLiftable_LongIsLiftable
else if (tpSym eq defn.FloatClass) defn.QuotedLiftable_FloatIsLiftable
else if (tpSym eq defn.DoubleClass) defn.QuotedLiftable_DoubleIsLiftable
else defn.QuotedLiftable_StringIsLiftable

ref(lifter).select("toExpr".toTermName).appliedTo(tree)
}

private def isStageNegOneValue(sym: Symbol)(implicit ctx: Context): Boolean =
(sym.is(Transparent) && sym.owner.is(Transparent) && !defn.isFunctionType(sym.info)) ||
sym == defn.TastyTopLevelSplice_tastyContext // intrinsic value at stage 0

private def liftList(list: List[Tree], tpe: Type)(implicit ctx: Context): Tree = {
list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) =>
acc.select("::".toTermName).appliedToType(tpe).appliedTo(x)
Expand Down
6 changes: 6 additions & 0 deletions tests/pos/i4846.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import scala.quoted._

object Test {
transparent def foo(transparent x: Int): Int = ~fooImpl(x, '(x), '( '(x) ), '( '( '(x) ) ))
def fooImpl(a: Int, b: Expr[Int], c: Expr[Expr[Int]], d: Expr[Expr[Expr[Int]]]): Expr[Int] = ???
}