diff options
author | Hubert Plociniczak <hubert.plociniczak@epfl.ch> | 2010-09-22 14:00:35 +0000 |
---|---|---|
committer | Hubert Plociniczak <hubert.plociniczak@epfl.ch> | 2010-09-22 14:00:35 +0000 |
commit | a5d47fb693d9b88ea9ed414762f16e027be64ada (patch) | |
tree | c6c5962343a7c7a4e1c7d98dcc6b2ad930c91344 | |
parent | 39a8b1042e04a5b838ac5688e014c29680bf0561 (diff) | |
download | scala-a5d47fb693d9b88ea9ed414762f16e027be64ada.tar.gz scala-a5d47fb693d9b88ea9ed414762f16e027be64ada.tar.bz2 scala-a5d47fb693d9b88ea9ed414762f16e027be64ada.zip |
Closes #1591.
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeGen.scala | 28 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/StdNames.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Types.scala | 7 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Flatten.scala | 4 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/LambdaLift.scala | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Mixin.scala | 11 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 76 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala | 9 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/StdNames.scala | 4 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/Symbols.scala | 5 | ||||
-rwxr-xr-x | src/library/scala/reflect/generic/UnPickler.scala | 18 | ||||
-rw-r--r-- | test/files/jvm/serialization.scala | 26 | ||||
-rw-r--r-- | test/files/pos/t1591.scala | 7 | ||||
-rw-r--r-- | test/files/run/t1591.check | 1 | ||||
-rw-r--r-- | test/files/run/t1591.scala | 14 |
15 files changed, 163 insertions, 53 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index a886e7468f..611780e776 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -259,30 +259,18 @@ abstract class TreeGen { ) } - // var m$: T = null; or, if class member: local var m$: T = _; - /*!!! - def mkModuleValDef(accessor: Symbol) = { - val mval = accessor.owner.newValue(accessor.pos.focus, nme.moduleVarName(accessor.name)) + def mkModuleVarDef(accessor: Symbol) = { + val mval = accessor.owner.newVariable(accessor.pos.focus, nme.moduleVarName(accessor.name)) .setInfo(accessor.tpe.finalResultType) - .setFlag(LAZY); + .setFlag(LAZY) + .setFlag(MODULEVAR) + mval.setLazyAccessor(accessor) if (mval.owner.isClass) { mval setFlag (PRIVATE | LOCAL | SYNTHETIC) mval.owner.info.decls.enter(mval) } - ValDef(mval, New(TypeTree(mval.tpe), List(List()))) - } - */ - - // var m$: T = null; or, if class member: local var m$: T = _; - def mkModuleVarDef(accessor: Symbol) = { - val mvar = accessor.owner.newVariable(accessor.pos.focus, nme.moduleVarName(accessor.name)) - .setInfo(accessor.tpe.finalResultType) - .setFlag(MODULEVAR); - if (mvar.owner.isClass) { - mvar setFlag (PRIVATE | LOCAL | SYNTHETIC) - mvar.owner.info.decls.enter(mvar) - } - ValDef(mvar, if (mvar.owner.isClass) EmptyTree else Literal(Constant(null))) + ValDef(mval, EmptyTree) + //ValDef(mval, newModule(accessor, mval.tpe)) } // def m: T = { if (m$ eq null) m$ = new m$class(...) m$ } @@ -295,7 +283,7 @@ abstract class TreeGen { def mkModuleAccessDef(accessor: Symbol, tpe: Type) = DefDef(accessor, newModule(accessor, tpe)) - private def newModule(accessor: Symbol, tpe: Type) = + def newModule(accessor: Symbol, tpe: Type) = New(TypeTree(tpe), List(for (pt <- tpe.typeSymbol.primaryConstructor.info.paramTypes) yield This(accessor.owner.enclClass))) diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index aa47441a1a..bcd1ac0627 100644 --- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala +++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala @@ -89,7 +89,6 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable => val LOCAL_SUFFIX = newTermName(LOCAL_SUFFIX_STRING) val SETTER_SUFFIX = encode("_=") val IMPL_CLASS_SUFFIX = newTermName("$class") - val MODULE_SUFFIX = newTermName("$module") val LOCALDUMMY_PREFIX = newTermName(LOCALDUMMY_PREFIX_STRING) val SELECTOR_DUMMY = newTermName("<unapply-selector>") @@ -175,9 +174,6 @@ trait StdNames extends reflect.generic.StdNames { self: SymbolTable => def interfaceName(implname: Name): Name = implname.subName(0, implname.length - IMPL_CLASS_SUFFIX.length) - def moduleVarName(name: Name): Name = - newTermName(name.toString() + MODULE_SUFFIX) - def superName(name: Name) = newTermName("super$" + name) val PROTECTED_PREFIX = "protected$" diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index deffa1d852..cced26a780 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -3764,7 +3764,10 @@ A type's typeSymbol should never be inspected directly. object adaptToNewRunMap extends TypeMap { private def adaptToNewRun(pre: Type, sym: Symbol): Symbol = { if (sym.isModuleClass && !phase.flatClasses) { - adaptToNewRun(pre, sym.sourceModule).moduleClass + if (!sym.owner.isPackageClass) + sym // Nested lazy object + else + adaptToNewRun(pre, sym.sourceModule).moduleClass } else if ((pre eq NoPrefix) || (pre eq NoType) || sym.owner.isPackageClass) { sym } else { @@ -4641,7 +4644,7 @@ A type's typeSymbol should never be inspected directly. def specializesSym(tp: Type, sym: Symbol): Boolean = tp.typeSymbol == NothingClass || tp.typeSymbol == NullClass && (sym.owner isSubClass ObjectClass) || - (tp.nonPrivateMember(sym.name).alternatives exists + (tp.member(sym.name).alternatives exists (alt => sym == alt || specializesSym(tp.narrow, alt, sym.owner.thisType, sym))) /** Does member `sym1' of `tp1' have a stronger type diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala index 1bee37bfe6..96c050b51f 100644 --- a/src/compiler/scala/tools/nsc/transform/Flatten.scala +++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala @@ -48,7 +48,9 @@ abstract class Flatten extends InfoTransform { for (sym <- decls.toList) { if (sym.isTerm && !sym.isStaticModule) { decls1 enter sym - if (sym.isModule) sym.moduleClass setFlag LIFTED + if (sym.isModule) sym.moduleClass setFlag LIFTED // Only top modules + // Nested modules (MODULE flag is reset so we access through lazy): + if (sym.isModuleVar && sym.hasFlag(LAZY)) sym.lazyAccessor.lazyAccessor setFlag LIFTED } else if (sym.isClass) { liftClass(sym) if (sym.needsImplClass) liftClass(erasure.implClass(sym)) diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 72914934cb..7791cc0b02 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -156,7 +156,7 @@ abstract class LambdaLift extends InfoTransform { } changedFreeVars = true if (settings.debug.value) log("" + sym + " is free in " + owner); - if (sym.isVariable && !(sym hasFlag CAPTURED)) { + if ((sym.isVariable || (sym.isValue && sym.hasFlag(LAZY))) && !(sym hasFlag CAPTURED)) { sym setFlag CAPTURED val symClass = sym.tpe.typeSymbol; atPhase(phase.next) { diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 92c92796cf..9c562dba86 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -383,7 +383,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * Each field has to be private and defined in the enclosing class, and there must * be exactly one lazy value using it. * - * Such fields will be nulled after the initializer has memoized the lazy value. + * Such fields will be nulled after the initializer has memorized the lazy value. */ def singleUseFields(templ: Template): collection.Map[Symbol, List[Symbol]] = { val usedIn = new mutable.HashMap[Symbol, List[Symbol]] { @@ -402,6 +402,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { && !(currentOwner.isGetter && currentOwner.accessed == sym) // getter && !definitions.isValueClass(sym.tpe.resultType.typeSymbol) && sym.owner == templ.symbol.owner + && !sym.hasFlag(LAZY) && !tree.isDef) { log("added use in: " + currentOwner + " -- " + tree) usedIn(sym) ::= currentOwner @@ -429,7 +430,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { class MixinTransformer(unit : CompilationUnit) extends Transformer { /** Within a static implementation method: the parameter referring to the - * current object undefined evrywhere else. + * current object undefined everywhere else. */ private var self: Symbol = _ @@ -719,6 +720,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { * the class constructor is changed to set the initialized bits. */ def addCheckedGetters(clazz: Symbol, stats: List[Tree]): List[Tree] = { + // TODO: not used? def findLazyAssignment(stats: List[Tree]): Tree = ( for (s @ Assign(lhs, _) <- stats ; if lhs.symbol hasFlag LAZY) yield s ) head // if there's no assignment then it's a bug and we crash @@ -905,11 +907,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { } else gen.mkCheckInit(accessedRef) }) - } else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) { - // add modules - val vdef = gen.mkModuleVarDef(sym) - addDef(position(sym), vdef) - addDef(position(sym), gen.mkCachedModuleAccessDef(sym, vdef.symbol)) } else if (!sym.isMethod) { // add fields addDef(position(sym), ValDef(sym)) diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 5c90a69c94..3044e61f2f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -51,11 +51,23 @@ abstract class RefChecks extends InfoTransform { new RefCheckTransformer(unit) override def changesBaseClasses = false - def transformInfo(sym: Symbol, tp: Type): Type = + def transformInfo(sym: Symbol, tp: Type): Type = { + def isNestedModule = sym.isModule && !sym.isRoot && !sym.owner.isPackageClass + if (sym.isModule && !sym.isStatic) { + sym setFlag (lateMETHOD | STABLE) + if (isNestedModule) { + val moduleVar = sym.owner.info.decl(nme.moduleVarName(sym.name)) + if (moduleVar == NoSymbol) { + gen.mkModuleVarDef(sym).symbol.setInfo(tp) + sym.resetFlag(MODULE) + sym.setFlag(LAZY | ACCESSOR) + } + } PolyType(List(), tp) } else tp + } val toJavaRepeatedParam = new TypeMap { def apply(tp: Type) = tp match { @@ -908,11 +920,12 @@ abstract class RefChecks extends InfoTransform { def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case ModuleDef(mods, name, impl) => val sym = tree.symbol - val cdef = ClassDef(mods | MODULE, name, List(), impl) - .setPos(tree.pos) - .setSymbol(sym.moduleClass) - .setType(NoType) 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) @@ -929,17 +942,48 @@ abstract class RefChecks extends InfoTransform { List(transform(cdef)) } } else { - val vdef = localTyper.typedPos(tree.pos) { gen.mkModuleVarDef(sym) } - val ddef = - atPhase(phase.next) { - localTyper.typed { - if (sym.owner.isTrait) gen.mkModuleAccessDcl(sym) - else gen.mkCachedModuleAccessDef(sym, vdef.symbol) + 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 vsym = sym.owner.info.decl(nme.moduleVarName(sym.name)) + assert(vsym != NoSymbol, "Nested object after transformInfo set module variable") + ValDef(vsym, EmptyTree) } } + val vsym = vdef.symbol - if (sym.owner.isTrait) transformTrees(List(cdef, ddef)) - else transformTrees(List(cdef, vdef, ddef)) + 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)) + } + lazyNestedObjectTrees(sym.hasFlag(LAZY)) } case ValDef(_, _, _, _) => @@ -963,11 +1007,11 @@ abstract class RefChecks extends InfoTransform { if (hasUnitType) typed(lazyDef) :: Nil else - typed(ValDef(vsym, EmptyTree)) :: typed(lazyDef) :: Nil + typed(ValDef(vsym, EmptyTree)) :: atPhase(phase.next) { typed(lazyDef) } :: Nil } else { - if (tree.symbol.isLocal && index <= currentLevel.maxindex && !tree.symbol.hasFlag(LAZY)) { + 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); + unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol) } List(tree1) } diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 52d79542c2..549e4d325f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -228,6 +228,13 @@ trait SyntheticMethods extends ast.TreeDSL { result } + def needsReadResolve = + // only nested objects inside objects should get readResolve automatically + // otherwise after de-serialization we get null references for lazy accessors (nested object -> lazy val + class def) + clazz.isSerializable && + ((!clazz.owner.isPackageClass && clazz.owner.isModuleClass) || clazz.owner.isPackageClass) + //(clazz.companionClass != NoSymbol)) + // A buffer collecting additional methods for the template body val ts = new ListBuffer[Tree] @@ -302,7 +309,7 @@ trait SyntheticMethods extends ast.TreeDSL { * the readResolve() method (see http://www.javaworld.com/javaworld/ * jw-04-2003/jw-0425-designpatterns_p.html) */ - if (clazz.isSerializable && !hasReadResolve) { + if (!hasReadResolve && needsReadResolve){ // PP: To this day I really can't figure out what this next comment is getting at: // the !!! normally means there is something broken, but if so, what is it? // diff --git a/src/library/scala/reflect/generic/StdNames.scala b/src/library/scala/reflect/generic/StdNames.scala index 7a3b9169d8..03cee8c909 100755 --- a/src/library/scala/reflect/generic/StdNames.scala +++ b/src/library/scala/reflect/generic/StdNames.scala @@ -17,10 +17,14 @@ trait StdNames { self: Universe => val ROOT = newTermName("<root>") val ROOTPKG = newTermName("_root_") val EMPTY = newTermName("") + val MODULE_SUFFIX = newTermName("$module") /** The expanded name of `name' relative to this class `base` with given `separator` */ def expandedName(name: Name, base: Symbol, separator: String = EXPAND_SEPARATOR_STRING): Name = newTermName(base.fullName('$') + separator + name) + + def moduleVarName(name: Name): Name = + newTermName(name.toString() + MODULE_SUFFIX) } } diff --git a/src/library/scala/reflect/generic/Symbols.scala b/src/library/scala/reflect/generic/Symbols.scala index f1226c7e19..a1c9a0c18d 100755 --- a/src/library/scala/reflect/generic/Symbols.scala +++ b/src/library/scala/reflect/generic/Symbols.scala @@ -107,6 +107,11 @@ trait Symbols { self: Universe => */ def moduleClass: Symbol + /** + * If symbol is a lazy val, it's lazy accessor + */ + def lazyAccessor: Symbol + // flags and kind tests def isTerm = false // to be overridden diff --git a/src/library/scala/reflect/generic/UnPickler.scala b/src/library/scala/reflect/generic/UnPickler.scala index 54ac1acc3d..b01ff6a567 100755 --- a/src/library/scala/reflect/generic/UnPickler.scala +++ b/src/library/scala/reflect/generic/UnPickler.scala @@ -191,7 +191,23 @@ abstract class UnPickler { // right member => return NoSymbol. This can only happen when unpickling a tree. // the "case Apply" in readTree() takes care of selecting the correct alternative // after parsing the arguments. - if (sym == NoSymbol && !owner.isOverloaded) errorMissingRequirement(name, owner) + if (sym == NoSymbol && !owner.isOverloaded) { + // Possibly a nested object symbol + tag match { + case EXTMODCLASSref => + val moduleVar = owner.info.decl(nme.moduleVarName(name)) + if (moduleVar.hasFlag(LAZY)) { + val lazyAcc = moduleVar.lazyAccessor + // TODO: Necessary check? + if (lazyAcc != NoSymbol) + sym = lazyAcc.lazyAccessor + } + case _ => + } + + if (sym == NoSymbol) + errorMissingRequirement(name, owner) + } case NONEsym => sym = NoSymbol diff --git a/test/files/jvm/serialization.scala b/test/files/jvm/serialization.scala index 06086f4038..b8656888c6 100644 --- a/test/files/jvm/serialization.scala +++ b/test/files/jvm/serialization.scala @@ -532,6 +532,31 @@ object Test6 { } //############################################################################ +// Nested objects cannot get readresolve automatically because after deserialization +// they would be null (they are treated as lazy vals) +@serializable +class Outer { + + @serializable + object Inner +} + +object Test7 { + val x = new Outer + x.Inner // initialize + try { + val y:Outer = read(write(x)) + if (y.Inner == null) + println("Inner object is null") + } + catch { + case e: Exception => + println("Error in Test7: " + e) + } + +} + +//############################################################################ // Test code object Test { @@ -542,6 +567,7 @@ object Test { Test4_xml Test5 Test6 + Test7 } } diff --git a/test/files/pos/t1591.scala b/test/files/pos/t1591.scala new file mode 100644 index 0000000000..4f55d7ce19 --- /dev/null +++ b/test/files/pos/t1591.scala @@ -0,0 +1,7 @@ +trait A + +object Test { + lazy val a = new A { + object Zenek + } +} diff --git a/test/files/run/t1591.check b/test/files/run/t1591.check new file mode 100644 index 0000000000..48082f72f0 --- /dev/null +++ b/test/files/run/t1591.check @@ -0,0 +1 @@ +12 diff --git a/test/files/run/t1591.scala b/test/files/run/t1591.scala new file mode 100644 index 0000000000..434064a5dd --- /dev/null +++ b/test/files/run/t1591.scala @@ -0,0 +1,14 @@ +abstract class A { + + lazy val lazyBar = bar + + object bar { + val foo = 12 + } + +} + +object Test extends Application { + val a = new A{} + println(a.lazyBar.foo) +} |