From de3e8492e61e02777520e8876e9e1bccb4b0f065 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Wed, 20 Oct 2010 13:26:02 +0000 Subject: Closes #2910. --- .../scala/tools/nsc/transform/LambdaLift.scala | 10 +- .../scala/tools/nsc/typechecker/RefChecks.scala | 205 +++++++++++---------- test/files/neg/t2910.check | 16 ++ test/files/neg/t2910.scala | 38 ++++ test/files/pos/t2910.scala | 33 ++++ 5 files changed, 205 insertions(+), 97 deletions(-) create mode 100644 test/files/neg/t2910.check create mode 100644 test/files/neg/t2910.scala create mode 100644 test/files/pos/t2910.scala diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 034dc04f3c..184065a7ed 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -155,7 +155,7 @@ abstract class LambdaLift extends InfoTransform { if (settings.debug.value) log("" + sym + " is free in " + owner); if ((sym.isVariable || (sym.isValue && sym.isLazy)) && !sym.hasFlag(CAPTURED)) { sym setFlag CAPTURED - val symClass = sym.tpe.typeSymbol; + val symClass = sym.tpe.typeSymbol atPhase(phase.next) { sym updateInfo ( if (sym.hasAnnotation(VolatileAttr)) @@ -433,7 +433,7 @@ abstract class LambdaLift extends InfoTransform { else if (sym.isLocal && !isSameOwnerEnclosure(sym)) atPos(tree.pos)(proxyRef(sym)) else tree - else tree; + else tree if (sym.isCapturedVariable) atPos(tree.pos) { val tp = tree.tpe @@ -441,6 +441,12 @@ abstract class LambdaLift extends InfoTransform { if (elemTree.tpe.typeSymbol != tp.typeSymbol) gen.mkAttributedCast(elemTree, tp) else elemTree } else tree1 + case Block(stats, expr0) => + val (lzyVals, rest) = stats.partition { + case stat@ValDef(_, _, _, _) if stat.symbol.isLazy => true + case _ => false + } + treeCopy.Block(tree, lzyVals:::rest, expr0) case _ => tree } diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7ea2aeec45..9fd1c69ef7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -795,16 +795,20 @@ abstract class RefChecks extends InfoTransform { currentLevel = currentLevel.outer } + private def normalizeSymToRef(sym: Symbol): Symbol = + if(sym isLazy) sym.lazyAccessor else sym + private def enterSyms(stats: List[Tree]) { var index = -1 for (stat <- stats) { index = index + 1; stat match { case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => - assert(stat.symbol != NoSymbol, stat);//debug - if (stat.symbol.isLocal) { - currentLevel.scope.enter(stat.symbol) - symIndex(stat.symbol) = index; + //assert(stat.symbol != NoSymbol, stat);//debug + val sym = normalizeSymToRef(stat.symbol) + if (sym.isLocal) { + currentLevel.scope.enter(sym) + symIndex(sym) = index; } case _ => } @@ -918,111 +922,115 @@ abstract class RefChecks extends InfoTransform { * - for all other lazy values z the accessor is a block of this form: * { z = ; z } where z can be an identifier or a field. */ - def transformStat(tree: Tree, index: Int): List[Tree] = tree match { - case ModuleDef(mods, name, impl) => - val sym = tree.symbol - if (sym.isStatic) { - val cdef = ClassDef(mods | MODULE, name, List(), impl) - .setPos(tree.pos) - .setSymbol(sym.moduleClass) - .setType(NoType) - - if (!sym.allOverriddenSymbols.isEmpty) { - val factory = sym.owner.newMethod(sym.pos, sym.name) - .setFlag(sym.flags | STABLE).resetFlag(MODULE) - .setInfo(PolyType(List(), sym.moduleClass.tpe)) - sym.owner.info.decls.enter(factory) - val ddef = - atPhase(phase.next) { - localTyper.typed { - gen.mkModuleAccessDef(factory, sym.tpe) - } - } - transformTrees(List(cdef, ddef)) - } else { - List(transform(cdef)) - } - } else { - def lazyNestedObjectTrees(transformedInfo: Boolean) = { + def transformStat(tree: Tree, index: Int): List[Tree] = { + def checkForwardReference(sym: Symbol) = + if (sym.isLocal && index <= currentLevel.maxindex) { + if (settings.debug.value) Console.println(currentLevel.refsym) + unit.error(currentLevel.refpos, "forward reference extends over definition of " + sym) + } + tree match { + case ModuleDef(mods, name, impl) => + val sym = tree.symbol + if (sym.isStatic) { val cdef = ClassDef(mods | MODULE, name, List(), impl) .setPos(tree.pos) - .setSymbol(if (!transformedInfo) sym.moduleClass else sym.lazyAccessor) + .setSymbol(sym.moduleClass) .setType(NoType) - val vdef = localTyper.typedPos(tree.pos){ - if (!transformedInfo) - gen.mkModuleVarDef(sym) - else { - val vsym = sym.owner.info.decl(nme.moduleVarName(sym.name)) - assert(vsym != NoSymbol, "Nested object after transformInfo set module variable") - ValDef(vsym, EmptyTree) - } + if (!sym.allOverriddenSymbols.isEmpty) { + val factory = sym.owner.newMethod(sym.pos, sym.name) + .setFlag(sym.flags | STABLE).resetFlag(MODULE) + .setInfo(PolyType(List(), sym.moduleClass.tpe)) + sym.owner.info.decls.enter(factory) + val ddef = + atPhase(phase.next) { + localTyper.typed { + gen.mkModuleAccessDef(factory, sym.tpe) + } + } + transformTrees(List(cdef, ddef)) + } else { + List(transform(cdef)) } - val vsym = vdef.symbol - - val ddef = atPhase(phase.next) { - localTyper.typed { - val rhs = gen.newModule(sym, vsym.tpe) - if (!transformedInfo) { - sym.resetFlag(MODULE | FINAL | CASE) - sym.setFlag(LAZY | ACCESSOR | SYNTHETIC) - - sym.setInfo(PolyType(List(), sym.tpe)) - sym setFlag (lateMETHOD | STABLE) + } else { + def lazyNestedObjectTrees(transformedInfo: Boolean) = { + val cdef = ClassDef(mods | MODULE, name, List(), impl) + .setPos(tree.pos) + .setSymbol(if (!transformedInfo) sym.moduleClass else sym.lazyAccessor) + .setType(NoType) + + val vdef = localTyper.typedPos(tree.pos){ + if (!transformedInfo) + gen.mkModuleVarDef(sym) + else { + val vsym0 = sym.owner.info.decl(nme.moduleVarName(sym.name)) + // In case we are dealing with local symbol then we already have correct error with forward reference + ValDef(if (vsym0 == NoSymbol) gen.mkModuleVarDef(sym).symbol else vsym0, EmptyTree) } + } + val vsym = vdef.symbol - val ownerTransformer = new ChangeOwnerTraverser(vsym, sym) - val lazyDef = atPos(tree.pos)( - DefDef(sym, ownerTransformer( - if (sym.owner.isTrait) rhs - else Block(List( - Assign(gen.mkAttributedRef(vsym), rhs)), - gen.mkAttributedRef(vsym))) - )) - lazyDef + val ddef = atPhase(phase.next) { + localTyper.typed { + val rhs = gen.newModule(sym, vsym.tpe) + if (!transformedInfo) { + sym.resetFlag(MODULE | FINAL | CASE) + sym.setFlag(LAZY | ACCESSOR | SYNTHETIC) + + sym.setInfo(PolyType(List(), sym.tpe)) + sym setFlag (lateMETHOD | STABLE) + } + + val ownerTransformer = new ChangeOwnerTraverser(vsym, sym) + val lazyDef = atPos(tree.pos)( + DefDef(sym, ownerTransformer( + if (sym.owner.isTrait) rhs + else Block(List( + Assign(gen.mkAttributedRef(vsym), rhs)), + gen.mkAttributedRef(vsym))) + )) + lazyDef + } } + transformTrees(List(cdef, vdef, ddef)) } - transformTrees(List(cdef, vdef, ddef)) + lazyNestedObjectTrees(sym.hasFlag(LAZY)) } - lazyNestedObjectTrees(sym.hasFlag(LAZY)) - } - case ValDef(_, _, _, _) => - val tree1 = transform(tree); // important to do before forward reference check - val ValDef(_, _, _, rhs) = tree1 - if (tree.symbol.hasFlag(LAZY)) { - assert(tree.symbol.isTerm, tree.symbol) - val vsym = tree.symbol - val hasUnitType = (tree.symbol.tpe.typeSymbol == UnitClass) - val lazyDefSym = vsym.lazyAccessor - assert(lazyDefSym != NoSymbol, vsym) - val ownerTransformer = new ChangeOwnerTraverser(vsym, lazyDefSym) - val lazyDef = atPos(tree.pos)( - DefDef(lazyDefSym, ownerTransformer( - if (tree.symbol.owner.isTrait // for traits, this is further transformed in mixins - || hasUnitType) rhs - else Block(List( - Assign(gen.mkAttributedRef(vsym), rhs)), - gen.mkAttributedRef(vsym))))) - log("Made lazy def: " + lazyDef) - if (hasUnitType) - typed(lazyDef) :: Nil - else - typed(ValDef(vsym, EmptyTree)) :: atPhase(phase.next) { typed(lazyDef) } :: Nil - } else { - if (tree.symbol.isLocal && index <= currentLevel.maxindex) { - if (settings.debug.value) - Console.println(currentLevel.refsym) - unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol); + case ValDef(_, _, _, _) => + val tree1 = transform(tree); // important to do before forward reference check + + if (tree.symbol.hasFlag(LAZY)) { + assert(tree.symbol.isTerm, tree.symbol) + val ValDef(_, _, _, rhs) = tree1 + val vsym = tree.symbol + val hasUnitType = (tree.symbol.tpe.typeSymbol == UnitClass) + val lazyDefSym = vsym.lazyAccessor + assert(lazyDefSym != NoSymbol, vsym) + val ownerTransformer = new ChangeOwnerTraverser(vsym, lazyDefSym) + val lazyDef = atPos(tree.pos)( + DefDef(lazyDefSym, ownerTransformer( + if (tree.symbol.owner.isTrait // for traits, this is further transformed in mixins + || hasUnitType) rhs + else Block(List( + Assign(gen.mkAttributedRef(vsym), rhs)), + gen.mkAttributedRef(vsym))))) + log("Made lazy def: " + lazyDef) + if (hasUnitType) + typed(lazyDef) :: Nil + else + typed(ValDef(vsym, EmptyTree)) :: atPhase(phase.next) { typed(lazyDef) } :: Nil + } else { + checkForwardReference(normalizeSymToRef(tree.symbol)) + List(tree1) } - List(tree1) - } - case Import(_, _) => - List() + case Import(_, _) => + List() - case _ => - List(transform(tree)) + case _ => + List(transform(tree)) + } } /******** Begin transform inner function section ********/ @@ -1329,6 +1337,13 @@ abstract class RefChecks extends InfoTransform { case x @ Select(_, _) => transformSelect(x) + case UnApply(fun, args) => + transform(fun) // just make sure we enterReference for unapply symbols, note that super.transform(tree) would not transform(fun) + // transformTrees(args) // TODO: is this necessary? could there be forward references in the args?? + // probably not, until we allow parameterised extractors + tree + + case _ => tree } result = result match { diff --git a/test/files/neg/t2910.check b/test/files/neg/t2910.check new file mode 100644 index 0000000000..afff73ec08 --- /dev/null +++ b/test/files/neg/t2910.check @@ -0,0 +1,16 @@ +t2910.scala:3: error: forward reference extends over definition of value ret + val ret = l.map({ case MyMatch(id) => id }) + ^ +t2910.scala:9: error: forward reference extends over definition of value z + println(s.length) + ^ +t2910.scala:16: error: forward reference extends over definition of value z + x + ^ +t2910.scala:30: error: forward reference extends over definition of value x + lazy val f: Int = x + ^ +t2910.scala:34: error: forward reference extends over definition of variable x + lazy val f: Int = g + ^ +5 errors found \ No newline at end of file diff --git a/test/files/neg/t2910.scala b/test/files/neg/t2910.scala new file mode 100644 index 0000000000..1c7ba54971 --- /dev/null +++ b/test/files/neg/t2910.scala @@ -0,0 +1,38 @@ +object Junk { + def f(l: List[String]): List[String] = { + val ret = l.map({ case MyMatch(id) => id }) + val MyMatch = "(\\d+)".r + ret + } + + def test2 { + println(s.length) + val z = 0 + lazy val s = "abc" + } + + def test4 { + lazy val x = { + x + val z = 0 + lazy val x = 12 + z + } + } +} + +// updated forwards.scala for lazy vals +object Test { + lazy val f: Int = x + val x: Int = f + + { + lazy val f: Int = x + val x: Int = f + } + { + lazy val f: Int = g + var x: Int = f + lazy val g: Int = x + } +} diff --git a/test/files/pos/t2910.scala b/test/files/pos/t2910.scala new file mode 100644 index 0000000000..d4d92fa765 --- /dev/null +++ b/test/files/pos/t2910.scala @@ -0,0 +1,33 @@ +object Test { + def test1 { + C + object C + } + + def test2 { + println(s.length) + lazy val s = "abc" + } + + def test3 { + lazy val lazyBar = bar + object bar { + val foo = 12 + } + lazy val lazyBar2 = bar + } + + def test4 { + lazy val x = { + x + lazy val x = 12 + 0 + } + } + + def test5 { + lazy val f: Int = g + Console.println("foo") + lazy val g: Int = f + } +} \ No newline at end of file -- cgit v1.2.3