aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2016-06-22 15:38:15 +0200
committerGitHub <noreply@github.com>2016-06-22 15:38:15 +0200
commit93599be7f5489c8913a8e961f1b291b4b132dca7 (patch)
treed081b213e58030741fd9c2f7ba80b5576f219be4
parent914d465aa13cf90fab71dbdf5ab314f9be191891 (diff)
parent3c93c5c48f5222c6ad40267d29d32cf7c597df41 (diff)
downloaddotty-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.scala27
-rw-r--r--src/dotty/tools/backend/jvm/DottyBackendInterface.scala11
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/core/NameOps.scala1
-rw-r--r--src/dotty/tools/dotc/core/Names.scala1
-rw-r--r--src/dotty/tools/dotc/core/SymDenotations.scala8
-rw-r--r--src/dotty/tools/dotc/transform/CheckStatic.scala20
-rw-r--r--src/dotty/tools/dotc/transform/Constructors.scala4
-rw-r--r--src/dotty/tools/dotc/transform/Getters.scala1
-rw-r--r--src/dotty/tools/dotc/transform/LazyVals.scala21
-rw-r--r--src/dotty/tools/dotc/transform/MoveStatics.scala77
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala5
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala2
-rw-r--r--tests/neg/static-implements.scala11
-rw-r--r--tests/neg/static-no-companion.scala4
-rw-r--r--tests/run/Lazies1.scala6
-rw-r--r--tests/run/Lazies2.scala8
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
+ }
+}