Fix and test case for #3855. The uncurry stage now notices more
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. git-svn-id: http://lampsvn.epfl.ch/svn-repos/scala/scala/trunk@23126 5e8d7ff9-d8ef-0310-90f0-a4852d11357a
This commit is contained in:
parent
dd752c9e33
commit
3de373725e
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue