Skip to content

Commit 64d6536

Browse files
authored
Merge pull request #8620 from dotty-staging/annot-leak
LazyAnnotation: avoid unnecessary memory leaks
2 parents 0ae852e + 5101901 commit 64d6536

File tree

3 files changed

+60
-41
lines changed

3 files changed

+60
-41
lines changed

compiler/src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 55 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ object Annotations {
3535
def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] =
3636
for (ConstantType(c) <- argument(i) map (_.tpe)) yield c
3737

38+
/** The tree evaluaton is in progress. */
39+
def isEvaluating: Boolean = false
40+
41+
/** The tree evaluation has finished. */
3842
def isEvaluated: Boolean = true
3943

4044
def ensureCompleted(implicit ctx: Context): Unit = tree
@@ -48,16 +52,32 @@ object Annotations {
4852
}
4953

5054
abstract class LazyAnnotation extends Annotation {
51-
override def symbol(implicit ctx: Context): Symbol
52-
def complete(implicit ctx: Context): Tree
53-
54-
private var myTree: Tree = null
55-
def tree(implicit ctx: Context): Tree = {
56-
if (myTree == null) myTree = complete(ctx)
57-
myTree
58-
}
55+
protected var mySym: Symbol | (Context => Symbol)
56+
override def symbol(using ctx: Context): Symbol =
57+
assert(mySym != null)
58+
mySym match {
59+
case symFn: (Context => Symbol) @unchecked =>
60+
mySym = null
61+
mySym = symFn(ctx)
62+
case sym: Symbol if sym.defRunId != ctx.runId =>
63+
mySym = sym.denot.current.symbol
64+
case _ =>
65+
}
66+
mySym.asInstanceOf[Symbol]
67+
68+
protected var myTree: Tree | (Context => Tree)
69+
def tree(using ctx: Context): Tree =
70+
assert(myTree != null)
71+
myTree match {
72+
case treeFn: (Context => Tree) @unchecked =>
73+
myTree = null
74+
myTree = treeFn(ctx)
75+
case _ =>
76+
}
77+
myTree.asInstanceOf[Tree]
5978

60-
override def isEvaluated: Boolean = myTree != null
79+
override def isEvaluating: Boolean = myTree == null
80+
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree]
6181
}
6282

6383
/** An annotation indicating the body of a right-hand side,
@@ -72,24 +92,31 @@ object Annotations {
7292
override def ensureCompleted(implicit ctx: Context): Unit = ()
7393
}
7494

75-
case class ConcreteBodyAnnotation(body: Tree) extends BodyAnnotation {
95+
class ConcreteBodyAnnotation(body: Tree) extends BodyAnnotation {
7696
def tree(implicit ctx: Context): Tree = body
7797
}
7898

79-
case class LazyBodyAnnotation(private var bodyExpr: Context => Tree) extends BodyAnnotation {
80-
// TODO: Make `bodyExpr` an IFT once #6865 os in bootstrap
81-
private var evaluated = false
82-
private var myBody: Tree = _
83-
def tree(implicit ctx: Context): Tree = {
84-
if (evaluated) assert(myBody != null)
85-
else {
86-
evaluated = true
87-
myBody = bodyExpr(ctx)
88-
bodyExpr = null
99+
abstract class LazyBodyAnnotation extends BodyAnnotation {
100+
// Copy-pasted from LazyAnnotation to avoid having to turn it into a trait
101+
protected var myTree: Tree | (Context => Tree)
102+
def tree(using ctx: Context): Tree =
103+
assert(myTree != null)
104+
myTree match {
105+
case treeFn: (Context => Tree) @unchecked =>
106+
myTree = null
107+
myTree = treeFn(ctx)
108+
case _ =>
89109
}
90-
myBody
91-
}
92-
override def isEvaluated: Boolean = evaluated
110+
myTree.asInstanceOf[Tree]
111+
112+
override def isEvaluating: Boolean = myTree == null
113+
override def isEvaluated: Boolean = myTree.isInstanceOf[Tree]
114+
}
115+
116+
object LazyBodyAnnotation {
117+
def apply(bodyFn: Context ?=> Tree): LazyBodyAnnotation =
118+
new LazyBodyAnnotation:
119+
protected var myTree: Tree | (Context => Tree) = ctx => bodyFn(using ctx)
93120
}
94121

95122
object Annotation {
@@ -120,23 +147,15 @@ object Annotations {
120147
/** Create an annotation where the tree is computed lazily. */
121148
def deferred(sym: Symbol)(treeFn: Context ?=> Tree)(implicit ctx: Context): Annotation =
122149
new LazyAnnotation {
123-
override def symbol(implicit ctx: Context): Symbol = sym
124-
def complete(implicit ctx: Context) = treeFn(using ctx)
150+
protected var myTree: Tree | (Context => Tree) = ctx => treeFn(using ctx)
151+
protected var mySym: Symbol | (Context => Symbol) = sym
125152
}
126153

