diff options
author | Dmitry Petrashko <dark@d-d.me> | 2016-06-22 15:38:15 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-06-22 15:38:15 +0200 |
commit | 93599be7f5489c8913a8e961f1b291b4b132dca7 (patch) | |
tree | d081b213e58030741fd9c2f7ba80b5576f219be4 | |
parent | 914d465aa13cf90fab71dbdf5ab314f9be191891 (diff) | |
parent | 3c93c5c48f5222c6ad40267d29d32cf7c597df41 (diff) | |
download | dotty-93599be7f5489c8913a8e961f1b291b4b132dca7.tar.gz dotty-93599be7f5489c8913a8e961f1b291b4b132dca7.tar.bz2 dotty-93599be7f5489c8913a8e961f1b291b4b132dca7.zip |
Merge pull request #1226 from dotty-staging/static-fixes
Multiple fixes to @static
-rw-r--r-- | src/dotty/runtime/LazyVals.scala | 27 | ||||
-rw-r--r-- | src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 11 | ||||
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/NameOps.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Names.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/CheckStatic.scala | 20 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/Constructors.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/Getters.scala | 1 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/LazyVals.scala | 21 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/MoveStatics.scala | 77 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/TreeChecker.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 2 | ||||
-rw-r--r-- | tests/neg/static-implements.scala | 11 | ||||
-rw-r--r-- | tests/neg/static-no-companion.scala | 4 | ||||
-rw-r--r-- | tests/run/Lazies1.scala | 6 | ||||
-rw-r--r-- | tests/run/Lazies2.scala | 8 |
17 files changed, 174 insertions, 34 deletions
diff --git a/src/dotty/runtime/LazyVals.scala b/src/dotty/runtime/LazyVals.scala index f09e96f57..4dea0d34d 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) @@ -51,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 @@ -68,7 +84,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" diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 002a6bf27..23073d317 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 @@ -716,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 diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 2ddd81718..3844f42a7 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/core/NameOps.scala b/src/dotty/tools/dotc/core/NameOps.scala index 72f89aec3..17af899e9 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/Names.scala b/src/dotty/tools/dotc/core/Names.scala index 223d95045..11f0b55a8 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("<init>") + val STATIC_CONSTRUCTOR = termName("<clinit>") val EMPTY_PACKAGE = termName("<empty>") val dontEncode = Set(CONSTRUCTOR, EMPTY_PACKAGE) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index ff99f3b55..5c4e120a8 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 static 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 @@ -1001,7 +1005,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) @@ -1499,7 +1503,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) diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala index 77c6dfc51..937a4f1cc 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._ @@ -57,17 +58,18 @@ class CheckStatic extends MiniPhaseTransform { thisTransformer => } val companion = ctx.owner.companionClass - if (!companion.exists) { - ctx.error("object that conatin @static members should have companion class", defn.pos) - } + def clashes = companion.asClass.membersNamed(defn.name) - val clashes = companion.asClass.membersNamed(defn.name) - if (clashes.exists) { + if (!companion.exists) { + 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) - } - - 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.Mutable) && companion.is(Flags.Trait)) { + 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", defn.pos) + } else if (defn.symbol.allOverriddenSymbols.nonEmpty) { + ctx.error("@static members cannot override or implement non-static ones", defn.pos) } } else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef] diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 44638ce48..db850e944 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)) diff --git a/src/dotty/tools/dotc/transform/Getters.scala b/src/dotty/tools/dotc/transform/Getters.scala index 75235d0f5..31171dfab 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 diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index e42c7bae9..504f9250b 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.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 diff --git a/src/dotty/tools/dotc/transform/MoveStatics.scala b/src/dotty/tools/dotc/transform/MoveStatics.scala new file mode 100644 index 000000000..5c2cd3145 --- /dev/null +++ b/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -0,0 +1,77 @@ +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 +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.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 + 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 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 | Flags.Private, 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 { + 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}) + Trees.flatten(rebuild(companion, newCompanionBody) :: rebuild(module, newModuleBody) :: Nil) + } + } + val newPairs = + for ((name, classes) <- pairs) + yield + if (classes.tail.isEmpty) + if (classes.head.symbol.is(Flags.Module)) move(classes.head, null) + 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 + } +} diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 0dce0fd36..ce160d7b0 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 diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 07710d3b1..ae8900c43 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -485,7 +485,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))) diff --git a/tests/neg/static-implements.scala b/tests/neg/static-implements.scala new file mode 100644 index 000000000..8e8a8800c --- /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) + } +} diff --git a/tests/neg/static-no-companion.scala b/tests/neg/static-no-companion.scala new file mode 100644 index 000000000..982dddf88 --- /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 +} diff --git a/tests/run/Lazies1.scala b/tests/run/Lazies1.scala new file mode 100644 index 000000000..34fecaf80 --- /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 000000000..6b9aa8a39 --- /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 + } +} |