diff options
author | Paul Phillips <paulp@improving.org> | 2011-05-02 03:28:58 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-05-02 03:28:58 +0000 |
commit | cb74fc1c8a4919edacf1274aee7c9229d064a35e (patch) | |
tree | 16ea39660f83ae0978fe8adf09e390319ed4729d | |
parent | 70f18a67e5cc55e534a504b71ee26a93488b40f8 (diff) | |
download | scala-cb74fc1c8a4919edacf1274aee7c9229d064a35e.tar.gz scala-cb74fc1c8a4919edacf1274aee7c9229d064a35e.tar.bz2 scala-cb74fc1c8a4919edacf1274aee7c9229d064a35e.zip |
Figuring it couldn't hurt if more people had a ...
Figuring it couldn't hurt if more people had a command of some of
our binary compatibility impacting code, I went over the ModuleDef
elimination with my clarify stick and made the machinery more
transparent, to me anyway. Review by plocinic.
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/TreeGen.scala | 26 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/Trees.scala | 13 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Symbols.scala | 3 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 264 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 3 |
5 files changed, 150 insertions, 159 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index d232bee46d..b655507658 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -111,6 +111,11 @@ abstract class TreeGen { if (sym.owner.isClass) mkAttributedRef(sym.owner.thisType, sym) else mkAttributedIdent(sym) + /** Builds an untyped reference to given symbol. */ + def mkUnattributedRef(sym: Symbol): Tree = + if (sym.owner.isClass) Select(This(sym.owner), sym) + else Ident(sym) + /** Replaces tree type with a stable type if possible */ def stabilize(tree: Tree): Tree = { for(tp <- stableTypeFor(tree)) tree.tpe = tp @@ -255,7 +260,7 @@ abstract class TreeGen { def mkSoftRef(expr: Tree): Tree = New(TypeTree(SoftReferenceClass.tpe), List(List(expr))) def mkCached(cvar: Symbol, expr: Tree): Tree = { - val cvarRef = if (cvar.owner.isClass) Select(This(cvar.owner), cvar) else Ident(cvar) + val cvarRef = mkUnattributedRef(cvar) Block( List( If(Apply(Select(cvarRef, nme.eq), List(Literal(Constant(null)))), @@ -265,17 +270,24 @@ abstract class TreeGen { ) } + // Builds a tree of the form "{ lhs = rhs ; lhs }" + def mkAssignAndReturn(lhs: Symbol, rhs: Tree): Tree = { + val lhsRef = mkAttributedRef(lhs) + Block(Assign(lhsRef, rhs) :: Nil, lhsRef) + } + def mkModuleVarDef(accessor: Symbol) = { - val mval = accessor.owner.newVariable(accessor.pos.focus, nme.moduleVarName(accessor.name)) - .setInfo(accessor.tpe.finalResultType) - .setFlag(LAZY) - .setFlag(MODULEVAR) - mval.setLazyAccessor(accessor) + val mval = ( + accessor.owner.newVariable(accessor.pos.focus, nme.moduleVarName(accessor.name)) + setInfo accessor.tpe.finalResultType + setFlag (LAZY | MODULEVAR) + setLazyAccessor accessor + ) if (mval.owner.isClass) { mval setFlag (PRIVATE | LOCAL | SYNTHETIC) mval.owner.info.decls.enter(mval) } - ValDef(mval, EmptyTree) + ValDef(mval) } // def m: T = { if (m$ eq null) m$ = new m$class(...) m$ } diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala index 8bb7b77154..56a7ee6ec8 100644 --- a/src/compiler/scala/tools/nsc/ast/Trees.scala +++ b/src/compiler/scala/tools/nsc/ast/Trees.scala @@ -85,6 +85,12 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable => ft.result } + def changeOwner(pairs: (Symbol, Symbol)*): Tree = { + pairs.foldLeft(tree) { case (t, (oldOwner, newOwner)) => + new ChangeOwnerTraverser(oldOwner, newOwner) apply t + } + } + /** Is there part of this tree which satisfies predicate `p'? */ def exists(p: Tree => Boolean): Boolean = !find(p).isEmpty @@ -1034,13 +1040,6 @@ trait Trees extends reflect.generic.Trees { self: SymbolTable => } } - final class TreeList { - private var trees = List[Tree]() - def append(t: Tree): TreeList = { trees = t :: trees; this } - def append(ts: List[Tree]): TreeList = { trees = ts reverse_::: trees; this } - def toList: List[Tree] = trees.reverse - } - object posAssigner extends Traverser { var pos: Position = _ override def traverse(t: Tree) { diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala index faa698bfe8..524b5a047d 100644 --- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala +++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala @@ -1177,6 +1177,9 @@ trait Symbols extends reflect.generic.Symbols { self: SymbolTable => /** For a lazy value, its lazy accessor. NoSymbol for all others */ def lazyAccessor: Symbol = NoSymbol + /** If this is a lazy value, the lazy accessor; otherwise this symbol. */ + def lazyAccessorOrSelf: Symbol = if (isLazy) lazyAccessor else this + /** For an outer accessor: The class from which the outer originates. * For all other symbols: NoSymbol */ diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 099a03acbd..c08c55f4e4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -867,9 +867,6 @@ 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) { @@ -877,7 +874,7 @@ abstract class RefChecks extends InfoTransform { stat match { case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) | ValDef(_, _, _, _) => //assert(stat.symbol != NoSymbol, stat);//debug - val sym = normalizeSymToRef(stat.symbol) + val sym = stat.symbol.lazyAccessorOrSelf if (sym.isLocal) { currentLevel.scope.enter(sym) symIndex(sym) = index; @@ -1023,11 +1020,75 @@ abstract class RefChecks extends InfoTransform { override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = { pushLevel() - enterSyms(stats) - var index = -1 - val stats1 = stats flatMap { stat => index += 1; transformStat(stat, index) } - popLevel() - stats1 + try { + enterSyms(stats) + var index = -1 + stats flatMap { stat => index += 1; transformStat(stat, index) } + } + finally popLevel() + } + + /** Eliminate ModuleDefs. + * - A top level object is replaced with their module class. + * - An inner object is transformed into a module var, created on first access. + * + * In both cases, this transformation returns the list of replacement trees: + * - Top level: the module class accessor definition + * - Inner: a class definition, declaration of module var, and module var accessor + */ + private def eliminateModuleDefs(tree: Tree): List[Tree] = { + val ModuleDef(mods, name, impl) = tree + val sym = tree.symbol + + // transformedInfo check is necessary here because the object info may already + // have been transformed, and we do not want to have duplicate lazy accessors + // (through duplicate nested object -> lazy val transformation.) + val transformedInfo = sym.isLazy + val classSym = if (transformedInfo) sym.lazyAccessor else sym.moduleClass + val cdef = ClassDef(mods | MODULE, name.toTypeName, Nil, impl) setSymbol classSym setType NoType + + def findOrCreateModuleVar() = localTyper.typedPos(tree.pos) { + lazy val createModuleVar = gen.mkModuleVarDef(sym) + if (!transformedInfo) createModuleVar + else sym.owner.info.decl(nme.moduleVarName(sym.name.toTermName)) match { + // In case we are dealing with local symbol then we already have + // to correct error with forward reference + case NoSymbol => createModuleVar + case vsym => ValDef(vsym) + } + } + def createStaticModuleAccessor() = atPhase(phase.next) { + val method = ( + sym.owner.newMethod(sym.pos, sym.name.toTermName) + setFlag (sym.flags | STABLE) resetFlag MODULE setInfo NullaryMethodType(sym.moduleClass.tpe) + ) + sym.owner.info.decls enter method + localTyper.typedPos(tree.pos)(gen.mkModuleAccessDef(method, sym)) + } + def createInnerModuleAccessor(vdef: Tree) = List( + vdef, + localTyper.typedPos(tree.pos) { + val vsym = vdef.symbol + atPhase(phase.next) { + val rhs = gen.newModule(sym, vsym.tpe) + // side effecting symbol flags + if (!transformedInfo) { + sym resetFlag (MODULE | FINAL | CASE) + sym setFlag (LAZY | ACCESSOR | SYNTHETIC) + sym setInfo NullaryMethodType(sym.tpe) + sym setFlag (lateMETHOD | STABLE) + } + val body = if (sym.owner.isTrait) rhs else gen.mkAssignAndReturn(vsym, rhs) + DefDef(sym, body.changeOwner(vsym -> sym)) + } + } + ) + transformTrees(cdef :: { + if (sym.isStatic) + if (sym.allOverriddenSymbols.isEmpty) Nil + else List(createStaticModuleAccessor()) + else createInnerModuleAccessor(findOrCreateModuleVar) + }) } /** Implements lazy value accessors: @@ -1036,127 +1097,47 @@ abstract class RefChecks extends InfoTransform { * - for all other lazy values z the accessor is a block of this form: * { z = <rhs>; z } where z can be an identifier or a field. */ - 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 - def mkClassDef(transformedInfo: Boolean) = { - ClassDef(mods | MODULE, name.toTypeName, Nil, impl) - .setPos(tree.pos) - .setSymbol(if (transformedInfo) sym.lazyAccessor else sym.moduleClass) - .setType(NoType) - } - if (sym.isStatic) { - val cdef = mkClassDef(false) - - if (!sym.allOverriddenSymbols.isEmpty) { - val factory = sym.owner.newMethod(sym.pos, sym.name.toTermName) - .setFlag(sym.flags | STABLE).resetFlag(MODULE) - .setInfo(NullaryMethodType(sym.moduleClass.tpe)) - sym.owner.info.decls.enter(factory) - val ddef = - atPhase(phase.next) { - localTyper.typed { - gen.mkModuleAccessDef(factory, sym) - } - } - transformTrees(List(cdef, ddef)) - } else { - List(transform(cdef)) - } - } else { - def lazyNestedObjectTrees(transformedInfo: Boolean) = { - // transformedInfo flag is necessary here because it is possible - // that the object info was already run through the transformInfo. - // Since we do not want to have duplicate lazy accessors - // (through duplicate nested object -> lazy val transformation) we have this check here. - val cdef = mkClassDef(transformedInfo) - val vdef = localTyper.typedPos(tree.pos){ - if (!transformedInfo) - gen.mkModuleVarDef(sym) - else { - val vsym0 = sym.owner.info.decl(nme.moduleVarName(sym.name.toTermName)) - // 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 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(NullaryMethodType(sym.tpe)) - sym setFlag (lateMETHOD | STABLE) - } + private def makeLazyAccessor(tree: Tree, rhs: Tree): List[Tree] = { + val vsym = tree.symbol + assert(vsym.isTerm, vsym) + val hasUnitType = vsym.tpe.typeSymbol == UnitClass + val lazySym = vsym.lazyAccessor + assert(lazySym != NoSymbol, vsym) + + // for traits, this is further transformed in mixins + val body = ( + if (tree.symbol.owner.isTrait || hasUnitType) rhs + else gen.mkAssignAndReturn(vsym, rhs) + ) + val lazyDef = atPos(tree.pos)(DefDef(lazySym, body.changeOwner(vsym -> lazySym))) + log("Made lazy def: " + lazyDef) - 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)) - } + if (hasUnitType) List(typed(lazyDef)) + else List( + typed(ValDef(vsym)), + atPhase(phase.next)(typed(lazyDef)) + ) + } - 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) + def transformStat(tree: Tree, index: Int): List[Tree] = tree match { + case ModuleDef(_, _, _) => eliminateModuleDefs(tree) + case ValDef(_, _, _, _) => + val tree1 @ ValDef(_, _, _, rhs) = transform(tree) // important to do before forward reference check + if (tree.symbol.isLazy) + makeLazyAccessor(tree, rhs) + else { + val lazySym = tree.symbol.lazyAccessorOrSelf + if (lazySym.isLocal && index <= currentLevel.maxindex) { + if (settings.debug.value) + Console.println(currentLevel.refsym) + unit.error(currentLevel.refpos, "forward reference extends over definition of " + lazySym) } - - case Import(_, _) => - List() - - case _ => - List(transform(tree)) - } + List(tree1) + } + case Import(_, _) => Nil + case _ => List(transform(tree)) } - /******** Begin transform inner function section ********/ - - /** The private functions between here and 'transform' are conceptually - * inner functions to that method, but have been moved outside of it to - * ease the burden on the optimizer. - */ - /* Check whether argument types conform to bounds of type parameters */ private def checkBounds(pre: Type, owner: Symbol, tparams: List[Symbol], argtps: List[Type], pos: Position): Unit = try typer.infer.checkBounds(pos, pre, owner, tparams, argtps, "") @@ -1170,25 +1151,22 @@ abstract class RefChecks extends InfoTransform { () } } - private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = { - val result = pat match { - case Apply(_, args) => - val clazz = pat.tpe.typeSymbol; - clazz == seltpe.typeSymbol && - clazz.isCaseClass && - (args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable) // @PP: corresponds - case Typed(pat, tpt) => - seltpe <:< tpt.tpe - case Ident(tpnme.WILDCARD) => - true - case Bind(_, pat) => - isIrrefutable(pat, seltpe) - case _ => - false - } - //Console.println("is irefutable? " + pat + ":" + pat.tpe + " against " + seltpe + ": " + result);//DEBUG - result + private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = pat match { + case Apply(_, args) => + val clazz = pat.tpe.typeSymbol + clazz == seltpe.typeSymbol && + clazz.isCaseClass && + (args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable) + case Typed(pat, tpt) => + seltpe <:< tpt.tpe + case Ident(tpnme.WILDCARD) => + true + case Bind(_, pat) => + isIrrefutable(pat, seltpe) + case _ => + false } + /** If symbol is deprecated, and the point of reference is not enclosed * in either a deprecated member or a scala bridge method, issue a warning. */ diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 19d520e28e..8e49626df6 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3198,8 +3198,7 @@ trait Typers extends Modes { context.tree match { case ValDef(mods, _, _, Apply(Select(`tree`, _), _)) if !mods.isMutable && sym != null && sym != NoSymbol => val sym1 = if (sym.owner.isClass && sym.getter(sym.owner) != NoSymbol) sym.getter(sym.owner) - else if (sym.isLazyAccessor) sym.lazyAccessor - else sym + else sym.lazyAccessorOrSelf val pre = if (sym1.owner.isClass) sym1.owner.thisType else NoPrefix intersectionType(List(tp, singleType(pre, sym1))) case _ => tp |