diff options
-rw-r--r-- | src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 7 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Inliner.scala | 45 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 2 | ||||
-rw-r--r-- | tests/run/inlinedAssign.scala | 9 |
5 files changed, 40 insertions, 27 deletions
diff --git a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index b148cced5..1b8434129 100644 --- a/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -489,8 +489,11 @@ class TreeUnpickler(reader: TastyReader, tastyName: TastyName.Table, posUnpickle forkAt(templateStart).indexTemplateParams()(localContext(sym)) } else if (annots.exists(_.symbol == defn.InlineAnnot)) { - val inlineCtx = localContext(sym).addMode(Mode.ReadPositions) - Inliner.registerInlineInfo(sym, implicit ctx => forkAt(rhsStart).readTerm())(inlineCtx) + Inliner.registerInlineInfo( + sym, + implicit ctx => forkAt(rhsStart).readTerm(), + implicit ctx => localContext(sym).addMode(Mode.ReadPositions)) + // Previous line avoids space leaks because it does not capture the current context. } goto(start) sym diff --git a/src/dotty/tools/dotc/typer/Inliner.scala b/src/dotty/tools/dotc/typer/Inliner.scala index c11001363..44946c37b 100644 --- a/src/dotty/tools/dotc/typer/Inliner.scala +++ b/src/dotty/tools/dotc/typer/Inliner.scala @@ -42,11 +42,16 @@ object Inliner { * * @param treeExpr A function that computes the tree to be inlined, given a context * This tree may still refer to non-public members. - * @param inlineCtx The context in which to compute the tree. This context needs + * @param inlineCtxFn A function that maps the current context to the context in + * which to compute the tree. The resulting context needs * to have the inlined method as owner. + * + * The reason to use a function rather than a fixed context here + * is to avoid space leaks. InlineInfos can survive multiple runs + * because they might be created as part of an unpickled method + * and consumed only in a future run (or never). */ - private final class InlineInfo(treeExpr: Context => Tree, var inlineCtx: Context) { - private val inlineMethod = inlineCtx.owner + private final class InlineInfo(treeExpr: Context => Tree, var inlineCtxFn: Context => Context) { private val myAccessors = new mutable.ListBuffer[MemberDef] private var myBody: Tree = _ private var evaluated = false @@ -56,7 +61,8 @@ object Inliner { * This means that references to a provate type will lead to typing failures * on the code when it is inlined. Less than ideal, but hard to do better (see below). */ - private def prepareForInline = new TreeMap { + private def prepareForInline(inlineCtx: Context) = new TreeMap { + val inlineMethod = inlineCtx.owner /** A definition needs an accessor if it is private, protected, or qualified private */ def needsAccessor(sym: Symbol)(implicit ctx: Context) = @@ -182,19 +188,22 @@ object Inliner { def isEvaluated = evaluated private def ensureEvaluated()(implicit ctx: Context) = - try if (!evaluated) { + if (!evaluated) { evaluated = true // important to set early to prevent overwrites by attachInlineInfo in typedDefDef - myBody = treeExpr(inlineCtx) - myBody = prepareForInline.transform(myBody)(inlineCtx) - inlining.println(i"inlinable body of ${inlineCtx.owner} = $myBody") - inlineCtx = null // null out to avoid space leaks + val inlineCtx = inlineCtxFn(ctx) + inlineCtxFn = null // null out to avoid space leaks + try { + myBody = treeExpr(inlineCtx) + myBody = prepareForInline(inlineCtx).transform(myBody)(inlineCtx) + inlining.println(i"inlinable body of ${inlineCtx.owner} = $myBody") + } + catch { + case ex: AssertionError => + println(i"failure while expanding ${inlineCtx.owner}") + throw ex + } } else assert(myBody != null) - catch { - case ex: AssertionError => - println(i"failure while expanding $inlineMethod") - throw ex - } /** The body to inline */ def body(implicit ctx: Context): Tree = { @@ -224,16 +233,18 @@ object Inliner { * @param sym The symbol denotatioon of the inline method for which info is registered * @param treeExpr A function that computes the tree to be inlined, given a context * This tree may still refer to non-public members. - * @param ctx The current context is the one in which the tree is computed. It needs + * @param inlineCtxFn A function that maps the current context to the context in + * which to compute the tree. The resulting context needs * to have the inlined method as owner. */ - def registerInlineInfo(sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = { + def registerInlineInfo( + sym: SymDenotation, treeExpr: Context => Tree, inlineCtxFn: Context => Context)(implicit ctx: Context): Unit = { val inlineAnnot = sym.unforcedAnnotation(defn.InlineAnnot).get inlineAnnot.tree.getAttachment(InlineInfo) match { case Some(inlineInfo) if inlineInfo.isEvaluated => // keep existing attachment case _ => if (!ctx.isAfterTyper) - inlineAnnot.tree.putAttachment(InlineInfo, new InlineInfo(treeExpr, ctx)) + inlineAnnot.tree.putAttachment(InlineInfo, new InlineInfo(treeExpr, inlineCtxFn)) } } diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 2e714ab6d..9ba6a8963 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -588,8 +588,8 @@ class Namer { typer: Typer => case original: untpd.DefDef => Inliner.registerInlineInfo( denot, - implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs - )(localContext(denot.symbol)) + implicit ctx => typedAheadExpr(original).asInstanceOf[tpd.DefDef].rhs, + _ => localContext(denot.symbol)) case _ => } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 58fb47f2c..85102841e 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1176,7 +1176,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // Overwrite inline body to make sure it is not evaluated twice if (sym.hasAnnotation(defn.InlineAnnot)) - Inliner.registerInlineInfo(sym, ctx => rhs1) + Inliner.registerInlineInfo(sym, _ => rhs1, _ => ctx) if (sym.isAnonymousFunction) { // If we define an anonymous function, make sure the return type does not diff --git a/tests/run/inlinedAssign.scala b/tests/run/inlinedAssign.scala index bb81aea26..f405f5073 100644 --- a/tests/run/inlinedAssign.scala +++ b/tests/run/inlinedAssign.scala @@ -1,7 +1,6 @@ object Test { - @`inline` - def swap[T](x: T, x_= : T => Unit, y: T, y_= : T => Unit) = { + inline def swap[T](x: T, x_= : T => Unit, y: T, y_= : T => Unit) = { val t = x x_=(y) y_=(t) @@ -10,9 +9,9 @@ object Test { def main(args: Array[String]) = { var x = 1 var y = 2 - @`inline` def setX(z: Int) = x = z - @`inline` def setY(z: Int) = y = z - swap[Int](x, setX, y, setY) + inline def setX(z: Int) = x = z + inline def setY(z: Int) = y = z + swap(x, setX, y, setY) assert(x == 2 && y == 1) } } |