Skip to content

Commit 50d5fc3

Browse files
committed
Allow nested Quotes with a different owners
This makes it possible to create `Expr`s with different owners to avoid a call to `changeOwner`.
1 parent a771c86 commit 50d5fc3

File tree

7 files changed

+84
-0
lines changed

7 files changed

+84
-0
lines changed

compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,6 +2617,8 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
26172617

26182618
def show(using printer: Printer[Symbol]): String = printer.show(self)
26192619

2620+
def asQuotes: Nested = new QuotesImpl(using ctx.withOwner(self))
2621+
26202622
end extension
26212623

26222624
private def appliedTypeRef(sym: Symbol): TypeRepr =
@@ -2907,6 +2909,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
29072909
|which has the AST representation
29082910
|${Printer.TreeStructure.show(tree)}
29092911
|
2912+
|
2913+
|
2914+
|Tip: The owner of a tree can be changed using method `Tree.changeOwner`.
2915+
|Tip: The default owner of definitions created in quotes can be changed using method `Symbol.asQuotes`.
29102916
|""".stripMargin)
29112917
case _ => traverseChildren(t)
29122918
}.traverse(tree)

library/src/scala/quoted/Quotes.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3774,6 +3774,27 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
37743774

37753775
/** Case class or case object children of a sealed trait or cases of an `enum`. */
37763776
def children: List[Symbol]
3777+
3778+
/** Returns a nested quote with this symbol as splice owner (`Symbol.spliceOwner`).
3779+
*
3780+
* Changes the owner under which the definition in a quote are created.
3781+
*
3782+
* Usage:
3783+
* ```scala
3784+
* new TreeMap:
3785+
* override def transformTerm(tree: Term)(owner: Symbol): Term =
3786+
* tree match
3787+
* case tree: Ident =>
3788+
* given Quotes = owner.asQuotes
3789+
* // Definitions contained in the quote will be owned by `owner`.
3790+
* // No need to use `changeOwner` in this case.
3791+
* '{ val x = ???; x }.asTerm
3792+
* ```
3793+
* @syntax markdown
3794+
*/
3795+
@experimental
3796+
def asQuotes: Nested
3797+
37773798
end extension
37783799
}
37793800

project/MiMaFilters.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ import com.typesafe.tools.mima.core.ProblemFilters._
44

55
object MiMaFilters {
66
val Library: Seq[ProblemFilter] = Seq(
7+
// New APIs that will be introduced in 3.2.0
8+
exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.asQuotes"),
9+
exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.asQuotes"),
710
)
811
}

tests/pos-macros/i13571/Macro_1.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import scala.quoted.*
2+
3+
inline def checked2[A](inline n: A): A =
4+
${ checkedImpl2[A]('{n}) }
5+
6+
private def checkedImpl2[A](n: Expr[A])(using Quotes, Type[A]): Expr[A] =
7+
import quotes.reflect.*
8+
val tree: Term = n.asTerm
9+
val acc = new TreeMap:
10+
override def transformTerm(tree: Term)(owner: Symbol): Term =
11+
tree match
12+
case Apply(Select(x, "*"), List(y)) =>
13+
given Quotes = owner.asQuotes
14+
'{
15+
val xt = ${x.asExprOf[Long]}
16+
xt
17+
}.asTerm
18+
case _ =>
19+
super.transformTerm(tree)(owner)
20+
acc.transformTerm(tree)(Symbol.spliceOwner).asExprOf[A]

tests/pos-macros/i13571/Test_2.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def test = {
2+
val u = 3L
3+
checked2(List(1L, 2L).map { k =>
4+
u * 2L
5+
})
6+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import scala.quoted.*
2+
3+
inline def checked2[A](inline n: A): A =
4+
${ checkedImpl2[A]('{n}) }
5+
6+
private def checkedImpl2[A](n: Expr[A])(using Quotes, Type[A]): Expr[A] =
7+
import quotes.reflect.*
8+
val tree: Term = n.asTerm
9+
val acc = new TreeMap:
10+
override def transformTerm(tree: Term)(owner: Symbol): Term =
11+
tree match
12+
case Apply(Select(x, "*"), List(y)) =>
13+
bindLong(x.asExprOf[Long])(using owner.asQuotes).asTerm
14+
case _ =>
15+
super.transformTerm(tree)(owner)
16+
acc.transformTerm(tree)(Symbol.spliceOwner).asExprOf[A]
17+
18+
def bindLong(expr: Expr[Long])(using Quotes): Expr[Long] =
19+
'{
20+
val xt = $expr
21+
xt
22+
}

tests/pos-macros/i13571b/Test_2.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
def test = {
2+
val u = 3L
3+
checked2(List(1L, 2L).map { k =>
4+
u * 2L
5+
})
6+
}

0 commit comments

Comments
 (0)