Skip to content

Commit b3dd94f

Browse files
authored
Merge pull request #2876 from dotty-staging/fix-leaks
Fix some memory leaks
2 parents 26a697b + beb2e41 commit b3dd94f

File tree

10 files changed

+60
-14
lines changed

10 files changed

+60
-14
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ object desugar {
7575
else {
7676
def msg =
7777
s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}"
78-
if (ctx.reporter.errorsReported) new ErrorType(msg)
78+
if (ctx.reporter.errorsReported) ErrorType(msg)
7979
else throw new java.lang.Error(msg)
8080
}
8181
case _ =>

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

+8
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import Implicits.ContextualImplicits
2424
import config.Settings._
2525
import config.Config
2626
import reporting._
27+
import reporting.diagnostic.Message
2728
import collection.mutable
2829
import collection.immutable.BitSet
2930
import printing._
@@ -636,6 +637,12 @@ object Contexts {
636637
*/
637638
private[dotty] var unsafeNonvariant: RunId = NoRunId
638639

640+
/** A map from ErrorType to associated message computation. We use this map
641+
* instead of storing message computations directly in ErrorTypes in order
642+
* to avoid space leaks - the message computation usually captures a context.
643+
*/
644+
private[core] val errorTypeMsg = mutable.Map[ErrorType, () => Message]()
645+
639646
// Phases state
640647

641648
private[core] var phasesPlan: List[List[Phase]] = _
@@ -662,6 +669,7 @@ object Contexts {
662669

663670
def reset() = {
664671
for ((_, set) <- uniqueSets) set.clear()
672+
errorTypeMsg.clear()
665673
}
666674

667675
// Test that access is single threaded

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1904,7 +1904,7 @@ object SymDenotations {
19041904
case denot: ClassDenotation =>
19051905
ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope)
19061906
case _ =>
1907-
new ErrorType(errMsg)
1907+
ErrorType(errMsg)
19081908
}
19091909
denot.privateWithin = NoSymbol
19101910
}

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ trait Symbols { this: Context =>
298298
def newSkolem(tp: Type) = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | Permanent, tp)
299299

300300
def newErrorSymbol(owner: Symbol, name: Name, msg: => Message) = {
301-
val errType = new ErrorType(msg)
301+
val errType = ErrorType(msg)
302302
newSymbol(owner, name, SyntheticArtifact,
303303
if (name.isTypeName) TypeAlias(errType) else errType)
304304
}
@@ -348,9 +348,9 @@ trait Symbols { this: Context =>
348348
info = completer,
349349
privateWithin = ttmap1.mapOwner(odenot.privateWithin), // since this refers to outer symbols, need not include copies (from->to) in ownermap here.
350350
annotations = odenot.annotations)
351-
352351
}
353352

353+
copies.foreach(_.ensureCompleted()) // avoid memory leak
354354
copies
355355
}
356356

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import printing.{Showable, Printer}
1111
import printing.Texts._
1212
import config.Config
1313
import collection.mutable
14+
import java.lang.ref.WeakReference
1415

