Skip to content

Commit f88d996

Browse files
Merge pull request #13652 from dotty-staging/add-quotes-change-owner
Allow nested Quotes with a different owners
2 parents 981a34e + e0989a0 commit f88d996

File tree

10 files changed

+190
-4
lines changed

10 files changed

+190
-4
lines changed

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

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

26282628
def show(using printer: Printer[Symbol]): String = printer.show(self)
26292629

2630+
def asQuotes: Nested = new QuotesImpl(using ctx.withOwner(self))
2631+
26302632
end extension
26312633

26322634
private def appliedTypeRef(sym: Symbol): TypeRepr =
@@ -2918,6 +2920,10 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler
29182920
|which has the AST representation
29192921
|${Printer.TreeStructure.show(tree)}
29202922
|
2923+
|
2924+
|
2925+
|Tip: The owner of a tree can be changed using method `Tree.changeOwner`.
2926+
|Tip: The default owner of definitions created in quotes can be changed using method `Symbol.asQuotes`.
29212927
|""".stripMargin)
29222928
case _ => traverseChildren(t)
29232929
}.traverse(tree)

library/src/scala/quoted/Quotes.scala

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,8 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
525525
* `Some` containing the implementation of the method. Returns `None` the method has no implementation.
526526
* Any definition directly inside the implementation should have `symbol` as owner.
527527
*
528+
* Use `Symbol.asQuotes` to create the rhs using quoted code.
529+
*
528530
* See also: `Tree.changeOwner`
529531
*/
530532
def apply(symbol: Symbol, rhsFn: List[List[Tree]] => Option[Term]): DefDef
@@ -602,20 +604,53 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
602604
* Returns `None` the method has no implementation.
603605
* Any definition directly inside the implementation should have `symbol` as owner.
604606
*
607+
* Use `Symbol.asQuotes` to create the rhs using quoted code.
608+
*
605609
* See also: `Tree.changeOwner`
606610
*/
607611
def apply(symbol: Symbol, rhs: Option[Term]): ValDef
608612
def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef
609613
def unapply(vdef: ValDef): (String, TypeTree, Option[Term])
610614

611-
/** Creates a block `{ val <name> = <rhs: Term>; <body(x): Term> }` */
615+
/** Creates a block `{ val <name> = <rhs: Term>; <body(x): Term> }`
616+
*
617+
* Usage:
618+
* ```
619+
* ValDef.let(owner, "x", rhs1) { x =>
620+
* ValDef.let(x.symbol.owner, "y", rhs2) { y =>
621+
* // use `x` and `y`
622+
* }
623+
* }
624+
* ```
625+
* @syntax markdown
626+
*/
612627
def let(owner: Symbol, name: String, rhs: Term)(body: Ref => Term): Term
613628

614-
/** Creates a block `{ val x = <rhs: Term>; <body(x): Term> }` */
629+
/** Creates a block `{ val x = <rhs: Term>; <body(x): Term> }`
630+
*
631+
* Usage:
632+
* ```
633+
* ValDef.let(owner, rhs1) { x =>
634+
* ValDef.let(owner, rhs2) { y =>
635+
* // use `x` and `y`
636+
* }
637+
* }
638+
* ```
639+
* @syntax markdown
640+
*/
615641
def let(owner: Symbol, rhs: Term)(body: Ref => Term): Term =
616642
let(owner, "x", rhs)(body)
617643

618-
/** Creates a block `{ val x1 = <terms(0): Term>; ...; val xn = <terms(n-1): Term>; <body(List(x1, ..., xn)): Term> }` */
644+
/** Creates a block `{ val x1 = <terms(0): Term>; ...; val xn = <terms(n-1): Term>; <body(List(x1, ..., xn)): Term> }`
645+
*
646+
* Usage:
647+
* ```
648+
* ValDef.let(owner, rhsList) { xs =>
649+
* ...
650+
* }
651+
* ```
652+
* @syntax markdown
653+
*/
619654
def let(owner: Symbol, terms: List[Term])(body: List[Ref] => Term): Term
620655
}
621656

@@ -1341,6 +1376,28 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
13411376
* ```scala sc:nocompile
13421377
* Block((DefDef(_, _, params :: Nil, _, Some(rhsFn(meth, paramRefs)))) :: Nil, Closure(meth, _))
13431378
* ```
1379+
*
1380+
* Usage:
1381+
* ```
1382+
* val mtpe = MethodType(List("arg1"))(_ => List(TypeRepr.of[Int]), _ => TypeRepr.of[Int])
1383+
* Lambda(owner, mtpe, {
1384+
* case (methSym, List(arg1: Term)) =>
1385+
* ValDef.let(methSym, f(arg1)) { ... }
1386+
* }
1387+
* )
1388+
* ```
1389+
*
1390+
* Usage with quotes:
1391+
* ```
1392+
* val mtpe = MethodType(List("arg1"))(_ => List(TypeRepr.of[Int]), _ => TypeRepr.of[Int])
1393+
* Lambda(owner, mtpe, {
1394+
* case (methSym, List(arg1: Term)) =>
1395+
* given Quotes = methSym.asQuotes
1396+
* '{ ... }
1397+
* }
1398+
* )
1399+
* ```
1400+
*
13441401
* @param owner: owner of the generated `meth` symbol
13451402
* @param tpe: Type of the definition
13461403
* @param rhsFn: Function that receives the `meth` symbol and the a list of references to the `params`
@@ -3817,6 +3874,35 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
38173874

38183875
/** Case class or case object children of a sealed trait or cases of an `enum`. */
38193876
def children: List[Symbol]
3877+
3878+
/** Returns a nested quote with this symbol as splice owner (`Symbol.spliceOwner`).
3879+
*
3880+
* Changes the owner under which the definition in a quote are created.
3881+
*
3882+
* Usages:
3883+
* ```scala
3884+
* def rhsExpr(using Quotes): Expr[Unit] = '{ val y = ???; (y, y) }
3885+
* def aValDef(using Quotes)(owner: Symbol) =
3886+
* val sym = Symbol.newVal(owner, "x", TypeRepr.of[Unit], Flags.EmptyFlags, Symbol.noSymbol)
3887+
* val rhs = rhsExpr(using sym.asQuotes).asTerm
3888+
* ValDef(sym, Some(rhs))
3889+
* ```
3890+
*
3891+
* ```scala
3892+
* new TreeMap:
3893+
* override def transformTerm(tree: Term)(owner: Symbol): Term =
3894+
* tree match
3895+
* case tree: Ident =>
3896+
* given Quotes = owner.asQuotes
3897+
* // Definitions contained in the quote will be owned by `owner`.
3898+
* // No need to use `changeOwner` in this case.
3899+
* '{ val x = ???; x }.asTerm
3900+
* ```
3901+
* @syntax markdown
3902+
*/
3903+
@experimental
3904+
def asQuotes: Nested
3905+
38203906
end extension
38213907
}
38223908

@@ -4497,6 +4583,9 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching =>
44974583
* override def transformTree(tree: Tree)(owner: Symbol): Tree = ???
44984584
* }
44994585
* ```
4586+
*
4587+
* Use `Symbol.asQuotes` to create quotes with the correct owner within the TreeMap.
4588+
*
45004589
* @syntax markdown
45014590
*/
45024591
trait TreeMap:

project/MiMaFilters.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ object MiMaFilters {
55
val Library: Seq[ProblemFilter] = Seq(
66
// Experimental APIs that can be added in 3.2.0
77
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.runtime.Tuples.append"),
8+
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.asQuotes"),
9+
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#SymbolMethods.asQuotes"),
810
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeReprMethods.substituteTypes"),
911
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeReprMethods.substituteTypes"),
1012
ProblemFilters.exclude[ReversedMissingMethodProblem]("scala.quoted.Quotes#reflectModule#TypeReprMethods.typeArgs"),

scaladoc/src/dotty/tools/scaladoc/snippets/SnippetCompiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class SnippetCompiler(
8585

8686
private def additionalMessages(wrappedSnippet: WrappedSnippet, arg: SnippetCompilerArg, sourceFile: SourceFile, context: Context): Seq[SnippetCompilerMessage] = {
8787
Option.when(arg.flag == SCFlags.Fail && !context.reporter.hasErrors)(
88-
SnippetCompilerMessage(None, "Snippet should not compile but compiled succesfully", MessageLevel.Error)
88+
SnippetCompilerMessage(None, "Snippet should not compile but compiled successfully", MessageLevel.Error)
8989
).toList
9090
}
9191

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+
}

tests/pos-macros/i13922/Macro_1.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import scala.quoted.*
2+
3+
inline def optPrettyPrinter[T]: Option[T] => Option[T] =
4+
${ optPrettyPrinterImpl[T] }
5+
6+
private def optPrettyPrinterImpl[T: Type](using Quotes): Expr[Option[T] => Option[T]] = {
7+
import quotes.reflect.*
8+
9+
val tpe = TypeRepr.of[T]
10+
11+
val fn = Lambda(
12+
Symbol.spliceOwner,
13+
MethodType(List("macroVal"))(
14+
_ => List(tpe),
15+
_ => tpe
16+
),
17+
{
18+
case (m, List(arg: Term)) =>
19+
given Quotes = m.asQuotes
20+
ValDef.let(m, "v", arg) { v =>
21+
'{
22+
val vv = ${ v.asExprOf[T] }
23+
println("v=" + vv.toString())
24+
vv
25+
}.asTerm
26+
}
27+
28+
case _ =>
29+
report.errorAndAbort("Fails compile")
30+
}
31+
).asExprOf[T => T]
32+
33+
'{ (_: Option[T]).map(${ fn }) }
34+
}

tests/pos-macros/i13922/Test_2.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
def test = optPrettyPrinter(Some("foo"))

0 commit comments

Comments
 (0)