Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement @static sip. #1155

Merged
merged 7 commits into from Mar 9, 2016
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/dotty/tools/backend/jvm/DottyBackendInterface.scala
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/Compiler.scala
Expand Up @@ -44,6 +44,7 @@ class Compiler {
List(new FirstTransform,
new CheckReentrant),
List(new RefChecks,
new CheckStatic,
new ElimRepeated,
new NormalizeFlags,
new ExtensionMethods,
Expand Down
4 changes: 3 additions & 1 deletion src/dotty/tools/dotc/ast/tpd.scala
Expand Up @@ -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))
}
Expand All @@ -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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation issue?

else tp.prefix match {
case pre: SingletonType => followOuterLinks(singleton(pre)).select(tp)
case pre => SelectFromTypeTree(TypeTree(pre), tp)
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Definitions.scala
Expand Up @@ -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")
Expand Down
4 changes: 4 additions & 0 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Expand Up @@ -285,6 +285,10 @@ object SymDenotations {
final def addAnnotation(annot: Annotation): Unit =
annotations = annot :: myAnnotations

/** Add given annotation to the annotations of this denotation */
final def addAnnotation(annot: ClassSymbol)(implicit ctx: Context): Unit =
addAnnotation(ConcreteAnnotation(tpd.ref(annot)))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need that here. Annotation already contains a bunch of overloaded apply methods to create annotations; we should not add more overloadings in other places because then there are two places to look for the right variant instead of one. You can get what you want already by writing

addAnnotation(Annotation(cls, Nil))

If one objects to the Nil, one can add another variant of apply to Annotation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

symbol.addAnnotation(Annotation(defn.ScalaStaticAnnot)))

seems like too much repetition to me, but if you insist, I'll make the change.

/** Remove annotation with given class from this denotation */
final def removeAnnotation(cls: Symbol)(implicit ctx: Context): Unit =
annotations = myAnnotations.filterNot(_ matches cls)
Expand Down
83 changes: 83 additions & 0 deletions src/dotty/tools/dotc/transform/CheckStatic.scala
@@ -0,0 +1,83 @@
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 = "elimRepeated"


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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop { ... } around single-statement blocks?


}
tree
}

override def transformSelect(tree: tpd.Select)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
if (tree.symbol.hasAnnotation(defn.ScalaStaticAnnot))
ref(tree.symbol)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will silently drop any side effect of evaluating the qualifier.

If the qualifier is a simple reference to the enclosing object, that's fine (that's part of the semantic changes of @static), but it's not fine for { x += 1; SomeObject}.someStatic

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix incoming

else tree
}
}
6 changes: 5 additions & 1 deletion src/dotty/tools/dotc/transform/LazyVals.scala
Expand Up @@ -91,6 +91,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer with Nee
appendOffsetDefs.get(cls) match {
case None => template
case Some(data) =>
data.defs.foreach(_.symbol.addAnnotation(defn.ScalaStaticAnnot))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid the repetition, why not define in LazyVals:

def staticAnnot = Annotation(defn.ScalaStaticAnnot, Nil)

cpy.Template(template)(body = addInFront(data.defs, template.body))
}

Expand Down Expand Up @@ -357,6 +358,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(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)))
Expand All @@ -366,6 +368,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(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)))
Expand All @@ -375,9 +378,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)
Expand Down
13 changes: 13 additions & 0 deletions 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why @param?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency. Now if any kind of member is created from a @static one it has to still be @static

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But parameters cannot be @static?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not if written by user. But I would like to leave the door open for tools\optimizers\analyzers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the question is: what is a static param, even if produced by a tool/optimizer/analyzer? I don't have any idea what that would look like in the bytecode.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simply a static field.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it's covered by @field, isn't it?

@setter
final class static extends StaticAnnotation
40 changes: 40 additions & 0 deletions 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
}