Skip to content

fix #9894: clean ownership when destructing lambda-expressions #10132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dotty.tools.dotc.quoted

import dotty.tools.dotc
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.tpd.TreeOps
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.core.Annotations
import dotty.tools.dotc.core.Contexts._
Expand Down Expand Up @@ -745,7 +746,9 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext:
def unapply(tree: Block): Option[(List[ValDef], Term)] = tree match {
case Block((ddef @ DefDef(_, _, params :: Nil, _, Some(body))) :: Nil, Closure(meth, _))
if ddef.symbol == meth.symbol =>
Some((params, body))
val cleanParams = params.map(_.changeOwner(meth.symbol,ctx.owner))
val cleanBody = body.changeOwner(meth.symbol,ctx.owner)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot rely on this body being used inside ctx.owner. We need to change adapt it when we construct the Lambda. We have some logic that knows how to do this. I created an alternative fix that does just that in #10142.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fix was helped me understand the source of the issue. Thank you.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, thanks

Some((cleanParams, cleanBody))
case _ => None
}
end Lambda
Expand Down
67 changes: 67 additions & 0 deletions tests/pos-macros/i9894/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package x

import scala.quoted._

trait CB[T]:
def map[S](f: T=>S): CB[S] = ???

class MyArr[A]:
def map1[B](f: A=>B):MyArr[B] = ???
def map1Out[B](f: A=> CB[B]): CB[MyArr[B]] = ???

def await[T](x:CB[T]):T = ???

object CBM:
def pure[T](t:T):CB[T] = ???

object X:

inline def process[T](inline f:T) = ${
processImpl[T]('f)
}

def processImpl[T:Type](f:Expr[T])(using qctx: QuoteContext):Expr[CB[T]] =
import qctx.reflect._

def transform(term:Term):Term =
term match
case ap@Apply(TypeApply(Select(obj,"map1"),targs),args) =>
val nArgs = args.map(x => shiftLambda(x))
val nSelect = Select.unique(obj, "map1Out")
Apply(TypeApply(nSelect,targs),nArgs)
//Apply.copy(ap)(TypeApply(nSelect,targs),nArgs)
case Apply(TypeApply(Ident("await"),targs),args) => args.head
case Apply(x,y) =>
Apply(x, y.map(transform))
case Block(stats, last) => Block(stats, transform(last))
case Inlined(x,List(),body) => transform(body)
case l@Literal(x) =>
'{ CBM.pure(${term.seal}) }.unseal
case other =>
throw RuntimeException(s"Not supported $other")

def shiftLambda(term:Term): Term =
term match
case lt@Lambda(params, body) =>
val paramTypes = params.map(_.tpt.tpe)
val paramNames = params.map(_.name)
val mt = MethodType(paramNames)(_ => paramTypes, _ => Type[CB].unseal.tpe.appliedTo(body.tpe.widen) )
val r = Lambda(mt, args => changeArgs(params,args,transform(body)) )
r
case _ =>
throw RuntimeException("lambda expected")

def changeArgs(oldArgs:List[Tree], newArgs:List[Tree], body:Term):Term =
val association: Map[Symbol, Term] = (oldArgs zip newArgs).foldLeft(Map.empty){
case (m, (oldParam, newParam: Term)) => m.updated(oldParam.symbol, newParam)
case (m, (oldParam, newParam: Tree)) => throw RuntimeException("Term expected")
}
val changes = new TreeMap() {
override def transformTerm(tree:Term)(using Context): Term =
tree match
case ident@Ident(name) => association.getOrElse(ident.symbol, super.transformTerm(tree))
case _ => super.transformTerm(tree)
}
changes.transformTerm(body)

transform(f.unseal).seal.cast[CB[T]]
15 changes: 15 additions & 0 deletions tests/pos-macros/i9894/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package x


object Main {

def main(args:Array[String]):Unit =
val arr = new MyArr[Int]()
val r = X.process{
arr.map1( zDebug =>
await(CBM.pure(1).map(a => zDebug + a))
)
}
println("r")

}