aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/typer
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2016-09-12 18:53:53 +0200
committerMartin Odersky <odersky@gmail.com>2016-10-02 16:12:28 +0200
commit4de907a313e9b85058cd9611116a1cbcf2bd3a4f (patch)
tree6992c2247f9a7e0b3b623e36da8ccfb1e2de461c /src/dotty/tools/dotc/typer
parent7537cfcfa5ccf47d400e2841b9bc4ff2cac1eada (diff)
downloaddotty-4de907a313e9b85058cd9611116a1cbcf2bd3a4f.tar.gz
dotty-4de907a313e9b85058cd9611116a1cbcf2bd3a4f.tar.bz2
dotty-4de907a313e9b85058cd9611116a1cbcf2bd3a4f.zip
Always use implicit context at the current period
An implicit method might be unpickled in one run and the implicit body might be selected first in a subsequent run. In that case the inlined code was read with the original context, but that context needs to run at the current period. This resulted in denotation out of date errors in bringForward. Another problem with this design was space leaks: An context might survive multiple runs as part of an ImplicitInfo of an unpickled method. The new design avoids both problems. Implicit contexts are always up to date and leaks are avoided.
Diffstat (limited to 'src/dotty/tools/dotc/typer')
-rw-r--r--src/dotty/tools/dotc/typer/Inliner.scala45
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala4
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala2
3 files changed, 31 insertions, 20 deletions
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