diff options
32 files changed, 764 insertions, 126 deletions
@@ -499,7 +499,7 @@ LOCAL DEPENDENCY (Adapted ASM) destdir="${build-asm.dir}/classes" classpath="${build-asm.dir}/classes" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> </javac> <touch file="${build-asm.dir}/asm.complete" verbose="no"/> @@ -540,7 +540,7 @@ LOCAL DEPENDENCY (FORKJOIN) classpath="${build-libs.dir}/classes/forkjoin" includes="**/*.java" debug="true" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> </javac> <touch file="${build-libs.dir}/forkjoin.complete" verbose="no"/> @@ -587,7 +587,7 @@ LOCAL DEPENDENCY (FJBG) classpath="${build-libs.dir}/classes/fjbg" includes="**/*.java" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> </javac> <touch file="${build-libs.dir}/fjbg.complete" verbose="no"/> @@ -635,7 +635,7 @@ LOCAL REFERENCE BUILD (LOCKER) srcdir="${src.dir}/library" destdir="${build-locker.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> <classpath> <path refid="forkjoin.classpath"/> @@ -749,7 +749,7 @@ LOCAL REFERENCE BUILD (LOCKER) includes="**/*.java" excludes="**/tests/**" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args}"/> </javac> <scalacfork @@ -1009,7 +1009,7 @@ QUICK BUILD (QUICK) srcdir="${src.dir}/library" destdir="${build-quick.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1020,7 +1020,7 @@ QUICK BUILD (QUICK) srcdir="${src.dir}/actors" destdir="${build-quick.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args}"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1145,7 +1145,7 @@ QUICK BUILD (QUICK) includes="**/*.java" excludes="**/tests/**" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args}"/> </javac> <scalacfork @@ -1349,7 +1349,7 @@ QUICK BUILD (QUICK) <javac srcdir="${src.dir}/partest" destdir="${build-quick.dir}/classes/partest" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <classpath> <pathelement location="${build-quick.dir}/classes/library"/> <pathelement location="${build-quick.dir}/classes/reflect"/> @@ -1691,7 +1691,7 @@ BOOTSTRAPPING BUILD (STRAP) srcdir="${src.dir}/library" destdir="${build-strap.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args} -XDignore.symbol.file"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1702,7 +1702,7 @@ BOOTSTRAPPING BUILD (STRAP) srcdir="${src.dir}/actors" destdir="${build-strap.dir}/classes/library" includes="**/*.java" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <compilerarg line="${javac.args}"/> <classpath> <path refid="forkjoin.classpath"/> @@ -1826,7 +1826,7 @@ BOOTSTRAPPING BUILD (STRAP) includes="**/*.java" excludes="**/tests/**" debug="true" - target="1.5" source="1.4"> + target="1.6" source="1.4"> <compilerarg line="${javac.args}"/> </javac> <scalacfork @@ -1994,7 +1994,7 @@ BOOTSTRAPPING BUILD (STRAP) <javac srcdir="${src.dir}/partest" destdir="${build-strap.dir}/classes/partest" - target="1.5" source="1.5"> + target="1.6" source="1.5"> <classpath> <pathelement location="${build-strap.dir}/classes/library"/> <pathelement location="${build-strap.dir}/classes/reflect"/> diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index e70716885e..c6809fb48e 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -99,7 +99,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { /** Defines valid values for the `target` property. */ object Target extends PermissibleValue { - val values = List("jvm-1.5", "jvm-1.6", "jvm-1.7", "msil") + val values = List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil") } /** Defines valid values for the `deprecation` and `unchecked` properties. */ diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index e378d71944..83335c4f62 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/src/compiler/scala/tools/nsc/Global.scala @@ -1447,6 +1447,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter) settings.userSetSettings filter (_.isDeprecated) foreach { s => unit.deprecationWarning(NoPosition, s.name + " is deprecated: " + s.deprecationMessage.get) } + if (settings.target.value.contains("jvm-1.5")) + unit.deprecationWarning(NoPosition, settings.target.name + ":" + settings.target.value + " is deprecated: use target for Java 1.6 or above.") } /* An iterator returning all the units being compiled in this run */ diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index b638745327..982267097b 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.findMember(m.symbol.accessed.name, NoFlags, NoFlags, false) + + if (m.symbol.isGetter) { + ctx1.bb.emit(LOAD_FIELD(staticfield, 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, 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.findMember(sym.accessed.name, NoFlags, NoFlags, false) + + if (sym.isGetter) { + ctx.bb.emit(LOAD_FIELD(staticfield, true) setHostClass hostClass, tree.pos) + ctx + } else if (sym.isSetter) { + val ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx) + ctx1.bb.emit(STORE_FIELD(staticfield, 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 e3d1f61535..025046f19e 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -598,7 +598,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { reverseJavaName.put(internalName, trackedSym) case Some(oldsym) => assert((oldsym == trackedSym) || (oldsym == RuntimeNothingClass) || (oldsym == RuntimeNullClass), // In contrast, neither NothingClass nor NullClass show up bytecode-level. - "how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name.") + "how can getCommonSuperclass() do its job if different class symbols get the same bytecode-level internal name: " + internalName) } } @@ -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/settings/StandardScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala index f0ee8b11f3..0991577829 100644 --- a/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/StandardScalaSettings.scala @@ -40,9 +40,9 @@ trait StandardScalaSettings { val nowarn = BooleanSetting ("-nowarn", "Generate no warnings.") val optimise: BooleanSetting // depends on post hook which mutates other settings val print = BooleanSetting ("-print", "Print program with Scala-specific features removed.") - val target = ChoiceSetting ("-target", "target", "Target platform for object files.", + val target = ChoiceSetting ("-target", "target", "Target platform for object files. All JVM 1.5 targets are deprecated.", List("jvm-1.5", "jvm-1.5-fjbg", "jvm-1.5-asm", "jvm-1.6", "jvm-1.7", "msil"), - "jvm-1.5-asm") + "jvm-1.6") val unchecked = BooleanSetting ("-unchecked", "Enable detailed unchecked (erasure) warnings.") val uniqid = BooleanSetting ("-uniqid", "Uniquely tag all identifiers in debugging output.") val usejavacp = BooleanSetting ("-usejavacp", "Utilize the java.class.path in classpath resolution.") diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala index bbdf10a021..e672f1914a 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,73 @@ 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 } + val isPrivate = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(PRIVATE) } + val isProtected = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(PROTECTED) } + val isLazy = atPhase(currentRun.typerPhase) { sym.getter(owner).hasFlag(LAZY) } + 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 (isPrivate || isProtected) { + reporter.error(tree.pos, "The @static annotation is only allowed on public members.") + tree.symbol.removeAnnotation(StaticClass) + super.transform(tree) + } else if (isLazy) { + reporter.error(tree.pos, "The @static annotation is not allowed on lazy members.") + 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 +735,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 +748,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/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index ab7bbc591b..afbe528b1f 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -46,10 +46,13 @@ abstract class ExplicitOuter extends InfoTransform private def haveSameOuter(parent: Type, clazz: Symbol) = parent match { case TypeRef(pre, sym, _) => val owner = clazz.owner + + //println(s"have same outer $parent $clazz $sym ${sym.owner} $owner $pre") sym.isClass && owner.isClass && - owner == sym.owner && + (owner isSubClass sym.owner) && owner.thisType =:= pre + case _ => false } @@ -480,7 +483,7 @@ abstract class ExplicitOuter extends InfoTransform val vparamss1 = if (isInner(clazz)) { // (4) val outerParam = - sym.newValueParameter(nme.OUTER, sym.pos) setInfo outerField(clazz).info + sym.newValueParameter(nme.OUTER, sym.pos) setInfo clazz.outerClass.thisType ((ValDef(outerParam) setType NoType) :: vparamss.head) :: vparamss.tail } else vparamss super.transform(copyDefDef(tree)(vparamss = vparamss1)) diff --git a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala index b54f127417..43edad3576 100644 --- a/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala +++ b/src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala @@ -54,6 +54,25 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } import debugging.patmatDebug + // to govern how much time we spend analyzing matches for unreachability/exhaustivity + object AnalysisBudget { + import scala.tools.cmd.FromString.IntFromString + val max = sys.props.get("scalac.patmat.analysisBudget").collect(IntFromString.orElse{case "off" => Integer.MAX_VALUE}).getOrElse(256) + + abstract class Exception extends RuntimeException("CNF budget exceeded") { + val advice: String + def warn(pos: Position, kind: String) = currentUnit.uncheckedWarning(pos, s"Cannot check match for $kind.\n$advice") + } + + object exceeded extends Exception { + val advice = s"(The analysis required more space than allowed. Please try with scalac -Dscalac.patmat.analysisBudget=${AnalysisBudget.max*2} or -Dscalac.patmat.analysisBudget=off.)" + } + + object stackOverflow extends Exception { + val advice = "(There was a stack overflow. Please try increasing the stack available to the compiler using e.g., -Xss2m.)" + } + } + def newTransformer(unit: CompilationUnit): Transformer = if (opt.virtPatmat) new MatchTransformer(unit) else noopTransformer @@ -422,7 +441,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL The pattern matches any value v such that r == v (ยง12.1). The type of r must conform to the expected type of the pattern. **/ - case Literal(Constant(_)) | Ident(_) | Select(_, _) => + case Literal(Constant(_)) | Ident(_) | Select(_, _) | This(_) => noFurtherSubPats(EqualityTestTreeMaker(patBinder, patTree, pos)) case Alternative(alts) => @@ -439,7 +458,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL patmatDebug("WARNING: Bind tree with unbound symbol "+ patTree) noFurtherSubPats() // there's no symbol -- something's wrong... don't fail here though (or should we?) - // case Star(_) | ArrayValue | This => error("stone age pattern relics encountered!") + // case Star(_) | ArrayValue => error("stone age pattern relics encountered!") case _ => error("unsupported pattern: "+ patTree +"(a "+ patTree.getClass +")") @@ -1946,14 +1965,14 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL type Formula def andFormula(a: Formula, b: Formula): Formula - class CNFBudgetExceeded extends RuntimeException("CNF budget exceeded") - // may throw an CNFBudgetExceeded - def propToSolvable(p: Prop) = { + // may throw an AnalysisBudget.Exception + def propToSolvable(p: Prop): Formula = { val (eqAxioms, pure :: Nil) = removeVarEq(List(p), modelNull = false) eqFreePropToSolvable(And(eqAxioms, pure)) } + // may throw an AnalysisBudget.Exception def eqFreePropToSolvable(p: Prop): Formula def cnfString(f: Formula): String @@ -1979,7 +1998,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL type Lit def Lit(sym: Sym, pos: Boolean = true): Lit - // throws an CNFBudgetExceeded when the prop results in a CNF that's too big + // throws an AnalysisBudget.Exception when the prop results in a CNF that's too big def eqFreePropToSolvable(p: Prop): Formula = { // TODO: for now, reusing the normalization from DPLL def negationNormalForm(p: Prop): Prop = p match { @@ -2001,9 +2020,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL def lit(s: Sym) = formula(clause(Lit(s))) def negLit(s: Sym) = formula(clause(Lit(s, false))) - def conjunctiveNormalForm(p: Prop, budget: Int = 256): Formula = { + def conjunctiveNormalForm(p: Prop, budget: Int = AnalysisBudget.max): Formula = { def distribute(a: Formula, b: Formula, budget: Int): Formula = - if (budget <= 0) throw new CNFBudgetExceeded + if (budget <= 0) throw AnalysisBudget.exceeded else (a, b) match { // true \/ _ = true @@ -2018,7 +2037,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL big flatMap (c => distribute(formula(c), small, budget - (big.size*small.size))) } - if (budget <= 0) throw new CNFBudgetExceeded + if (budget <= 0) throw AnalysisBudget.exceeded p match { case True => TrueF @@ -2037,9 +2056,17 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL } val start = Statistics.startTimer(patmatCNF) - val res = conjunctiveNormalForm(negationNormalForm(p)) + val res = + try { + conjunctiveNormalForm(negationNormalForm(p)) + } catch { case ex : StackOverflowError => + throw AnalysisBudget.stackOverflow + } + Statistics.stopTimer(patmatCNF, start) - patmatCNFSizes(res.size).value += 1 + + // + if (Statistics.enabled) patmatCNFSizes(res.size).value += 1 // patmatDebug("cnf for\n"+ p +"\nis:\n"+cnfString(res)) res @@ -2440,6 +2467,7 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL // right now hackily implement this by pruning counter-examples // unreachability would also benefit from a more faithful representation + // reachability (dead code) // computes the first 0-based case index that is unreachable (if any) @@ -2508,9 +2536,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL if (reachable) None else Some(caseIndex) } catch { - case e : CNFBudgetExceeded => -// debugWarn(util.Position.formatMessage(prevBinder.pos, "Cannot check match for reachability", false)) -// e.printStackTrace() + case ex: AnalysisBudget.Exception => + ex.warn(prevBinder.pos, "unreachability") None // CNF budget exceeded } } @@ -2651,9 +2678,8 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL Statistics.stopTimer(patmatAnaExhaust, start) pruned } catch { - case e : CNFBudgetExceeded => - patmatDebug(util.Position.formatMessage(prevBinder.pos, "Cannot check match for exhaustivity", false)) - // e.printStackTrace() + case ex : AnalysisBudget.Exception => + ex.warn(prevBinder.pos, "exhaustivity") Nil // CNF budget exceeded } } 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/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala index a6488b602f..a2ef42fac8 100644 --- a/src/library/scala/concurrent/package.scala +++ b/src/library/scala/concurrent/package.scala @@ -18,41 +18,6 @@ package object concurrent { type CancellationException = java.util.concurrent.CancellationException type TimeoutException = java.util.concurrent.TimeoutException - @implicitNotFound("Don't call `Awaitable` methods directly, use the `Await` object.") - sealed trait CanAwait - private implicit object AwaitPermission extends CanAwait - - /** - * `Await` is what is used to ensure proper handling of blocking for `Awaitable` instances. - */ - object Await { - /** - * Invokes ready() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. - * ready() blocks until the awaitable has completed or the timeout expires. - * - * Throws a TimeoutException if the timeout expires, as that is in the contract of `Awaitable.ready`. - * @param awaitable the `Awaitable` on which `ready` is to be called - * @param atMost the maximum timeout for which to wait - * @return the result of `awaitable.ready` which is defined to be the awaitable itself. - */ - @throws(classOf[TimeoutException]) - def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = - blocking(awaitable.ready(atMost)) - - /** - * Invokes result() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. - * result() blocks until the awaitable has completed or the timeout expires. - * - * Throws a TimeoutException if the timeout expires, or any exception thrown by `Awaitable.result`. - * @param awaitable the `Awaitable` on which `result` is to be called - * @param atMost the maximum timeout for which to wait - * @return the result of `awaitable.result` - */ - @throws(classOf[Exception]) - def result[T](awaitable: Awaitable[T], atMost: Duration): T = - blocking(awaitable.result(atMost)) - } - /** Starts an asynchronous computation and returns a `Future` object with the result of that computation. * * The result becomes available once the asynchronous computation is completed. @@ -85,5 +50,46 @@ package object concurrent { * - TimeoutException - in the case that the blockable object timed out */ @throws(classOf[Exception]) - def blocking[T](body: =>T): T = BlockContext.current.blockOn(body) + def blocking[T](body: =>T): T = BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission) +} + +package concurrent { + @implicitNotFound("Don't call `Awaitable` methods directly, use the `Await` object.") + sealed trait CanAwait + + /** + * Internal usage only, implementation detail. + */ + private[concurrent] object AwaitPermission extends CanAwait + + /** + * `Await` is what is used to ensure proper handling of blocking for `Awaitable` instances. + */ + object Await { + /** + * Invokes ready() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. + * ready() blocks until the awaitable has completed or the timeout expires. + * + * Throws a TimeoutException if the timeout expires, as that is in the contract of `Awaitable.ready`. + * @param awaitable the `Awaitable` on which `ready` is to be called + * @param atMost the maximum timeout for which to wait + * @return the result of `awaitable.ready` which is defined to be the awaitable itself. + */ + @throws(classOf[TimeoutException]) + def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type = + blocking(awaitable.ready(atMost)(AwaitPermission)) + + /** + * Invokes result() on the awaitable, properly wrapped by a call to `scala.concurrent.blocking`. + * result() blocks until the awaitable has completed or the timeout expires. + * + * Throws a TimeoutException if the timeout expires, or any exception thrown by `Awaitable.result`. + * @param awaitable the `Awaitable` on which `result` is to be called + * @param atMost the maximum timeout for which to wait + * @return the result of `awaitable.result` + */ + @throws(classOf[Exception]) + def result[T](awaitable: Awaitable[T], atMost: Duration): T = + blocking(awaitable.result(atMost)(AwaitPermission)) + } } diff --git a/src/partest/scala/tools/partest/DirectTest.scala b/src/partest/scala/tools/partest/DirectTest.scala index 4e7f36bdc9..5b4e1b4b25 100644 --- a/src/partest/scala/tools/partest/DirectTest.scala +++ b/src/partest/scala/tools/partest/DirectTest.scala @@ -38,7 +38,8 @@ abstract class DirectTest extends App { // new compiler def newCompiler(args: String*): Global = { val settings = newSettings((CommandLineParser tokenize extraSettings) ++ args.toList) - new Global(settings) + if (settings.Yrangepos.value) new Global(settings) with interactive.RangePositions + else new Global(settings) } def newSources(sourceCodes: String*) = sourceCodes.toList.zipWithIndex map { case (src, idx) => new BatchSourceFile("newSource" + (idx + 1), src) @@ -69,7 +70,7 @@ abstract class DirectTest extends App { /** Constructor/main body **/ try show() - catch { case t => println(t) ; t.printStackTrace ; sys.exit(1) } + catch { case t => println(t.getMessage) ; t.printStackTrace ; sys.exit(1) } /** Debugger interest only below this line **/ protected def isDebug = (sys.props contains "partest.debug") || (sys.env contains "PARTEST_DEBUG") diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 7284a199b7..d9b63529eb 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -898,6 +898,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 e5d0e96d9c..75962ff9d0 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 d484617767..10d02376b1 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -933,6 +933,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/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala index 7c80ddd37d..df4a3336c3 100644 --- a/src/reflect/scala/reflect/internal/util/SourceFile.scala +++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala @@ -102,17 +102,21 @@ class ScriptSourceFile(underlying: BatchSourceFile, content: Array[Char], overri } /** a file whose contents do not change over time */ -class BatchSourceFile(val file : AbstractFile, val content: Array[Char]) extends SourceFile { - +class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extends SourceFile { def this(_file: AbstractFile) = this(_file, _file.toCharArray) def this(sourceName: String, cs: Seq[Char]) = this(new VirtualFile(sourceName), cs.toArray) def this(file: AbstractFile, cs: Seq[Char]) = this(file, cs.toArray) - override def equals(that : Any) = that match { - case that : BatchSourceFile => file.path == that.file.path && start == that.start - case _ => false - } - override def hashCode = file.path.## + start.## + // If non-whitespace tokens run all the way up to EOF, + // positions go wrong because the correct end of the last + // token cannot be used as an index into the char array. + // The least painful way to address this was to add a + // newline to the array. + val content = ( + if (content0.length == 0 || !content0.last.isWhitespace) + content0 :+ '\n' + else content0 + ) val length = content.length def start = 0 def isSelfContained = true @@ -158,4 +162,10 @@ class BatchSourceFile(val file : AbstractFile, val content: Array[Char]) extends lastLine = findLine(0, lines.length, lastLine) lastLine } + + override def equals(that : Any) = that match { + case that : BatchSourceFile => file.path == that.file.path && start == that.start + case _ => false + } + override def hashCode = file.path.## + start.## } diff --git a/src/reflect/scala/tools/nsc/io/VirtualFile.scala b/src/reflect/scala/tools/nsc/io/VirtualFile.scala index b9a946598c..805bc04165 100644 --- a/src/reflect/scala/tools/nsc/io/VirtualFile.scala +++ b/src/reflect/scala/tools/nsc/io/VirtualFile.scala @@ -55,7 +55,7 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF } } - def container: AbstractFile = unsupported + def container: AbstractFile = NoAbstractFile /** Is this abstract file a directory? */ def isDirectory: Boolean = false diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala index 1209b710b0..43d4c9dc71 100644 --- a/test/files/jvm/scala-concurrent-tck.scala +++ b/test/files/jvm/scala-concurrent-tck.scala @@ -700,9 +700,18 @@ trait Blocking extends TestBase { } } + def testFQCNForAwaitAPI(): Unit = once { + done => + + assert(classOf[CanAwait].getName == "scala.concurrent.CanAwait") + assert(Await.getClass.getName == "scala.concurrent.Await") + + done() + } + testAwaitSuccess() testAwaitFailure() - + testFQCNForAwaitAPI() } trait BlockContexts extends TestBase { diff --git a/test/files/neg/static-annot.check b/test/files/neg/static-annot.check new file mode 100644 index 0000000000..66efebdcee --- /dev/null +++ b/test/files/neg/static-annot.check @@ -0,0 +1,19 @@ +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:37: error: The @static annotation is only allowed on public members. + @static private val bar = 1 + ^ +static-annot.scala:38: error: The @static annotation is only allowed on public members. + @static private val baz = 2 + ^ +static-annot.scala:39: error: The @static annotation is not allowed on lazy members. + @static lazy val bam = 3 + ^ +static-annot.scala:14: error: Only members of top-level objects and their nested objects can be annotated with @static. + @static val blah = 2 + ^ +6 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..c6c626d42b --- /dev/null +++ b/test/files/neg/static-annot.scala @@ -0,0 +1,47 @@ + + +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 +} + + +object PrivateProtectedLazy { + @static private val bar = 1 + @static private val baz = 2 + @static lazy val bam = 3 +} + + +class PrivateProtectedLazy { + println(PrivateProtectedLazy.bar) + println(PrivateProtectedLazy.baz) + println(PrivateProtectedLazy.bam) +} diff --git a/test/files/neg/t4069.check b/test/files/neg/t4069.check index 91bf882cec..08e937bdfe 100644 --- a/test/files/neg/t4069.check +++ b/test/files/neg/t4069.check @@ -12,5 +12,5 @@ t4069.scala:4: error: I encountered a '}' where I didn't expect one, maybe this ^ t4069.scala:10: error: '}' expected but eof found. } -^ + ^ 5 errors found diff --git a/test/files/neg/t4584.check b/test/files/neg/t4584.check index 060160d76a..419f5704b1 100644 --- a/test/files/neg/t4584.check +++ b/test/files/neg/t4584.check @@ -1,4 +1,7 @@ -t4584.scala:1: error: incomplete unicode escape +t4584.scala:1: error: error in unicode escape +class A { val /u2 + ^ +t4584.scala:1: error: illegal character '/uffff' class A { val /u2 ^ -one error found +two errors found diff --git a/test/files/neg/unicode-unterminated-quote.check b/test/files/neg/unicode-unterminated-quote.check index fc5caa6d7e..5085505fb4 100644 --- a/test/files/neg/unicode-unterminated-quote.check +++ b/test/files/neg/unicode-unterminated-quote.check @@ -1,4 +1,7 @@ unicode-unterminated-quote.scala:2: error: unclosed string literal val x = /u0022 ^ -one error found +unicode-unterminated-quote.scala:2: error: '}' expected but eof found. + val x = /u0022 + ^ +two errors found diff --git a/test/files/pos/t5957/T_1.scala b/test/files/pos/t5957/T_1.scala index 1db5a3891f..339dcbf0f0 100644 --- a/test/files/pos/t5957/T_1.scala +++ b/test/files/pos/t5957/T_1.scala @@ -1,6 +1,8 @@ abstract class T { - def t1: Test$Bar + // see: SI-6109 + // def t1: Test$Bar def t2: Test#Bar - def t3: Test$Baz + // see: SI-6109 + // def t3: Test$Baz def t4: Test.Baz } diff --git a/test/files/run/outertest.scala b/test/files/run/outertest.scala new file mode 100644 index 0000000000..3cc96afa5b --- /dev/null +++ b/test/files/run/outertest.scala @@ -0,0 +1,26 @@ +// A test for the case where the outer field of class B#J should be eliminated. +// You can verify this by running a javap on B.J +abstract class A { + + abstract class I { + + } + + val foo = "foo" + +} + +class B extends A { + + class J extends I { + val bar = foo + } + +} + +object Test extends App { + + val b = new B + assert((new b.J).bar == b.foo) + +} diff --git a/test/files/run/static-annot/field.scala b/test/files/run/static-annot/field.scala new file mode 100644 index 0000000000..a7d8158321 --- /dev/null +++ b/test/files/run/static-annot/field.scala @@ -0,0 +1,243 @@ + + + +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") + } +} + + + +/* TEST 8 */ + +object Foo8 { + @static val field = 7 + + val function: () => Int = () => { + field + 1 + } + + val anon = new Runnable { + def run() { + assert(field == 7, "runnable asserting field is 7") + } + } + + @static var mutable = 10 + + val mutation: () => Unit = () => { + mutable += 1 + } +} + +object Test8 { + def test() { + assert(Foo8.function() == 8, "function must return 8") + Foo8.anon.run() + assert(Foo8.mutable == 10, "mutable is 10") + Foo8.mutation() + assert(Foo8.mutable == 11, "mutable is 11") + Foo8.mutation() + assert(Foo8.mutable == 12, "mutable is 12") + } +} + + + + +/* main */ + +object Test { + + def main(args: Array[String]) { + Test1.test() + Test2.test() + Test3.test() + Test4.test() + Test5.test() + Test6.test() + Test7.test() + Test8.test() + } + +} + + + + + + diff --git a/test/files/run/t5385.check b/test/files/run/t5385.check new file mode 100644 index 0000000000..1df74fcfb5 --- /dev/null +++ b/test/files/run/t5385.check @@ -0,0 +1,8 @@ +[0:9] class Azz +[0:9] class Bzz +[0:9] class Czz +[0:9] class Dzz +[0:11] class Ezz +[0:11] class Fzz +[0:13] class Gzz +[0:13] class Hzz diff --git a/test/files/run/t5385.scala b/test/files/run/t5385.scala new file mode 100644 index 0000000000..b803897e71 --- /dev/null +++ b/test/files/run/t5385.scala @@ -0,0 +1,16 @@ +import scala.tools.partest._ + +object Test extends CompilerTest { + import global._ + override def extraSettings = super.extraSettings + " -Yrangepos" + override def sources = List( + "class Azz", "class Bzz ", "class Czz ", "class Dzz\n", + "class Ezz{}", "class Fzz{} ", "class Gzz { }", "class Hzz { } " + ) + def check(source: String, unit: CompilationUnit) { + unit.body foreach { + case cdef: ClassDef => println("%-15s class %s".format(cdef.pos.show, cdef.name)) + case _ => + } + } +} diff --git a/test/files/run/t6104.check b/test/files/run/t6104.check new file mode 100644 index 0000000000..9766475a41 --- /dev/null +++ b/test/files/run/t6104.check @@ -0,0 +1 @@ +ok diff --git a/test/files/run/t6104.scala b/test/files/run/t6104.scala new file mode 100644 index 0000000000..8ab12c7752 --- /dev/null +++ b/test/files/run/t6104.scala @@ -0,0 +1,8 @@ +class A { Self => + val ok = "ok" + this match { + case me@Self => println(me.ok) + } +} + +object Test extends A with App
\ No newline at end of file |