Skip to content

Commit cd34a2a

Browse files
committed
Improve handling of owners in reflection API
* Add `changeOwner` * Add owner and symbol to `Lambda.apply` * Add owner to etaExpand for the creation of the closure * Ycheck the owners durring macro expansion * Fix #10151 * Fix #10211
1 parent cd15c99 commit cd34a2a

File tree

12 files changed

+111
-34
lines changed

12 files changed

+111
-34
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

+73-18
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

@@ -357,15 +365,15 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
357365
def tpe: TypeRepr = self.tpe
358366
def underlyingArgument: Term = new tpd.TreeOps(self).underlyingArgument
359367
def underlying: Term = new tpd.TreeOps(self).underlying
360-
def etaExpand: Term = self.tpe.widen match {
368+
def etaExpand(owner: Symbol): Term = self.tpe.widen match {
361369
case mtpe: Types.MethodType if !mtpe.isParamDependent =>
362370
val closureResType = mtpe.resType match {
363371
case t: Types.MethodType => t.toFunctionType()
364372
case t => t
365373
}
366374
val closureTpe = Types.MethodType(mtpe.paramNames, mtpe.paramInfos, closureResType)
367-
val closureMethod = dotc.core.Symbols.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, closureTpe)
368-
tpd.Closure(closureMethod, tss => new tpd.TreeOps(self).appliedToArgs(tss.head).etaExpand)
375+
val closureMethod = dotc.core.Symbols.newSymbol(owner, nme.ANON_FUN, Synthetic | Method, closureTpe)
376+
tpd.Closure(closureMethod, tss => new tpd.TreeOps(self).appliedToArgs(tss.head).etaExpand(closureMethod))
369377
case _ => self
370378
}
371379

@@ -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

+25-3
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 */
@@ -477,7 +483,7 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
477483
def underlying: Term
478484

479485
/** Converts a partally applied term into a lambda expression */
480-
def etaExpand: Term
486+
def etaExpand(owner: Symbol): Term
481487

482488
/** A unary apply node with given argument: `tree(arg)` */
483489
def appliedTo(arg: Term): Term
@@ -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")

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ object UnsafeExpr {
7070
}
7171
private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.reflect.ValDef], Expr[R]) = {
7272
import qctx.reflect._
73-
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = Term.of(f).etaExpand
73+
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = Term.of(f).etaExpand(Symbol.currentOwner)
7474
(params, body.asExpr.asInstanceOf[Expr[R]])
7575
}
7676

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ object UnsafeExpr {
8282
}
8383
private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.reflect.ValDef], Expr[R]) = {
8484
import qctx.reflect._
85-
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = Term.of(f).etaExpand
85+
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = Term.of(f).etaExpand(Symbol.currentOwner)
8686
(params, body.asExpr.asInstanceOf[Expr[R]])
8787
}
8888

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ object UnsafeExpr {
3434
}
3535
private def paramsAndBody[R](using qctx: QuoteContext)(f: Expr[Any]): (List[qctx.reflect.ValDef], Expr[R]) = {
3636
import qctx.reflect._
37-
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = Term.of(f).etaExpand
37+
val Block(List(DefDef("$anonfun", Nil, List(params), _, Some(body))), Closure(Ident("$anonfun"), None)) = Term.of(f).etaExpand(Symbol.currentOwner)
3838
(params, body.asExpr.asInstanceOf[Expr[R]])
3939
}
4040

tests/run-macros/tasty-seal-method/quoted_1.scala

+8-8
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ object Asserts {
1414
fn.tpe.widen match {
1515
case _: MethodType =>
1616
args.size match {
17-
case 0 => Expr.betaReduce('{ ${fn.etaExpand.asExprOf[() => Int]}() })
18-
case 1 => Expr.betaReduce('{ ${fn.etaExpand.asExprOf[Int => Int]}(0) })
19-
case 2 => Expr.betaReduce('{ ${fn.etaExpand.asExprOf[(Int, Int) => Int]}(0, 0) })
20-
case 3 => Expr.betaReduce('{ ${fn.etaExpand.asExprOf[(Int, Int, Int) => Int]}(0, 0, 0) })
17+
case 0 => Expr.betaReduce('{ ${fn.etaExpand(Symbol.currentOwner).asExprOf[() => Int]}() })
18+
case 1 => Expr.betaReduce('{ ${fn.etaExpand(Symbol.currentOwner).asExprOf[Int => Int]}(0) })
19+
case 2 => Expr.betaReduce('{ ${fn.etaExpand(Symbol.currentOwner).asExprOf[(Int, Int) => Int]}(0, 0) })
20+
case 3 => Expr.betaReduce('{ ${fn.etaExpand(Symbol.currentOwner).asExprOf[(Int, Int, Int) => Int]}(0, 0, 0) })
2121
}
2222
}
2323
case _ => x
@@ -35,10 +35,10 @@ object Asserts {
3535
case Apply(fn, args) =>
3636
val pre = rec(fn)
3737
args.size match {
38-
case 0 => Term.of(Expr.betaReduce('{ ${pre.etaExpand.asExprOf[() => Any]}() }))
39-
case 1 => Term.of(Expr.betaReduce('{ ${pre.etaExpand.asExprOf[Int => Any]}(0) }))
40-
case 2 => Term.of(Expr.betaReduce('{ ${pre.etaExpand.asExprOf[(Int, Int) => Any]}(0, 0) }))
41-
case 3 => Term.of(Expr.betaReduce('{ ${pre.etaExpand.asExprOf[(Int, Int, Int) => Any]}(0, 0, 0) }))
38+
case 0 => Term.of(Expr.betaReduce('{ ${pre.etaExpand(Symbol.currentOwner).asExprOf[() => Any]}() }))
39+
case 1 => Term.of(Expr.betaReduce('{ ${pre.etaExpand(Symbol.currentOwner).asExprOf[Int => Any]}(0) }))
40+
case 2 => Term.of(Expr.betaReduce('{ ${pre.etaExpand(Symbol.currentOwner).asExprOf[(Int, Int) => Any]}(0, 0) }))
41+
case 3 => Term.of(Expr.betaReduce('{ ${pre.etaExpand(Symbol.currentOwner).asExprOf[(Int, Int, Int) => Any]}(0, 0, 0) }))
4242
}
4343
case _ => term
4444
}

0 commit comments

Comments
 (0)