diff options
Diffstat (limited to 'src')
19 files changed, 280 insertions, 143 deletions
diff --git a/src/compiler/scala/reflect/internal/NameManglers.scala b/src/compiler/scala/reflect/internal/NameManglers.scala index ef092f16bb..97a74c2383 100644 --- a/src/compiler/scala/reflect/internal/NameManglers.scala +++ b/src/compiler/scala/reflect/internal/NameManglers.scala @@ -85,7 +85,7 @@ trait NameManglers { def isConstructorName(name: Name) = name == CONSTRUCTOR || name == MIXIN_CONSTRUCTOR def isExceptionResultName(name: Name) = name startsWith EXCEPTION_RESULT_PREFIX - def isImplClassName(name: Name) = stripAnonNumberSuffix(name) endsWith IMPL_CLASS_SUFFIX + def isImplClassName(name: Name) = name endsWith IMPL_CLASS_SUFFIX def isLocalDummyName(name: Name) = name startsWith LOCALDUMMY_PREFIX def isLocalName(name: Name) = name endsWith LOCAL_SUFFIX_STRING def isLoopHeaderLabel(name: Name) = (name startsWith WHILE_PREFIX) || (name startsWith DO_WHILE_PREFIX) @@ -176,25 +176,18 @@ trait NameManglers { else name.toTermName } - /** !!! I'm putting this logic in place because I can witness - * trait impls get lifted and acquiring names like 'Foo$class$1' - * while clearly still being what they were. It's only being used on - * isImplClassName. However, it's anyone's guess how much more - * widely this logic actually ought to be applied. Anything which - * tests for how a name ends is a candidate for breaking down once - * something is lifted from a method. - * - * TODO: resolve this significant problem. - */ - def stripAnonNumberSuffix(name: Name): Name = { - val str = "" + name - if (str == "" || !str.endChar.isDigit) name - else { - val idx = name.lastPos('$') - if (idx < 0 || str.substring(idx + 1).exists(c => !c.isDigit)) name - else name.subName(0, idx) - } - } + // This isn't needed at the moment since I fixed $class$1 but + // I expect it will be at some point. + // + // def anonNumberSuffix(name: Name): Name = { + // ("" + name) lastIndexOf '$' match { + // case -1 => nme.EMPTY + // case idx => + // val s = name drop idx + // if (s.toString forall (_.isDigit)) s + // else nme.EMPTY + // } + // } def stripModuleSuffix(name: Name): Name = ( if (isModuleName(name)) name dropRight MODULE_SUFFIX_STRING.length else name diff --git a/src/compiler/scala/reflect/internal/Names.scala b/src/compiler/scala/reflect/internal/Names.scala index b960695f51..907b564d4c 100644 --- a/src/compiler/scala/reflect/internal/Names.scala +++ b/src/compiler/scala/reflect/internal/Names.scala @@ -77,7 +77,11 @@ trait Names extends api.Names { def newTermName(cs: Array[Char]): TermName = newTermName(cs, 0, cs.length) def newTypeName(cs: Array[Char]): TypeName = newTypeName(cs, 0, cs.length) - /** Create a term name from the characters in cs[offset..offset+len-1]. */ + /** Create a term name from the characters in cs[offset..offset+len-1]. + * TODO - have a mode where name validation is performed at creation time + * (e.g. if a name has the string "$class" in it, then fail if that + * string is not at the very end.) + */ protected def newTermName(cs: Array[Char], offset: Int, len: Int, cachedString: String): TermName = { val h = hashValue(cs, offset, len) & HASH_MASK var n = termHashtable(h) diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index b4d2b1531f..a943b6fe24 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1425,7 +1425,10 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ def sourceModule: Symbol = NoSymbol - /** The implementation class of a trait. */ + /** The implementation class of a trait. If available it will be the + * symbol with the same owner, and the name of this symbol with $class + * appended to it. + */ final def implClass: Symbol = owner.info.decl(nme.implClassName(name)) /** The class that is logically an outer class of given `clazz`. @@ -2538,7 +2541,6 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def enclClass: Symbol = this override def toplevelClass: Symbol = this override def enclMethod: Symbol = this - override def owner: Symbol = abort("no-symbol does not have owner") override def sourceFile: AbstractFileType = null override def ownerChain: List[Symbol] = List() override def ownersIterator: Iterator[Symbol] = Iterator.empty @@ -2551,6 +2553,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => override def accessBoundary(base: Symbol): Symbol = RootClass def cloneSymbolImpl(owner: Symbol, newFlags: Long): Symbol = abort() override def originalEnclosingMethod = this + + override def owner: Symbol = + abort("no-symbol does not have an owner (this is a bug: scala " + scala.util.Properties.versionString + ")") + override def typeConstructor: Type = + abort("no-symbol does not have a type constructor (this may indicate scalac cannot find fundamental classes)") } /** Derives a new list of symbols from the given list by mapping the given diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index 7aff4e3e8e..04ff0c440d 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -608,7 +608,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared { if (!deprecation.isEmpty) settings.deprecation.value = deprecation.get if (!nobootcp.isEmpty) settings.nobootcp.value = nobootcp.get if (!nowarn.isEmpty) settings.nowarn.value = nowarn.get - if (!optimise.isEmpty) settings.XO.value = optimise.get + if (!optimise.isEmpty) settings.optimise.value = optimise.get if (!unchecked.isEmpty) settings.unchecked.value = unchecked.get if (!usejavacp.isEmpty) settings.usejavacp.value = usejavacp.get diff --git a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl index 9f1fbc4524..5949689b24 100644 --- a/src/compiler/scala/tools/ant/templates/tool-windows.tmpl +++ b/src/compiler/scala/tools/ant/templates/tool-windows.tmpl @@ -86,4 +86,6 @@ goto :eof :end
@@endlocal
-exit /b %errorlevel%
+
+REM exit code fix, see http://stackoverflow.com/questions/4632891/exiting-batch-with-exit-b-x-where-x-1-acts-as-if-command-completed-successfu
+@@%COMSPEC% /C exit %errorlevel% >nul
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 15b4c8c708..badf5d70d1 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -1528,7 +1528,7 @@ abstract class GenICode extends SubComponent { if (mustUseAnyComparator) { // when -optimise is on we call the @inline-version of equals, found in ScalaRunTime val equalsMethod = - if (!settings.XO.value) { + if (!settings.optimise.value) { def default = platform.externalEquals platform match { case x: JavaPlatform => @@ -1550,7 +1550,7 @@ abstract class GenICode extends SubComponent { val ctx1 = genLoad(l, ctx, ObjectReference) val ctx2 = genLoad(r, ctx1, ObjectReference) - ctx2.bb.emit(CALL_METHOD(equalsMethod, if (settings.XO.value) Dynamic else Static(false))) + ctx2.bb.emit(CALL_METHOD(equalsMethod, if (settings.optimise.value) Dynamic else Static(false))) ctx2.bb.emit(CZJUMP(thenCtx.bb, elseCtx.bb, NE, BOOL)) ctx2.bb.close } diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 3e8ef3f611..66f802f74f 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -62,6 +62,15 @@ abstract class Inliners extends SubComponent { override def apply(c: IClass) { inliner analyzeClass c } + + override def run() { + try { + super.run() + } finally { + inliner.NonPublicRefs.usesNonPublics.clear() + inliner.recentTFAs.clear + } + } } def isBottomType(sym: Symbol) = sym == NullClass || sym == NothingClass @@ -79,17 +88,10 @@ abstract class Inliners extends SubComponent { val Private, Protected, Public = Value /** Cache whether a method calls private members. */ - val usesNonPublics: mutable.Map[IMethod, Value] = perRunCaches.newMap() + val usesNonPublics = mutable.Map.empty[IMethod, Value] } import NonPublicRefs._ - /* fresh name counter */ - val fresh = perRunCaches.newMap[String, Int]() withDefaultValue 0 - def freshName(s: String): TermName = { - fresh(s) += 1 - newTermName(s + fresh(s)) - } - private def hasInline(sym: Symbol) = sym hasAnnotation ScalaInlineClass private def hasNoInline(sym: Symbol) = sym hasAnnotation ScalaNoInlineClass @@ -97,6 +99,28 @@ abstract class Inliners extends SubComponent { private var currentIClazz: IClass = _ private def warn(pos: Position, msg: String) = currentIClazz.cunit.warning(pos, msg) + val recentTFAs = mutable.Map.empty[Symbol, Tuple2[Boolean, analysis.MethodTFA]] + private def getRecentTFA(incm: IMethod): (Boolean, analysis.MethodTFA) = { + + def containsRETURN(blocks: List[BasicBlock]) = blocks exists { bb => bb.lastInstruction.isInstanceOf[RETURN] } + + val opt = recentTFAs.get(incm.symbol) + if(opt.isDefined) { + // FYI val cachedBBs = opt.get._2.in.keySet + // FYI assert(incm.blocks.toSet == cachedBBs) + // incm.code.touched plays no role here + return opt.get + } + + val hasRETURN = containsRETURN(incm.code.blocksList) || (incm.exh exists { eh => containsRETURN(eh.blocks) }) + var a: analysis.MethodTFA = null + if(hasRETURN) { a = new analysis.MethodTFA(incm); a.run } + + if(hasInline(incm.symbol)) { recentTFAs.put(incm.symbol, (hasRETURN, a)) } + + (hasRETURN, a) + } + def analyzeClass(cls: IClass): Unit = if (settings.inline.value) { debuglog("Analyzing " + cls) @@ -106,7 +130,7 @@ abstract class Inliners extends SubComponent { ms foreach { im => if(hasInline(im.symbol)) { log("Not inlining into " + im.symbol.originalName.decode + " because it is marked @inline.") - } else if(im.hasCode) { + } else if(im.hasCode && !im.symbol.isBridge) { analyzeMethod(im) } } @@ -114,20 +138,21 @@ abstract class Inliners extends SubComponent { val tfa = new analysis.MTFAGrowable() tfa.stat = global.opt.printStats - val staleOut = new mutable.ListBuffer[BasicBlock] + val staleOut = new mutable.ListBuffer[BasicBlock] val splicedBlocks = mutable.Set.empty[BasicBlock] - val staleIn = mutable.Set.empty[BasicBlock] - - // how many times have we already inlined this method here? - private val inlinedMethodCount = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0 + val staleIn = mutable.Set.empty[BasicBlock] def analyzeMethod(m: IMethod): Unit = { - var sizeBeforeInlining = if (m.hasCode) m.code.blockCount else 0 - var instrBeforeInlining = if (m.hasCode) m.code.instructionCount else 0 + var sizeBeforeInlining = m.code.blockCount + var instrBeforeInlining = m.code.instructionCount var retry = false var count = 0 - fresh.clear() - inlinedMethodCount.clear() + + // fresh name counter + val fresh = mutable.HashMap.empty[String, Int] withDefaultValue 0 + // how many times have we already inlined this method here? + val inlinedMethodCount = mutable.HashMap.empty[Symbol, Int] withDefaultValue 0 + val caller = new IMethodInfo(m) var info: tfa.lattice.Elem = null @@ -146,17 +171,20 @@ abstract class Inliners extends SubComponent { warn(i.pos, "Could not inline required method %s because %s.".format(msym.originalName.decode, reason)) } - if (shouldLoadImplFor(concreteMethod, receiver)) { + def isAvailable = icodes available concreteMethod.enclClass + + if (!isAvailable && shouldLoadImplFor(concreteMethod, receiver)) { // Until r22824 this line was: // icodes.icode(concreteMethod.enclClass, true) // - // Changing it to the below was the proximate cause for SI-3882: + // Changing it to + // icodes.load(concreteMethod.enclClass) + // was the proximate cause for SI-3882: // error: Illegal index: 0 overlaps List((variable par1,LONG)) // error: Illegal index: 0 overlaps List((variable par1,LONG)) icodes.load(concreteMethod.enclClass) } - def isAvailable = icodes available concreteMethod.enclClass def isCandidate = ( isClosureClass(receiver) || concreteMethod.isEffectivelyFinal @@ -179,7 +207,7 @@ abstract class Inliners extends SubComponent { lookupIMethod(concreteMethod, receiver) match { case Some(callee) => val inc = new IMethodInfo(callee) - val pair = new CallerCalleeInfo(caller, inc) + val pair = new CallerCalleeInfo(caller, inc, fresh, inlinedMethodCount) if (pair isStampedForInlining info.stack) { retry = true @@ -196,6 +224,7 @@ abstract class Inliners extends SubComponent { * might have changed after the inlining. */ usesNonPublics -= m + recentTFAs -= m.symbol } else { if (settings.debug.value) @@ -315,11 +344,10 @@ abstract class Inliners extends SubComponent { def inline = hasInline(sym) def noinline = hasNoInline(sym) - def numInlined = inlinedMethodCount(sym) def isBridge = sym.isBridge def isInClosure = isClosureClass(owner) - def isHigherOrder = isHigherOrderMethod(sym) + val isHigherOrder = isHigherOrderMethod(sym) def isMonadic = isMonadicMethod(sym) def handlers = m.exh @@ -328,7 +356,7 @@ abstract class Inliners extends SubComponent { def length = blocks.length def openBlocks = blocks filterNot (_.closed) def instructions = m.code.instructions - def linearized = linearizer linearize m + // def linearized = linearizer linearize m def isSmall = (length <= SMALL_METHOD_SIZE) && blocks(0).length < 10 def isLarge = length > MAX_INLINE_SIZE @@ -347,9 +375,14 @@ abstract class Inliners extends SubComponent { def addHandlers(exhs: List[ExceptionHandler]) = m.exh = exhs ::: m.exh } - class CallerCalleeInfo(val caller: IMethodInfo, val inc: IMethodInfo) { + class CallerCalleeInfo(val caller: IMethodInfo, val inc: IMethodInfo, fresh: mutable.Map[String, Int], inlinedMethodCount: collection.Map[Symbol, Int]) { def isLargeSum = caller.length + inc.length - 1 > SMALL_METHOD_SIZE + private def freshName(s: String): TermName = { + fresh(s) += 1 + newTermName(s + fresh(s)) + } + /** Inline 'inc' into 'caller' at the given block and instruction. * The instruction must be a CALL_METHOD. */ @@ -364,7 +397,7 @@ abstract class Inliners extends SubComponent { def newLocal(baseName: String, kind: TypeKind) = new Local(caller.sym.newVariable(freshName(baseName), targetPos), kind, false) - val a = new analysis.MethodTFA(inc.m) + val (hasRETURN, a) = getRecentTFA(inc.m) /* The exception handlers that are active at the current block. */ val activeHandlers = caller.handlers filter (_ covered block) @@ -393,7 +426,7 @@ abstract class Inliners extends SubComponent { case x => newLocal("$retVal", x) } - val inlinedLocals = perRunCaches.newMap[Local, Local]() + val inlinedLocals = mutable.HashMap.empty[Local, Local] /** Add a new block in the current context. */ def newBlock() = { @@ -475,9 +508,6 @@ abstract class Inliners extends SubComponent { inlinedBlock(b).varsInScope ++= (b.varsInScope map inlinedLocals) } - // analyse callee - a.run - // re-emit the instructions before the call block.open block.clear @@ -494,7 +524,7 @@ abstract class Inliners extends SubComponent { // duplicate the other blocks in the callee val calleeLin = inc.m.linearizedBlocks() calleeLin foreach { bb => - var info = a in bb + var info = if(hasRETURN) (a in bb) else null def emitInlined(i: Instruction) = inlinedBlock(bb).emit(i, targetPos) def emitDrops(toDrop: Int) = info.stack.types drop toDrop foreach (t => emitInlined(DROP(t))) @@ -510,7 +540,7 @@ abstract class Inliners extends SubComponent { case _ => () } emitInlined(map(i)) - info = a.interpret(info, i) + info = if(hasRETURN) a.interpret(info, i) else null } inlinedBlock(bb).close } @@ -538,7 +568,7 @@ abstract class Inliners extends SubComponent { | isSafeToInline: %s | shouldInline: %s """.stripMargin.format( - inc.m, sameSymbols, inc.numInlined < 2, + inc.m, sameSymbols, inlinedMethodCount(inc.sym) < 2, inc.m.hasCode, isSafeToInline(stack), shouldInline ) ) @@ -635,28 +665,22 @@ abstract class Inliners extends SubComponent { var score = 0 - // better not inline inside closures, but hope that the closure itself - // is repeatedly inlined - if (caller.isInClosure) score -= 2 + // better not inline inside closures, but hope that the closure itself is repeatedly inlined + if (caller.isInClosure) score -= 2 else if (caller.inlinedCalls < 1) score -= 1 // only monadic methods can trigger the first inline - if (inc.isSmall) - score += 1 + if (inc.isSmall) score += 1; + if (inc.isLarge) score -= 1; if (caller.isSmall && isLargeSum) { score -= 1 debuglog("shouldInline: score decreased to " + score + " because small " + caller + " would become large") } - if (inc.isLarge) - score -= 1 - if (inc.isMonadic) - score += 3 - else if (inc.isHigherOrder) - score += 1 - if (inc.isInClosure) - score += 2 - if (inc.numInlined > 2) - score -= 2 + if (inc.isMonadic) score += 3 + else if (inc.isHigherOrder) score += 1 + + if (inc.isInClosure) score += 2; + if (inlinedMethodCount(inc.sym) > 2) score -= 2; log("shouldInline(" + inc.m + ") score: " + score) diff --git a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala index 0dc51d5eb0..7c71438b98 100644 --- a/src/compiler/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/compiler/scala/tools/nsc/interpreter/ILoop.scala @@ -110,7 +110,7 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) class ILoopInterpreter extends IMain(settings, out) { outer => - private class ThreadStoppingLineManager extends Line.Manager(parentClassLoader) { + private class ThreadStoppingLineManager(classLoader: ClassLoader) extends Line.Manager(classLoader) { override def onRunaway(line: Line[_]): Unit = { val template = """ |// She's gone rogue, captain! Have to take her out! @@ -126,8 +126,8 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) override lazy val formatting = new Formatting { def prompt = ILoop.this.prompt } - override protected def createLineManager(): Line.Manager = - new ThreadStoppingLineManager + override protected def createLineManager(classLoader: ClassLoader): Line.Manager = + new ThreadStoppingLineManager(classLoader) override protected def parentClassLoader = settings.explicitParentLoader.getOrElse( classOf[ILoop].getClassLoader ) diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 0f0ab69e6d..8cdd2334ab 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -269,7 +269,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends /** Create a line manager. Overridable. */ protected def noLineManager = ReplPropsKludge.noThreadCreation(settings) - protected def createLineManager(): Line.Manager = new Line.Manager(_classLoader) + protected def createLineManager(classLoader: ClassLoader): Line.Manager = new Line.Manager(classLoader) /** Instantiate a compiler. Overridable. */ protected def newCompiler(settings: Settings, reporter: Reporter) = { @@ -304,7 +304,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends final def ensureClassLoader() { if (_classLoader == null) { _classLoader = makeClassLoader() - _lineManager = if (noLineManager) null else createLineManager() + _lineManager = if (noLineManager) null else createLineManager(_classLoader) } } def classLoader: AbstractFileClassLoader = { diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala index 099145d3ae..b818927ceb 100644 --- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala +++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala @@ -606,7 +606,7 @@ abstract class ClassfileParser { def parseField() { val jflags = in.nextChar var sflags = toScalaFieldFlags(jflags) - if ((sflags & PRIVATE) != 0L && !global.settings.XO.value) { + if ((sflags & PRIVATE) != 0L && !global.settings.optimise.value) { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) @@ -637,7 +637,7 @@ abstract class ClassfileParser { def parseMethod() { val jflags = in.nextChar.toInt var sflags = toScalaMethodFlags(jflags) - if (isPrivate(jflags) && !global.settings.XO.value) { + if (isPrivate(jflags) && !global.settings.optimise.value) { val name = pool.getName(in.nextChar) if (name == nme.CONSTRUCTOR) sawPrivateConstructor = true @@ -645,7 +645,7 @@ abstract class ClassfileParser { } else { if ((jflags & JAVA_ACC_BRIDGE) != 0) sflags |= BRIDGE - if ((sflags & PRIVATE) != 0L && global.settings.XO.value) { + if ((sflags & PRIVATE) != 0L && global.settings.optimise.value) { in.skip(4); skipAttributes() } else { val name = pool.getName(in.nextChar) diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala index 8f5d308b8f..17d63ea439 100644 --- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala +++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala @@ -77,31 +77,51 @@ abstract class AddInterfaces extends InfoTransform { def implClassPhase = currentRun.erasurePhase.next /** Return the implementation class of a trait; create a new one of one does not yet exist */ - def implClass(iface: Symbol): Symbol = implClassMap.getOrElse(iface, { - atPhase(implClassPhase) { - val implName = nme.implClassName(iface.name) - var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol - if (impl != NoSymbol && settings.XO.value) { - log("unlinking impl class " + impl) - iface.owner.info.decls.unlink(impl) - impl = NoSymbol - } - if (impl == NoSymbol) { - impl = iface.cloneSymbolImpl(iface.owner) - impl.name = implName - impl.sourceFile = iface.sourceFile - if (iface.owner.isClass) - iface.owner.info.decls enter impl + def implClass(iface: Symbol): Symbol = { + iface.info + + implClassMap.getOrElse(iface, { + atPhase(implClassPhase) { + log("%s.implClass == %s".format(iface, iface.implClass)) + val implName = nme.implClassName(iface.name) + var impl = if (iface.owner.isClass) iface.owner.info.decl(implName) else NoSymbol + impl.info + + val originalImpl = impl + val originalImplString = originalImpl.hasFlagsToString(-1L) + if (impl != NoSymbol) { + // Unlink a pre-existing symbol only if the implementation class is + // visible on the compilation classpath. In general this is true under + // -optimise and not otherwise, but the classpath can use arbitrary + // logic so the classpath must be queried. + if (classPath.context.isValidName(implName + ".class")) { + log("unlinking impl class " + impl) + iface.owner.info.decls.unlink(impl) + impl = NoSymbol + } + else log("not unlinking existing " + impl + " as the impl class is not visible on the classpath.") + } + if (impl == NoSymbol) { + impl = iface.cloneSymbolImpl(iface.owner) + impl.name = implName + impl.sourceFile = iface.sourceFile + if (iface.owner.isClass) + iface.owner.info.decls enter impl + } + if (currentRun.compiles(iface)) currentRun.symSource(impl) = iface.sourceFile + impl setPos iface.pos + impl.flags = iface.flags & ~(INTERFACE | lateINTERFACE) | IMPLCLASS + impl setInfo new LazyImplClassType(iface) + implClassMap(iface) = impl + debuglog( + "generating impl class " + impl + " " + impl.hasFlagsToString(-1L) + " in " + iface.owner + ( + if (originalImpl == NoSymbol) "" else " (cloned from " + originalImpl.fullLocationString + " " + originalImplString + ")" + ) + ) + impl } - if (currentRun.compiles(iface)) currentRun.symSource(impl) = iface.sourceFile - impl setPos iface.pos - impl.flags = iface.flags & ~(INTERFACE | lateINTERFACE) | IMPLCLASS - impl setInfo new LazyImplClassType(iface) - implClassMap(iface) = impl - debuglog("generating impl class " + impl + " in " + iface.owner)//debug - impl - } - }) + }) + } /** A lazy type to set the info of an implementation class * The parents of an implementation class for trait iface are: diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala index 0f11161914..712298bd89 100644 --- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala +++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala @@ -9,7 +9,8 @@ package transform import symtab._ import Flags._ import util.TreeSet -import scala.collection.mutable.{ LinkedHashMap, ListBuffer } +import scala.collection.{ mutable, immutable } +import scala.collection.mutable.LinkedHashMap abstract class LambdaLift extends InfoTransform { import global._ @@ -64,6 +65,8 @@ abstract class LambdaLift extends InfoTransform { /** The set of symbols that need to be renamed. */ private val renamable = newSymSet + private val renamableImplClasses = mutable.HashMap[Name, Symbol]() withDefaultValue NoSymbol + /** A flag to indicate whether new free variables have been found */ private var changedFreeVars: Boolean = _ @@ -152,7 +155,21 @@ abstract class LambdaLift extends InfoTransform { tree match { case ClassDef(_, _, _, _) => liftedDefs(tree.symbol) = Nil - if (sym.isLocal) renamable addEntry sym + if (sym.isLocal) { + // Don't rename implementation classes independently of their interfaces. If + // the interface is to be renamed, then we will rename the implementation + // class at that time. You'd think we could call ".implClass" on the trait + // rather than collecting them in another map, but that seems to fail for + // exactly the traits being renamed here (i.e. defined in methods.) + // + // !!! - it makes no sense to have methods like "implClass" and + // "companionClass" which fail for an arbitrary subset of nesting + // arrangements, and then have separate methods which attempt to compensate + // for that failure. There should be exactly one method for any given + // entity which always gives the right answer. + if (sym.isImplClass) renamableImplClasses(nme.interfaceName(sym.name)) = sym + else renamable addEntry sym + } case DefDef(_, _, _, _, _, _) => if (sym.isLocal) { renamable addEntry sym @@ -196,8 +213,8 @@ abstract class LambdaLift extends InfoTransform { for (caller <- called.keys ; callee <- called(caller) ; fvs <- free get callee ; fv <- fvs) markFree(fv, caller) } while (changedFreeVars) - - for (sym <- renamable) { + + def renameSym(sym: Symbol) { val originalName = sym.name val base = sym.name + nme.NAME_JOIN_STRING + ( if (sym.isAnonymousFunction && sym.owner.isMethod) @@ -211,6 +228,25 @@ abstract class LambdaLift extends InfoTransform { debuglog("renaming in %s: %s => %s".format(sym.owner.fullLocationString, originalName, sym.name)) } + /** Rename a trait's interface and implementation class in coordinated fashion. + */ + def renameTrait(traitSym: Symbol, implSym: Symbol) { + val originalImplName = implSym.name + renameSym(traitSym) + implSym.name = nme.implClassName(traitSym.name) + + debuglog("renaming impl class in step with %s: %s => %s".format(traitSym, originalImplName, implSym.name)) + } + + for (sym <- renamable) { + // If we renamed a trait from Foo to Foo$1, we must rename the implementation + // class from Foo$class to Foo$1$class. (Without special consideration it would + // become Foo$class$1 instead.) + val implClass = if (sym.isTrait) renamableImplClasses(sym.name) else NoSymbol + if ((implClass ne NoSymbol) && (sym.owner == implClass.owner)) renameTrait(sym, implClass) + else renameSym(sym) + } + atPhase(phase.next) { for ((owner, freeValues) <- free.toList) { val newFlags = SYNTHETIC | ( if (owner.isClass) PARAMACCESSOR | PrivateLocal else PARAM ) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 0fbde03e97..216ad6cd4c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2838,9 +2838,22 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { def packSymbols(hidden: List[Symbol], tp: Type): Type = if (hidden.isEmpty) tp else existentialTransform(hidden, tp)(existentialAbstraction) + + def isReferencedFrom(ctx: Context, sym: Symbol): Boolean = + ctx.owner.isTerm && + (ctx.scope.exists { dcl => dcl.isInitialized && (dcl.info contains sym) }) || + { + var ctx1 = ctx.outer + while ((ctx1 != NoContext) && (ctx1.scope eq ctx.scope)) ctx1 = ctx1.outer + (ctx1 != NoContext) && isReferencedFrom(ctx1, sym) + } def isCapturedExistential(sym: Symbol) = - sym hasAllFlags (EXISTENTIAL | CAPTURED) // todo refine this + (sym hasAllFlags (EXISTENTIAL | CAPTURED)) && { + val start = startTimer(isReferencedNanos) + try !isReferencedFrom(context, sym) + finally stopTimer(isReferencedNanos, start) + } def packCaptured(tpe: Type): Type = { val captured = mutable.Set[Symbol]() diff --git a/src/compiler/scala/tools/nsc/util/Statistics.scala b/src/compiler/scala/tools/nsc/util/Statistics.scala index 27239b9b9f..f7c27dceb5 100644 --- a/src/compiler/scala/tools/nsc/util/Statistics.scala +++ b/src/compiler/scala/tools/nsc/util/Statistics.scala @@ -20,7 +20,7 @@ class Statistics extends scala.reflect.internal.util.Statistics { val typedSelectCount = new Counter val typerNanos = new Timer val classReadNanos = new Timer - + val failedApplyNanos = new Timer val failedOpEqNanos = new Timer val failedSilentNanos = new Timer @@ -48,6 +48,7 @@ class Statistics extends scala.reflect.internal.util.Statistics { val subtypeImprovCount = new SubCounter(subtypeCount) val subtypeETNanos = new Timer val matchesPtNanos = new Timer + val isReferencedNanos = new Timer val ctr1 = new Counter val ctr2 = new Counter val ctr3 = new Counter @@ -137,6 +138,7 @@ abstract class StatisticsInfo { inform("time spent in failed : "+showRelTyper(failedSilentNanos)) inform(" failed apply : "+showRelTyper(failedApplyNanos)) inform(" failed op= : "+showRelTyper(failedOpEqNanos)) + inform("time spent ref scanning : "+showRelTyper(isReferencedNanos)) inform("micros by tree node : "+showCounts(microsByType)) inform("#visits by tree node : "+showCounts(visitsByType)) val average = new ClassCounts diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala index b175fb9e1d..824e048e73 100644 --- a/src/library/scala/Predef.scala +++ b/src/library/scala/Predef.scala @@ -269,7 +269,7 @@ object Predef extends LowPriorityImplicits { def printf(text: String, xs: Any*) = Console.print(text.format(xs: _*)) def readLine(): String = Console.readLine() - def readLine(text: String, args: Any*) = Console.readLine(text, args) + def readLine(text: String, args: Any*) = Console.readLine(text, args: _*) def readBoolean() = Console.readBoolean() def readByte() = Console.readByte() def readShort() = Console.readShort() diff --git a/src/library/scala/collection/immutable/List.scala b/src/library/scala/collection/immutable/List.scala index c6f056bd81..e9ecc75e0f 100644 --- a/src/library/scala/collection/immutable/List.scala +++ b/src/library/scala/collection/immutable/List.scala @@ -316,28 +316,7 @@ final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extend override def tail : List[B] = tl override def isEmpty: Boolean = false - import java.io._ - private def writeObject(out: ObjectOutputStream) { - var xs: List[B] = this - while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail } - out.writeObject(ListSerializeEnd) - } - - private def readObject(in: ObjectInputStream) { - hd = in.readObject.asInstanceOf[B] - assert(hd != ListSerializeEnd) - var current: ::[B] = this - while (true) in.readObject match { - case ListSerializeEnd => - current.tl = Nil - return - case a : Any => - val list : ::[B] = new ::(a.asInstanceOf[B], Nil) - current.tl = list - current = list - } - } } /** $factoryInfo diff --git a/src/library/scala/collection/immutable/Stream.scala b/src/library/scala/collection/immutable/Stream.scala index e6587f9615..2eb2f8eb09 100644 --- a/src/library/scala/collection/immutable/Stream.scala +++ b/src/library/scala/collection/immutable/Stream.scala @@ -929,13 +929,19 @@ self => /** A specialized, extra-lazy implementation of a stream iterator, so it can * iterate as lazily as it traverses the tail. */ -final class StreamIterator[+A](self: Stream[A]) extends AbstractIterator[A] with Iterator[A] { +final class StreamIterator[+A] private() extends AbstractIterator[A] with Iterator[A] { + def this(self: Stream[A]) { + this() + these = new LazyCell(self) + } + // A call-by-need cell. class LazyCell(st: => Stream[A]) { lazy val v = st } - private var these = new LazyCell(self) + private var these: LazyCell = _ + def hasNext: Boolean = these.v.nonEmpty def next(): A = if (isEmpty) Iterator.empty.next diff --git a/src/library/scala/collection/mutable/ListBuffer.scala b/src/library/scala/collection/mutable/ListBuffer.scala index 131cdd0005..eb871135df 100644 --- a/src/library/scala/collection/mutable/ListBuffer.scala +++ b/src/library/scala/collection/mutable/ListBuffer.scala @@ -13,6 +13,7 @@ package mutable import generic._ import immutable.{List, Nil, ::} +import java.io._ /** A `Buffer` implementation back up by a list. It provides constant time * prepend and append. Most other operations are linear. @@ -53,6 +54,7 @@ final class ListBuffer[A] override def companion: GenericCompanion[ListBuffer] = ListBuffer import scala.collection.Traversable + import scala.collection.immutable.ListSerializeEnd private var start: List[A] = Nil private var last0: ::[A] = _ @@ -60,7 +62,49 @@ final class ListBuffer[A] private var len = 0 protected def underlying: immutable.Seq[A] = start - + + private def writeObject(out: ObjectOutputStream) { + // write start + var xs: List[A] = start + while (!xs.isEmpty) { out.writeObject(xs.head); xs = xs.tail } + out.writeObject(ListSerializeEnd) + + // no need to write last0 + + // write if exported + out.writeBoolean(exported) + + // write the length + out.writeInt(len) + } + + private def readObject(in: ObjectInputStream) { + // read start, set last0 appropriately + var elem: A = in.readObject.asInstanceOf[A] + if (elem == ListSerializeEnd) { + start = Nil + last0 = null + } else { + var current = new ::(elem, Nil) + start = current + elem = in.readObject.asInstanceOf[A] + while (elem != ListSerializeEnd) { + val list = new ::(elem, Nil) + current.tl = list + current = list + elem = in.readObject.asInstanceOf[A] + } + last0 = current + start + } + + // read if exported + exported = in.readBoolean() + + // read the length + len = in.readInt() + } + /** The current length of the buffer. * * This operation takes constant time. diff --git a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala index f39debf31d..7c6dd0848f 100644 --- a/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala +++ b/src/partest/scala/tools/partest/nest/ReflectiveRunner.scala @@ -58,10 +58,17 @@ class ReflectiveRunner { if (isPartestDebug) println("Loading classes from:\n" + sepUrls.mkString("\n")) - val paths = classPath match { - case Some(cp) => Nil - case _ => files.toList map (_.path) - } + // @partest maintainer: it seems to me that commented lines are incorrect + // if classPath is not empty, then it has been provided by the --classpath option + // which points to the root of Scala home (see ConsoleFileManager's testClasses and the true flag in the ctor for more information) + // this doesn't mean that we had custom Java classpath set, so we don't have to override latestXXXFiles from the file manager + // + //val paths = classPath match { + // case Some(cp) => Nil + // case _ => files.toList map (_.path) + //} + val paths = files.toList map (_.path) + val newClasspath = ClassPath.join(paths: _*) setProp("java.class.path", newClasspath) |