Skip to content

Commit 2b783dd

Browse files
authored
Merge pull request #10142 from dotty-staging/fix-#9894
Fix #9894: Fix owner when constructing a Lambda
2 parents 8e9ac75 + 68c6e43 commit 2b783dd

File tree

6 files changed

+124
-20
lines changed

6 files changed

+124
-20
lines changed

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

+4-17
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import scala.internal.quoted.PickledQuote
2323
import scala.quoted.QuoteContext
2424
import scala.collection.mutable
2525

26+
import QuoteUtils._
27+
2628
object PickledQuotes {
2729
import tpd._
2830

@@ -39,14 +41,14 @@ object PickledQuotes {
3941
def quotedExprToTree[T](expr: quoted.Expr[T])(using Context): Tree = {
4042
val expr1 = expr.asInstanceOf[scala.internal.quoted.Expr[Tree]]
4143
QuoteContextImpl.checkScopeId(expr1.scopeId)
42-
healOwner(expr1.tree)
44+
changeOwnerOfTree(expr1.tree, ctx.owner)
4345
}
4446

4547
/** Transform the expression into its fully spliced TypeTree */
4648
def quotedTypeToTree(tpe: quoted.Type[?])(using Context): Tree = {
4749
val tpe1 = tpe.asInstanceOf[scala.internal.quoted.Type[Tree]]
4850
QuoteContextImpl.checkScopeId(tpe1.scopeId)
49-
healOwner(tpe1.typeTree)
51+
changeOwnerOfTree(tpe1.typeTree, ctx.owner)
5052
}
5153

5254
/** Unpickle the tree contained in the TastyExpr */
@@ -195,19 +197,4 @@ object PickledQuotes {
195197
tree
196198
}
197199

198-
/** Make sure that the owner of this tree is `ctx.owner` */
199-
def healOwner(tree: Tree)(using Context): Tree = {
200-
val getCurrentOwner = new TreeAccumulator[Option[Symbol]] {
201-
def apply(x: Option[Symbol], tree: tpd.Tree)(using Context): Option[Symbol] =
202-
if (x.isDefined) x
203-
else tree match {
204-
case tree: DefTree => Some(tree.symbol.owner)
205-
case _ => foldOver(x, tree)
206-
}
207-
}
208-
getCurrentOwner(None, tree) match {
209-
case Some(owner) if owner != ctx.owner => tree.changeOwner(owner, ctx.owner)
210-
case _ => tree
211-
}
212-
}
213200
}

compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Flags._
1010
import dotty.tools.dotc.core.NameKinds
1111
import dotty.tools.dotc.core.StdNames._
1212
import dotty.tools.dotc.quoted.reflect._
13+
import dotty.tools.dotc.quoted.QuoteUtils._
1314
import dotty.tools.dotc.core.Decorators._
1415

1516
import scala.quoted.QuoteContext
@@ -741,7 +742,9 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext:
741742

742743
object Lambda extends LambdaModule:
743744
def apply(tpe: MethodType, rhsFn: List[Tree] => Tree): Block =
744-
tpd.Lambda(tpe, rhsFn)
745+
val meth = dotc.core.Symbols.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic | Method, tpe)
746+
tpd.Closure(meth, tss => changeOwnerOfTree(rhsFn(tss.head), meth))
747+
745748
def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match {
746749
case Block((ddef @ DefDef(_, _, params :: Nil, _, Some(body))) :: Nil, Closure(meth, _))
747750
if ddef.symbol == meth.symbol =>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import dotty.tools.dotc.ast.Trees._
4+
import dotty.tools.dotc.ast.tpd
5+
import dotty.tools.dotc.core.Contexts._
6+
import dotty.tools.dotc.core.Decorators._
7+
import dotty.tools.dotc.core.Symbols._
8+
9+
object QuoteUtils:
10+
import tpd._
11+
12+
/** Get the owner of a tree if it has one */
13+
def treeOwner(tree: Tree)(using Context): Option[Symbol] = {
14+
val getCurrentOwner = new TreeAccumulator[Option[Symbol]] {
15+
def apply(x: Option[Symbol], tree: tpd.Tree)(using Context): Option[Symbol] =
16+
if (x.isDefined) x
17+
else tree match {
18+
case tree: DefTree => Some(tree.symbol.owner)
19+
case _ => foldOver(x, tree)
20+
}
21+
}
22+
getCurrentOwner(None, tree)
23+
}
24+
25+
/** Changes the owner of the tree based on the current owner of the tree */
26+
def changeOwnerOfTree(tree: Tree, owner: Symbol)(using Context): Tree = {
27+
treeOwner(tree) match
28+
case Some(oldOwner) if oldOwner != owner => tree.changeOwner(oldOwner, owner)
29+
case _ => tree
30+
}
31+
32+
end QuoteUtils

compiler/src/dotty/tools/dotc/transform/Splicer.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -323,10 +323,10 @@ object Splicer {
323323
}
324324

325325
private def interpretQuote(tree: Tree)(implicit env: Env): Object =
326-
new scala.internal.quoted.Expr(Inlined(EmptyTree, Nil, PickledQuotes.healOwner(tree)).withSpan(tree.span), QuoteContextImpl.scopeId)
326+
new scala.internal.quoted.Expr(Inlined(EmptyTree, Nil, QuoteUtils.changeOwnerOfTree(tree, ctx.owner)).withSpan(tree.span), QuoteContextImpl.scopeId)
327327

328328
private def interpretTypeQuote(tree: Tree)(implicit env: Env): Object =
329-
new scala.internal.quoted.Type(PickledQuotes.healOwner(tree), QuoteContextImpl.scopeId)
329+
new scala.internal.quoted.Type(QuoteUtils.changeOwnerOfTree(tree, ctx.owner), QuoteContextImpl.scopeId)
330330

331331
private def interpretLiteral(value: Any)(implicit env: Env): Object =
332332
value.asInstanceOf[Object]

tests/pos-macros/i9894/Macro_1.scala

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package x
2+
3+
import scala.quoted._
4+
5+
trait CB[T]:
6+
def map[S](f: T=>S): CB[S] = ???
7+
8+
class MyArr[A]:
9+
def map1[B](f: A=>B):MyArr[B] = ???
10+
def map1Out[B](f: A=> CB[B]): CB[MyArr[B]] = ???
11+
12+
def await[T](x:CB[T]):T = ???
13+
14+
object CBM:
15+
def pure[T](t:T):CB[T] = ???
16+
17+
object X:
18+
19+
inline def process[T](inline f:T) = ${
20+
processImpl[T]('f)
21+
}
22+
23+
def processImpl[T:Type](f:Expr[T])(using qctx: QuoteContext):Expr[CB[T]] =
24+
import qctx.reflect._
25+
26+
def transform(term:Term):Term =
27+
term match
28+
case ap@Apply(TypeApply(Select(obj,"map1"),targs),args) =>
29+
val nArgs = args.map(x => shiftLambda(x))
30+
val nSelect = Select.unique(obj, "map1Out")
31+
Apply(TypeApply(nSelect,targs),nArgs)
32+
//Apply.copy(ap)(TypeApply(nSelect,targs),nArgs)
33+
case Apply(TypeApply(Ident("await"),targs),args) => args.head
34+
case Apply(x,y) =>
35+
Apply(x, y.map(transform))
36+
case Block(stats, last) => Block(stats, transform(last))
37+
case Inlined(x,List(),body) => transform(body)
38+
case l@Literal(x) =>
39+
'{ CBM.pure(${term.seal}) }.unseal
40+
case other =>
41+
throw RuntimeException(s"Not supported $other")
42+
43+
def shiftLambda(term:Term): Term =
44+
term match
45+
case lt@Lambda(params, body) =>
46+
val paramTypes = params.map(_.tpt.tpe)
47+
val paramNames = params.map(_.name)
48+
val mt = MethodType(paramNames)(_ => paramTypes, _ => Type[CB].unseal.tpe.appliedTo(body.tpe.widen) )
49+
val r = Lambda(mt, args => changeArgs(params,args,transform(body)) )
50+
r
51+
case _ =>
52+
throw RuntimeException("lambda expected")
53+
54+
def changeArgs(oldArgs:List[Tree], newArgs:List[Tree], body:Term):Term =
55+
val association: Map[Symbol, Term] = (oldArgs zip newArgs).foldLeft(Map.empty){
56+
case (m, (oldParam, newParam: Term)) => m.updated(oldParam.symbol, newParam)
57+
case (m, (oldParam, newParam: Tree)) => throw RuntimeException("Term expected")
58+
}
59+
val changes = new TreeMap() {
60+
override def transformTerm(tree:Term)(using Context): Term =
61+
tree match
62+
case ident@Ident(name) => association.getOrElse(ident.symbol, super.transformTerm(tree))
63+
case _ => super.transformTerm(tree)
64+
}
65+
changes.transformTerm(body)
66+
67+
transform(f.unseal).seal.cast[CB[T]]

tests/pos-macros/i9894/Test_2.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package x
2+
3+
4+
object Main {
5+
6+
def main(args:Array[String]):Unit =
7+
val arr = new MyArr[Int]()
8+
val r = X.process{
9+
arr.map1( zDebug =>
10+
await(CBM.pure(1).map(a => zDebug + a))
11+
)
12+
}
13+
println("r")
14+
15+
}

0 commit comments

Comments
 (0)