1516
class TyperState(r: Reporter) extends DotClass with Showable {
1617

@@ -143,8 +144,7 @@ extends TyperState(r) {
143144
if (targetState.constraint eq previousConstraint) constraint
144145
else targetState.constraint & constraint
145146
constraint foreachTypeVar { tvar =>
146-
if (tvar.owningState eq this)
147-
tvar.owningState = targetState
147+
if (tvar.owningState.get eq this) tvar.owningState = new WeakReference(targetState)
148148
}
149149
targetState.ephemeral |= ephemeral
150150
targetState.gc()
@@ -157,7 +157,7 @@ extends TyperState(r) {
157157
constraint foreachTypeVar { tvar =>
158158
if (!tvar.inst.exists) {
159159
val inst = instType(tvar)
160-
if (inst.exists && (tvar.owningState eq this)) {
160+
if (inst.exists && (tvar.owningState.get eq this)) {
161161
tvar.inst = inst
162162
val lam = tvar.origin.binder
163163
if (constraint.isRemovable(lam)) toCollect += lam

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

+26-6
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import Flags.FlagSet
3636
import language.implicitConversions
3737
import scala.util.hashing.{ MurmurHash3 => hashing }
3838
import config.Printers.{core, typr, cyclicErrors}
39+
import java.lang.ref.WeakReference
3940

4041
object Types {
4142

@@ -3175,12 +3176,18 @@ object Types {
31753176
final class TypeVar(val origin: TypeParamRef, creatorState: TyperState, val bindingTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType {
31763177

31773178
/** The permanent instance type of the variable, or NoType is none is given yet */
3178-
private[core] var inst: Type = NoType
3179+
private[this] var myInst: Type = NoType
3180+
3181+
private[core] def inst = myInst
3182+
private[core] def inst_=(tp: Type) = {
3183+
myInst = tp
3184+
if (tp.exists) owningState = null // no longer needed; null out to avoid a memory leak
3185+
}
31793186

31803187
/** The state owning the variable. This is at first `creatorState`, but it can
31813188
* be changed to an enclosing state on a commit.
31823189
*/
3183-
private[core] var owningState = creatorState
3190+
private[core] var owningState = new WeakReference(creatorState)
31843191

31853192
/** The instance type of this variable, or NoType if the variable is currently
31863193
* uninstantiated
@@ -3198,7 +3205,7 @@ object Types {
31983205
private def instantiateWith(tp: Type)(implicit ctx: Context): Type = {
31993206
assert(tp ne this, s"self instantiation of ${tp.show}, constraint = ${ctx.typerState.constraint.show}")
32003207
typr.println(s"instantiating ${this.show} with ${tp.show}")
3201-
if ((ctx.typerState eq owningState) && !ctx.typeComparer.subtypeCheckInProgress)
3208+
if ((ctx.typerState eq owningState.get) && !ctx.typeComparer.subtypeCheckInProgress)
32023209
inst = tp
32033210
ctx.typerState.constraint = ctx.typerState.constraint.replace(origin, tp)
32043211
tp
@@ -3546,11 +3553,24 @@ object Types {
35463553
*/
35473554
abstract class FlexType extends UncachedGroundType with ValueType
35483555

3549-
class ErrorType(_msg: => Message) extends FlexType {
3550-
def msg = _msg
3556+
class ErrorType private[Types] () extends FlexType {
3557+
def msg(implicit ctx: Context): Message =
3558+
ctx.errorTypeMsg.get(this) match {
3559+
case Some(msgFun) => msgFun()
3560+
case None => "error message from previous run no longer available"
3561+
}
3562+
}
3563+
object ErrorType {
3564+
def apply(msg: => Message)(implicit ctx: Context): ErrorType = {
3565+
val et = new ErrorType
3566+
ctx.base.errorTypeMsg(et) = () => msg
3567+
et
3568+
}
35513569
}
35523570

3553-
object UnspecifiedErrorType extends ErrorType("unspecified error")
3571+
object UnspecifiedErrorType extends ErrorType() {
3572+
override def msg(implicit ctx: Context): Message = "unspecified error"
3573+
}
35543574

35553575
/* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */
35563576
object TryDynamicCallType extends FlexType

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

+1
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
202202
ctx.debuglog(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure")
203203
val intermediate =
204204
if (enclosure.is(PackageClass)) enclosure
205+
else if (enclosure.isConstructor) markFree(sym, enclosure.owner.enclosure)
205206
else markFree(sym, enclosure.enclosure)
206207
narrowLiftedOwner(enclosure, intermediate orElse sym.enclosingClass)
207208
if (!intermediate.isRealClass || enclosure.isConstructor) {

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ object ErrorReporting {
2323

2424
def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = {
2525
ctx.error(msg, pos)
26-
new ErrorType(msg)
26+
ErrorType(msg)
2727
}
2828

2929
def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {

tests/run/i2883.check

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

tests/run/i2883.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class Wrapper(val value: Int)
2+
3+
abstract class Foo(val x: Int)
4+
5+
class Test {
6+
def foo(wrapper: Wrapper): Unit = {
7+
new Foo(wrapper.value) {}
8+
}
9+
}
10+
object Test extends App {
11+
def foo(wrapper: Wrapper): Foo =
12+
new Foo(wrapper.value) {}
13+
def printFields(obj: Any) =
14+
println(obj.getClass.getDeclaredFields.map(_.toString).sorted.deep.mkString("\n"))
15+
printFields(foo(new Wrapper(1)))
16+
}

0 commit comments

Comments
 (0)