From 256aca612204f1316e5281af6d10a14300d58ad1 Mon Sep 17 00:00:00 2001 From: Hubert Plociniczak Date: Fri, 1 Oct 2010 17:10:27 +0000 Subject: Another attempt for #1591. --- src/compiler/scala/tools/nsc/Global.scala | 2 + src/compiler/scala/tools/nsc/Phase.scala | 1 + src/compiler/scala/tools/nsc/ast/TreeGen.scala | 27 ++------ .../scala/tools/nsc/doc/model/ModelFactory.scala | 24 +++++-- src/compiler/scala/tools/nsc/symtab/StdNames.scala | 4 -- src/compiler/scala/tools/nsc/symtab/Symbols.scala | 9 ++- src/compiler/scala/tools/nsc/symtab/Types.scala | 7 +- .../scala/tools/nsc/transform/ExplicitOuter.scala | 3 +- .../scala/tools/nsc/transform/Flatten.scala | 4 +- .../scala/tools/nsc/transform/LambdaLift.scala | 2 +- src/compiler/scala/tools/nsc/transform/Mixin.scala | 9 +-- .../scala/tools/nsc/typechecker/RefChecks.scala | 75 +++++++++++++++++----- .../tools/nsc/typechecker/SyntheticMethods.scala | 10 ++- 13 files changed, 118 insertions(+), 59 deletions(-) (limited to 'src/compiler/scala/tools/nsc') diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index a52235fd90..ff3a5cdc0b 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -271,6 +271,8 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable override def devirtualized: Boolean = isDevirtualized // (part of DEVIRTUALIZE) private val isSpecialized = prev.name == "specialize" || prev.specialized override def specialized: Boolean = isSpecialized + private val isRefChecked = prev.name == "refchecks" || prev.refChecked + override def refChecked: Boolean = isRefChecked /** Is current phase cancelled on this unit? */ def cancelled(unit: CompilationUnit) = { diff --git a/src/compiler/scala/tools/nsc/Phase.scala b/src/compiler/scala/tools/nsc/Phase.scala index 8cf93f6eea..6ff932acc2 100644 --- a/src/compiler/scala/tools/nsc/Phase.scala +++ b/src/compiler/scala/tools/nsc/Phase.scala @@ -36,6 +36,7 @@ abstract class Phase(val prev: Phase) { def specialized: Boolean = false def erasedTypes: Boolean = false def flatClasses: Boolean = false + def refChecked: Boolean = false def keepsTypeParams = true def run: Unit diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index a886e7468f..34f07baef2 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -259,30 +259,17 @@ 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) } // def m: T = { if (m$ eq null) m$ = new m$class(...) m$ } @@ -295,7 +282,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/doc/model/ModelFactory.scala b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala index f7c5c5d724..e0c1453c3a 100644 --- a/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala +++ b/src/compiler/scala/tools/nsc/doc/model/ModelFactory.scala @@ -366,7 +366,12 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory members collect { case d: Constructor => d } def primaryConstructor = constructors find { _.isPrimary } } - else + else if (isNestedObjectLazyVal(bSym)) { + new DocTemplateImpl(bSym, minimumInTpl) with Object { + override def isObject = true + override def isLazyVal = false + } + } else throw new Error("'" + bSym + "' that isn't a class, trait or object cannot be built as a documentable template") } @@ -375,9 +380,14 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory def makeMember0(bSym: Symbol): Option[MemberImpl] = { if (bSym.isGetter && bSym.isLazy) - Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { - override def isLazyVal = true - }) + if (isNestedObjectLazyVal(bSym)) + if (templateShouldDocument(bSym)) + Some(makeDocTemplate(bSym, inTpl)) + else None + else + Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { + override def isLazyVal = true + }) else if (bSym.isGetter && bSym.accessed.isMutable) Some(new NonTemplateMemberImpl(bSym, inTpl) with Val { override def isVar = true @@ -561,12 +571,16 @@ class ModelFactory(val global: Global, val settings: doc.Settings) { thisFactory ( aSym.owner == NoSymbol || templateShouldDocument(aSym.owner) ) && !isEmptyJavaObject(aSym) } + def isNestedObjectLazyVal(aSym: Symbol): Boolean = { + aSym.isLazy && !aSym.isRootPackage && !aSym.owner.isPackageClass && (aSym.lazyAccessor != NoSymbol) + } + def isEmptyJavaObject(aSym: Symbol): Boolean = { def hasMembers = aSym.info.members.exists(s => localShouldDocument(s) && (!s.isConstructor || s.owner == aSym)) aSym.isModule && aSym.hasFlag(Flags.JAVA) && !hasMembers } def localShouldDocument(aSym: Symbol): Boolean = { - !aSym.isPrivate && (aSym.isProtected || aSym.privateWithin == NoSymbol) && !aSym.isSynthetic + !aSym.isPrivate && (aSym.isProtected || aSym.privateWithin == NoSymbol) && (!aSym.isSynthetic || isNestedObjectLazyVal(aSym)) } } diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala index eee61a4312..832cc22c54 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("") @@ -181,9 +180,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/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index b5e3366893..7a0799a897 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -1275,10 +1275,13 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => /** A helper method that factors the common code used the discover a companion module of a class. If a companion * module exists, its symbol is returned, otherwise, `NoSymbol` is returned. The method assumes that `this` - * symbol has already been checked to be a class (using `isClass`). */ - private final def companionModule0: Symbol = + * symbol has already been checked to be a class (using `isClass`). + * After refchecks nested objects get transformed to lazy vals so we filter on LAZY flag*/ + private final def companionModule0: Symbol = { + val f = if (phase.refChecked && isNestedClass) LAZY else MODULE flatOwnerInfo.decl(name.toTermName).suchThat( - sym => (sym hasFlag MODULE) && (sym isCoDefinedWith this)) + sym => (sym hasFlag f) && (sym isCoDefinedWith this)) + } /** The module or case class factory with the same name in the same * package as this class. diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala index d5dfc37a31..2535a38eb3 100644 --- a/src/compiler/scala/tools/nsc/symtab/Types.scala +++ b/src/compiler/scala/tools/nsc/symtab/Types.scala @@ -3706,7 +3706,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 { @@ -4593,7 +4596,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/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index 160a0d305e..c4251ffeea 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -162,7 +162,8 @@ abstract class ExplicitOuter extends InfoTransform // On the other hand, mixing in the trait into a separately compiled // class needs to have a common naming scheme, independently of whether // the field was accessed from an inner class or not. See #2946 - if (sym.owner.isTrait && (sym hasFlag LOCAL) && (sym.getter(sym.owner.toInterface) == NoSymbol)) + if (sym.owner.isTrait && (sym hasFlag LOCAL) && + ((sym.getter(sym.owner.toInterface) == NoSymbol) && (!sym.isLazy || (sym.lazyAccessor == NoSymbol)))) sym.makeNotPrivate(sym.owner) tp } 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 342106697d..ecf68fa922 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -153,7 +153,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.isLazy)) && !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 9cd1a68dd0..94e5e2561b 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -415,6 +415,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 @@ -442,7 +443,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 = _ @@ -732,6 +733,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 @@ -918,11 +920,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 d7b8d6f2c5..08fb0438de 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -51,11 +51,24 @@ 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.isRootPackage && !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) { + val tree = gen.mkModuleVarDef(sym) + tree.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 +921,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 +943,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,9 +1008,9 @@ 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); diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala index 896ceb5bb6..c719f5e546 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala @@ -228,6 +228,14 @@ 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) + // since the bitmap gets serialized but the moduleVar not + clazz.isSerializable && + ((!clazz.owner.isPackageClass && clazz.owner.isModuleClass) || clazz.owner.isPackageClass) + ) + // A buffer collecting additional methods for the template body val ts = new ListBuffer[Tree] @@ -302,7 +310,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? // -- cgit v1.2.3