Skip to content

Commit 7c6e94b

Browse files
committed
Improve handling of owners in reflection API
* Add `changeOwner` * Add symbol to `Lambda.apply` * Ycheck the owners durring macro expansion * Fix #10151 * Fix #10211
1 parent cd15c99 commit 7c6e94b

File tree

8 files changed

+96
-19
lines changed

8 files changed

+96
-19
lines changed

compiler/src/scala/quoted/internal/impl/Matcher.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ object Matcher {
211211
}
212212
val argTypes = args.map(x => x.tpe.widenTermRefExpr)
213213
val resType = pattern.tpe
214-
val res = Lambda(MethodType(names)(_ => argTypes, _ => resType), bodyFn)
214+
val res = Lambda(Symbol.currentOwner, MethodType(names)(_ => argTypes, _ => resType), (meth, x) => bodyFn(x).changeOwner(meth))
215215
matched(res.asExpr)
216216

217217
//

compiler/src/scala/quoted/internal/impl/QuoteContextImpl.scala

+70-15
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ object QuoteContextImpl {
4545

4646
class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickler, QuoteMatching:
4747

48+
private val yCheck: Boolean =
49+
ctx.settings.Ycheck.value(using ctx).exists(x => x == "all" || x == "macros")
50+
4851
extension [T](self: scala.quoted.Expr[T]):
4952
def show: String =
5053
reflect.TreeMethodsImpl.show(reflect.Term.of(self))
@@ -118,6 +121,11 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
118121
QuoteContextImpl.this.asExprOf[T](self.asExpr)(using tp)
119122
end extension
120123

124+
extension [ThisTree <: Tree](self: ThisTree):
125+
def changeOwner(newOwner: Symbol): ThisTree =
126+
tpd.TreeOps(self).changeNonLocalOwners(newOwner).asInstanceOf[ThisTree]
127+
end extension
128+
121129
end TreeMethodsImpl
122130

123131
type PackageClause = tpd.PackageDef
@@ -238,9 +246,9 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
238246

239247
object DefDef extends DefDefModule:
240248
def apply(symbol: Symbol, rhsFn: List[TypeRepr] => List[List[Term]] => Option[Term]): DefDef =
241-
withDefaultPos(tpd.polyDefDef(symbol.asTerm, tparams => vparamss => rhsFn(tparams)(vparamss).getOrElse(tpd.EmptyTree)))
249+
withDefaultPos(tpd.polyDefDef(symbol.asTerm, tparams => vparamss => yCheckedOwners(rhsFn(tparams)(vparamss), symbol).getOrElse(tpd.EmptyTree)))
242250
def copy(original: Tree)(name: String, typeParams: List[TypeDef], paramss: List[List[ValDef]], tpt: TypeTree, rhs: Option[Term]): DefDef =
243-
tpd.cpy.DefDef(original)(name.toTermName, typeParams, paramss, tpt, rhs.getOrElse(tpd.EmptyTree))
251+
tpd.cpy.DefDef(original)(name.toTermName, typeParams, paramss, tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree))
244252
def unapply(ddef: DefDef): Option[(String, List[TypeDef], List[List[ValDef]], TypeTree, Option[Term])] =
245253
Some((ddef.name.toString, ddef.typeParams, ddef.paramss, ddef.tpt, optional(ddef.rhs)))
246254
end DefDef
@@ -264,9 +272,9 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
264272

265273
object ValDef extends ValDefModule:
266274
def apply(symbol: Symbol, rhs: Option[Term]): ValDef =
267-
tpd.ValDef(symbol.asTerm, rhs.getOrElse(tpd.EmptyTree))
275+
tpd.ValDef(symbol.asTerm, yCheckedOwners(rhs, symbol).getOrElse(tpd.EmptyTree))
268276
def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef =
269-
tpd.cpy.ValDef(original)(name.toTermName, tpt, rhs.getOrElse(tpd.EmptyTree))
277+
tpd.cpy.ValDef(original)(name.toTermName, tpt, yCheckedOwners(rhs, original.symbol).getOrElse(tpd.EmptyTree))
270278
def unapply(vdef: ValDef): Option[(String, TypeTree, Option[Term])] =
271279
Some((vdef.name.toString, vdef.tpt, optional(vdef.rhs)))
272280

