diff options
author | Paul Phillips <paulp@improving.org> | 2010-09-28 15:43:32 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-09-28 15:43:32 +0000 |
commit | cb91343d2b02b038f6c297ba79a052e0758caae8 (patch) | |
tree | 3165095121f5b2e02ad134bbac42dc2f2f276572 /src | |
parent | c18c3e108131bbfd6d2c4bbc3395c3a7f76a581b (diff) | |
download | scala-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.
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 36 |
1 files changed, 35 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) |