Skip to content

Fix some memory leaks #2876

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

Merged
merged 7 commits into from
Jul 18, 2017
Merged
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ object desugar {
else {
def msg =
s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}"
if (ctx.reporter.errorsReported) new ErrorType(msg)
if (ctx.reporter.errorsReported) ErrorType(msg)
else throw new java.lang.Error(msg)
}
case _ =>
Expand Down
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Implicits.ContextualImplicits
import config.Settings._
import config.Config
import reporting._
import reporting.diagnostic.Message
import collection.mutable
import collection.immutable.BitSet
import printing._
Expand Down Expand Up @@ -636,6 +637,12 @@ object Contexts {
*/
private[dotty] var unsafeNonvariant: RunId = NoRunId

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

// Phases state

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

def reset() = {
for ((_, set) <- uniqueSets) set.clear()
errorTypeMsg.clear()
}

// Test that access is single threaded
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1904,7 +1904,7 @@ object SymDenotations {
case denot: ClassDenotation =>
ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope)
case _ =>
new ErrorType(errMsg)
ErrorType(errMsg)
}
denot.privateWithin = NoSymbol
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ trait Symbols { this: Context =>
def newSkolem(tp: Type) = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | Permanent, tp)

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

}

copies.foreach(_.ensureCompleted()) // avoid memory leak
copies
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TyperState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import printing.{Showable, Printer}
import printing.Texts._
import config.Config
import collection.mutable
import java.lang.ref.WeakReference

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

Expand Down Expand Up @@ -143,8 +144,7 @@ extends TyperState(r) {
if (targetState.constraint eq previousConstraint) constraint
else targetState.constraint & constraint
constraint foreachTypeVar { tvar =>
if (tvar.owningState eq this)
tvar.owningState = targetState
if (tvar.owningState.get eq this) tvar.owningState = new WeakReference(targetState)
}
targetState.ephemeral |= ephemeral
targetState.gc()
Expand All @@ -157,7 +157,7 @@ extends TyperState(r) {
constraint foreachTypeVar { tvar =>
if (!tvar.inst.exists) {
val inst = instType(tvar)
if (inst.exists && (tvar.owningState eq this)) {
if (inst.exists && (tvar.owningState.get eq this)) {
tvar.inst = inst
val lam = tvar.origin.binder
if (constraint.isRemovable(lam)) toCollect += lam
Expand Down
32 changes: 26 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import Flags.FlagSet
import language.implicitConversions
import scala.util.hashing.{ MurmurHash3 => hashing }
import config.Printers.{core, typr, cyclicErrors}
import java.lang.ref.WeakReference

object Types {

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

/** The permanent instance type of the variable, or NoType is none is given yet */
private[core] var inst: Type = NoType
private[this] var myInst: Type = NoType

private[core] def inst = myInst
private[core] def inst_=(tp: Type) = {
myInst = tp
if (tp.exists) owningState = null // no longer needed; null out to avoid a memory leak
}

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

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

class ErrorType(_msg: => Message) extends FlexType {
def msg = _msg
class ErrorType private[Types] () extends FlexType {
def msg(implicit ctx: Context): Message =
ctx.errorTypeMsg.get(this) match {
case Some(msgFun) => msgFun()
case None => "error message from previous run no longer available"
}
}
object ErrorType {
def apply(msg: => Message)(implicit ctx: Context): ErrorType = {
val et = new ErrorType
ctx.base.errorTypeMsg(et) = () => msg
et
}
}

object UnspecifiedErrorType extends ErrorType("unspecified error")
object UnspecifiedErrorType extends ErrorType() {
override def msg(implicit ctx: Context): Message = "unspecified error"
}

/* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */
object TryDynamicCallType extends FlexType
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/transform/LambdaLift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ class LambdaLift extends MiniPhase with IdentityDenotTransformer { thisTransform
ctx.debuglog(i"mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure")
val intermediate =
if (enclosure.is(PackageClass)) enclosure
else if (enclosure.isConstructor) markFree(sym, enclosure.owner.enclosure)
else markFree(sym, enclosure.enclosure)
narrowLiftedOwner(enclosure, intermediate orElse sym.enclosingClass)
if (!intermediate.isRealClass || enclosure.isConstructor) {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object ErrorReporting {

def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = {
ctx.error(msg, pos)
new ErrorType(msg)
ErrorType(msg)
}

def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {
Expand Down
1 change: 1 addition & 0 deletions tests/run/i2883.check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

16 changes: 16 additions & 0 deletions tests/run/i2883.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Wrapper(val value: Int)

abstract class Foo(val x: Int)

class Test {
def foo(wrapper: Wrapper): Unit = {
new Foo(wrapper.value) {}
}
}
object Test extends App {
def foo(wrapper: Wrapper): Foo =
new Foo(wrapper.value) {}
def printFields(obj: Any) =
println(obj.getClass.getDeclaredFields.map(_.toString).sorted.deep.mkString("\n"))
printFields(foo(new Wrapper(1)))
}