127154
/** Create an annotation where the symbol and the tree are computed lazily. */
128-
def deferredSymAndTree(symf: Context ?=> Symbol)(treeFn: Context ?=> Tree)(implicit ctx: Context): Annotation =
155+
def deferredSymAndTree(symFn: Context ?=> Symbol)(treeFn: Context ?=> Tree)(implicit ctx: Context): Annotation =
129156
new LazyAnnotation {
130-
private var mySym: Symbol = _
131-
132-
override def symbol(implicit ctx: Context): Symbol = {
133-
if (mySym == null || mySym.defRunId != ctx.runId) {
134-
mySym = symf(using ctx)
135-
assert(mySym != null)
136-
}
137-
mySym
138-
}
139-
def complete(implicit ctx: Context) = treeFn(using ctx)
157+
protected var mySym: Symbol | (Context => Symbol) = ctx => symFn(using ctx)
158+
protected var myTree: Tree | (Context => Tree) = ctx => treeFn(using ctx)
140159
}
141160

142161
def deferred(atp: Type, args: List[Tree])(implicit ctx: Context): Annotation =

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,9 @@ class TreeUnpickler(reader: TastyReader,
581581
forkAt(templateStart).indexTemplateParams()(localContext(sym))
582582
}
583583
else if (sym.isInlineMethod)
584-
sym.addAnnotation(LazyBodyAnnotation { ctx0 =>
584+
sym.addAnnotation(LazyBodyAnnotation { (using ctx0: Context) =>
585585
val ctx1 = localContext(sym)(ctx0).addMode(Mode.ReadPositions)
586-
implicit val ctx: Context = sourceChangeContext(Addr(0))(ctx1)
586+
given Context = sourceChangeContext(Addr(0))(ctx1)
587587
// avoids space leaks by not capturing the current context
588588
forkAt(rhsStart).readTerm()
589589
})

compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,12 @@ object PrepareInlineable {
216216
inlined: Symbol, treeExpr: Context => Tree)(implicit ctx: Context): Unit =
217217
inlined.unforcedAnnotation(defn.BodyAnnot) match {
218218
case Some(ann: ConcreteBodyAnnotation) =>
219-
case Some(ann: LazyBodyAnnotation) if ann.isEvaluated =>
219+
case Some(ann: LazyBodyAnnotation) if ann.isEvaluated || ann.isEvaluating =>
220220
case _ =>
221221
if (!ctx.isAfterTyper) {
222222
val inlineCtx = ctx
223-
inlined.updateAnnotation(LazyBodyAnnotation { _ =>
224-
implicit val ctx = inlineCtx
223+
inlined.updateAnnotation(LazyBodyAnnotation {
224+
given ctx as Context = inlineCtx
225225
val initialErrorCount = ctx.reporter.errorCount
226226
var inlinedBody = treeExpr(ctx)
227227
if (ctx.reporter.errorCount == initialErrorCount) {

0 commit comments

Comments
 (0)