Skip to content

Commit bdd8c35

Browse files
committed
Merge pull request #1155 from dotty-staging/static
Implement @static sip.
2 parents 560810e + a068498 commit bdd8c35

File tree

9 files changed

+164
-3
lines changed

9 files changed

+164
-3
lines changed

src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,8 @@ class DottyBackendInterface(outputDirectory: AbstractFile)(implicit ctx: Context
601601
isPrivate //|| (sym.isPrimaryConstructor && sym.owner.isTopLevelModuleClass)
602602

603603
def isFinal: Boolean = sym is Flags.Final
604-
def isStaticMember: Boolean = (sym ne NoSymbol) && ((sym is Flags.JavaStatic) || (owner is Flags.ImplClass))
604+
def isStaticMember: Boolean = (sym ne NoSymbol) &&
605+
((sym is Flags.JavaStatic) || (owner is Flags.ImplClass) || toDenot(sym).hasAnnotation(ctx.definitions.ScalaStaticAnnot))
605606
// guard against no sumbol cause this code is executed to select which call type(static\dynamic) to use to call array.clone
606607

607608
def isBottomClass: Boolean = (sym ne defn.NullClass) && (sym ne defn.NothingClass)

src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class Compiler {
4444
List(new FirstTransform,
4545
new CheckReentrant),
4646
List(new RefChecks,
47+
new CheckStatic,
4748
new ElimRepeated,
4849
new NormalizeFlags,
4950
new ExtensionMethods,

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
320320
case _ =>
321321
false
322322
}
323-
try test || tp.symbol.is(JavaStatic)
323+
try test || tp.symbol.is(JavaStatic) || tp.symbol.hasAnnotation(defn.ScalaStaticAnnot)
324324
catch { // See remark in SymDenotations#accessWithin
325325
case ex: NotDefinedHere => test(ctx.addMode(Mode.FutureDefsOK))
326326
}
@@ -337,6 +337,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
337337
else if (prefixIsElidable(tp)) Ident(tp)
338338
else if (tp.symbol.is(Module) && ctx.owner.isContainedIn(tp.symbol.moduleClass))
339339
followOuterLinks(This(tp.symbol.moduleClass.asClass))
340+
else if (tp.symbol hasAnnotation defn.ScalaStaticAnnot)
341+
Ident(tp)
340342
else tp.prefix match {
341343
case pre: SingletonType => followOuterLinks(singleton(pre)).select(tp)
342344
case pre => SelectFromTypeTree(TypeTree(pre), tp)

src/dotty/tools/dotc/core/Annotations.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ object Annotations {
4747

4848
def apply(tree: Tree) = ConcreteAnnotation(tree)
4949

50+
def apply(cls: ClassSymbol)(implicit ctx: Context): Annotation =
51+
apply(cls, Nil)
52+
5053
def apply(cls: ClassSymbol, arg: Tree)(implicit ctx: Context): Annotation =
5154
apply(cls, arg :: Nil)
5255

src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,8 @@ class Definitions {
462462
def ScalaLongSignatureAnnot(implicit ctx: Context) = ScalaLongSignatureAnnotType.symbol.asClass
463463
lazy val ScalaStrictFPAnnotType = ctx.requiredClassRef("scala.annotation.strictfp")
464464
def ScalaStrictFPAnnot(implicit ctx: Context) = ScalaStrictFPAnnotType.symbol.asClass
465+
lazy val ScalaStaticAnnotType = ctx.requiredClassRef("scala.annotation.static")
466+
def ScalaStaticAnnot(implicit ctx: Context) = ScalaStaticAnnotType.symbol.asClass
465467
lazy val SerialVersionUIDAnnotType = ctx.requiredClassRef("scala.SerialVersionUID")
466468
def SerialVersionUIDAnnot(implicit ctx: Context) = SerialVersionUIDAnnotType.symbol.asClass
467469
lazy val TASTYSignatureAnnotType = ctx.requiredClassRef("scala.annotation.internal.TASTYSignature")
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import Names._
6+
import StdNames.nme
7+
import Types._
8+
import dotty.tools.dotc.transform.TreeTransforms.{AnnotationTransformer, TransformerInfo, MiniPhaseTransform, TreeTransformer}
9+
import ast.Trees._
10+
import Flags._
11+
import Contexts.Context
12+
import Symbols._
13+
import Constants._
14+
import Denotations._, SymDenotations._
15+
import Decorators.StringInterpolators
16+
import dotty.tools.dotc.ast.tpd
17+
import dotty.tools.dotc.core.Annotations.ConcreteAnnotation
18+
import scala.collection.mutable
19+
import DenotTransformers._
20+
import Names.Name
21+
import NameOps._
22+
import Decorators._
23+
import TypeUtils._
24+
25+
/** A transformer that check that requirements of Static fields\methods are implemented:
26+
* 1. Only objects can have members annotated with `@static`
27+
* 2. The fields annotated with `@static` should preceed any non-`@static` fields.
28+
* This ensures that we do not introduce surprises for users in initialization order.
29+
* 3. If a member `foo` of an `object C` is annotated with `@static`,
30+
* the companion class `C` is not allowed to define term members with name `foo`.
31+
* 4. If a member `foo` of an `object C` is annotated with `@static`, the companion class `C`
32+
* is not allowed to inherit classes that define a term member with name `foo`.
33+
* 5. Only `@static` methods and vals are supported in companions of traits.
34+
* Java8 supports those, but not vars, and JavaScript does not have interfaces at all.
35+
*/
36+
class CheckStatic extends MiniPhaseTransform { thisTransformer =>
37+
import ast.tpd._
38+
39+
override def phaseName = "checkStatic"
40+
41+
42+
def check(tree: tpd.DefTree)(implicit ctx: Context) = {
43+
44+
}
45+
46+
override def transformTemplate(tree: tpd.Template)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
47+
val defns = tree.body.collect{case t: ValOrDefDef => t}
48+
var hadNonStaticField = false
49+
for(defn <- defns) {
50+
if (defn.symbol.hasAnnotation(ctx.definitions.ScalaStaticAnnot)) {
51+
if(!ctx.owner.is(Module)) {
52+
ctx.error("@static fields are only allowed inside objects", defn.pos)
53+
}
54+
55+
if (defn.isInstanceOf[ValDef] && hadNonStaticField) {
56+
ctx.error("@static fields should preceed non-static ones", defn.pos)
57+
}
58+
59+
val companion = ctx.owner.companionClass
60+
if (!companion.exists) {
61+
ctx.error("object that conatin @static members should have companion class", defn.pos)
62+
}
63+
64+
val clashes = companion.asClass.membersNamed(defn.name)
65+
if (clashes.exists) {
66+
ctx.error("companion classes cannot define members with same name as @static member", defn.pos)
67+
}
68+
69+
if (defn.symbol.is(Flags.Mutable) && companion.is(Flags.Trait)) {
70+
ctx.error("Companions of traits cannot define mutable @static fields")
71+
}
72+
} else hadNonStaticField = hadNonStaticField || defn.isInstanceOf[ValDef]
73+
74+
}
75+
tree
76+
}
77+
78+
override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
79+
if (tree.symbol.hasAnnotation(defn.ScalaStaticAnnot)) {
80+
val symbolWhitelist = tree.symbol.ownersIterator.flatMap(x => if (x.is(Flags.Module)) List(x, x.companionModule) else List(x)).toSet
81+
def isSafeQual(t: Tree): Boolean = { // follow the desugared paths created by typer
82+
t match {
83+
case t: This => true
84+
case t: Select => isSafeQual(t.qualifier) && symbolWhitelist.contains(t.symbol)
85+
case t: Ident => symbolWhitelist.contains(t.symbol)
86+
case t: Block => t.stats.forall(tpd.isPureExpr) && isSafeQual(t.expr)
87+
}
88+
}
89+
if (isSafeQual(tree.qualifier))
90+
ref(tree.symbol)
91+
else tree
92+
} else tree
93+
}
94+
}

src/dotty/tools/dotc/transform/LazyVals.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.dotc
22
package transform
33

4+
import dotty.tools.dotc.core.Annotations.Annotation
45
import dotty.tools.dotc.core.Phases.NeedsCompanions
56
import dotty.tools.dotc.typer.Mode
67

@@ -91,6 +92,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
9192
appendOffsetDefs.get(cls) match {
9293
case None => template
9394
case Some(data) =>
95+
data.defs.foreach(_.symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)))
9496
cpy.Template(template)(body = addInFront(data.defs, template.body))
9597
}
9698

@@ -357,6 +359,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
357359
.symbol.asTerm
358360
} else { // need to create a new flag
359361
offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + id.toString).toTermName, Flags.Synthetic, defn.LongType).enteredAfter(this)
362+
offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot))
360363
val flagName = (StdNames.nme.BITMAP_PREFIX + id.toString).toTermName
361364
val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this)
362365
flag = ValDef(flagSymbol, Literal(Constants.Constant(0L)))
@@ -366,6 +369,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
366369

