summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala83
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala5
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala12
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala138
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala7
-rw-r--r--src/library/scala/annotation/static.scala20
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala1
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala1
-rw-r--r--test/files/neg/static-annot.check10
-rw-r--r--test/files/neg/static-annot.scala33
-rw-r--r--test/files/run/static-annot/field.scala205
12 files changed, 479 insertions, 37 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index b638745327..9ec212b084 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -113,26 +113,42 @@ abstract class GenICode extends SubComponent {
m.native = m.symbol.hasAnnotation(definitions.NativeAttr)
if (!m.isAbstractMethod && !m.native) {
- ctx1 = genLoad(rhs, ctx1, m.returnType);
-
- // reverse the order of the local variables, to match the source-order
- m.locals = m.locals.reverse
-
- rhs match {
- case Block(_, Return(_)) => ()
- case Return(_) => ()
- case EmptyTree =>
- globalError("Concrete method has no definition: " + tree + (
- if (settings.debug.value) "(found: " + m.symbol.owner.info.decls.toList.mkString(", ") + ")"
- else "")
- )
- case _ => if (ctx1.bb.isEmpty)
- ctx1.bb.closeWith(RETURN(m.returnType), rhs.pos)
- else
+ if (m.symbol.isAccessor && m.symbol.accessed.hasStaticAnnotation) {
+ // in companion object accessors to @static fields, we access the static field directly
+ val hostClass = m.symbol.owner.companionClass
+ val staticfield = hostClass.info.decls.find(_.name.toString.trim == m.symbol.accessed.name.toString.trim)
+
+ if (m.symbol.isGetter) {
+ ctx1.bb.emit(LOAD_FIELD(staticfield.get, true) setHostClass hostClass, tree.pos)
ctx1.bb.closeWith(RETURN(m.returnType))
+ } else if (m.symbol.isSetter) {
+ ctx1.bb.emit(LOAD_LOCAL(m.locals.head), tree.pos)
+ ctx1.bb.emit(STORE_FIELD(staticfield.get, true), tree.pos)
+ ctx1.bb.closeWith(RETURN(m.returnType))
+ } else assert(false, "unreachable")
+ } else {
+ ctx1 = genLoad(rhs, ctx1, m.returnType);
+
+ // reverse the order of the local variables, to match the source-order
+ m.locals = m.locals.reverse
+
+ rhs match {
+ case Block(_, Return(_)) => ()
+ case Return(_) => ()
+ case EmptyTree =>
+ globalError("Concrete method has no definition: " + tree + (
+ if (settings.debug.value) "(found: " + m.symbol.owner.info.decls.toList.mkString(", ") + ")"
+ else "")
+ )
+ case _ =>
+ if (ctx1.bb.isEmpty)
+ ctx1.bb.closeWith(RETURN(m.returnType), rhs.pos)
+ else
+ ctx1.bb.closeWith(RETURN(m.returnType))
+ }
+ if (!ctx1.bb.closed) ctx1.bb.close
+ prune(ctx1.method)
}
- if (!ctx1.bb.closed) ctx1.bb.close
- prune(ctx1.method)
} else
ctx1.method.setCode(NoCode)
ctx1
@@ -854,9 +870,32 @@ abstract class GenICode extends SubComponent {
generatedType = toTypeKind(fun.symbol.tpe.resultType)
ctx1
+ case app @ Apply(fun @ Select(qual, _), args)
+ if !ctx.method.symbol.isStaticConstructor
+ && fun.symbol.isAccessor && fun.symbol.accessed.hasStaticAnnotation =>
+ // bypass the accessor to the companion object and load the static field directly
+ // the only place were this bypass is not done, is the static intializer for the static field itself
+ val sym = fun.symbol
+ generatedType = toTypeKind(sym.accessed.info)
+ val hostClass = qual.tpe.typeSymbol.orElse(sym.owner).companionClass
+ val staticfield = hostClass.info.decls.find(_.name.toString.trim == sym.accessed.name.toString.trim)
+
+ if (sym.isGetter) {
+ ctx.bb.emit(LOAD_FIELD(staticfield.get, true) setHostClass hostClass, tree.pos)
+ ctx
+ } else if (sym.isSetter) {
+ val ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx)
+ ctx1.bb.emit(STORE_FIELD(staticfield.get, true), tree.pos)
+ ctx1.bb.emit(CONSTANT(Constant(false)), tree.pos)
+ ctx1
+ } else {
+ assert(false, "supposedly unreachable")
+ ctx
+ }
+
case app @ Apply(fun, args) =>
val sym = fun.symbol
-
+
if (sym.isLabel) { // jump to a label
val label = ctx.labels.getOrElse(sym, {
// it is a forward jump, scan for labels
@@ -1623,8 +1662,12 @@ abstract class GenICode extends SubComponent {
* backend emits them as static).
* No code is needed for this module symbol.
*/
- for (f <- cls.info.decls ; if !f.isMethod && f.isTerm && !f.isModule)
+ for (
+ f <- cls.info.decls;
+ if !f.isMethod && f.isTerm && !f.isModule && !(f.owner.isModuleClass && f.hasStaticAnnotation)
+ ) {
ctx.clazz addField new IField(f)
+ }
}
/**
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 756d90bc53..ade99267f8 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -1170,7 +1170,9 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
log("No forwarder for " + m + " due to conflict with " + linkedClass.info.member(m.name))
else {
log("Adding static forwarder for '%s' from %s to '%s'".format(m, jclassName, moduleClass))
- addForwarder(isRemoteClass, jclass, moduleClass, m)
+ if (m.isAccessor && m.accessed.hasStaticAnnotation) {
+ log("@static: accessor " + m + ", accessed: " + m.accessed)
+ } else addForwarder(isRemoteClass, jclass, moduleClass, m)
}
}
}
@@ -1675,6 +1677,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
jmethod = clinitMethod
jMethodName = CLASS_CONSTRUCTOR_NAME
jmethod.visitCode()
+ computeLocalVarsIndex(m)
genCode(m, false, true)
jmethod.visitMaxs(0, 0) // just to follow protocol, dummy arguments
jmethod.visitEnd()
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 912a5b0e90..0ae2adac84 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -1021,6 +1021,8 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
method = m
jmethod = clinitMethod
+
+ computeLocalVarsIndex(m)
genCode(m)
case None =>
legacyStaticInitializer(cls, clinit)
@@ -1114,7 +1116,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
linkedClass.info.members collect { case sym if sym.name.isTermName => sym.name } toSet
}
debuglog("Potentially conflicting names for forwarders: " + conflictingNames)
-
+
for (m <- moduleClass.info.membersBasedOnFlags(ExcludedForwarderFlags, Flags.METHOD)) {
if (m.isType || m.isDeferred || (m.owner eq ObjectClass) || m.isConstructor)
debuglog("No forwarder for '%s' from %s to '%s'".format(m, className, moduleClass))
@@ -1122,7 +1124,9 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
log("No forwarder for " + m + " due to conflict with " + linkedClass.info.member(m.name))
else {
log("Adding static forwarder for '%s' from %s to '%s'".format(m, className, moduleClass))
- addForwarder(jclass, moduleClass, m)
+ if (m.isAccessor && m.accessed.hasStaticAnnotation) {
+ log("@static: accessor " + m + ", accessed: " + m.accessed)
+ } else addForwarder(jclass, moduleClass, m)
}
}
}
@@ -1304,7 +1308,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
jclass.getType())
}
}
-
+
style match {
case Static(true) => dbg("invokespecial"); jcode.emitINVOKESPECIAL(jowner, jname, jtype)
case Static(false) => dbg("invokestatic"); jcode.emitINVOKESTATIC(jowner, jname, jtype)
@@ -1885,7 +1889,7 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
*/
def computeLocalVarsIndex(m: IMethod) {
var idx = if (m.symbol.isStaticMember) 0 else 1;
-
+
for (l <- m.params) {
debuglog("Index value for " + l + "{" + l.## + "}: " + idx)
l.index = idx
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index bbdf10a021..1d8d58ccf7 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -23,9 +23,12 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
new CleanUpTransformer(unit)
class CleanUpTransformer(unit: CompilationUnit) extends Transformer {
- private val newStaticMembers = mutable.Buffer.empty[Tree]
- private val newStaticInits = mutable.Buffer.empty[Tree]
- private val symbolsStoredAsStatic = mutable.Map.empty[String, Symbol]
+ private val newStaticMembers = mutable.Buffer.empty[Tree]
+ private val newStaticInits = mutable.Buffer.empty[Tree]
+ private val symbolsStoredAsStatic = mutable.Map.empty[String, Symbol]
+ private val staticBodies = mutable.Map.empty[(Symbol, Symbol), Tree]
+ private val syntheticClasses = mutable.Map.empty[Symbol, mutable.Set[Tree]] // package and trees
+ private val classNames = mutable.Map.empty[Symbol, Set[Name]]
private def clearStatics() {
newStaticMembers.clear()
newStaticInits.clear()
@@ -45,15 +48,16 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
result
}
private def transformTemplate(tree: Tree) = {
- val Template(parents, self, body) = tree
+ val t @ Template(parents, self, body) = tree
clearStatics()
+
val newBody = transformTrees(body)
val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody)
try addStaticInits(templ) // postprocess to include static ctors
finally clearStatics()
}
private def mkTerm(prefix: String): TermName = unit.freshTermName(prefix)
-
+
/** Kludge to provide a safe fix for #4560:
* If we generate a reference in an implementation class, we
* watch out for embedded This(..) nodes that point to the interface.
@@ -555,7 +559,62 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
else tree
}
-
+
+ case ValDef(mods, name, tpt, rhs) if tree.symbol.hasStaticAnnotation =>
+ log("moving @static valdef field: " + name + ", in: " + tree.symbol.owner)
+ val sym = tree.symbol
+ val owner = sym.owner
+
+ val staticBeforeLifting = atPhase(currentRun.erasurePhase) { owner.isStatic }
+ if (!owner.isModuleClass || !staticBeforeLifting) {
+ if (!sym.isSynthetic) {
+ reporter.error(tree.pos, "Only members of top-level objects and their nested objects can be annotated with @static.")
+ tree.symbol.removeAnnotation(StaticClass)
+ }
+ super.transform(tree)
+ } else if (owner.isModuleClass) {
+ val linkedClass = owner.companionClass match {
+ case NoSymbol =>
+ // create the companion class if it does not exist
+ val enclosing = owner.owner
+ val compclass = enclosing.newClass(newTypeName(owner.name.toString))
+ compclass setInfo ClassInfoType(List(ObjectClass.tpe), newScope, compclass)
+ enclosing.info.decls enter compclass
+
+ val compclstree = ClassDef(compclass, NoMods, List(List()), List(List()), List(), tree.pos)
+
+ syntheticClasses.getOrElseUpdate(enclosing, mutable.Set()) += compclstree
+
+ compclass
+ case comp => comp
+ }
+
+ // create a static field in the companion class for this @static field
+ val stfieldSym = linkedClass.newVariable(newTermName(name), tree.pos, STATIC | SYNTHETIC | FINAL) setInfo sym.tpe
+ stfieldSym.addAnnotation(StaticClass)
+
+ val names = classNames.getOrElseUpdate(linkedClass, linkedClass.info.decls.collect {
+ case sym if sym.name.isTermName => sym.name
+ } toSet)
+ if (names(stfieldSym.name)) {
+ reporter.error(
+ tree.pos,
+ "@static annotated field " + tree.symbol.name + " has the same name as a member of class " + linkedClass.name
+ )
+ } else {
+ linkedClass.info.decls enter stfieldSym
+
+ val initializerBody = rhs
+
+ // static field was previously initialized in the companion object itself, like this:
+ // staticBodies((linkedClass, stfieldSym)) = Select(This(owner), sym.getter(owner))
+ // instead, we move the initializer to the static ctor of the companion class
+ // we save the entire ValDef/DefDef to extract the rhs later
+ staticBodies((linkedClass, stfieldSym)) = tree
+ }
+ }
+ super.transform(tree)
+
/* MSIL requires that the stack is empty at the end of a try-block.
* Hence, we here rewrite all try blocks with a result != {Unit, All} such that they
* store their result in a local variable. The catch blocks are adjusted as well.
@@ -665,6 +724,11 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
if (newStaticInits.isEmpty)
template
else {
+ val ctorBody = newStaticInits.toList flatMap {
+ case Block(stats, expr) => stats :+ expr
+ case t => List(t)
+ }
+
val newCtor = findStaticCtor(template) match {
// in case there already were static ctors - augment existing ones
// currently, however, static ctors aren't being generated anywhere else
@@ -673,22 +737,76 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
deriveDefDef(ctor) {
case block @ Block(stats, expr) =>
// need to add inits to existing block
- treeCopy.Block(block, newStaticInits.toList ::: stats, expr)
+ treeCopy.Block(block, ctorBody ::: stats, expr)
case term: TermTree =>
// need to create a new block with inits and the old term
- treeCopy.Block(term, newStaticInits.toList, term)
+ treeCopy.Block(term, ctorBody, term)
}
case _ =>
// create new static ctor
val staticCtorSym = currentClass.newStaticConstructor(template.pos)
- val rhs = Block(newStaticInits.toList, Literal(Constant(())))
+ val rhs = Block(ctorBody, Literal(Constant(())))
localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs))
}
deriveTemplate(template)(newCtor :: _)
}
}
-
+
+ private def addStaticDeclarations(tree: Template, clazz: Symbol) {
+ // add static field initializer statements for each static field in clazz
+ if (!clazz.isModuleClass) for {
+ staticSym <- clazz.info.decls
+ if staticSym.hasStaticAnnotation
+ } staticSym match {
+ case stfieldSym if stfieldSym.isVariable =>
+ val valdef = staticBodies((clazz, stfieldSym))
+ val ValDef(_, _, _, rhs) = valdef
+ val fixedrhs = rhs.changeOwner((valdef.symbol, clazz.info.decl(nme.CONSTRUCTOR)))
+
+ val stfieldDef = localTyper.typedPos(tree.pos)(VAL(stfieldSym) === EmptyTree)
+ val flattenedInit = fixedrhs match {
+ case Block(stats, expr) => Block(stats, safeREF(stfieldSym) === expr)
+ case rhs => safeREF(stfieldSym) === rhs
+ }
+ val stfieldInit = localTyper.typedPos(tree.pos)(flattenedInit)
+
+ // add field definition to new defs
+ newStaticMembers append stfieldDef
+ newStaticInits append stfieldInit
+ }
+ }
+
+
+
+ override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
+ super.transformStats(stats, exprOwner) ++ {
+ // flush pending synthetic classes created in this owner
+ val synthclassdefs = syntheticClasses.get(exprOwner).toList.flatten
+ syntheticClasses -= exprOwner
+ synthclassdefs map {
+ cdef => localTyper.typedPos(cdef.pos)(cdef)
+ }
+ } map {
+ case clsdef @ ClassDef(mods, name, tparams, t @ Template(parent, self, body)) =>
+ // process all classes in the package again to add static initializers
+ clearStatics()
+
+ addStaticDeclarations(t, clsdef.symbol)
+
+ val templ = deriveTemplate(t)(_ => transformTrees(newStaticMembers.toList) ::: body)
+ val ntempl =
+ try addStaticInits(templ)
+ finally clearStatics()
+
+ val derived = deriveClassDef(clsdef)(_ => ntempl)
+ classNames.remove(clsdef.symbol)
+ derived
+
+ case stat => stat
+ }
+ }
+
} // CleanUpTransformer
}
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index e5119eac71..70bd0bd21b 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -186,12 +186,15 @@ abstract class Constructors extends Transform with ast.TreeDSL {
// before the superclass constructor call, otherwise it goes after.
// Lazy vals don't get the assignment in the constructor.
if (!stat.symbol.tpe.isInstanceOf[ConstantType]) {
- if (rhs != EmptyTree && !stat.symbol.isLazy) {
+ if (stat.symbol.hasStaticAnnotation) {
+ debuglog("@static annotated field initialization skipped.")
+ defBuf += deriveValDef(stat)(tree => tree)
+ } else if (rhs != EmptyTree && !stat.symbol.isLazy) {
val rhs1 = intoConstructor(stat.symbol, rhs);
(if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign(
stat.symbol, rhs1)
+ defBuf += deriveValDef(stat)(_ => EmptyTree)
}
- defBuf += deriveValDef(stat)(_ => EmptyTree)
}
case ClassDef(_, _, _, _) =>
// classes are treated recursively, and left in the template
diff --git a/src/library/scala/annotation/static.scala b/src/library/scala/annotation/static.scala
new file mode 100644
index 0000000000..f2955c756c
--- /dev/null
+++ b/src/library/scala/annotation/static.scala
@@ -0,0 +1,20 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2011, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.annotation
+
+/**
+ * An annotation that marks a member in the companion object as static
+ * and ensures that the compiler generates static fields/methods for it.
+ * This is important for Java interoperability and performance reasons.
+ *
+ * @since 2.10
+ */
+final class static extends StaticAnnotation {
+ // TODO document exact semantics above!
+}
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index cd243b9df0..f5172eb9aa 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -899,6 +899,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val SwitchClass = requiredClass[scala.annotation.switch]
lazy val TailrecClass = requiredClass[scala.annotation.tailrec]
lazy val VarargsClass = requiredClass[scala.annotation.varargs]
+ lazy val StaticClass = requiredClass[scala.annotation.static]
lazy val uncheckedStableClass = requiredClass[scala.annotation.unchecked.uncheckedStable]
lazy val uncheckedVarianceClass = requiredClass[scala.annotation.unchecked.uncheckedVariance]
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 165e04863c..d41670591c 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -246,6 +246,7 @@ trait StdNames {
final val BeanPropertyAnnot: NameType = "BeanProperty"
final val BooleanBeanPropertyAnnot: NameType = "BooleanBeanProperty"
final val bridgeAnnot: NameType = "bridge"
+ final val staticAnnot: NameType = "static"
// Classfile Attributes
final val AnnotationDefaultATTR: NameType = "AnnotationDefault"
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index fab5c5a2e7..82e4f98028 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -938,6 +938,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
|| hasAnnotation(SerializableAttr) // last part can be removed, @serializable annotation is deprecated
)
def hasBridgeAnnotation = hasAnnotation(BridgeClass)
+ def hasStaticAnnotation = hasAnnotation(StaticClass)
def isDeprecated = hasAnnotation(DeprecatedAttr)
def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 0)
def deprecationVersion = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 1)
diff --git a/test/files/neg/static-annot.check b/test/files/neg/static-annot.check
new file mode 100644
index 0000000000..80aebcd406
--- /dev/null
+++ b/test/files/neg/static-annot.check
@@ -0,0 +1,10 @@
+static-annot.scala:8: error: Only members of top-level objects and their nested objects can be annotated with @static.
+ @static val bar = 1
+ ^
+static-annot.scala:27: error: @static annotated field bar has the same name as a member of class Conflicting
+ @static val bar = 1
+ ^
+static-annot.scala:14: error: Only members of top-level objects and their nested objects can be annotated with @static.
+ @static val blah = 2
+ ^
+three errors found \ No newline at end of file
diff --git a/test/files/neg/static-annot.scala b/test/files/neg/static-annot.scala
new file mode 100644
index 0000000000..b8c4651076
--- /dev/null
+++ b/test/files/neg/static-annot.scala
@@ -0,0 +1,33 @@
+
+
+import annotation.static
+
+
+
+class StaticInClass {
+ @static val bar = 1
+}
+
+
+class NestedObjectInClass {
+ object Nested {
+ @static val blah = 2
+ }
+}
+
+
+object NestedObjectInObject {
+ object Nested {
+ @static val succeed = 3
+ }
+}
+
+
+object Conflicting {
+ @static val bar = 1
+}
+
+
+class Conflicting {
+ val bar = 45
+}
diff --git a/test/files/run/static-annot/field.scala b/test/files/run/static-annot/field.scala
new file mode 100644
index 0000000000..0677082cf6
--- /dev/null
+++ b/test/files/run/static-annot/field.scala
@@ -0,0 +1,205 @@
+
+
+
+import java.lang.reflect.Modifier
+import annotation.static
+import reflect._
+
+
+
+/* TEST 1 */
+
+/* A @static-annotated field in the companion object should yield
+ * a static field in its companion class.
+ */
+object Foo {
+ @static val bar = 17
+}
+
+
+class Foo
+
+
+trait Check {
+ def checkStatic(cls: Class[_]) {
+ cls.getDeclaredFields.find(_.getName == "bar") match {
+ case Some(f) => assert(Modifier.isStatic(f.getModifiers), "no static modifier")
+ case None => assert(false, "no static field bar in class")
+ }
+ }
+
+ def test(): Unit
+}
+
+
+object Test1 extends Check {
+ def test() {
+ checkStatic(classOf[Foo])
+ assert(Foo.bar == 17, "Companion object field should be 17.")
+ }
+}
+
+
+/* TEST 2 */
+
+class Foo2
+
+
+/** The order of declaring the class and its companion is inverted now. */
+object Foo2 {
+ @static val bar = 199
+}
+
+
+object Test2 extends Check {
+ def test() {
+ checkStatic(Class.forName("Foo3"))
+ assert(Foo3.bar == 1984, "Companion object field should be 1984.")
+ }
+}
+
+
+/* TEST 3 */
+
+/** The case where there is no explicit companion class */
+object Foo3 {
+ @static val bar = 1984
+}
+
+
+object Test3 extends Check {
+ def test() {
+ checkStatic(Class.forName("Foo3"))
+ assert(Foo3.bar == 1984, "Companion object field should be 1984.")
+ }
+}
+
+
+/* TEST 4 */
+
+/** We want to be able to generate atomic reference field updaters on the companion object
+ * so that they are created only once per class declaration, but we want them to actually
+ * be initialize __in the static initializer of the class itself__.
+ * This is extremely important, because otherwise the creation of the ARFU fails, since it uses
+ * trickery to detect the caller and compare it to the owner of the field being modified.
+ * Previously, this used to be circumvented through the use of Java base classes. A pain.
+ */
+class ArfuTarget {
+ @volatile var strfield = ArfuTarget.STR
+
+ def CAS(ov: String, nv: String): Boolean = {
+ ArfuTarget.arfu.compareAndSet(this, ov, nv)
+ }
+}
+
+
+object ArfuTarget {
+ @static val arfu = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(classOf[ArfuTarget], classOf[String], "strfield")
+ val STR = "Some string"
+}
+
+
+object Test4 extends Check {
+ def checkArfu() {
+ val at = new ArfuTarget
+ assert(at.strfield == ArfuTarget.STR)
+ at.CAS(ArfuTarget.STR, null)
+ assert(at.strfield == null)
+ }
+
+ def test() {
+ checkArfu()
+ }
+}
+
+
+/* TEST 5 */
+
+/** Although our main use-case is to use final static fields, we should be able to use non-final too.
+ * Here we set the static field of the class by using the setters in the companion object.
+ * It is legal to do so using the reference to `Foo` directly (in which case the callsites
+ * are rewritten to access the static field directly), or through an interface `Var` (in
+ * which case the getter and the setter for `field` access the static field in `Var`).
+ */
+trait Var {
+ var field: Int
+}
+
+object VarHolder extends Var {
+ @static var field = 1
+}
+
+
+object Test5 extends Check {
+ def test() {
+ assert(VarHolder.field == 1)
+ VarHolder.field = 2
+ assert(VarHolder.field == 2)
+ val vh: Var = VarHolder
+ vh.field = 3
+ assert(vh.field == 3)
+ }
+}
+
+
+/* TEST 6 */
+
+/** Here we test flattening the static ctor body and changing the owners of local definitions. */
+object Foo6 {
+ var companionField = 101
+ @static val staticField = {
+ val intermediate = companionField + 1
+ intermediate * 2
+ }
+}
+
+
+object Test6 extends Check {
+ def test() {
+ assert(Foo6.staticField == 204)
+ }
+}
+
+
+
+/* TEST 7 */
+
+/** Here we test objects nested in top-level objects */
+object Foo7 {
+ object AndHisFriend {
+ @static val bar = "string"
+ }
+ class AndHisFriend
+}
+
+
+object Test7 extends Check {
+ def test() {
+ checkStatic(classOf[Foo7.AndHisFriend])
+ assert(Foo7.AndHisFriend.bar == "string")
+ }
+}
+
+
+
+/* main */
+
+object Test {
+
+ def main(args: Array[String]) {
+ Test1.test()
+ Test2.test()
+ Test3.test()
+ Test4.test()
+ Test5.test()
+ Test6.test()
+ Test7.test()
+ }
+
+}
+
+
+
+
+
+