diff options
Diffstat (limited to 'src')
9 files changed, 86 insertions, 69 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index 332acf4a26..3167f87383 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -242,11 +242,14 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { def mkSynchronizedCheck(clazz: Symbol, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = mkSynchronizedCheck(mkAttributedThis(clazz), cond, syncBody, stats) - def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = - Block(mkSynchronized( - attrThis, - If(cond, Block(syncBody: _*), EmptyTree)) :: - stats: _*) + def mkSynchronizedCheck(attrThis: Tree, cond: Tree, syncBody: List[Tree], stats: List[Tree]): Tree = { + def blockOrStat(stats: List[Tree]): Tree = stats match { + case head :: Nil => head + case _ => Block(stats : _*) + } + val sync = mkSynchronized(attrThis, If(cond, blockOrStat(syncBody), EmptyTree)) + blockOrStat(sync :: stats) + } /** Creates a tree representing new Object { stats }. * To make sure an anonymous subclass of Object is created, diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala index 93e41dde7b..ae2a9184b4 100644 --- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala +++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala @@ -55,9 +55,28 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0 import symtab.Flags._ + private def flattenThickets(stats: List[Tree]): List[Tree] = stats.flatMap(_ match { + case b @ Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if b.tpe == null && n1.endsWith(nme.LAZY_SLOW_SUFFIX) => + List(d1, d2) + case stat => + List(stat) + }) /** Perform the following transformations: * - for a lazy accessor inside a method, make it check the initialization bitmap + * - implement double checked locking of member modules for non-trait owners (trait just have the abstract accessor) + * ``` + * // typer + * class C { object x } + * // refchecks + * class C { var x$module; def x() = { x$module = new x; x$module } + * // lazyvals + * class C { + * var x$module // module var + * def x() = { if (x$module == null) x$lzycompute() else x$module // fast path + * def x$lzycompute() = { synchronized { if (x$module == null) x$module = new x }; x$module } // slow path + * } + * ``` * - for all methods, add enough int vars to allow one flag per lazy local value * - blocks in template bodies behave almost like methods. A single bitmaps section is * added in the first block, for all lazy values defined in such blocks. @@ -73,20 +92,14 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD case Block(_, _) => val block1 = super.transform(tree) val Block(stats, expr) = block1 - val stats1 = stats.flatMap(_ match { - case Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if (nme.newLazyValSlowComputeName(n2) == n1) => - List(d1, d2) - case stat => - List(stat) - }) - treeCopy.Block(block1, stats1, expr) + treeCopy.Block(block1, flattenThickets(stats), expr) case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) { val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) { val enclosingClassOrDummyOrMethod = { val enclMethod = sym.enclMethod - if (enclMethod != NoSymbol ) { + if (enclMethod != NoSymbol) { val enclClass = sym.enclClass if (enclClass != NoSymbol && enclMethod == enclClass.enclMethod) enclClass @@ -101,11 +114,26 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD val (rhs1, sDef) = mkLazyDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym) sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR) (rhs1, sDef) - } else + } else if (sym.hasAllFlags(MODULE | METHOD) && !sym.owner.isTrait) { + rhs match { + case b @ Block((assign @ Assign(moduleRef, _)) :: Nil, expr) => + def cond = Apply(Select(moduleRef, Object_eq), List(Literal(Constant(null)))) + val (fastPath, slowPath) = mkFastPathBody(sym.owner.enclClass, moduleRef.symbol, cond, transform(assign) :: Nil, Nil, transform(expr)) + (localTyper.typedPos(tree.pos)(fastPath), localTyper.typedPos(tree.pos)(slowPath)) + case rhs => + global.reporter.error(tree.pos, "Unexpected tree on the RHS of a module accessor: " + rhs) + (rhs, EmptyTree) + } + } else { (transform(rhs), EmptyTree) + } val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res) - if (slowPathDef != EmptyTree) Block(slowPathDef, ddef1) else ddef1 + if (slowPathDef != EmptyTree) { + // The contents of this block are flattened into the enclosing statement sequence, see flattenThickets + // This is a poor man's version of dotty's Thicket: https://github.com/lampepfl/dotty/blob/d5280358d1/src/dotty/tools/dotc/ast/Trees.scala#L707 + Block(slowPathDef, ddef1) + } else ddef1 } case Template(_, _, body) => atOwner(currentOwner) { @@ -130,7 +158,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD }) toAdd0 } else List() - deriveTemplate(tree)(_ => innerClassBitmaps ++ stats) + deriveTemplate(tree)(_ => innerClassBitmaps ++ flattenThickets(stats)) } case ValDef(_, _, _, _) if !sym.owner.isModule && !sym.owner.isClass => @@ -201,7 +229,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD } - def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], + def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): (Tree, Tree) = { val slowPathDef: Tree = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal) (If(cond, Apply(Ident(slowPathDef.symbol), Nil), retVal), slowPathDef) @@ -256,7 +284,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD (mkBlock(rhs), UNIT) } - val cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind) + def cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind) val lazyDefs = mkFastPathBody(methOrClass.enclClass, lazyVal, cond, List(block), Nil, res) (atPos(tree.pos)(localTyper.typed {lazyDefs._1 }), atPos(tree.pos)(localTyper.typed {lazyDefs._2 })) } diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala index 00a994fe87..1cea9c159a 100644 --- a/src/compiler/scala/tools/nsc/transform/Mixin.scala +++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala @@ -784,12 +784,12 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { defSym } - def mkFastPathLazyBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], + def mkFastPathLazyBody(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): Tree = { mkFastPathBody(clazz, lzyVal, cond, syncBody, stats, retVal, gen.mkAttributedThis(clazz), List()) } - def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: Tree, syncBody: List[Tree], + def mkFastPathBody(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree, attrThis: Tree, args: List[Tree]): Tree = { val slowPathSym: Symbol = mkSlowPathDef(clazz, lzyVal, cond, syncBody, stats, retVal, attrThis, args) If(cond, fn (This(clazz), slowPathSym, args.map(arg => Ident(arg.symbol)): _*), retVal) @@ -861,16 +861,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { typedPos(init.head.pos)(mkFastPathLazyBody(clazz, lzyVal, cond, syncBody, nulls, retVal)) } - def mkInnerClassAccessorDoubleChecked(attrThis: Tree, rhs: Tree, moduleSym: Symbol, args: List[Tree]): Tree = - rhs match { - case Block(List(assign), returnTree) => - val Assign(moduleVarRef, _) = assign - val cond = Apply(Select(moduleVarRef, Object_eq), List(NULL)) - mkFastPathBody(clazz, moduleSym, cond, List(assign), List(NULL), returnTree, attrThis, args) - case _ => - abort(s"Invalid getter $rhs for module in $clazz") - } - def mkCheckedAccessor(clazz: Symbol, retVal: Tree, offset: Int, pos: Position, fieldSym: Symbol): Tree = { val sym = fieldSym.getterIn(fieldSym.owner) val bitmapSym = bitmapFor(clazz, offset, sym) @@ -926,18 +916,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { deriveDefDef(stat)(rhs => Block(List(rhs, localTyper.typed(mkSetFlag(clazz, fieldOffset(getter), getter, bitmapKind(getter)))), UNIT)) else stat } - else if (sym.isModule && (!clazz.isTrait || clazz.isImplClass) && !sym.isBridge) { - deriveDefDef(stat)(rhs => - typedPos(stat.pos)( - mkInnerClassAccessorDoubleChecked( - // Martin to Hubert: I think this can be replaced by selfRef(tree.pos) - // @PP: It does not seem so, it crashes for me trying to bootstrap. - if (clazz.isImplClass) gen.mkAttributedIdent(stat.vparamss.head.head.symbol) else gen.mkAttributedThis(clazz), - rhs, sym, stat.vparamss.head - ) - ) - ) - } else stat } stats map { @@ -1082,18 +1060,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL { }) } else if (sym.isModule && !(sym hasFlag LIFTED | BRIDGE)) { - // add modules - val vsym = sym.owner.newModuleVarSymbol(sym) - addDef(position(sym), ValDef(vsym)) - - // !!! TODO - unravel the enormous duplication between this code and - // eliminateModuleDefs in RefChecks. - val rhs = gen.newModule(sym, vsym.tpe) - val assignAndRet = gen.mkAssignAndReturn(vsym, rhs) - val attrThis = gen.mkAttributedThis(clazz) - val rhs1 = mkInnerClassAccessorDoubleChecked(attrThis, assignAndRet, sym, List()) - - addDefDef(sym, rhs1) + // Moved to Refchecks } else if (!sym.isMethod) { // add fields diff --git a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala index dc3313e2e4..d5adfe12e9 100644 --- a/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala +++ b/src/compiler/scala/tools/nsc/transform/TypingTransformers.scala @@ -26,7 +26,7 @@ trait TypingTransformers { def atOwner[A](tree: Tree, owner: Symbol)(trans: => A): A = { val savedLocalTyper = localTyper - localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner) + localTyper = localTyper.atOwner(tree, if (owner.isModuleNotMethod) owner.moduleClass else owner) val result = super.atOwner(owner)(trans) localTyper = savedLocalTyper result diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7252cae0b5..915ffcc7d0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -1188,14 +1188,6 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // set NoType so it will be ignored. val cdef = ClassDef(module.moduleClass, impl) setType NoType - def newInnerObject() = { - val moduleVar = site newModuleVarSymbol module - val rhs = gen.newModule(module, moduleVar.tpe) - val body = if (site.isTrait) rhs else gen.mkAssignAndReturn(moduleVar, rhs) - val accessor = DefDef(module, body.changeOwner(moduleVar -> module)) - - ValDef(moduleVar) :: accessor :: Nil - } def matchingInnerObject() = { val newFlags = (module.flags | STABLE) & ~MODULE val newInfo = NullaryMethodType(module.moduleClass.tpe) @@ -1208,10 +1200,36 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // trait T { def f: Object }; object O extends T { object f }. Need to generate method f in O. if (module.isOverridingSymbol) matchingInnerObject() else Nil else - newInnerObject() + newInnerObject(site, module) ) transformTrees(newTrees map localTyper.typedPos(moduleDef.pos)) } + def newInnerObject(site: Symbol, module: Symbol): List[Tree] = { + if (site.isTrait) + DefDef(module, EmptyTree) :: Nil + else { + val moduleVar = site newModuleVarSymbol module + // used for the mixin case: need a new symbol owned by the subclass for the accessor, rather than repurposing the module symbol + def mkAccessorSymbol = + site.newMethod(module.name.toTermName, site.pos, STABLE | MODULE | MIXEDIN) + .setInfo(moduleVar.tpe) + .andAlso(self => if (module.isPrivate) self.expandName(module.owner)) + + val accessor = if (module.owner == site) module else mkAccessorSymbol + val accessorDef = DefDef(accessor, gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.tpe)).changeOwner(moduleVar -> accessor)) + + ValDef(moduleVar) :: accessorDef :: Nil + } + } + + def mixinModuleDefs(clazz: Symbol): List[Tree] = { + val res = for { + mixinClass <- clazz.mixinClasses.iterator + module <- mixinClass.info.decls.iterator.filter(_.isModule) + newMember <- newInnerObject(clazz, module) + } yield transform(localTyper.typedPos(clazz.pos)(newMember)) + res.toList + } def transformStat(tree: Tree, index: Int): List[Tree] = tree match { case t if treeInfo.isSelfConstrCall(t) => @@ -1643,11 +1661,12 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans // SI-7870 default getters for constructors live in the companion module checkOverloadedRestrictions(currentOwner, currentOwner.companionModule) val bridges = addVarargBridges(currentOwner) + val moduleDesugared = if (currentOwner.isTrait) Nil else mixinModuleDefs(currentOwner) checkAllOverrides(currentOwner) checkAnyValSubclass(currentOwner) if (currentOwner.isDerivedValueClass) currentOwner.primaryConstructor makeNotPrivate NoSymbol // SI-6601, must be done *after* pickler! - if (bridges.nonEmpty) deriveTemplate(tree)(_ ::: bridges) else tree + if (bridges.nonEmpty || moduleDesugared.nonEmpty) deriveTemplate(tree)(_ ::: bridges ::: moduleDesugared) else tree case dc@TypeTreeWithDeferredRefCheck() => abort("adapt should have turned dc: TypeTreeWithDeferredRefCheck into tpt: TypeTree, with tpt.original == dc") case tpt@TypeTree() => diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala index d237ccbb96..a1bec13999 100644 --- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala +++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala @@ -387,7 +387,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT val savedValid = validCurrentOwner if (owner.isClass) validCurrentOwner = true val savedLocalTyper = localTyper - localTyper = localTyper.atOwner(tree, if (owner.isModule) owner.moduleClass else owner) + localTyper = localTyper.atOwner(tree, if (owner.isModuleNotMethod) owner.moduleClass else owner) typers = typers updated (owner, localTyper) val result = super.atOwner(tree, owner)(trans) localTyper = savedLocalTyper diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index f1016e1b76..9a31634751 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -876,7 +876,7 @@ trait StdNames { val toCharacter: NameType = "toCharacter" val toInteger: NameType = "toInteger" - def newLazyValSlowComputeName(lzyValName: Name) = lzyValName append LAZY_SLOW_SUFFIX + def newLazyValSlowComputeName(lzyValName: Name) = (lzyValName stripSuffix MODULE_VAR_SUFFIX append LAZY_SLOW_SUFFIX).toTermName // ASCII names for operators val ADD = encode("+") diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index ce93841567..14afd0d42d 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -323,7 +323,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => def newModuleVarSymbol(accessor: Symbol): TermSymbol = { val newName = nme.moduleVarName(accessor.name.toTermName) val newFlags = MODULEVAR | ( if (this.isClass) PrivateLocal | SYNTHETIC else 0 ) - val newInfo = accessor.tpe.finalResultType + val newInfo = thisType.memberType(accessor).finalResultType val mval = newVariable(newName, accessor.pos.focus, newFlags.toLong) addAnnotation VolatileAttr if (this.isClass) diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala index 9853a0fa0c..f661978fcd 100644 --- a/src/reflect/scala/reflect/internal/transform/Erasure.scala +++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala @@ -123,7 +123,7 @@ trait Erasure { case tref @ TypeRef(pre, sym, args) => if (sym == ArrayClass) if (unboundedGenericArrayLevel(tp) == 1) ObjectTpe - else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectTpe) + else if (args.head.typeSymbol.isBottomClass) arrayType(ObjectTpe) else typeRef(apply(pre), sym, args map applyInArray) else if (sym == AnyClass || sym == AnyValClass || sym == SingletonClass) ObjectTpe else if (sym == UnitClass) BoxedUnitTpe |