@@ -727,9 +735,9 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
727735
end ClosureMethodsImpl
728736

729737
object Lambda extends LambdaModule:
730-
def apply(tpe: MethodType, rhsFn: List[Tree] => Tree): Block =
731-
val meth = dotc.core.Symbols.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, tpe)
732-
tpd.Closure(meth, tss => changeOwnerOfTree(rhsFn(tss.head), meth))
738+
def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block =
739+
val meth = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, tpe)
740+
tpd.Closure(meth, tss => yCheckedOwners(rhsFn(meth, tss.head), meth))
733741

734742
def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match {
735743
case Block((ddef @ DefDef(_, _, params :: Nil, _, Some(body))) :: Nil, Closure(meth, _))
@@ -2201,14 +2209,14 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
22012209
def requiredModule(path: String): Symbol = dotc.core.Symbols.requiredModule(path)
22022210
def requiredMethod(path: String): Symbol = dotc.core.Symbols.requiredMethod(path)
22032211
def classSymbol(fullName: String): Symbol = dotc.core.Symbols.requiredClass(fullName)
2204-
def newMethod(parent: Symbol, name: String, tpe: TypeRepr): Symbol =
2205-
newMethod(parent, name, tpe, Flags.EmptyFlags, noSymbol)
2206-
def newMethod(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
2207-
dotc.core.Symbols.newSymbol(parent, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin)
2208-
def newVal(parent: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
2209-
dotc.core.Symbols.newSymbol(parent, name.toTermName, flags, tpe, privateWithin)
2210-
def newBind(parent: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol =
2211-
dotc.core.Symbols.newSymbol(parent, name.toTermName, flags | Case, tpe)
2212+
def newMethod(owner: Symbol, name: String, tpe: TypeRepr): Symbol =
2213+
newMethod(owner, name, tpe, Flags.EmptyFlags, noSymbol)
2214+
def newMethod(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
2215+
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | dotc.core.Flags.Method, tpe, privateWithin)
2216+
def newVal(owner: Symbol, name: String, tpe: TypeRepr, flags: Flags, privateWithin: Symbol): Symbol =
2217+
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags, tpe, privateWithin)
2218+
def newBind(owner: Symbol, name: String, flags: Flags, tpe: TypeRepr): Symbol =
2219+
dotc.core.Symbols.newSymbol(owner, name.toTermName, flags | Case, tpe)
22122220
def noSymbol: Symbol = dotc.core.Symbols.NoSymbol
22132221
end Symbol
22142222

@@ -2541,6 +2549,53 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
25412549
private def withDefaultPos[T <: Tree](fn: Context ?=> T): T =
25422550
fn(using ctx.withSource(Position.ofMacroExpansion.source)).withSpan(Position.ofMacroExpansion.span)
25432551

2552+
/** Checks that all definitions in this tree have the expected owner.
2553+
* Nested definitions are ignored and assumed to be correct by construction.
2554+
*/
2555+
private def yCheckedOwners(tree: Option[Tree], owner: Symbol): tree.type =
2556+
if yCheck then
2557+
tree match
2558+
case Some(tree) =>
2559+
yCheckOwners(tree, owner)
2560+
case _ =>
2561+
tree
2562+
2563+
/** Checks that all definitions in this tree have the expected owner.
2564+
* Nested definitions are ignored and assumed to be correct by construction.
2565+
*/
2566+
private def yCheckedOwners(tree: Tree, owner: Symbol): tree.type =
2567+
if yCheck then
2568+
yCheckOwners(tree, owner)
2569+
tree
2570+
2571+
/** Checks that all definitions in this tree have the expected owner.
2572+
* Nested definitions are ignored and assumed to be correct by construction.
2573+
*/
2574+
private def yCheckOwners(tree: Tree, owner: Symbol): Unit =
2575+
new tpd.TreeTraverser {
2576+
def traverse(t: Tree)(using Context): Unit =
2577+
t match
2578+
case t: tpd.DefTree =>
2579+
val defOwner = t.symbol.owner
2580+
assert(defOwner == owner,
2581+
s"""Tree had an unexpected owner for ${t.symbol}
2582+
|Expected: $owner (${owner.fullName})
2583+
|But was: $defOwner (${defOwner.fullName})
2584+
|
2585+
|
2586+
|The code of the definition of ${t.symbol} is
2587+
|${TreeMethods.show(t)}
2588+
|
2589+
|which was found in the code
2590+
|${TreeMethods.show(tree)}
2591+
|
2592+
|which has the AST representation
2593+
|${TreeMethods.showExtractors(tree)}
2594+
|
2595+
|""".stripMargin)
2596+
case _ => traverseChildren(t)
2597+
}.traverse(tree)
2598+
25442599
end reflect
25452600

25462601
def unpickleExpr[T](pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?]): scala.quoted.Expr[T] =

library/src/scala/quoted/QuoteContext.scala

+24-2
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
182182
// CONTEXTS //
183183
//////////////
184184

185-
/** Compilation context */
185+
/** Context containing information on the current owner */
186186
type Context <: AnyRef
187187

188188
/** Context of the macro expansion */
@@ -238,6 +238,12 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
238238
/** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */
239239
extension [T](self: Tree)
240240
def asExprOf(using scala.quoted.Type[T]): scala.quoted.Expr[T]
241+
242+
extension [ThisTree <: Tree](self: ThisTree):
243+
/** Changes the owner of the symbols in the tree */
244+
def changeOwner(newOwner: Symbol): ThisTree
245+
end extension
246+
241247
}
242248

243249
/** Tree representing a pacakage clause in the source code */
@@ -962,8 +968,24 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
962968
val Lambda: LambdaModule
963969

964970
trait LambdaModule { this: Lambda.type =>
971+
/** Matches a lambda definition of the form
972+
* ```
973+
* Block((DefDef(_, _, params :: Nil, _, Some(body))) :: Nil, Closure(meth, _))
974+
* ```
975+
* Extracts the parameter definitions and body.
976+
*
977+
*/
965978
def unapply(tree: Block): Option[(List[ValDef], Term)]
966-
def apply(tpe: MethodType, rhsFn: List[Tree] => Tree): Block
979+
980+
/** Generates a lambda with the given method type.
981+
* ```
982+
* Block((DefDef(_, _, params :: Nil, _, Some(rhsFn(meth, paramRefs)))) :: Nil, Closure(meth, _))
983+
* ```
984+
* @param owner: owner of the generated `meth` symbol
985+
* @param tpe: Type of the definition
986+
* @param rhsFn: Funtion that recieves the `meth` symbol and the a list of references to the `params`
987+
*/
988+
def apply(owner: Symbol, tpe: MethodType, rhsFn: (Symbol, List[Tree]) => Tree): Block
967989
}
968990

969991
given TypeTest[Tree, If] = IfTypeTest

tests/pos-macros/i10151/Macro_1.scala

Whitespace-only changes.

tests/pos-macros/i10151/Test_2.scala

Whitespace-only changes.

tests/pos-macros/i10211/Macro_1.scala

Whitespace-only changes.

tests/pos-macros/i10211/Test_2.scala

Whitespace-only changes.

tests/pos-macros/i9894/Macro_1.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ object X:
4646
val paramTypes = params.map(_.tpt.tpe)
4747
val paramNames = params.map(_.name)
4848
val mt = MethodType(paramNames)(_ => paramTypes, _ => TypeRepr.of[CB].appliedTo(body.tpe.widen) )
49-
val r = Lambda(mt, args => changeArgs(params,args,transform(body)) )
49+
val r = Lambda(Symbol.currentOwner, mt, (newMeth, args) => changeArgs(params,args,transform(body).changeOwner(newMeth)) )
5050
r
5151
case _ =>
5252
throw RuntimeException("lambda expected")

0 commit comments

Comments
 (0)