367370
case None =>
368371
offsetSymbol = ctx.newSymbol(companion.moduleClass, (StdNames.nme.LAZY_FIELD_OFFSET + "0").toTermName, Flags.Synthetic, defn.LongType).enteredAfter(this)
372+
offsetSymbol.addAnnotation(Annotation(defn.ScalaStaticAnnot))
369373
val flagName = (StdNames.nme.BITMAP_PREFIX + "0").toTermName
370374
val flagSymbol = ctx.newSymbol(claz, flagName, containerFlags, defn.LongType).enteredAfter(this)
371375
flag = ValDef(flagSymbol, Literal(Constants.Constant(0L)))
@@ -375,9 +379,10 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
375379

376380
val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName
377381
val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this)
382+
378383
val containerTree = ValDef(containerSymbol, defaultValue(tpe))
379384

380-
val offset = ref(companion).ensureApplied.select(offsetSymbol)
385+
val offset = ref(offsetSymbol)
381386
val getFlag = Select(ref(helperModule), lazyNme.RLazyVals.get)
382387
val setFlag = Select(ref(helperModule), lazyNme.RLazyVals.setFlag)
383388
val wait = Select(ref(helperModule), lazyNme.RLazyVals.wait4Notification)

src/scala/annotation/static.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package scala.annotation
2+
3+
import scala.annotation.meta._
4+
5+
/** https://github.com/scala/scala.github.com/pull/491 */
6+
7+
@field
8+
@getter
9+
@beanGetter
10+
@beanSetter
11+
@param
12+
@setter
13+
final class static extends StaticAnnotation

tests/run/statics.scala

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import scala.annotation.static
2+
3+
class Foo{
4+
class Bar {
5+
def qwa =
6+
Bar.field
7+
// 0: invokestatic #31 // Method Foo$Bar$.field:()I
8+
// 3: ireturn
9+
}
10+
object Bar {
11+
@static
12+
val field = 1
13+
}
14+
}
15+
16+
object Foo{
17+
@static
18+
def method = 1
19+
20+
@static
21+
val field = 2
22+
23+
@static
24+
var mutable = 3
25+
26+
@static
27+
def accessor = field
28+
}
29+
30+
object Test {
31+
import Foo._
32+
def main(args: Array[String]): Unit = {
33+
method + field + mutable + accessor
34+
}
35+
}
36+
37+
class WithLazies{
38+
@volatile lazy val s = 1
39+
// 98: getstatic #30 // Field WithLazies$.OFFSET$0:J
40+
}

0 commit comments

Comments
 (0)