Skip to content

Commit 0a486e3

Browse files
Merge pull request #4460 from dotty-staging/fix-#4431
Fix #4431: Change semantics of inline params in macro
2 parents 7ca5a2c + b75714f commit 0a486e3

File tree

6 files changed

+44
-15
lines changed

6 files changed

+44
-15
lines changed

compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ import dotty.tools.dotc.core.quoted._
7676
* { ... x1$1 .... '{ ... T1$1.unary_~ ... x1$1.toExpr.unary_~ ... y1$1.unary_~ ... } ... }
7777
* }
7878
* ```
79+
* Where `inline` parameters with type Boolean, Byte, Short, Int, Long, Float, Double, Char and String are
80+
* passed as their actual runtime value. See `isStage0Value`. Other `inline` arguments such as functions are handled
81+
* like `y1: Y`.
82+
*
7983
* Note: the parameters of `foo` are kept for simple overloading resolution but they are not used in the body of `foo`.
8084
*
8185
* At inline site we will call reflectively the static method `foo` with dummy parameters, which will return a
@@ -243,7 +247,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
243247
def levelOK(sym: Symbol)(implicit ctx: Context): Boolean = levelOf.get(sym) match {
244248
case Some(l) =>
245249
l == level ||
246-
sym.is(Inline) && sym.owner.is(Macro) && sym.info.isValueType && l - 1 == level
250+
l == 1 && level == 0 && isStage0Value(sym)
247251
case None =>
248252
!sym.is(Param) || levelOK(sym.owner)
249253
}
@@ -374,8 +378,8 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
374378
else ref(defn.QuotedExpr_apply).appliedToType(body1.tpe.widen).appliedTo(body1)
375379
}
376380
else body match {
377-
case body: RefTree if isCaptured(body, level + 1) =>
378-
if (body.symbol.is(Inline)) {
381+
case body: RefTree if isCaptured(body.symbol, level + 1) =>
382+
if (isStage0Value(body.symbol)) {
379383
// Optimization: avoid the full conversion when capturing inlined `x`
380384
// in '{ x } to '{ x$1.toExpr.unary_~ } and go directly to `x$1.toExpr`
381385
liftValue(capturers(body.symbol)(body))
@@ -476,7 +480,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
476480
val tpw = tree.tpe.widen
477481
val argTpe =
478482
if (tree.isType) defn.QuotedTypeType.appliedTo(tpw)
479-
else if (tree.symbol.is(Inline)) tpw // inlined term
483+
else if (isStage0Value(tree.symbol)) tpw
480484
else defn.QuotedExprType.appliedTo(tpw)
481485
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argTpe)
482486
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
@@ -509,11 +513,11 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
509513
}
510514

511515
/** Returns true if this tree will be captured by `makeLambda` */
512-
private def isCaptured(tree: RefTree, level: Int)(implicit ctx: Context): Boolean = {
516+
private def isCaptured(sym: Symbol, level: Int)(implicit ctx: Context): Boolean = {
513517
// Check phase consistency and presence of capturer
514-
( (level == 1 && levelOf.get(tree.symbol).contains(1)) ||
515-
(level == 0 && tree.symbol.is(Inline))
516-
) && capturers.contains(tree.symbol)
518+
( (level == 1 && levelOf.get(sym).contains(1)) ||
519+
(level == 0 && isStage0Value(sym))
520+
) && capturers.contains(sym)
517521
}
518522

519523
/** Transform `tree` and return the resulting tree and all `embedded` quotes
@@ -550,13 +554,13 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
550554
splice(ref(splicedType).select(tpnme.UNARY_~))
551555
case tree: Select if tree.symbol.isSplice =>
552556
splice(tree)
553-
case tree: RefTree if isCaptured(tree, level) =>
557+
case tree: RefTree if isCaptured(tree.symbol, level) =>
554558
val capturer = capturers(tree.symbol)
555559
def captureAndSplice(t: Tree) =
556560
splice(t.select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~))
557-
if (tree.symbol.is(Inline) && level == 0) capturer(tree)
558-
else if (tree.symbol.is(Inline)) captureAndSplice(liftValue(capturer(tree)))
559-
else captureAndSplice(capturer(tree))
561+
if (!isStage0Value(tree.symbol)) captureAndSplice(capturer(tree))
562+
else if (level == 0) capturer(tree)
563+
else captureAndSplice(liftValue(capturer(tree)))
560564
case Block(stats, _) =>
561565
val last = enteredSyms
562566
stats.foreach(markDef)
@@ -626,6 +630,9 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
626630
}
627631
}
628632

633+
private def isStage0Value(sym: Symbol)(implicit ctx: Context): Boolean =
634+
sym.is(Inline) && sym.owner.is(Macro) && !defn.isFunctionType(sym.info)
635+
629636
private def liftList(list: List[Tree], tpe: Type)(implicit ctx: Context): Tree = {
630637
list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) =>
631638
acc.select("::".toTermName).appliedToType(tpe).appliedTo(x)

docs/docs/reference/principled-meta-programming.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,9 +394,10 @@ point. To reflect this, we loosen the phase consistency requirements
394394
as follows:
395395

396396
- If `x` is an inline value (or an inline parameter of an inline
397-
function), it can be accessed in all contexts where the number of
398-
splices minus the number of quotes between use and definition is
399-
either 0 or 1.
397+
function) of type Boolean, Byte, Short, Int, Long, Float, Double,
398+
Char or String, it can be accessed in all contexts where the number
399+
of splices minus the number of quotes between use and definition
400+
is either 0 or 1.
400401

401402
### Relationship with Staging
402403

tests/neg/i4433.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
object Foo {
3+
inline def g(inline p: Int => Boolean): Boolean = ~{
4+
if(p(5)) '(true) // error
5+
else '(false)
6+
}
7+
}

tests/run/i4431.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
abc42

tests/run/i4431/quoted_1.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import scala.quoted._
2+
3+
object Macros {
4+
inline def h(inline f: Int => String): String = ~ '(f(42))
5+
}

tests/run/i4431/quoted_2.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import scala.quoted._
2+
import Macros._
3+
4+
object Test {
5+
def main(args: Array[String]): Unit = {
6+
println(h(x => "abc" + x))
7+
}
8+
}

0 commit comments

Comments
 (0)