From 2e5ab542921bd5cf16cda398dc9e40e6af14dd72 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 17:32:49 +0200 Subject: [PATCH 01/25] Fix #1220. Dont die when having incorect static methods --- src/dotty/tools/dotc/transform/CheckStatic.scala | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala index 77c6dfc51952..2275331a5050 100644 --- a/src/dotty/tools/dotc/transform/CheckStatic.scala +++ b/src/dotty/tools/dotc/transform/CheckStatic.scala @@ -57,16 +57,13 @@ class CheckStatic extends MiniPhaseTransform { thisTransformer => } val companion = ctx.owner.companionClass + def clashes = companion.asClass.membersNamed(defn.name) + if (!companion.exists) { ctx.error("object that conatin @static members should have companion class", defn.pos) - } - - val clashes = companion.asClass.membersNamed(defn.name) - if (clashes.exists) { + } else if (clashes.exists) { ctx.error("companion classes cannot define members with same name as @static member", defn.pos) - } - - if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) { + } else if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) { ctx.error("Companions of traits cannot define mutable @static fields") } } else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef] From 7408f144589079ca5d0087e1d9ae3f44c5882f79 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 17:33:20 +0200 Subject: [PATCH 02/25] Test #1220 --- tests/neg/static-no-companion.scala | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/neg/static-no-companion.scala diff --git a/tests/neg/static-no-companion.scala b/tests/neg/static-no-companion.scala new file mode 100644 index 000000000000..982dddf88ae9 --- /dev/null +++ b/tests/neg/static-no-companion.scala @@ -0,0 +1,4 @@ +import annotation.static +object T { + @static val foo = 10 // error: needs companion class +} From bbd48d3d52449a379ea31f8619c5f5e5f9f1aaac Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:26:31 +0200 Subject: [PATCH 03/25] SymDenotations: Allow entering Static symbols. They aren't inherited and can be entered into frozen owners. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ff99f3b5527e..586e4db7fecc 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1499,7 +1499,7 @@ object SymDenotations { /** Enter a symbol in given `scope` without potentially replacing the old copy. */ def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = { - require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen) || (scope ne this.unforcedDecls)) + require((sym.denot.flagsUNSAFE is Private) || !(this is Frozen) || (scope ne this.unforcedDecls) || sym.hasAnnotation(defn.ScalaStaticAnnot)) scope.enter(sym) if (myMemberFingerPrint != FingerPrint.unknown) From 806a0294f6055122c1eb2f32801f14b71f35ae5b Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:27:10 +0200 Subject: [PATCH 04/25] Constructors: do not lift static val initialisation into constructors. --- src/dotty/tools/dotc/transform/Constructors.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 44638ce4892e..db850e944a25 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -91,7 +91,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th */ override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = { tree match { - case tree: ValDef if tree.symbol.exists && tree.symbol.owner.isClass && !tree.symbol.is(Lazy) => + case tree: ValDef if tree.symbol.exists && tree.symbol.owner.isClass && !tree.symbol.is(Lazy) && !tree.symbol.hasAnnotation(defn.ScalaStaticAnnot) => assert(tree.rhs.isEmpty, i"$tree: initializer should be moved to constructors") case tree: DefDef if !tree.symbol.is(LazyOrDeferred) => assert(!tree.rhs.isEmpty, i"unimplemented: $tree") @@ -181,7 +181,7 @@ class Constructors extends MiniPhaseTransform with IdentityDenotTransformer { th def splitStats(stats: List[Tree]): Unit = stats match { case stat :: stats1 => stat match { - case stat @ ValDef(name, tpt, _) if !stat.symbol.is(Lazy) => + case stat @ ValDef(name, tpt, _) if !stat.symbol.is(Lazy) && !stat.symbol.hasAnnotation(defn.ScalaStaticAnnot) => val sym = stat.symbol if (isRetained(sym)) { if (!stat.rhs.isEmpty && !isWildcardArg(stat.rhs)) From db6a07d445ab1e31888a779b70d41ffda129bb66 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:27:22 +0200 Subject: [PATCH 05/25] Getters: do not generate getters for static vals --- src/dotty/tools/dotc/transform/Getters.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala index 75235d0f5b8a..31171dfabe5e 100644 --- a/src/dotty/tools/dotc/transform/Getters.scala +++ b/src/dotty/tools/dotc/transform/Getters.scala @@ -56,6 +56,7 @@ class Getters extends MiniPhaseTransform with SymTransformer { thisTransform => d.is(NoGetterNeeded) || d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) && !isDerivedValueClass(d.owner) && !d.is(Flags.Lazy) || d.is(Module) && d.isStatic || + d.hasAnnotation(defn.ScalaStaticAnnot) || d.isSelfSym if (d.isTerm && (d.is(Lazy) || d.owner.isClass) && d.info.isValueType && !noGetterNeeded) { val maybeStable = if (d.isStable) Stable else EmptyFlags From 4ce8ab06470db959aed3527f013a261bc4c3c9a0 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:28:04 +0200 Subject: [PATCH 06/25] Allow creating static initialisers. --- src/dotty/tools/dotc/core/Names.scala | 1 + src/dotty/tools/dotc/transform/TreeChecker.scala | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Names.scala b/src/dotty/tools/dotc/core/Names.scala index 223d9504505a..11f0b55a8dbe 100644 --- a/src/dotty/tools/dotc/core/Names.scala +++ b/src/dotty/tools/dotc/core/Names.scala @@ -335,6 +335,7 @@ object Names { // can't move CONSTRUCTOR/EMPTY_PACKAGE to `nme` because of bootstrap failures in `encode`. val CONSTRUCTOR = termName("") + val STATIC_CONSTRUCTOR = termName("") val EMPTY_PACKAGE = termName("") val dontEncode = Set(CONSTRUCTOR, EMPTY_PACKAGE) diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 0dce0fd36ab3..ce160d7b0550 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -26,6 +26,9 @@ import collection.mutable import ProtoTypes._ import config.Printers import java.lang.AssertionError + +import dotty.tools.dotc.core.Names + import scala.util.control.NonFatal /** Run by -Ycheck option after a given phase, this class retypes all syntax trees @@ -382,7 +385,7 @@ class TreeChecker extends Phase with SymTransformer { override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = withDefinedSyms(ddef.tparams) { withDefinedSymss(ddef.vparamss) { - if (!sym.isClassConstructor) assert(isValidJVMMethodName(sym.name), s"${sym.fullName} name is invalid on jvm") + if (!sym.isClassConstructor && !(sym.name eq Names.STATIC_CONSTRUCTOR)) assert(isValidJVMMethodName(sym.name), s"${sym.fullName} name is invalid on jvm") val tpdTree = super.typedDefDef(ddef, sym) assert(isMethodType(sym.info), i"wrong type, expect a method type for ${sym.fullName}, but found: ${sym.info}") tpdTree From 27846bb2ba8519decbfe4a152460805e591ec98b Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:29:41 +0200 Subject: [PATCH 07/25] MoveStatic: Move static methods & fields into companion class As a funny side-effect this allows to execute arbitrary code in static initialisers: @static val a: Unit = {println("loaded")} --- .../backend/jvm/DottyBackendInterface.scala | 9 ++- src/dotty/tools/dotc/Compiler.scala | 1 + .../tools/dotc/transform/MoveStatics.scala | 64 +++++++++++++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/MoveStatics.scala diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 002a6bf27189..295a419f89f2 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -12,7 +12,7 @@ import scala.collection.generic.Clearable import scala.collection.mutable import scala.reflect.ClassTag import scala.reflect.internal.util.WeakHashSet -import scala.reflect.io.{Directory, PlainDirectory, AbstractFile} +import scala.reflect.io.{AbstractFile, Directory, PlainDirectory} import scala.tools.asm.{AnnotationVisitor, ClassVisitor, FieldVisitor, MethodVisitor} import scala.tools.nsc.backend.jvm.{BCodeHelpers, BackendInterface} import dotty.tools.dotc.core._ @@ -24,13 +24,16 @@ import Symbols._ import Denotations._ import Phases._ import java.lang.AssertionError -import dotty.tools.dotc.util.{Positions, DotClass} + +import dotty.tools.dotc.util.{DotClass, Positions} import Decorators._ import tpd._ + import scala.tools.asm import NameOps._ import StdNames.nme import NameOps._ +import dotty.tools.dotc.core class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context) extends BackendInterface{ type Symbol = Symbols.Symbol @@ -633,7 +636,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context toDenot(sym)(shiftedContext).isStatic(shiftedContext) } - def isStaticConstructor: Boolean = isStaticMember && isClassConstructor + def isStaticConstructor: Boolean = (isStaticMember && isClassConstructor) || (sym.name eq core.Names.STATIC_CONSTRUCTOR) // navigation diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 2ddd817185d2..3844f42a715c 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -91,6 +91,7 @@ class Compiler { new RestoreScopes), // Repair scopes rendered invalid by moving definitions in prior phases of the group List(new ExpandPrivate, // Widen private definitions accessed from nested classes new CollectEntryPoints, // Find classes with main methods + new MoveStatics, // Move static methods to companion classes new LabelDefs), // Converts calls to labels to jumps List(new GenSJSIR), // Generate .js code List(new GenBCode) // Generate JVM bytecode diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala new file mode 100644 index 000000000000..c7cc947aade8 --- /dev/null +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -0,0 +1,64 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.ast.{Trees, tpd} +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, SymTransformer} +import dotty.tools.dotc.core.SymDenotations.SymDenotation +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.NameOps._ +import dotty.tools.dotc.core.{Flags, Names} +import dotty.tools.dotc.core.Names.Name +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Types.MethodType +import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo} + +/** Move static methods from companion to the class itself */ +class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransformer => + + import tpd._ + override def phaseName = "moveStatic" + + + def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { + if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module)) { + sym.owner.companionClass.asClass.enter(sym.symbol) + val flags = if (sym.is(Flags.Method)) sym.flags else sym.flags | Flags.Mutable + sym.copySymDenotation(owner = sym.owner.companionClass, initFlags = flags) + } + else sym + } + + override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = { + if (ctx.owner.is(Flags.Package)) { + val (classes, others) = trees.partition(x => x.isInstanceOf[TypeDef] && x.symbol.isClass) + val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]] + + def move(companion: TypeDef, module: TypeDef): Thicket = { + if (companion.symbol.is(Flags.Module)) move(module, companion) + else { + val allMembers = companion.rhs.asInstanceOf[Template].body ++ module.rhs.asInstanceOf[Template].body + val (newCompanionBody, newModuleBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == companion.symbol}) + def rebuild(orig: TypeDef, newBody: List[Tree]) = { + val oldTemplate = orig.rhs.asInstanceOf[Template] + val statics = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] + val newBodyWithStaticConstr = + if (statics.nonEmpty) { + val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.JavaStatic | Flags.Method, MethodType(Nil, defn.UnitType)) + val staticAssigns = statics.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) + tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral)) :: newBody + } else newBody + + cpy.TypeDef(orig)(rhs = cpy.Template(orig.rhs)(oldTemplate.constr, oldTemplate.parents, oldTemplate.self, newBodyWithStaticConstr)) + } + Thicket(rebuild(companion, newCompanionBody), rebuild(module, newModuleBody)) + } + } + val newPairs = + for ((name, classes) <- pairs) + yield + if (classes.tail.isEmpty) classes.head + else move(classes.head, classes.tail.head) + Trees.flatten(newPairs.toList) + } else trees + } +} From 63eb88da7358f3387b499c36422607564aaa0d47 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:44:55 +0200 Subject: [PATCH 08/25] Drop support for @static lazy vals. It's not clear how they should be implemented. --- src/dotty/tools/dotc/transform/CheckStatic.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala index 2275331a5050..d47c8813081e 100644 --- a/src/dotty/tools/dotc/transform/CheckStatic.scala +++ b/src/dotty/tools/dotc/transform/CheckStatic.scala @@ -32,6 +32,7 @@ import TypeUtils._ * is not allowed to inherit classes that define a term member with name `foo`. * 5. Only `@static` methods and vals are supported in companions of traits. * Java8 supports those, but not vars, and JavaScript does not have interfaces at all. + * 6. `@static` Lazy vals are currently unsupported. */ class CheckStatic extends MiniPhaseTransform { thisTransformer => import ast.tpd._ @@ -65,6 +66,8 @@ class CheckStatic extends MiniPhaseTransform { thisTransformer => ctx.error("companion classes cannot define members with same name as @static member", defn.pos) } else if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) { ctx.error("Companions of traits cannot define mutable @static fields") + } else if (defn.symbol.is(Flags.Lazy)) { + ctx.error("Lazy @static fields are not supported") } } else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef] From ce2b9642c35c15bff80dfc274248f13a5c1a32d1 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:48:49 +0200 Subject: [PATCH 09/25] Fix type in SymDenotations. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 586e4db7fecc..d3e373dd0353 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1001,7 +1001,7 @@ object SymDenotations { if (!canMatchInheritedSymbols) Iterator.empty else overriddenFromType(owner.info) - /** Returns all all matching symbols defined in parents of the selftype. */ + /** Returns all matching symbols defined in parents of the selftype. */ final def extendedOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] = if (!canMatchInheritedSymbols) Iterator.empty else overriddenFromType(owner.asClass.classInfo.selfType) From 7cacd0f0625654b408c11fe37553342f027c8043 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:49:35 +0200 Subject: [PATCH 10/25] Fix #1224: static members do not override\implement parent symbols. --- src/dotty/tools/dotc/transform/CheckStatic.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala index d47c8813081e..3a452ca08563 100644 --- a/src/dotty/tools/dotc/transform/CheckStatic.scala +++ b/src/dotty/tools/dotc/transform/CheckStatic.scala @@ -61,13 +61,15 @@ class CheckStatic extends MiniPhaseTransform { thisTransformer => def clashes = companion.asClass.membersNamed(defn.name) if (!companion.exists) { - ctx.error("object that conatin @static members should have companion class", defn.pos) + ctx.error("object that contains @static members should have companion class", defn.pos) } else if (clashes.exists) { ctx.error("companion classes cannot define members with same name as @static member", defn.pos) } else if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) { - ctx.error("Companions of traits cannot define mutable @static fields") + ctx.error("Companions of traits cannot define mutable @static fields", defn.pos) } else if (defn.symbol.is(Flags.Lazy)) { - ctx.error("Lazy @static fields are not supported") + ctx.error("Lazy @static fields are not supported", defn.pos) + } else if (defn.symbol.allOverriddenSymbols.nonEmpty) { + ctx.error("@static members cannot override or implement non-static ones") } } else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef] From 1932b171d1043c9702f5b393a9a3f36b584fe41a Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Mon, 18 Apr 2016 21:49:49 +0200 Subject: [PATCH 11/25] Test #1224. --- tests/neg/static-implements.scala | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/neg/static-implements.scala diff --git a/tests/neg/static-implements.scala b/tests/neg/static-implements.scala new file mode 100644 index 000000000000..8e8a8800cc15 --- /dev/null +++ b/tests/neg/static-implements.scala @@ -0,0 +1,11 @@ +import annotation.static + +abstract class A { def x: Int } + +class T +object T extends A { + @static override val x = 10 // error: static methods cannot implement stuff + def main(args: Array[String]): Unit = { + println((this: A).x) + } +} From f650b1ef465dd99b43b7178258846c66fcf6cd67 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 11:33:12 +0200 Subject: [PATCH 12/25] LazyVals: do not share offsets between companions. There used to be a rare test when companion class and companion object would have gotten the very same offset, causing undefined behaviour in runtime. --- src/dotty/tools/dotc/transform/LazyVals.scala | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index e42c7bae9ece..af3666aa3dfa 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -26,7 +26,7 @@ import dotty.tools.dotc.core.SymDenotations.SymDenotation import dotty.tools.dotc.core.DenotTransformers.{SymTransformer, IdentityDenotTransformer, DenotTransformer} import Erasure.Boxing.adaptToType -class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with NeedsCompanions { +class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { import LazyVals._ import tpd._ @@ -49,11 +49,6 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee * before this phase starts processing same tree */ override def runsAfter = Set(classOf[Mixin]) - def isCompanionNeeded(cls: ClassSymbol)(implicit ctx: Context): Boolean = { - def hasLazyVal(cls: ClassSymbol) = cls.info.decls.exists(_.is(Flags.Lazy)) - hasLazyVal(cls) || cls.mixins.exists(hasLazyVal) - } - override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = transformLazyVal(tree) @@ -341,26 +336,28 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee val tpe = x.tpe.widen.resultType.widen val claz = x.symbol.owner.asClass val thizClass = Literal(Constant(claz.info)) - val companion = claz.companionModule val helperModule = ctx.requiredModule("dotty.runtime.LazyVals") val getOffset = Select(ref(helperModule), lazyNme.RLazyVals.getOffset) var offsetSymbol: TermSymbol = null var flag: Tree = EmptyTree var ord = 0 + def offsetName(id: Int) = (StdNames.nme.LAZY_FIELD_OFFSET + (if(x.symbol.owner.is(Flags.Module)) "_m_" else "") + id.toString).toTermName + // compute or create appropriate offsetSymol, bitmap and bits used by current ValDef - appendOffsetDefs.get(companion.moduleClass) match { + appendOffsetDefs.get(claz) match { case Some(info) => val flagsPerLong = (64 / dotty.runtime.LazyVals.BITS_PER_LAZY_VAL).toInt info.ord += 1 ord = info.ord % flagsPerLong val id = info.ord / flagsPerLong + val offsetById = offsetName(id) if (ord != 0) { // there are unused bits in already existing flag - offsetSymbol = companion.moduleClass.info.decl((StdNames.nme.LAZY_FIELD_OFFSET + id.toString).toTermName) + offsetSymbol = claz.moduleClass.info.decl(offsetById) .suchThat(sym => (sym is Flags.Synthetic) && sym.isTerm) .symbol.asTerm } else { // need to create a new flag - offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + id.toString).toTermName, Flags.Synthetic, defn.LongType).enteredAfter(this) + offsetSymbol = ctx.newSymbol(claz, offsetById, Flags.Synthetic, defn.LongType).enteredAfter(this) offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)) val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) @@ -370,13 +367,13 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee } case None => - offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + "0").toTermName, Flags.Synthetic, defn.LongType).enteredAfter(this) + offsetSymbol = ctx.newSymbol(claz, offsetName(0), Flags.Synthetic, defn.LongType).enteredAfter(this) offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)) val flagName = (StdNames.nme.BITMAP_PREFIX + "0").toTermName val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this) flag = ValDef(flagSymbol, Literal(Constants.Constant(0L))) val offsetTree = ValDef(offsetSymbol, getOffset.appliedTo(thizClass, Literal(Constant(flagName.toString)))) - appendOffsetDefs += (companion.moduleClass -> new OffsetInfo(List(offsetTree), ord)) + appendOffsetDefs += (claz -> new OffsetInfo(List(offsetTree), ord)) } val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName From 61fe99b6ad0fcd0a8402435cf15c504ce4b9c4ea Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 11:34:12 +0200 Subject: [PATCH 13/25] MoveStatics: fix two bugs. Unlink the static from the old scope, and don't drop top-level trees that are not TypeDefs. --- src/dotty/tools/dotc/transform/MoveStatics.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala index c7cc947aade8..23f19379b73e 100644 --- a/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -21,6 +21,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module)) { + sym.owner.asClass.delete(sym.symbol) sym.owner.companionClass.asClass.enter(sym.symbol) val flags = if (sym.is(Flags.Method)) sym.flags else sym.flags | Flags.Mutable sym.copySymDenotation(owner = sym.owner.companionClass, initFlags = flags) @@ -58,7 +59,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform yield if (classes.tail.isEmpty) classes.head else move(classes.head, classes.tail.head) - Trees.flatten(newPairs.toList) + Trees.flatten(newPairs.toList ++ others) } else trees } } From d9702d2173b7813a3a463f2cd93ac19284ca83ff Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 11:34:53 +0200 Subject: [PATCH 14/25] Fix Ycheck: allow assigning fields in static constructors. --- src/dotty/tools/dotc/core/NameOps.scala | 1 + src/dotty/tools/dotc/core/SymDenotations.scala | 4 ++++ src/dotty/tools/dotc/typer/Typer.scala | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 72f89aec3b37..17af899e9150 100644 --- a/src/dotty/tools/dotc/core/NameOps.scala +++ b/src/dotty/tools/dotc/core/NameOps.scala @@ -63,6 +63,7 @@ object NameOps { (if (name.isTermName) n.toTermName else n.toTypeName).asInstanceOf[N] def isConstructorName = name == CONSTRUCTOR || name == TRAIT_CONSTRUCTOR + def isStaticConstructorName = name == STATIC_CONSTRUCTOR def isExceptionResultName = name startsWith EXCEPTION_RESULT_PREFIX def isImplClassName = name endsWith IMPL_CLASS_SUFFIX def isLocalDummyName = name startsWith LOCALDUMMY_PREFIX diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index d3e373dd0353..ad454037b657 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -594,6 +594,10 @@ object SymDenotations { final def isPrimaryConstructor(implicit ctx: Context) = isConstructor && owner.primaryConstructor == symbol + /** Does this symbol denote the primary constructor of its enclosing class? */ + final def isStaticConstructor(implicit ctx: Context) = + name.isStaticConstructorName + /** Is this a subclass of the given class `base`? */ def isSubClass(base: Symbol)(implicit ctx: Context) = false diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 4f27912f115d..aa00fea560ee 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -481,7 +481,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def canAssign(sym: Symbol) = // allow assignments from the primary constructor to class fields sym.is(Mutable, butNot = Accessor) || ctx.owner.isPrimaryConstructor && !sym.is(Method) && sym.owner == ctx.owner.owner || - ctx.owner.name.isTraitSetterName + ctx.owner.name.isTraitSetterName || ctx.owner.isStaticConstructor lhsCore.tpe match { case ref: TermRef if canAssign(ref.symbol) => assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info))) From e1fcb4c8a70d2ffa32d22bef707bb87dd0acf439 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 11:35:47 +0200 Subject: [PATCH 15/25] LazyVals: support debug mode. Helps to spot usage of unsafe that would lead to undefined behaviour. --- src/dotty/runtime/LazyVals.scala | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/dotty/runtime/LazyVals.scala b/src/dotty/runtime/LazyVals.scala index f09e96f57851..e42532ce0db8 100644 --- a/src/dotty/runtime/LazyVals.scala +++ b/src/dotty/runtime/LazyVals.scala @@ -10,14 +10,24 @@ object LazyVals { final val BITS_PER_LAZY_VAL = 2L final val LAZY_VAL_MASK = 3L + final val debug = false - @inline def STATE(cur: Long, ord: Int) = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK + @inline def STATE(cur: Long, ord: Int) = { + val r = (cur >> (ord * BITS_PER_LAZY_VAL)) & LAZY_VAL_MASK + if (debug) + println(s"STATE($cur, $ord) = $r") + r + } @inline def CAS(t: Object, offset: Long, e: Long, v: Int, ord: Int) = { + if (debug) + println(s"CAS($t, $offset, $e, $v, $ord)") val mask = ~(LAZY_VAL_MASK << ord * BITS_PER_LAZY_VAL) val n = (e & mask) | (v << (ord * BITS_PER_LAZY_VAL)) compareAndSet(t, offset, e, n) } @inline def setFlag(t: Object, offset: Long, v: Int, ord: Int) = { + if (debug) + println(s"setFlag($t, $offset, $v, $ord)") var retry = true while (retry) { val cur = get(t, offset) @@ -35,6 +45,8 @@ object LazyVals { } } @inline def wait4Notification(t: Object, offset: Long, cur: Long, ord: Int) = { + if (debug) + println(s"wait4Notification($t, $offset, $cur, $ord)") var retry = true while (retry) { val cur = get(t, offset) @@ -68,7 +80,12 @@ object LazyVals { monitors(id) } - @inline def getOffset(clz: Class[_], name: String) = unsafe.objectFieldOffset(clz.getDeclaredField(name)) + @inline def getOffset(clz: Class[_], name: String) = { + val r = unsafe.objectFieldOffset(clz.getDeclaredField(name)) + if (debug) + println(s"getOffset($clz, $name) = $r") + r + } object Names { final val state = "STATE" From 990e962d50d5d688eee72fd64293e5fc8ce70617 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 11:41:43 +0200 Subject: [PATCH 16/25] SymDenotations: fix comment. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ad454037b657..5c4e120a81fe 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -594,7 +594,7 @@ object SymDenotations { final def isPrimaryConstructor(implicit ctx: Context) = isConstructor && owner.primaryConstructor == symbol - /** Does this symbol denote the primary constructor of its enclosing class? */ + /** Does this symbol denote the static constructor of its enclosing class? */ final def isStaticConstructor(implicit ctx: Context) = name.isStaticConstructorName From f2cfac5771b1b19c733e7be9b3d05dae411bc9e3 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 14:01:11 +0200 Subject: [PATCH 17/25] MoveStatics: survive absence of companions. Now moveStatics can correctly create static constructors for objects. Those static constructors would later be merged with synthetic module initialisers by GenBCode. This is a bit of magic, it would be good to move all this into this phase. --- .../tools/dotc/transform/MoveStatics.scala | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala index 23f19379b73e..7b9d27c2bf5d 100644 --- a/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -1,6 +1,7 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.{Trees, tpd} +import dotty.tools.dotc.core.Annotations.Annotation import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.DenotTransformers.{InfoTransformer, SymTransformer} import dotty.tools.dotc.core.SymDenotations.SymDenotation @@ -20,7 +21,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = { - if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module)) { + if (sym.hasAnnotation(defn.ScalaStaticAnnot) && sym.owner.is(Flags.Module) && sym.owner.companionClass.exists) { sym.owner.asClass.delete(sym.symbol) sym.owner.companionClass.asClass.enter(sym.symbol) val flags = if (sym.is(Flags.Method)) sym.flags else sym.flags | Flags.Mutable @@ -34,32 +35,41 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform val (classes, others) = trees.partition(x => x.isInstanceOf[TypeDef] && x.symbol.isClass) val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]] - def move(companion: TypeDef, module: TypeDef): Thicket = { - if (companion.symbol.is(Flags.Module)) move(module, companion) + def move(module: TypeDef, companion: TypeDef): List[Tree] = { + if (!module.symbol.is(Flags.Module)) move(companion, module) else { - val allMembers = companion.rhs.asInstanceOf[Template].body ++ module.rhs.asInstanceOf[Template].body - val (newCompanionBody, newModuleBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == companion.symbol}) - def rebuild(orig: TypeDef, newBody: List[Tree]) = { - val oldTemplate = orig.rhs.asInstanceOf[Template] - val statics = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] + val allMembers = + if(companion ne null) {companion.rhs.asInstanceOf[Template].body} else Nil ++ + module.rhs.asInstanceOf[Template].body + val (newModuleBody, newCompanionBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == module.symbol}) + def rebuild(orig: TypeDef, newBody: List[Tree]): Tree = { + if (orig eq null) return EmptyTree + + val staticFields = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] val newBodyWithStaticConstr = - if (statics.nonEmpty) { - val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.JavaStatic | Flags.Method, MethodType(Nil, defn.UnitType)) - val staticAssigns = statics.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) + if (staticFields.nonEmpty) { + val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method, MethodType(Nil, defn.UnitType)) + staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot)) + staticCostructor.entered + + val staticAssigns = staticFields.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral)) :: newBody } else newBody + val oldTemplate = orig.rhs.asInstanceOf[Template] cpy.TypeDef(orig)(rhs = cpy.Template(orig.rhs)(oldTemplate.constr, oldTemplate.parents, oldTemplate.self, newBodyWithStaticConstr)) } - Thicket(rebuild(companion, newCompanionBody), rebuild(module, newModuleBody)) + Trees.flatten(rebuild(companion, newCompanionBody) :: rebuild(module, newModuleBody) :: Nil) } } val newPairs = for ((name, classes) <- pairs) yield - if (classes.tail.isEmpty) classes.head + if (classes.tail.isEmpty) + if (classes.head.symbol.is(Flags.Module)) move(classes.head, null) + else List(classes.head) else move(classes.head, classes.tail.head) - Trees.flatten(newPairs.toList ++ others) + Trees.flatten(newPairs.toList.flatten ++ others) } else trees } } From fa6deee45d76cac09b23d27120c80d525d844e45 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 14:02:02 +0200 Subject: [PATCH 18/25] DottyBackendInterface: fix a bug in methodSymbols. This method is only used to find static initialisers. Previously, it was always wrong, but we didn't care as we never had them. --- src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 295a419f89f2..23073d317774 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -719,7 +719,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context toDenot(sym).info.decls.filter(p => p.isTerm && !p.is(Flags.Method)).toList } def methodSymbols: List[Symbol] = - for (f <- toDenot(sym).info.decls.toList if !f.isMethod && f.isTerm && !f.isModule) yield f + for (f <- toDenot(sym).info.decls.toList if f.isMethod && f.isTerm && !f.isModule) yield f def serialVUID: Option[Long] = None From 5f73175aeed3fd769345dab21bc3a52d9b8915e4 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 14:02:31 +0200 Subject: [PATCH 19/25] Add tests that were used to reproduce issues with LazyVals. --- tests/run/Lazies1.scala | 6 ++++++ tests/run/Lazies2.scala | 8 ++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/run/Lazies1.scala create mode 100644 tests/run/Lazies2.scala diff --git a/tests/run/Lazies1.scala b/tests/run/Lazies1.scala new file mode 100644 index 000000000000..34fecaf80da3 --- /dev/null +++ b/tests/run/Lazies1.scala @@ -0,0 +1,6 @@ +object T{ @volatile lazy val s = null} +object Test{ + def main(args: Array[String]): Unit = { + T.s + } +} diff --git a/tests/run/Lazies2.scala b/tests/run/Lazies2.scala new file mode 100644 index 000000000000..6b9aa8a39343 --- /dev/null +++ b/tests/run/Lazies2.scala @@ -0,0 +1,8 @@ +class T{ @volatile lazy val s = null} +object T{ @volatile lazy val s = null} +object Test{ + def main(args: Array[String]): Unit = { + T.s + (new T).s + } +} From 49ace48a4d8032573f84ee7482430ce8529e2c86 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 14:48:56 +0200 Subject: [PATCH 20/25] MoveStatics: fix a bug. Nicely spotted by Ycheck. --- src/dotty/tools/dotc/transform/MoveStatics.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala index 7b9d27c2bf5d..8c1fe309b1a9 100644 --- a/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -39,7 +39,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform if (!module.symbol.is(Flags.Module)) move(companion, module) else { val allMembers = - if(companion ne null) {companion.rhs.asInstanceOf[Template].body} else Nil ++ + (if(companion ne null) {companion.rhs.asInstanceOf[Template].body} else Nil) ++ module.rhs.asInstanceOf[Template].body val (newModuleBody, newCompanionBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == module.symbol}) def rebuild(orig: TypeDef, newBody: List[Tree]): Tree = { @@ -48,6 +48,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform val staticFields = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] val newBodyWithStaticConstr = if (staticFields.nonEmpty) { + /* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */ val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method, MethodType(Nil, defn.UnitType)) staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot)) staticCostructor.entered From 2c6a9be393f54407626234c41750f793a928b27e Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 15:57:09 +0200 Subject: [PATCH 21/25] CheckStatic: report error position in case of disallowed override --- src/dotty/tools/dotc/transform/CheckStatic.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala index 3a452ca08563..937a4f1cc137 100644 --- a/src/dotty/tools/dotc/transform/CheckStatic.scala +++ b/src/dotty/tools/dotc/transform/CheckStatic.scala @@ -69,7 +69,7 @@ class CheckStatic extends MiniPhaseTransform { thisTransformer => } else if (defn.symbol.is(Flags.Lazy)) { ctx.error("Lazy @static fields are not supported", defn.pos) } else if (defn.symbol.allOverriddenSymbols.nonEmpty) { - ctx.error("@static members cannot override or implement non-static ones") + ctx.error("@static members cannot override or implement non-static ones", defn.pos) } } else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef] From 9899a062a4ed47f48da8d00f828d00051f01f1b8 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Wed, 20 Apr 2016 16:07:18 +0200 Subject: [PATCH 22/25] LazyVals: fix leftover moduleClass usage. --- src/dotty/tools/dotc/transform/LazyVals.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index af3666aa3dfa..504f9250b003 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -353,7 +353,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer { val id = info.ord / flagsPerLong val offsetById = offsetName(id) if (ord != 0) { // there are unused bits in already existing flag - offsetSymbol = claz.moduleClass.info.decl(offsetById) + offsetSymbol = claz.info.decl(offsetById) .suchThat(sym => (sym is Flags.Synthetic) && sym.isTerm) .symbol.asTerm } else { // need to create a new flag From c428e746ff8031271ad5c170b88177f091138ef1 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Tue, 7 Jun 2016 15:20:58 +0200 Subject: [PATCH 23/25] LazyVals: do even more verbose debugging. --- src/dotty/runtime/LazyVals.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dotty/runtime/LazyVals.scala b/src/dotty/runtime/LazyVals.scala index e42532ce0db8..4dea0d34dbe9 100644 --- a/src/dotty/runtime/LazyVals.scala +++ b/src/dotty/runtime/LazyVals.scala @@ -63,7 +63,11 @@ object LazyVals { } @inline def compareAndSet(t: Object, off: Long, e: Long, v: Long) = unsafe.compareAndSwapLong(t, off, e, v) - @inline def get(t: Object, off: Long) = unsafe.getLongVolatile(t, off) + @inline def get(t: Object, off: Long) = { + if (debug) + println(s"get($t, $off)") + unsafe.getLongVolatile(t, off) + } val processors: Int = java.lang.Runtime.getRuntime.availableProcessors() val base: Int = 8 * processors * processors From de45fa17fe6a23a7100d9b1c15b9f08ab975a330 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Tue, 7 Jun 2016 15:23:07 +0200 Subject: [PATCH 24/25] MoveStatics: Fix classes without companion not getting static This broke lazy vals, as unsafe offsets were not initialised. --- .../tools/dotc/transform/MoveStatics.scala | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala index 8c1fe309b1a9..4f4deeffd225 100644 --- a/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -35,6 +35,25 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform val (classes, others) = trees.partition(x => x.isInstanceOf[TypeDef] && x.symbol.isClass) val pairs = classes.groupBy(_.symbol.name.stripModuleClassSuffix).asInstanceOf[Map[Name, List[TypeDef]]] + def rebuild(orig: TypeDef, newBody: List[Tree]): Tree = { + if (orig eq null) return EmptyTree + + val staticFields = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] + val newBodyWithStaticConstr = + if (staticFields.nonEmpty) { + /* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */ + val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method, MethodType(Nil, defn.UnitType)) + staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot)) + staticCostructor.entered + + val staticAssigns = staticFields.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) + tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral)) :: newBody + } else newBody + + val oldTemplate = orig.rhs.asInstanceOf[Template] + cpy.TypeDef(orig)(rhs = cpy.Template(orig.rhs)(oldTemplate.constr, oldTemplate.parents, oldTemplate.self, newBodyWithStaticConstr)) + } + def move(module: TypeDef, companion: TypeDef): List[Tree] = { if (!module.symbol.is(Flags.Module)) move(companion, module) else { @@ -42,24 +61,6 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform (if(companion ne null) {companion.rhs.asInstanceOf[Template].body} else Nil) ++ module.rhs.asInstanceOf[Template].body val (newModuleBody, newCompanionBody) = allMembers.partition(x => {assert(x.symbol.exists); x.symbol.owner == module.symbol}) - def rebuild(orig: TypeDef, newBody: List[Tree]): Tree = { - if (orig eq null) return EmptyTree - - val staticFields = newBody.filter(x => x.isInstanceOf[ValDef] && x.symbol.hasAnnotation(defn.ScalaStaticAnnot)).asInstanceOf[List[ValDef]] - val newBodyWithStaticConstr = - if (staticFields.nonEmpty) { - /* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */ - val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method, MethodType(Nil, defn.UnitType)) - staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot)) - staticCostructor.entered - - val staticAssigns = staticFields.map(x => Assign(ref(x.symbol), x.rhs.changeOwner(x.symbol, staticCostructor))) - tpd.DefDef(staticCostructor, Block(staticAssigns, tpd.unitLiteral)) :: newBody - } else newBody - - val oldTemplate = orig.rhs.asInstanceOf[Template] - cpy.TypeDef(orig)(rhs = cpy.Template(orig.rhs)(oldTemplate.constr, oldTemplate.parents, oldTemplate.self, newBodyWithStaticConstr)) - } Trees.flatten(rebuild(companion, newCompanionBody) :: rebuild(module, newModuleBody) :: Nil) } } @@ -68,7 +69,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform yield if (classes.tail.isEmpty) if (classes.head.symbol.is(Flags.Module)) move(classes.head, null) - else List(classes.head) + else List(rebuild(classes.head, classes.head.rhs.asInstanceOf[Template].body)) else move(classes.head, classes.tail.head) Trees.flatten(newPairs.toList.flatten ++ others) } else trees From 3c93c5c48f5222c6ad40267d29d32cf7c597df41 Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Tue, 7 Jun 2016 17:02:55 +0200 Subject: [PATCH 25/25] Make class initialisers private. Otherwise they break GenBCode. GenBCode checks if class already has static initialiser, the check is fooled if class inherited a static initialisers. --- src/dotty/tools/dotc/transform/MoveStatics.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala index 4f4deeffd225..5c2cd3145290 100644 --- a/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -42,7 +42,7 @@ class MoveStatics extends MiniPhaseTransform with SymTransformer { thisTransform val newBodyWithStaticConstr = if (staticFields.nonEmpty) { /* do NOT put Flags.JavaStatic here. It breaks .enclosingClass */ - val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method, MethodType(Nil, defn.UnitType)) + val staticCostructor = ctx.newSymbol(orig.symbol, Names.STATIC_CONSTRUCTOR, Flags.Synthetic | Flags.Method | Flags.Private, MethodType(Nil, defn.UnitType)) staticCostructor.addAnnotation(Annotation(defn.ScalaStaticAnnot)) staticCostructor.entered