diff options
Diffstat (limited to 'src')
16 files changed, 81 insertions, 45 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala index 0b07e12917..b0815b0008 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala @@ -488,16 +488,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder { bc emitRETURN returnType case nextCleanup :: rest => if (saveReturnValue) { - if (insideCleanupBlock) { - reporter.warning(r.pos, "Return statement found in finally-clause, discarding its return-value in favor of that of a more deeply nested return.") - bc drop returnType - } else { - // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. - if (earlyReturnVar == null) { - earlyReturnVar = locals.makeLocal(returnType, "earlyReturnVar") - } - locals.store(earlyReturnVar) + // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. + if (earlyReturnVar == null) { + earlyReturnVar = locals.makeLocal(returnType, "earlyReturnVar") } + locals.store(earlyReturnVar) } bc goTo nextCleanup shouldEmitCleanup = true diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala index dbad37cd5b..fdb5687311 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala @@ -255,7 +255,6 @@ abstract class BCodeSkelBuilder extends BCodeHelpers { // used by genLoadTry() and genSynchronized() var earlyReturnVar: Symbol = null var shouldEmitCleanup = false - var insideCleanupBlock = false // line numbers var lastEmittedLineNr = -1 diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala index 466793010f..add2c5ffe6 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSyncAndTry.scala @@ -36,7 +36,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { // if the synchronized block returns a result, store it in a local variable. // Just leaving it on the stack is not valid in MSIL (stack is cleaned when leaving try-blocks). val hasResult = (expectedType != UNIT) - val monitorResult: Symbol = if (hasResult) locals.makeLocal(tpeTK(args.head), "monitorResult") else null; + val monitorResult: Symbol = if (hasResult) locals.makeLocal(tpeTK(args.head), "monitorResult") else null /* ------ (1) pushing and entering the monitor, also keeping a reference to it in a local var. ------ */ genLoadQualifier(fun) @@ -215,7 +215,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { * please notice `tmp` has type tree.tpe, while `earlyReturnVar` has the method return type. * Because those two types can be different, dedicated vars are needed. */ - val tmp = if (guardResult) locals.makeLocal(tpeTK(tree), "tmp") else null; + val tmp = if (guardResult) locals.makeLocal(tpeTK(tree), "tmp") else null /* * upon early return from the try-body or one of its EHs (but not the EH-version of the finally-clause) @@ -238,6 +238,34 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { val endTryBody = currProgramPoint() bc goTo postHandlers + /** + * A return within a `try` or `catch` block where a `finally` is present ("early return") + * emits a store of the result to a local, jump to a "cleanup" version of the `finally` block, + * and sets `shouldEmitCleanup = true` (see [[PlainBodyBuilder.genReturn]]). + * + * If the try-catch is nested, outer `finally` blocks need to be emitted in a cleanup version + * as well, so the `shouldEmitCleanup` variable remains `true` until the outermost `finally`. + * Nested cleanup `finally` blocks jump to the next enclosing one. For the outermost, we emit + * a read of the local variable, a return, and we set `shouldEmitCleanup = false` (see + * [[pendingCleanups]]). + * + * Now, assume we have + * + * try { return 1 } finally { + * try { println() } finally { println() } + * } + * + * Here, the outer `finally` needs a cleanup version, but the inner one does not. The method + * here makes sure that `shouldEmitCleanup` is only propagated outwards, not inwards to + * nested `finally` blocks. + */ + def withFreshCleanupScope(body: => Unit) = { + val savedShouldEmitCleanup = shouldEmitCleanup + shouldEmitCleanup = false + body + shouldEmitCleanup = savedShouldEmitCleanup || shouldEmitCleanup + } + /* ------ (2) One EH for each case-clause (this does not include the EH-version of the finally-clause) * An EH in (2) is reached upon abrupt termination of (1). * An EH in (2) is protected by: @@ -246,8 +274,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { * ------ */ - for (ch <- caseHandlers) { - + for (ch <- caseHandlers) withFreshCleanupScope { // (2.a) emit case clause proper val startHandler = currProgramPoint() var endHandler: asm.Label = null @@ -277,9 +304,13 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { protect(startTryBody, endTryBody, startHandler, excType) // (2.c) emit jump to the program point where the finally-clause-for-normal-exit starts, or in effect `after` if no finally-clause was given. bc goTo postHandlers - } + // Need to save the state of `shouldEmitCleanup` at this point: while emitting the first + // version of the `finally` block below, the variable may become true. But this does not mean + // that we need a cleanup version for the current block, only for the enclosing ones. + val currentFinallyBlockNeedsCleanup = shouldEmitCleanup + /* ------ (3.A) The exception-handler-version of the finally-clause. * Reached upon abrupt termination of (1) or one of the EHs in (2). * Protected only by whatever protects the whole try-catch-finally expression. @@ -288,7 +319,7 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { // a note on terminology: this is not "postHandlers", despite appearances. // "postHandlers" as in the source-code view. And from that perspective, both (3.A) and (3.B) are invisible implementation artifacts. - if (hasFinally) { + if (hasFinally) withFreshCleanupScope { nopIfNeeded(startTryBody) val finalHandler = currProgramPoint() // version of the finally-clause reached via unhandled exception. protect(startTryBody, finalHandler, finalHandler, null) @@ -316,14 +347,11 @@ abstract class BCodeSyncAndTry extends BCodeBodyBuilder { // this is not "postHandlers" either. // `shouldEmitCleanup` can be set, and at the same time this try expression may lack a finally-clause. // In other words, all combinations of (hasFinally, shouldEmitCleanup) are valid. - if (hasFinally && shouldEmitCleanup) { - val savedInsideCleanup = insideCleanupBlock - insideCleanupBlock = true + if (hasFinally && currentFinallyBlockNeedsCleanup) { markProgramPoint(finCleanup) // regarding return value, the protocol is: in place of a `return-stmt`, a sequence of `adapt, store, jump` are inserted. emitFinalizer(finalizer, null, isDuplicate = true) pendingCleanups() - insideCleanupBlock = savedInsideCleanup } /* ------ (4) finally-clause-for-normal-nonEarlyReturn-exit diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 106b076eef..116c932365 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -472,7 +472,7 @@ abstract class RefChecks extends Transform { checkOverrideTypes() checkOverrideDeprecated() if (settings.warnNullaryOverride) { - if (other.paramss.isEmpty && !member.paramss.isEmpty) { + if (other.paramss.isEmpty && !member.paramss.isEmpty && !member.isJavaDefined) { reporter.warning(member.pos, "non-nullary method overrides nullary method") } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 7d48c548a1..cca6f280e3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -3139,10 +3139,25 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val initElems = scope.elems // SI-5877 The decls of a package include decls of the package object. But we don't want to add // the corresponding synthetics to the package class, only to the package object class. - def shouldAdd(sym: Symbol) = - inBlock || !context.isInPackageObject(sym, context.owner) + // SI-6734 Locality test below is meaningless if we're not even in the correct tree. + // For modules that are synthetic case companions, check that case class is defined here. + def shouldAdd(sym: Symbol): Boolean = { + def shouldAddAsModule: Boolean = + sym.moduleClass.attachments.get[ClassForCaseCompanionAttachment] match { + case Some(att) => + val cdef = att.caseClass + stats.exists { + case t @ ClassDef(_, _, _, _) => t.symbol == cdef.symbol // cdef ne t + case _ => false + } + case _ => true + } + + (!sym.isModule || shouldAddAsModule) && (inBlock || !context.isInPackageObject(sym, context.owner)) + } for (sym <- scope) - for (tree <- context.unit.synthetics get sym if shouldAdd(sym)) { // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop + // OPT: shouldAdd is usually true. Call it here, rather than in the outer loop + for (tree <- context.unit.synthetics.get(sym) if shouldAdd(sym)) { newStats += typedStat(tree) // might add even more synthetics to the scope context.unit.synthetics -= sym } diff --git a/src/library/scala/Array.scala b/src/library/scala/Array.scala index 6d829a9e5d..5d1c25732c 100644 --- a/src/library/scala/Array.scala +++ b/src/library/scala/Array.scala @@ -11,7 +11,6 @@ package scala import scala.collection.generic._ import scala.collection.{ mutable, immutable } import mutable.{ ArrayBuilder, ArraySeq } -import scala.compat.Platform.arraycopy import scala.reflect.ClassTag import scala.runtime.ScalaRunTime.{ array_apply, array_update } @@ -102,7 +101,7 @@ object Array extends FallbackArrayBuilding { def copy(src: AnyRef, srcPos: Int, dest: AnyRef, destPos: Int, length: Int) { val srcClass = src.getClass if (srcClass.isArray && dest.getClass.isAssignableFrom(srcClass)) - arraycopy(src, srcPos, dest, destPos, length) + java.lang.System.arraycopy(src, srcPos, dest, destPos, length) else slowcopy(src, srcPos, dest, destPos, length) } diff --git a/src/library/scala/collection/immutable/Vector.scala b/src/library/scala/collection/immutable/Vector.scala index a162fdaaf8..d9d925705f 100644 --- a/src/library/scala/collection/immutable/Vector.scala +++ b/src/library/scala/collection/immutable/Vector.scala @@ -11,7 +11,6 @@ package collection package immutable import scala.annotation.unchecked.uncheckedVariance -import scala.compat.Platform import scala.collection.generic._ import scala.collection.mutable.{Builder, ReusableBuilder} import scala.collection.parallel.immutable.ParVector @@ -478,12 +477,12 @@ override def companion: GenericCompanion[Vector] = Vector // if (array eq null) // println("OUCH!!! " + right + "/" + depth + "/"+startIndex + "/" + endIndex + "/" + focus) val a2 = new Array[AnyRef](array.length) - Platform.arraycopy(array, 0, a2, 0, right) + java.lang.System.arraycopy(array, 0, a2, 0, right) a2 } private def copyRight(array: Array[AnyRef], left: Int): Array[AnyRef] = { val a2 = new Array[AnyRef](array.length) - Platform.arraycopy(array, left, a2, left, a2.length - left) + java.lang.System.arraycopy(array, left, a2, left, a2.length - left) a2 } @@ -955,7 +954,7 @@ private[immutable] trait VectorPointer[T] { private[immutable] final def copyOf(a: Array[AnyRef]) = { val b = new Array[AnyRef](a.length) - Platform.arraycopy(a, 0, b, 0, a.length) + java.lang.System.arraycopy(a, 0, b, 0, a.length) b } @@ -1119,7 +1118,7 @@ private[immutable] trait VectorPointer[T] { private[immutable] final def copyRange(array: Array[AnyRef], oldLeft: Int, newLeft: Int) = { val elems = new Array[AnyRef](32) - Platform.arraycopy(array, oldLeft, elems, newLeft, 32 - math.max(newLeft,oldLeft)) + java.lang.System.arraycopy(array, oldLeft, elems, newLeft, 32 - math.max(newLeft,oldLeft)) elems } diff --git a/src/library/scala/collection/mutable/ArrayBuffer.scala b/src/library/scala/collection/mutable/ArrayBuffer.scala index 167e04ccbd..23d386f729 100644 --- a/src/library/scala/collection/mutable/ArrayBuffer.scala +++ b/src/library/scala/collection/mutable/ArrayBuffer.scala @@ -67,7 +67,7 @@ class ArrayBuffer[A](override protected val initialSize: Int) override def sizeHint(len: Int) { if (len > size && len >= 1) { val newarray = new Array[AnyRef](len) - scala.compat.Platform.arraycopy(array, 0, newarray, 0, size0) + java.lang.System.arraycopy(array, 0, newarray, 0, size0) array = newarray } } diff --git a/src/library/scala/collection/mutable/PriorityQueue.scala b/src/library/scala/collection/mutable/PriorityQueue.scala index 107a2bfa0e..ed43ef6db9 100644 --- a/src/library/scala/collection/mutable/PriorityQueue.scala +++ b/src/library/scala/collection/mutable/PriorityQueue.scala @@ -331,8 +331,8 @@ sealed class PriorityQueue[A](implicit val ord: Ordering[A]) val pq = new PriorityQueue[A] val n = resarr.p_size0 pq.resarr.p_ensureSize(n) + java.lang.System.arraycopy(resarr.p_array, 1, pq.resarr.p_array, 1, n-1) pq.resarr.p_size0 = n - scala.compat.Platform.arraycopy(resarr.p_array, 1, pq.resarr.p_array, 1, n-1) pq } } diff --git a/src/library/scala/collection/mutable/ResizableArray.scala b/src/library/scala/collection/mutable/ResizableArray.scala index 85a299216e..50d3513784 100644 --- a/src/library/scala/collection/mutable/ResizableArray.scala +++ b/src/library/scala/collection/mutable/ResizableArray.scala @@ -101,7 +101,7 @@ trait ResizableArray[A] extends IndexedSeq[A] if (newSize > Int.MaxValue) newSize = Int.MaxValue val newArray: Array[AnyRef] = new Array(newSize.toInt) - scala.compat.Platform.arraycopy(array, 0, newArray, 0, size0) + java.lang.System.arraycopy(array, 0, newArray, 0, size0) array = newArray } } diff --git a/src/library/scala/collection/parallel/immutable/ParRange.scala b/src/library/scala/collection/parallel/immutable/ParRange.scala index 8fd5382ce9..de2b53a6c0 100644 --- a/src/library/scala/collection/parallel/immutable/ParRange.scala +++ b/src/library/scala/collection/parallel/immutable/ParRange.scala @@ -107,6 +107,7 @@ self => } } + override def toString = s"Par$range" } object ParRange { diff --git a/src/library/scala/concurrent/duration/Duration.scala b/src/library/scala/concurrent/duration/Duration.scala index f69030bd3d..8d77d47b3f 100644 --- a/src/library/scala/concurrent/duration/Duration.scala +++ b/src/library/scala/concurrent/duration/Duration.scala @@ -120,7 +120,7 @@ object Duration { def fromNanos(nanos: Double): Duration = { if (nanos.isInfinite) if (nanos > 0) Inf else MinusInf - else if (nanos.isNaN) + else if (JDouble.isNaN(nanos)) Undefined else if (nanos > Long.MaxValue || nanos < Long.MinValue) throw new IllegalArgumentException("trying to construct too large duration with " + nanos + "ns") @@ -196,11 +196,11 @@ object Duration { } def *(factor: Double): Duration = - if (factor == 0d || factor.isNaN) Undefined + if (factor == 0d || JDouble.isNaN(factor)) Undefined else if (factor < 0d) -this else this def /(divisor: Double): Duration = - if (divisor.isNaN || divisor.isInfinite) Undefined + if (JDouble.isNaN(divisor) || divisor.isInfinite) Undefined else if ((divisor compare 0d) < 0) -this else this def /(divisor: Duration): Double = divisor match { @@ -627,13 +627,13 @@ final class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duratio def *(factor: Double) = if (!factor.isInfinite) fromNanos(toNanos * factor) - else if (factor.isNaN) Undefined + else if (JDouble.isNaN(factor)) Undefined else if ((factor > 0) ^ (this < Zero)) Inf else MinusInf def /(divisor: Double) = if (!divisor.isInfinite) fromNanos(toNanos / divisor) - else if (divisor.isNaN) Undefined + else if (JDouble.isNaN(divisor)) Undefined else Zero // if this is made a constant, then scalac will elide the conditional and always return +0.0, SI-6331 diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala index 626540425f..7fcc8c9f2d 100644 --- a/src/library/scala/concurrent/impl/Promise.scala +++ b/src/library/scala/concurrent/impl/Promise.scala @@ -384,7 +384,7 @@ private[concurrent] object Promise { private[this] final def thisAs[S]: Future[S] = future.asInstanceOf[Future[S]] override def onSuccess[U](pf: PartialFunction[T, U])(implicit executor: ExecutionContext): Unit = () - override def failed: Future[Throwable] = thisAs[Throwable] + override def failed: Future[Throwable] = KeptPromise(Success(result.exception)).future override def foreach[U](f: T => U)(implicit executor: ExecutionContext): Unit = () override def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = thisAs[S] override def flatMap[S](f: T => Future[S])(implicit executor: ExecutionContext): Future[S] = thisAs[S] diff --git a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala index 78f9721713..0ef52213e5 100644 --- a/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala +++ b/src/reflect/scala/reflect/internal/BaseTypeSeqs.scala @@ -100,7 +100,7 @@ trait BaseTypeSeqs { def copy(head: Type, offset: Int): BaseTypeSeq = { val arr = new Array[Type](elems.length + offset) - scala.compat.Platform.arraycopy(elems, 0, arr, offset, elems.length) + java.lang.System.arraycopy(elems, 0, arr, offset, elems.length) arr(0) = head newBaseTypeSeq(parents, arr) } diff --git a/src/reflect/scala/reflect/internal/Constants.scala b/src/reflect/scala/reflect/internal/Constants.scala index 7b47798ff7..cd2debfaf4 100644 --- a/src/reflect/scala/reflect/internal/Constants.scala +++ b/src/reflect/scala/reflect/internal/Constants.scala @@ -87,8 +87,8 @@ trait Constants extends api.Constants { } def isNaN = value match { - case f: Float => f.isNaN - case d: Double => d.isNaN + case f: Float => java.lang.Float.isNaN(f) + case d: Double => java.lang.Double.isNaN(d) case _ => false } diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala index 97f51149ba..9d39ef8b42 100644 --- a/src/reflect/scala/reflect/internal/Names.scala +++ b/src/reflect/scala/reflect/internal/Names.scala @@ -68,7 +68,7 @@ trait Names extends api.Names { while (i < len) { if (nc + i == chrs.length) { val newchrs = new Array[Char](chrs.length * 2) - scala.compat.Platform.arraycopy(chrs, 0, newchrs, 0, chrs.length) + java.lang.System.arraycopy(chrs, 0, newchrs, 0, chrs.length) chrs = newchrs } chrs(nc + i) = cs(offset + i) @@ -220,7 +220,7 @@ trait Names extends api.Names { /** Copy bytes of this name to buffer cs, starting at position `offset`. */ final def copyChars(cs: Array[Char], offset: Int) = - scala.compat.Platform.arraycopy(chrs, index, cs, offset, len) + java.lang.System.arraycopy(chrs, index, cs, offset, len) /** @return the ascii representation of this name */ final def toChars: Array[Char] = { // used by ide |