summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-09-28 15:43:32 +0000
committerPaul Phillips <paulp@improving.org>2010-09-28 15:43:32 +0000
commitcb91343d2b02b038f6c297ba79a052e0758caae8 (patch)
tree3165095121f5b2e02ad134bbac42dc2f2f276572
parentc18c3e108131bbfd6d2c4bbc3395c3a7f76a581b (diff)
downloadscala-cb91343d2b02b038f6c297ba79a052e0758caae8.tar.gz
scala-cb91343d2b02b038f6c297ba79a052e0758caae8.tar.bz2
scala-cb91343d2b02b038f6c297ba79a052e0758caae8.zip
Fix and test case for #3855.
situations where a mutable var will later be lifted. As a point of interest, this bug reveals itself fairly clearly if you use a build since r23112 and run the checker thusly: scalac -d /tmp -Ycheck-debug -Ycheck:icode -Xprint:icode test/files/run/bug3855.scala It dies with the following explanation: Output changed for Block 3 [S: 2] [P: 1, 4] Exception in thread "main" scala.tools.nsc.backend.icode.CheckerException: Incompatible stacks: TypeStack() and TypeStack(2 elems) { REFERENCE(class IntRef) REFERENCE(class IntRef) } in Test.main at entry to block: 2 And indeed that was the source of the reported verifyerror. Review by i. dragos.
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala36
-rw-r--r--test/files/run/bug3855.scala18
2 files changed, 53 insertions, 1 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 85db55472d..b7a70bdf4b 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -704,17 +704,51 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
/** Set of mutable local variables that are free in some inner method. */
private val freeMutableVars: mutable.Set[Symbol] = new mutable.HashSet
+ /** PP: There is apparently some degree of overlap between the CAPTURED
+ * flag and the role being filled here. I think this is how this was able
+ * to go for so long looking only at DefDef and Ident nodes, as bugs
+ * would only emerge under more complicated conditions such as #3855.
+ * I'll try to figure it all out, but if someone who already knows the
+ * whole story wants to fill it in, that too would be great.
+ */
private val freeLocalsTraverser = new Traverser {
var currentMethod: Symbol = NoSymbol
+ var maybeEscaping = false
+
+ def withEscaping(body: => Unit) {
+ val savedEscaping = maybeEscaping
+ maybeEscaping = true
+ body
+ maybeEscaping = savedEscaping
+ }
+
override def traverse(tree: Tree) = tree match {
case DefDef(_, _, _, _, _, _) =>
val lastMethod = currentMethod
currentMethod = tree.symbol
super.traverse(tree)
currentMethod = lastMethod
+ /** A method call with a by-name parameter represents escape. */
+ case Apply(fn, args) if fn.symbol.paramss.nonEmpty =>
+ traverse(fn)
+ (fn.symbol.paramss.head zip args) foreach {
+ case (param, arg) =>
+ if (param.tpe != null && param.tpe.typeSymbol == ByNameParamClass)
+ withEscaping(traverse(arg))
+ else
+ traverse(arg)
+ }
+ /** The rhs of a closure represents escape. */
+ case Function(vparams, body) =>
+ vparams foreach traverse
+ withEscaping(traverse(body))
+
+ /** The appearance of an ident outside the method where it was defined or
+ * anytime maybeEscaping is true implies escape.
+ */
case Ident(_) =>
val sym = tree.symbol
- if (sym.isVariable && sym.owner.isMethod && sym.owner != currentMethod)
+ if (sym.isVariable && sym.owner.isMethod && (maybeEscaping || sym.owner != currentMethod))
freeMutableVars += sym
case _ =>
super.traverse(tree)
diff --git a/test/files/run/bug3855.scala b/test/files/run/bug3855.scala
new file mode 100644
index 0000000000..32dfb1e23d
--- /dev/null
+++ b/test/files/run/bug3855.scala
@@ -0,0 +1,18 @@
+object Test {
+ def byval[A](a: => A) = a
+ def closure[A](f: () => A) = f()
+
+ def f1(s: String) = {
+ var n = try { s.toInt } catch { case _ => 1 }
+ byval(n)
+ }
+ def f2(s: String) = {
+ var n = try { s.toInt } catch { case _ => 1 }
+ closure(() => n)
+ }
+
+ def main(args: Array[String]) = {
+ val sum = f1("12") + f2("the witch is dead")
+ assert(sum == 13)
+ }
+}