aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/backend/jvm/DottyBackendInterface.scala3
-rw-r--r--src/dotty/tools/dotc/Compiler.scala1
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala4
-rw-r--r--src/dotty/tools/dotc/core/Annotations.scala3
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala2
-rw-r--r--src/dotty/tools/dotc/transform/CheckStatic.scala94
-rw-r--r--src/dotty/tools/dotc/transform/LazyVals.scala7
-rw-r--r--src/scala/annotation/static.scala13
-rw-r--r--tests/run/statics.scala40
9 files changed, 164 insertions, 3 deletions
diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
index 5776cc8e2..ef8e4997f 100644
--- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
+++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala
@@ -601,7 +601,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
isPrivate //|| (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass)
def isFinal: Boolean = sym is Flags.Final
- def isStaticMember: Boolean = (sym ne NoSymbol) && ((sym is Flags.JavaStatic) || (owner is Flags.ImplClass))
+ def isStaticMember: Boolean = (sym ne NoSymbol) &&
+ ((sym is Flags.JavaStatic) || (owner is Flags.ImplClass) || toDenot(sym).hasAnnotation(ctx.definitions.ScalaStaticAnnot))
// guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone
def isBottomClass: Boolean = (sym ne defn.NullClass) && (sym ne defn.NothingClass)
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index aee9b5b15..d9f1a3dca 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -44,6 +44,7 @@ class Compiler {
List(new FirstTransform,
new CheckReentrant),
List(new RefChecks,
+ new CheckStatic,
new ElimRepeated,
new NormalizeFlags,
new ExtensionMethods,
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index 7cd469d7a..bf6084d7a 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -320,7 +320,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
case _ =>
false
}
- try test || tp.symbol.is(JavaStatic)
+ try test || tp.symbol.is(JavaStatic) || tp.symbol.hasAnnotation(defn.ScalaStaticAnnot)
catch { // See remark in SymDenotations#accessWithin
case ex: NotDefinedHere => test(ctx.addMode(Mode.FutureDefsOK))
}
@@ -337,6 +337,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
else if (prefixIsElidable(tp)) Ident(tp)
else if (tp.symbol.is(Module) && ctx.owner.isContainedIn(tp.symbol.moduleClass))
followOuterLinks(This(tp.symbol.moduleClass.asClass))
+ else if (tp.symbol hasAnnotation defn.ScalaStaticAnnot)
+ Ident(tp)
else tp.prefix match {
case pre: SingletonType => followOuterLinks(singleton(pre)).select(tp)
case pre => SelectFromTypeTree(TypeTree(pre), tp)
diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala
index b9c0eca43..2b27b5e01 100644
--- a/src/dotty/tools/dotc/core/Annotations.scala
+++ b/src/dotty/tools/dotc/core/Annotations.scala
@@ -47,6 +47,9 @@ object Annotations {
def apply(tree: Tree) = ConcreteAnnotation(tree)
+ def apply(cls: ClassSymbol)(implicit ctx: Context): Annotation =
+ apply(cls, Nil)
+
def apply(cls: ClassSymbol, arg: Tree)(implicit ctx: Context): Annotation =
apply(cls, arg :: Nil)
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index f16f25b23..6f8a8f837 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -462,6 +462,8 @@ class Definitions {
def ScalaLongSignatureAnnot(implicit ctx: Context) = ScalaLongSignatureAnnotType.symbol.asClass
lazy val ScalaStrictFPAnnotType = ctx.requiredClassRef("scala.annotation.strictfp")
def ScalaStrictFPAnnot(implicit ctx: Context) = ScalaStrictFPAnnotType.symbol.asClass
+ lazy val ScalaStaticAnnotType = ctx.requiredClassRef("scala.annotation.static")
+ def ScalaStaticAnnot(implicit ctx: Context) = ScalaStaticAnnotType.symbol.asClass
lazy val SerialVersionUIDAnnotType = ctx.requiredClassRef("scala.SerialVersionUID")
def SerialVersionUIDAnnot(implicit ctx: Context) = SerialVersionUIDAnnotType.symbol.asClass
lazy val TASTYSignatureAnnotType = ctx.requiredClassRef("scala.annotation.internal.TASTYSignature")
diff --git a/src/dotty/tools/dotc/transform/CheckStatic.scala b/src/dotty/tools/dotc/transform/CheckStatic.scala
new file mode 100644
index 000000000..445e9f839
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/CheckStatic.scala
@@ -0,0 +1,94 @@
+package dotty.tools.dotc
+package transform
+
+import core._
+import Names._
+import StdNames.nme
+import Types._
+import dotty.tools.dotc.transform.TreeTransforms.{AnnotationTransformer, TransformerInfo, MiniPhaseTransform, TreeTransformer}
+import ast.Trees._
+import Flags._
+import Contexts.Context
+import Symbols._
+import Constants._
+import Denotations._, SymDenotations._
+import Decorators.StringInterpolators
+import dotty.tools.dotc.ast.tpd
+import dotty.tools.dotc.core.Annotations.ConcreteAnnotation
+import scala.collection.mutable
+import DenotTransformers._
+import Names.Name
+import NameOps._
+import Decorators._
+import TypeUtils._
+
+/** A transformer that check that requirements of Static fields\methods are implemented:
+ * 1. Only objects can have members annotated with `@static`
+ * 2. The fields annotated with `@static` should preceed any non-`@static` fields.
+ * This ensures that we do not introduce surprises for users in initialization order.
+ * 3. If a member `foo` of an `object C` is annotated with `@static`,
+ * the companion class `C` is not allowed to define term members with name `foo`.
+ * 4. If a member `foo` of an `object C` is annotated with `@static`, the companion class `C`
+ * 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.
+ */
+class CheckStatic extends MiniPhaseTransform { thisTransformer =>
+ import ast.tpd._
+
+ override def phaseName = "checkStatic"
+
+
+ def check(tree: tpd.DefTree)(implicit ctx: Context) = {
+
+ }
+
+ override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ val defns = tree.body.collect{case t: ValOrDefDef => t}
+ var hadNonStaticField = false
+ for(defn <- defns) {
+ if (defn.symbol.hasAnnotation(ctx.definitions.ScalaStaticAnnot)) {
+ if(!ctx.owner.is(Module)) {
+ ctx.error("@static fields are only allowed inside objects", defn.pos)
+ }
+
+ if (defn.isInstanceOf[ValDef] && hadNonStaticField) {
+ ctx.error("@static fields should preceed non-static ones", defn.pos)
+ }
+
+ val companion = ctx.owner.companionClass
+ 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) {
+ 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 hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef]
+
+ }
+ tree
+ }
+
+ override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ if (tree.symbol.hasAnnotation(defn.ScalaStaticAnnot)) {
+ val symbolWhitelist = tree.symbol.ownersIterator.flatMap(x => if (x.is(Flags.Module)) List(x, x.companionModule) else List(x)).toSet
+ def isSafeQual(t: Tree): Boolean = { // follow the desugared paths created by typer
+ t match {
+ case t: This => true
+ case t: Select => isSafeQual(t.qualifier) && symbolWhitelist.contains(t.symbol)
+ case t: Ident => symbolWhitelist.contains(t.symbol)
+ case t: Block => t.stats.forall(tpd.isPureExpr) && isSafeQual(t.expr)
+ }
+ }
+ if (isSafeQual(tree.qualifier))
+ ref(tree.symbol)
+ else tree
+ } else tree
+ }
+}
diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala
index 0d0ba191e..25b9afa68 100644
--- a/src/dotty/tools/dotc/transform/LazyVals.scala
+++ b/src/dotty/tools/dotc/transform/LazyVals.scala
@@ -1,6 +1,7 @@
package dotty.tools.dotc
package transform
+import dotty.tools.dotc.core.Annotations.Annotation
import dotty.tools.dotc.core.Phases.NeedsCompanions
import dotty.tools.dotc.typer.Mode
@@ -91,6 +92,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
appendOffsetDefs.get(cls) match {
case None => template
case Some(data) =>
+ data.defs.foreach(_.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)))
cpy.Template(template)(body = addInFront(data.defs, template.body))
}
@@ -357,6 +359,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
.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.addAnnotation(Annotation(defn.ScalaStaticAnnot))
val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName
val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this)
flag = ValDef(flagSymbol, Literal(Constants.Constant(0L)))
@@ -366,6 +369,7 @@ 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.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)))
@@ -375,9 +379,10 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName
val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this)
+
val containerTree = ValDef(containerSymbol, defaultValue(tpe))
- val offset = ref(companion).ensureApplied.select(offsetSymbol)
+ val offset = ref(offsetSymbol)
val getFlag = Select(ref(helperModule), lazyNme.RLazyVals.get)
val setFlag = Select(ref(helperModule), lazyNme.RLazyVals.setFlag)
val wait = Select(ref(helperModule), lazyNme.RLazyVals.wait4Notification)
diff --git a/src/scala/annotation/static.scala b/src/scala/annotation/static.scala
new file mode 100644
index 000000000..b00072b18
--- /dev/null
+++ b/src/scala/annotation/static.scala
@@ -0,0 +1,13 @@
+package scala.annotation
+
+import scala.annotation.meta._
+
+/** https://github.com/scala/scala.github.com/pull/491 */
+
+@field
+@getter
+@beanGetter
+@beanSetter
+@param
+@setter
+final class static extends StaticAnnotation
diff --git a/tests/run/statics.scala b/tests/run/statics.scala
new file mode 100644
index 000000000..8425e7f77
--- /dev/null
+++ b/tests/run/statics.scala
@@ -0,0 +1,40 @@
+import scala.annotation.static
+
+class Foo{
+ class Bar {
+ def qwa =
+ Bar.field
+ // 0: invokestatic #31 // Method Foo$Bar$.field:()I
+ // 3: ireturn
+ }
+ object Bar {
+ @static
+ val field = 1
+ }
+}
+
+object Foo{
+ @static
+ def method = 1
+
+ @static
+ val field = 2
+
+ @static
+ var mutable = 3
+
+ @static
+ def accessor = field
+}
+
+object Test {
+ import Foo._
+ def main(args: Array[String]): Unit = {
+ method + field + mutable + accessor
+ }
+}
+
+class WithLazies{
+ @volatile lazy val s = 1
+ // 98: getstatic #30 // Field WithLazies$.OFFSET$0:J
+}