Skip to content

Commit 9185dc4

Browse files
committed
Move Expr.open to UnsafeExpr.open
1 parent 4bd610b commit 9185dc4

File tree

5 files changed

+58
-45
lines changed

5 files changed

+58
-45
lines changed

library/src/scala/quoted/Expr.scala

-36
Original file line numberDiff line numberDiff line change
@@ -181,40 +181,4 @@ object Expr {
181181
ofTuple(elems).cast[Tuple.InverseMap[T, Expr]]
182182
}
183183

184-
// TODO generalize for any function arity (see Expr.betaReduce)
185-
def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(given qctx: QuoteContext): X = {
186-
import qctx.tasty.{given, _}
187-
val (params, bodyExpr) = paramsAndBody(f)
188-
content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]])
189-
}
190-
191-
def open[T1, T2, R, X](f: Expr[(T1, T2) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2]) => Expr[t]) => X)(given qctx: QuoteContext)(given DummyImplicit): X = {
192-
import qctx.tasty.{given, _}
193-
val (params, bodyExpr) = paramsAndBody(f)
194-
content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal)).seal.asInstanceOf[Expr[t]])
195-
}
196-
197-
def open[T1, T2, T3, R, X](f: Expr[(T1, T2, T3) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2], Expr[T3]) => Expr[t]) => X)(given qctx: QuoteContext)(given DummyImplicit, DummyImplicit): X = {
198-
import qctx.tasty.{given, _}
199-
val (params, bodyExpr) = paramsAndBody(f)
200-
content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2], v3: Expr[T3]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal, v3.unseal)).seal.asInstanceOf[Expr[t]])
201-
}
202-
203-
private def paramsAndBody[R](given qctx: QuoteContext)(f: Expr[Any]) = {
204-
import qctx.tasty.{given, _}
205-
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand
206-
(params, body.seal.asInstanceOf[Expr[R]])
207-
}
208-
209-
private def bodyFn[t](given qctx: QuoteContext)(e: qctx.tasty.Term, params: List[qctx.tasty.ValDef], args: List[qctx.tasty.Term]): qctx.tasty.Term = {
210-
import qctx.tasty.{given, _}
211-
val map = params.map(_.symbol).zip(args).toMap
212-
new TreeMap {
213-
override def transformTerm(tree: Term)(given ctx: Context): Term =
214-
super.transformTerm(tree) match
215-
case tree: Ident => map.getOrElse(tree.symbol, tree)
216-
case tree => tree
217-
}.transformTerm(e)
218-
}
219-
220184
}

library/src/scala/quoted/unsafe/UnsafeExpr.scala

+47
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,52 @@ object UnsafeExpr {
2424
expr.unseal.underlyingArgument.seal.asInstanceOf[Expr[T]]
2525
}
2626

27+
// TODO generalize for any function arity (see Expr.betaReduce)
28+
/** Allows inspection or transformation of the body of the expression of function.
29+
* This body may have references to the arguments of the function which should be closed
30+
* over if the expression will be spliced.
31+
*
32+
* ```
33+
* val f: Expr[T => R] = ...
34+
* UnsafeExpr.open(f) { (body, close) =>
35+
* val newParam: Expr[T] = ...
36+
* ...
37+
* close(body)(newParam) // body or part of the body
38+
* }
39+
* ```
40+
*/
41+
def open[T1, R, X](f: Expr[T1 => R])(content: (Expr[R], [t] => Expr[t] => Expr[T1] => Expr[t]) => X)(given qctx: QuoteContext): X = {
42+
import qctx.tasty.{given, _}
43+
val (params, bodyExpr) = paramsAndBody(f)
44+
content(bodyExpr, [t] => (e: Expr[t]) => (v: Expr[T1]) => bodyFn[t](e.unseal, params, List(v.unseal)).seal.asInstanceOf[Expr[t]])
45+
}
2746

47+
def open[T1, T2, R, X](f: Expr[(T1, T2) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2]) => Expr[t]) => X)(given qctx: QuoteContext)(given DummyImplicit): X = {
48+
import qctx.tasty.{given, _}
49+
val (params, bodyExpr) = paramsAndBody(f)
50+
content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal)).seal.asInstanceOf[Expr[t]])
51+
}
52+
53+
def open[T1, T2, T3, R, X](f: Expr[(T1, T2, T3) => R])(content: (Expr[R], [t] => Expr[t] => (Expr[T1], Expr[T2], Expr[T3]) => Expr[t]) => X)(given qctx: QuoteContext)(given DummyImplicit, DummyImplicit): X = {
54+
import qctx.tasty.{given, _}
55+
val (params, bodyExpr) = paramsAndBody(f)
56+
content(bodyExpr, [t] => (e: Expr[t]) => (v1: Expr[T1], v2: Expr[T2], v3: Expr[T3]) => bodyFn[t](e.unseal, params, List(v1.unseal, v2.unseal, v3.unseal)).seal.asInstanceOf[Expr[t]])
57+
}
58+
59+
private def paramsAndBody[R](given qctx: QuoteContext)(f: Expr[Any]) = {
60+
import qctx.tasty.{given, _}
61+
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = f.unseal.etaExpand
62+
(params, body.seal.asInstanceOf[Expr[R]])
63+
}
64+
65+
private def bodyFn[t](given qctx: QuoteContext)(e: qctx.tasty.Term, params: List[qctx.tasty.ValDef], args: List[qctx.tasty.Term]): qctx.tasty.Term = {
66+
import qctx.tasty.{given, _}
67+
val map = params.map(_.symbol).zip(args).toMap
68+
new TreeMap {
69+
override def transformTerm(tree: Term)(given ctx: Context): Term =
70+
super.transformTerm(tree) match
71+
case tree: Ident => map.getOrElse(tree.symbol, tree)
72+
case tree => tree
73+
}.transformTerm(e)
74+
}
2875
}

