Skip to content

Commit 9cd6432

Browse files
committed
Refine handling of inline parameters of macros
These are at runtime trees with constant types. We can extract the constant from the tree, so they can also be used in a splicing context.
1 parent 6cf9c66 commit 9cd6432

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,19 @@ class ReifyQuotes extends MacroTransform {
173173
case _ =>
174174
}
175175

176+
/** Does the level of `sym` match the current level?
177+
* An exception is made for inline vals in macros. These are also OK if their level
178+
* is one higher than the current level, because on execution such values
179+
* are constant expression trees and we can pull out the constant from the tree.
180+
*/
181+
def levelOK(sym: Symbol)(implicit ctx: Context): Boolean = levelOf.get(sym) match {
182+
case Some(l) =>
183+
l == level ||
184+
sym.is(Inline) && sym.owner.is(Macro) && sym.info.isValueType && l - 1 == level
185+
case None =>
186+
true
187+
}
188+
176189
/** Issue a "splice outside quote" error unless we ar in the body of an inline method */
177190
def spliceOutsideQuotes(pos: Position)(implicit ctx: Context) =
178191
ctx.error(i"splice outside quotes", pos)
@@ -188,7 +201,7 @@ class ReifyQuotes extends MacroTransform {
188201
else i"${sym.name}.this"
189202
if (!isThis && sym.maybeOwner.isType)
190203
check(sym.owner, sym.owner.thisType, pos)
191-
else if (sym.exists && !sym.isStaticOwner && levelOf.getOrElse(sym, level) != level)
204+
else if (sym.exists && !sym.isStaticOwner && !levelOK(sym))
192205
tp match {
193206
case tp: TypeRef =>
194207
importedTypes += tp
@@ -329,7 +342,7 @@ class ReifyQuotes extends MacroTransform {
329342
cpy.Select(expansion)(cpy.Inlined(tree)(call, bindings, body), name)
330343
case _: Import =>
331344
tree
332-
case tree: DefDef if tree.symbol.is(Macro) =>
345+
case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>
333346
val tree1 = nested(isQuote = true).transform(tree)
334347
// check macro code as it if appeared in a quoted context
335348
cpy.DefDef(tree)(rhs = EmptyTree)

tests/neg/inlinevals.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ object Test {
1717

1818
inline val M = X // error: rhs must be constant expression
1919

20+
inline val xs = List(1, 2, 3) // error: must be a constant expression
21+
22+
def f(inline xs: List[Int]) = xs
23+
24+
f(List(1, 2, 3)) // error: must be a constant expression
25+
2026
def byname(inline f: => String): Int = ??? // ok
2127

2228
byname("hello" ++ " world")

tests/pos/quoted.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ class Test {
1010
def assertImpl(expr: Expr[Boolean]) =
1111
'{ if !(~expr) then throw new AssertionError(s"failed assertion: ${~expr}") }
1212

13+
inline def power(inline n: Int, x: Double) = ~powerCode(n, '(x))
14+
15+
def powerCode(n: Int, x: Expr[Double]): Expr[Double] =
16+
if (n == 0) '(1.0)
17+
else if (n == 1) x
18+
else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } }
19+
else '{ ~x * ~powerCode(n - 1, x) }
1320
}
1421

1522
val program = '{
@@ -19,6 +26,12 @@ class Test {
1926
assert(x != 0)
2027

2128
~assertImpl('(x != 0))
29+
30+
val y = math.sqrt(2.0)
31+
32+
power(3, y)
33+
34+
~powerCode(3, '{math.sqrt(2.0)})
2235
}
2336

2437
program.run

0 commit comments

Comments
 (0)