Skip to content

Commit 7429644

Browse files
committed
Fix #7142: Detect escaped variables across macro expansion
1 parent 46aec62 commit 7429644

File tree

4 files changed

+20
-5
lines changed

4 files changed

+20
-5
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,13 @@ object Splicer {
4141
case Quoted(quotedTree) => quotedTree
4242
case _ =>
4343
val interpreter = new Interpreter(pos, classLoader)
44-
val macroOwner = ctx.newSymbol(ctx.owner, NameKinds.UniqueName.fresh(nme.MACROkw), Synthetic, defn.AnyType, coord = tree.span)
44+
val macroOwner = ctx.newSymbol(ctx.owner, nme.MACROkw, Macro | Synthetic, defn.AnyType, coord = tree.span)
4545
try {
4646
given Context = ctx.withOwner(macroOwner)
4747
// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
4848
val interpretedExpr = interpreter.interpret[scala.quoted.QuoteContext => scala.quoted.Expr[Any]](tree)
4949
val interpretedTree = interpretedExpr.fold(tree)(macroClosure => PickledQuotes.quotedExprToTree(macroClosure(QuoteContext())))
50-
checkEscapedVariables(interpretedTree, macroOwner).changeOwner(macroOwner, ctx.owner)
50+
checkEscapedVariables(interpretedTree).changeOwner(macroOwner, ctx.owner)
5151
}
5252
catch {
5353
case ex: CompilationUnit.SuspendException =>
@@ -70,7 +70,7 @@ object Splicer {
7070
}
7171

7272
/** Checks that no symbol that whas generated within the macro expansion has an out of scope reference */
73-
def checkEscapedVariables(tree: Tree, expansionOwner: Symbol)(given ctx: Context): tree.type =
73+
def checkEscapedVariables(tree: Tree)(given ctx: Context): tree.type =
7474
new TreeTraverser {
7575
private[this] var locals = Set.empty[Symbol]
7676
private def markSymbol(sym: Symbol)(implicit ctx: Context): Unit =
@@ -100,7 +100,7 @@ object Splicer {
100100
traverseChildren(tree)
101101
private def isEscapedVariable(sym: Symbol)(given ctx: Context): Boolean =
102102
sym.exists && !sym.is(Package)
103-
&& sym.owner.ownersIterator.contains(expansionOwner) // symbol was generated within the macro expansion
103+
&& sym.owner.ownersIterator.exists(x => x.is(Macro, butNot = Method) && x.name == nme.MACROkw) // symbol was generated within a macro expansion
104104
&& !locals.contains(sym) // symbol is not in current scope
105105
}.traverse(tree)
106106
tree

staging/src/scala/quoted/staging/QuoteCompiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private class QuoteCompiler extends Compiler {
7272
given Context = unitCtx.withOwner(meth)
7373
val qctx = dotty.tools.dotc.quoted.QuoteContext()
7474
val quoted = PickledQuotes.quotedExprToTree(exprUnit.exprBuilder.apply(qctx))
75-
checkEscapedVariables(quoted, meth)
75+
checkEscapedVariables(quoted)
7676
}
7777

7878
getLiteral(quoted) match {

tests/neg-macros/i7142h/Macro_1.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package macros
2+
import scala.quoted._
3+
4+
var saved = Option.empty[Expr[Int]]
5+
6+
def oops(given QuoteContext) = {
7+
if (saved.isEmpty) '{ (x: Int) => ${ saved = Some('{x}); 'x } }
8+
else saved.get
9+
}
10+
11+
inline def test = ${oops}

tests/neg-macros/i7142h/Test_2.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object Test {
2+
macros.test
3+
macros.test // error
4+
}

0 commit comments

Comments
 (0)