diff options
5 files changed, 87 insertions, 60 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala index 6a5a107d2d..d528f56ff8 100644 --- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala +++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala @@ -126,19 +126,19 @@ abstract class Inliners extends SubComponent { var info: tfa.lattice.Elem = null def analyzeInc(msym: Symbol, i: Instruction, bb: BasicBlock) = { - val inc = new SymMethodInfo(msym) - val receiver = (info.stack.types drop inc.paramTypes.length).head match { + def paramTypes = msym.info.paramTypes + val receiver = (info.stack.types drop paramTypes.length).head match { case REFERENCE(s) => s case _ => NoSymbol } - val concreteMethod = inc lookupImplFor receiver + val concreteMethod = lookupImplFor(msym, receiver) def warnNoInline(reason: String) = { - if (inc.inline && !caller.isBridge) + if (hasInline(msym) && !caller.isBridge) warn(i.pos, "Could not inline required method %s because %s.".format(msym.originalName.decode, reason)) } - if (shouldLoad(receiver, concreteMethod)) + if (shouldLoadImplFor(concreteMethod, receiver)) icodes.icode(receiver, true) def isAvailable = icodes available receiver @@ -221,50 +221,64 @@ abstract class Inliners extends SubComponent { m.normalize } - class SymMethodInfo(val sym: Symbol) { - val name = sym.name - def owner = sym.owner - def paramTypes = sym.info.paramTypes - def minimumStack = paramTypes.length + 1 + private def isMonadicMethod(sym: Symbol) = sym.name match { + case nme.foreach | nme.filter | nme.map | nme.flatMap => true + case _ => false + } + private def isHigherOrderMethod(sym: Symbol) = + sym.isMethod && atPhase(currentRun.erasurePhase.prev)(sym.info.paramTypes exists isFunctionType) - def inline = hasInline(sym) - def noinline = hasNoInline(sym) - def numInlined = inlinedMethodCount(sym) + /** Should method 'sym' being called in 'receiver' be loaded from disk? */ + def shouldLoadImplFor(sym: Symbol, receiver: Symbol): Boolean = { + if (settings.debug.value) + log("shouldLoadImplFor: " + receiver + "." + sym) - def lookupImplFor(clazz: Symbol): Symbol = { - // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info) - def needsLookup = (clazz != NoSymbol) && (clazz != owner) && !isEffectivelyFinal && clazz.isFinal + def alwaysLoad = (receiver.enclosingPackage == RuntimePackage) || (receiver == PredefModule.moduleClass) + def loadCondition = sym.isEffectivelyFinal && isMonadicMethod(sym) && isHigherOrderMethod(sym) - def lookup(clazz: Symbol): Symbol = { - // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner) - if (owner == clazz || isBottomType(clazz)) sym - else sym.overridingSymbol(clazz) match { - case NoSymbol => if (owner.isTrait) sym else lookup(clazz.superClass) - case imp => imp - } - } - if (needsLookup) { - val concreteMethod = lookup(clazz) - if (settings.debug.value) - log("\tlooked up method: " + concreteMethod.fullName) + hasInline(sym) || alwaysLoad || loadCondition + } - concreteMethod + /** Look up implementation of method 'sym in 'clazz'. + */ + def lookupImplFor(sym: Symbol, clazz: Symbol): Symbol = { + // TODO: verify that clazz.superClass is equivalent here to clazz.tpe.parents(0).typeSymbol (.tpe vs .info) + def needsLookup = (clazz != NoSymbol) && (clazz != sym.owner) && !sym.isEffectivelyFinal && clazz.isFinal + + def lookup(clazz: Symbol): Symbol = { + // println("\t\tlooking up " + meth + " in " + clazz.fullName + " meth.owner = " + meth.owner) + if (sym.owner == clazz || isBottomType(clazz)) sym + else sym.overridingSymbol(clazz) match { + case NoSymbol => if (sym.owner.isTrait) sym else lookup(clazz.superClass) + case imp => imp } - else sym } + if (needsLookup) { + val concreteMethod = lookup(clazz) + if (settings.debug.value) + log("\tlooked up method: " + concreteMethod.fullName) - def isBridge = sym.isBridge - def isInClosure = isClosureClass(owner) - def isHigherOrder = sym.isMethod && atPhase(currentRun.erasurePhase.prev)(paramTypes exists isFunctionType) - def isMonadic = name match { - case nme.foreach | nme.filter | nme.map | nme.flatMap => true - case _ => false + concreteMethod } - def isEffectivelyFinal = sym.isEffectivelyFinal - def shouldBeLoaded = inline || (isEffectivelyFinal && isMonadic && isHigherOrder) + else sym } - class IMethodInfo(val m: IMethod) extends SymMethodInfo(m.symbol) { + class IMethodInfo(val m: IMethod) { + val sym = m.symbol + val name = sym.name + def owner = sym.owner + def paramTypes = sym.info.paramTypes + def minimumStack = paramTypes.length + 1 + + def inline = hasInline(sym) + def noinline = hasNoInline(sym) + def numInlined = inlinedMethodCount(sym) + + def isBridge = sym.isBridge + def isInClosure = isClosureClass(owner) + def isHigherOrder = isHigherOrderMethod(sym) + def isMonadic = isMonadicMethod(sym) + def handlers = m.exh def blocks = m.code.blocks def locals = m.locals @@ -288,6 +302,7 @@ abstract class Inliners extends SubComponent { class CallerCalleeInfo(val caller: IMethodInfo, val inc: IMethodInfo) { def isLargeSum = caller.length + inc.length - 1 > SMALL_METHOD_SIZE + /** Inline 'inc' into 'caller' at the given block and instruction. * The instruction must be a CALL_METHOD. */ @@ -309,14 +324,14 @@ abstract class Inliners extends SubComponent { val varsInScope: mutable.Set[Local] = HashSet() ++= block.varsInScope - val instrBefore = block.toList.takeWhile { - case i @ SCOPE_ENTER(l) => varsInScope += l - i ne instr - case i => - i ne instr + /** Side effects varsInScope when it sees SCOPE_ENTERs. */ + def instrBeforeFilter(i: Instruction): Boolean = { + i match { case SCOPE_ENTER(l) => varsInScope += l ; case _ => () } + i ne instr } + val instrBefore = block.toList takeWhile instrBeforeFilter + val instrAfter = block.toList drop (instrBefore.length + 1) - val instrAfter = block.toList.drop(instrBefore.length + 1) assert(!instrAfter.isEmpty, "CALL_METHOD cannot be the last instruction in block!") // store the '$this' into the special local @@ -438,7 +453,7 @@ abstract class Inliners extends SubComponent { // re-emit the instructions before the call block.open block.clear - instrBefore foreach (i => block.emit(i, i.pos)) + block emit instrBefore // store the arguments into special locals inc.m.params.reverse foreach (p => blockEmit(STORE_LOCAL(inlinedLocals(p)))) @@ -471,7 +486,7 @@ abstract class Inliners extends SubComponent { inlinedBlock(bb).close } - instrAfter foreach (i => afterBlock.emit(i, i.pos)) + afterBlock emit instrAfter afterBlock.close count += 1 @@ -621,18 +636,10 @@ abstract class Inliners extends SubComponent { }) } - /** Should the given method be loaded from disk? */ - def shouldLoad(receiver: Symbol, method: Symbol): Boolean = { - if (settings.debug.value) - log("shouldLoad: " + receiver + "." + method) - - val caller = new SymMethodInfo(method) - def alwaysLoad = (receiver.enclosingPackage == RuntimePackage) || (receiver == PredefModule.moduleClass) + def lookupIMethod(meth: Symbol, receiver: Symbol): Option[IMethod] = { + def tryParent(sym: Symbol) = icodes icode sym flatMap (_ lookupMethod meth) - caller.shouldBeLoaded || alwaysLoad + receiver.info.baseClasses.iterator map tryParent find (_.isDefined) getOrElse None } - - def lookupIMethod(meth: Symbol, receiver: Symbol): Option[IMethod] = - icodes.icode(receiver).get lookupMethod meth } /* class Inliner */ } /* class Inliners */ diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala index 0c124c9c19..b9afcda3ec 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSAnnotationChecker.scala @@ -14,6 +14,7 @@ abstract class CPSAnnotationChecker extends CPSUtils { import definitions._ //override val verbose = true + @inline override final def vprintln(x: =>Any): Unit = if (verbose) println(x) /** * Checks whether @cps annotations conform diff --git a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala index 57cba6e829..d1a35df04b 100644 --- a/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala +++ b/src/continuations/plugin/scala/tools/selectivecps/CPSUtils.scala @@ -11,8 +11,7 @@ trait CPSUtils { var cpsEnabled = false val verbose: Boolean = System.getProperty("cpsVerbose", "false") == "true" - @inline final def vprintln(x: =>Any): Unit = if (verbose) println(x) - + def vprintln(x: =>Any): Unit = if (verbose) println(x) lazy val MarkerCPSSym = definitions.getClass("scala.util.continuations.cpsSym") lazy val MarkerCPSTypes = definitions.getClass("scala.util.continuations.cpsParam") diff --git a/test/files/pos/bug3234.flags b/test/files/pos/bug3234.flags new file mode 100644 index 0000000000..c9cefdc4b9 --- /dev/null +++ b/test/files/pos/bug3234.flags @@ -0,0 +1 @@ +-Yinline -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/pos/bug3234.scala b/test/files/pos/bug3234.scala new file mode 100644 index 0000000000..1553f1fa05 --- /dev/null +++ b/test/files/pos/bug3234.scala @@ -0,0 +1,19 @@ +trait Trait1 { + // need more work before this one works + // @inline + def foo2(n: Int) = n*n +} + +trait Trait2 { + @inline def foo3(n: Int) = 1 +} + +class Base extends Trait1 { + @inline def foo(n: Int) = n +} + +object Test extends Base with Trait2 { + def main(args: Array[String]) = { + println(foo(42) + foo2(11) + foo3(2)) + } +}
\ No newline at end of file |