tests/run-macros/quote-matcher-symantics-2/quoted_1.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import scala.quoted._
22
import scala.quoted.matching._
3+
import scala.quoted.unsafe._
34

45
object Macros {
56

@@ -22,7 +23,7 @@ object Macros {
2223
case '{ ($f: DSL => DSL)($x: DSL) } => sym.app(liftFun(f), lift(x))
2324

2425
case '{ val x: DSL = $value; ($bodyFn: DSL => DSL)(x) } =>
25-
Expr.open(bodyFn) { (body1, close) =>
26+
UnsafeExpr.open(bodyFn) { (body1, close) =>
2627
val (i, nEnvVar) = freshEnvVar()
2728
lift(close(body1)(nEnvVar))(env + (i -> lift(value)))
2829
}
@@ -38,7 +39,7 @@ object Macros {
3839
def liftFun(e: Expr[DSL => DSL])(implicit env: Map[Int, Expr[T]]): Expr[T => T] = e match {
3940
case '{ (x: DSL) => ($bodyFn: DSL => DSL)(x) } =>
4041
sym.lam((y: Expr[T]) =>
41-
Expr.open(bodyFn) { (body1, close) =>
42+
UnsafeExpr.open(bodyFn) { (body1, close) =>
4243
val (i, nEnvVar) = freshEnvVar()
4344
lift(close(body1)(nEnvVar))(env + (i -> y))
4445
}

tests/run-macros/quote-matcher-symantics-3/quoted_1.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import scala.quoted._
22
import scala.quoted.matching._
3+
import scala.quoted.unsafe._
34

45
object Macros {
56

@@ -46,17 +47,17 @@ object Macros {
4647

4748
case '{ (x0: Int) => ($bodyFn: Int => Any)(x0) } =>
4849
val (i, nEnvVar) = freshEnvVar[Int]()
49-
val body2 = Expr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
50+
val body2 = UnsafeExpr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
5051
'{ $sym.lam((x: R[Int]) => ${given Env = envWith(i, 'x)(given env); lift(body2)}).asInstanceOf[R[T]] }
5152

5253
case '{ (x0: Boolean) => ($bodyFn: Boolean => Any)(x0) } =>
5354
val (i, nEnvVar) = freshEnvVar[Boolean]()
54-
val body2 = Expr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
55+
val body2 = UnsafeExpr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
5556
'{ $sym.lam((x: R[Boolean]) => ${given Env = envWith(i, 'x)(given env); lift(body2)}).asInstanceOf[R[T]] }
5657

5758
case '{ (x0: Int => Int) => ($bodyFn: (Int => Int) => Any)(x0) } =>
5859
val (i, nEnvVar) = freshEnvVar[Int => Int]()
59-
val body2 = Expr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
60+
val body2 = UnsafeExpr.open(bodyFn) { (body1, close) => close(body1)(nEnvVar) }
6061
'{ $sym.lam((x: R[Int => Int]) => ${given Env = envWith(i, 'x)(given env); lift(body2)}).asInstanceOf[R[T]] }
6162

6263
case '{ Symantics.fix[$t, $u]($f) } =>

tests/run-macros/quote-matching-open/Macro_1.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import scala.quoted._
2-
2+
import scala.quoted.unsafe._
33
object Macro {
44

55
inline def openTest(x: => Any): Any = ${ Macro.impl('x) }
66

77
def impl(x: Expr[Any])(given QuoteContext): Expr[Any] = {
88
x match {
9-
case '{ (x: Int) => ($body: Int => Int)(x) } => Expr.open(body) { (body, close) => close(body)(Expr(2)) }
10-
case '{ (x1: Int, x2: Int) => ($body: (Int, Int) => Int)(x1, x2) } => Expr.open(body) { (body, close) => close(body)(Expr(2), Expr(3)) }
11-
case '{ (x1: Int, x2: Int, x3: Int) => ($body: (Int, Int, Int) => Int)(x1, x2, x3) } => Expr.open(body) { (body, close) => close(body)(Expr(2), Expr(3), Expr(4)) }
9+
case '{ (x: Int) => ($body: Int => Int)(x) } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2)) }
10+
case '{ (x1: Int, x2: Int) => ($body: (Int, Int) => Int)(x1, x2) } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2), Expr(3)) }
11+
case '{ (x1: Int, x2: Int, x3: Int) => ($body: (Int, Int, Int) => Int)(x1, x2, x3) } => UnsafeExpr.open(body) { (body, close) => close(body)(Expr(2), Expr(3), Expr(4)) }
1212
}
1313
}
1414

0 commit comments

Comments
 (0)