Skip to content

Commit 3b0128a

Browse files
committed
Handle failures to pickle macro parameters
1 parent 257d7b4 commit 3b0128a

File tree

11 files changed

+72
-2
lines changed

11 files changed

+72
-2
lines changed

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ object PickledQuotes {
2424

2525
/** Pickle the tree of the a quoted.Expr */
2626
def pickleExpr(tree: Tree)(implicit ctx: Context): scala.quoted.Expr[Any] = {
27+
// Check that there are no free variables
28+
new TreeTraverser {
29+
private val definedHere = scala.collection.mutable.Set.empty[Symbol]
30+
def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = tree match {
31+
case tree: Ident if tree.symbol.exists && !definedHere(tree.symbol) =>
32+
throw new scala.quoted.FreeVariableError(tree.name.toString)
33+
case tree: DefTree =>
34+
definedHere += tree.symbol
35+
traverseChildren(tree)
36+
case _ =>
37+
traverseChildren(tree)
38+
}
39+
}.traverse(tree)
2740
val pickled = pickleQuote(tree)
2841
scala.runtime.quoted.Unpickler.unpickleExpr(pickled, Nil)
2942
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,8 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
560560
// see PostTyper `case Inlined(...) =>` for description of the simplification
561561
val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
562562
val spliced = Splicer.splice(body, call, bindings, tree.pos, macroClassLoader).withPos(tree.pos)
563-
transform(cpy.Inlined(tree)(call2, bindings, spliced))
563+
if (ctx.reporter.hasErrors) EmptyTree
564+
else transform(cpy.Inlined(tree)(call2, bindings, spliced))
564565
}
565566
else super.transform(tree)
566567

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package scala.quoted
2+
3+
class FreeVariableError(val name: String) extends QuoteError("Free variable " + name + " could not be handled")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.quoted._
2+
3+
import dotty.tools.dotc.quoted.Toolbox._
4+
5+
object Macros {
6+
inline def foo(i: => Int): Int = ~{
7+
val y: Int = ('(i)).run
8+
y.toExpr
9+
}
10+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Macros._
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
val x = 3
5+
println(foo(x)) // error
6+
}
7+
}

tests/run/quote-run-in-macro-1.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
1
22
4
3+
5

tests/run/quote-run-in-macro-1/quoted_1.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import scala.quoted._
33
import dotty.tools.dotc.quoted.Toolbox._
44

55
object Macros {
6-
inline def foo(i: Int): Int = ~{
6+
inline def foo(i: => Int): Int = ~{
77
val y: Int = ('(i)).run
88
y.toExpr
99
}

tests/run/quote-run-in-macro-1/quoted_2.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,10 @@ object Test {
33
def main(args: Array[String]): Unit = {
44
println(foo(1))
55
println(foo(1 + 3))
6+
val x = 3
7+
println(foo {
8+
val x = 5
9+
x
10+
})
611
}
712
}

tests/run/quote-run-in-macro-2.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"> 3"
2+
"> 12"
3+
(x: scala.Int) => "> ".+(y).apply(3)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import scala.quoted._
2+
3+
import dotty.tools.dotc.quoted.Toolbox._
4+
5+
object Macros {
6+
inline def foo(f: => Int => String): String = ~bar('(f))
7+
def bar(f: Expr[Int => String]): Expr[String] = {
8+
try {
9+
val y: Int => String = f.run
10+
val res = y(3).toExpr // evaluate at compile time
11+
res.show.toExpr
12+
} catch {
13+
case ex: scala.quoted.FreeVariableError =>
14+
val res = ('((~f)(3))) // evaluate at run time
15+
res.show.toExpr
16+
}
17+
}
18+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import Macros._
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
println(foo(x => "> " + x))
5+
println(foo(x => "> " + 4 * x))
6+
val y = 9
7+
println(foo(x => "> " + y))
8+
}
9+
}

0 commit comments

Comments
 (0)