Skip to content

Commit

Permalink
Clear temp var for captured var expr to permit GC
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Jan 3, 2022
1 parent 48abca8 commit 49b6b27
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 4 deletions.
13 changes: 11 additions & 2 deletions compiler/src/dotty/tools/dotc/transform/CapturedVars.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import core.Decorators._
import core.StdNames.nme
import core.Names._
import core.NameKinds.TempResultName
import core.Constants._
import ast.Trees._
import util.Store
import collection.mutable
Expand Down Expand Up @@ -45,6 +46,9 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisPhase =

val boxedRefClasses: collection.Set[Symbol] =
refClassKeys.flatMap(k => Set(refClass(k), volatileRefClass(k)))

val objectRefClasses: collection.Set[Symbol] =
Set(refClass(defn.ObjectClass), volatileRefClass(defn.ObjectClass))
}

private var myRefInfo: RefInfo = null
Expand Down Expand Up @@ -144,8 +148,13 @@ class CapturedVars extends MiniPhase with IdentityDenotTransformer { thisPhase =
val Select(_, nme.elem) = qual
recur(qual)
case Select(_, nme.elem) if refInfo.boxedRefClasses.contains(lhs.symbol.maybeOwner) =>
val tempDef = transformFollowing(SyntheticValDef(TempResultName.fresh(), tree.rhs))
transformFollowing(Block(tempDef :: Nil, cpy.Assign(tree)(lhs, ref(tempDef.symbol))))
val tempDef = transformFollowing {
ValDef(newSymbol(ctx.owner, TempResultName.fresh(), Mutable | Synthetic, tree.rhs.tpe.widen, coord = tree.rhs.span), tree.rhs)
}
val update = cpy.Assign(tree)(lhs, ref(tempDef.symbol))
def reset = cpy.Assign(tree)(ref(tempDef.symbol), Literal(Constant(null)))
val res = if (refInfo.objectRefClasses(lhs.symbol.maybeOwner)) reset else Literal(Constant(()))
transformFollowing(Block(tempDef :: update :: Nil, res))
case _ =>
tree
}
Expand Down
34 changes: 34 additions & 0 deletions tests/run/i14198.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicReference

final class Mark

object Test {

def main(args: Array[String]): Unit = {
myTest()
}

final def myAssert(cond: => Boolean): Unit = {
assert(cond)
}

def myTest(): Unit = {
val ref = new AtomicReference[WeakReference[AnyRef]]
var mark: AnyRef = null
assert(ref.compareAndSet(null, new WeakReference(new Mark)))
mark = ref.get().get()
myAssert(mark ne null) // in theory this could fail, but it isn't
mark = null
System.gc()
var n = 10
while (n > 0 && ref.get().get() != null) {
System.gc()
Thread.`yield`()
//print(".")
n -= 1
}
assert(ref.get().get() == null)
}
}
23 changes: 21 additions & 2 deletions tests/run/liftedTry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,34 @@ object Test {
assert(x == 1)
assert(foo(2) == 2)
assert(foo(try raise(3) catch handle) == 3)
Tr.foo
assert(Tr.foo == 3)
}
}

object Tr {
def fun(a: Int => Unit) = a(2)
def foo: Int = {
var s = 1
s = try {fun(s = _); 3} catch{ case ex: Throwable => val x = 4; s = x; 5 }
s = try {fun(s = _); 3} catch { case ex: Throwable => val x = 4; s = x; 5 }
s
}
}

/* was:
Caused by: java.lang.VerifyError: Inconsistent stackmap frames at branch target 33
Exception Details:
Location:
Tr$.foo()I @30: goto
Reason:
Current frame's stack size doesn't match stackmap.
Current Frame:
bci: @30
flags: { }
locals: { 'Tr$', 'scala/runtime/IntRef', 'java/lang/Throwable', integer }
stack: { integer }
Stackmap Frame:
bci: @33
flags: { }
locals: { 'Tr$', 'scala/runtime/IntRef' }
stack: { top, integer }
*/

0 comments on commit 49b6b27

Please sign in to comment.