summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala5
-rw-r--r--src/compiler/scala/tools/nsc/ast/Trees.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala55
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala8
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala27
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala21
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala5
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/Inliners.scala243
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala3
-rw-r--r--src/compiler/scala/tools/nsc/settings/MutableSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala13
-rw-r--r--src/compiler/scala/tools/nsc/transform/AddInterfaces.scala20
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala12
-rw-r--r--src/compiler/scala/tools/nsc/transform/Flatten.scala16
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala15
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala27
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala10
-rw-r--r--src/library/scala/collection/GenIterableViewLike.scala1
-rw-r--r--src/library/scala/concurrent/Awaitable.scala23
-rw-r--r--src/library/scala/concurrent/BlockContext.scala3
-rw-r--r--src/library/scala/concurrent/ExecutionContext.scala1
-rw-r--r--src/library/scala/concurrent/Future.scala1
-rw-r--r--src/library/scala/concurrent/impl/ExecutionContextImpl.scala1
-rw-r--r--src/library/scala/concurrent/impl/Promise.scala42
-rw-r--r--src/library/scala/concurrent/package.scala33
-rw-r--r--src/library/scala/concurrent/util/Duration.scala610
-rw-r--r--src/library/scala/deprecatedInheritance.scala22
-rw-r--r--src/library/scala/deprecatedOverriding.scala21
-rw-r--r--src/library/scala/math/BigDecimal.scala1
-rw-r--r--src/library/scala/math/BigInt.scala1
-rw-r--r--src/partest/scala/tools/partest/TestUtil.scala12
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Names.scala12
-rw-r--r--src/reflect/scala/reflect/internal/SymbolTable.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala41
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala6
-rw-r--r--test/ant/test-basic/build.xml33
-rw-r--r--test/ant/test-basic/src/test-1.scala0
-rw-r--r--test/files/jvm/duration-java.check46
-rw-r--r--test/files/jvm/duration-java/Test.java12
-rw-r--r--test/files/jvm/duration-tck.scala186
-rw-r--r--test/files/jvm/scala-concurrent-tck.scala29
-rw-r--r--test/files/neg/deadline-inf-illegal.check15
-rw-r--r--test/files/neg/deadline-inf-illegal.scala8
-rw-r--r--test/files/neg/override.check2
-rwxr-xr-xtest/files/neg/override.scala2
-rw-r--r--test/files/neg/t6162-inheritance.check10
-rw-r--r--test/files/neg/t6162-inheritance.flags1
-rw-r--r--test/files/neg/t6162-inheritance.scala19
-rw-r--r--test/files/neg/t6162-overriding.check7
-rw-r--r--test/files/neg/t6162-overriding.flags1
-rw-r--r--test/files/neg/t6162-overriding.scala17
-rw-r--r--test/files/neg/t6276.check19
-rw-r--r--test/files/neg/t6276.flags1
-rw-r--r--test/files/neg/t6276.scala44
-rw-r--r--test/files/neg/t6335.check9
-rw-r--r--test/files/neg/t6335.scala7
-rw-r--r--test/files/pos/t6335.scala25
-rw-r--r--test/files/run/compiler-asSeenFrom.check12
-rw-r--r--test/files/run/t6271.scala32
-rw-r--r--test/flaky/pos/t2868.cmds (renamed from test/files/pos/t2868.cmds)0
-rw-r--r--test/flaky/pos/t2868/Jann.java (renamed from test/files/pos/t2868/Jann.java)0
-rw-r--r--test/flaky/pos/t2868/Nest.java (renamed from test/files/pos/t2868/Nest.java)0
-rw-r--r--test/flaky/pos/t2868/pick_1.scala (renamed from test/files/pos/t2868/pick_1.scala)0
-rw-r--r--test/flaky/pos/t2868/t2868_src_2.scala (renamed from test/files/pos/t2868/t2868_src_2.scala)0
70 files changed, 1465 insertions, 417 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 98a8359f89..d101337087 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -266,9 +266,8 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
log("Running operation '%s' after every phase.\n".format(msg) + describeAfterEveryPhase(op))
}
- def shouldLogAtThisPhase = (
- (settings.log.isSetByUser)
- && ((settings.log containsPhase globalPhase) || (settings.log containsPhase phase))
+ override def shouldLogAtThisPhase = settings.log.isSetByUser && (
+ (settings.log containsPhase globalPhase) || (settings.log containsPhase phase)
)
// Over 200 closure objects are eliminated by inlining this.
@inline final def log(msg: => AnyRef) {
diff --git a/src/compiler/scala/tools/nsc/ast/Trees.scala b/src/compiler/scala/tools/nsc/ast/Trees.scala
index 085ce82025..bd5c9b2f68 100644
--- a/src/compiler/scala/tools/nsc/ast/Trees.scala
+++ b/src/compiler/scala/tools/nsc/ast/Trees.scala
@@ -48,12 +48,12 @@ trait Trees extends reflect.internal.Trees { self: Global =>
override def isType = definition.isType
}
- /** Array selection <qualifier> . <name> only used during erasure */
+ /** Array selection `<qualifier> . <name>` only used during erasure */
case class SelectFromArray(qualifier: Tree, name: Name, erasure: Type)
extends RefTree with TermTree
- /** Derived value class injection (equivalent to: new C(arg) after easure); only used during erasure
- * The class C is stored as the symbol of the tree node.
+ /** Derived value class injection (equivalent to: `new C(arg)` after erasure); only used during erasure.
+ * The class `C` is stored as a tree attachment.
*/
case class InjectDerivedValue(arg: Tree)
extends SymTree
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 739aa2b184..24662e2ac3 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -128,7 +128,6 @@ abstract class GenICode extends SubComponent {
if (staticfield != NoSymbol) {
// in companion object accessors to @static fields, we access the static field directly
val hostClass = m.symbol.owner.companionClass
-
if (m.symbol.isGetter) {
ctx1.bb.emit(LOAD_FIELD(staticfield, true) setHostClass hostClass, tree.pos)
ctx1.bb.closeWith(RETURN(m.returnType))
@@ -662,16 +661,16 @@ abstract class GenICode extends SubComponent {
} else {
val sym = tree.symbol
val local = ctx.method.addLocal(new Local(sym, toTypeKind(sym.info), false))
-
+
if (rhs == EmptyTree) {
debuglog("Uninitialized variable " + tree + " at: " + (tree.pos));
ctx.bb.emit(getZeroOf(local.kind))
}
-
+
var ctx1 = ctx
if (rhs != EmptyTree)
ctx1 = genLoad(rhs, ctx, local.kind);
-
+
ctx1.bb.emit(STORE_LOCAL(local), tree.pos)
ctx1.scope.add(local)
ctx1.bb.emit(SCOPE_ENTER(local))
@@ -730,10 +729,10 @@ abstract class GenICode extends SubComponent {
ctx1.bb.enterIgnoreMode
generatedType = expectedType
ctx1
- }
+ }
genLoadReturn
- case t @ Try(_, _, _) =>
+ case t @ Try(_, _, _) =>
genLoadTry(t, ctx, generatedType = _)
case Throw(expr) =>
@@ -753,7 +752,7 @@ abstract class GenICode extends SubComponent {
case Object_asInstanceOf => true
case _ => abort("Unexpected type application " + fun + "[sym: " + sym.fullName + "]" + " in: " + tree)
}
-
+
val Select(obj, _) = fun
val l = toTypeKind(obj.tpe)
val r = toTypeKind(targs.head.tpe)
@@ -797,7 +796,7 @@ abstract class GenICode extends SubComponent {
ctx.bb.emit(THIS(ctx.clazz.symbol), tree.pos)
val ctx1 = genLoadArguments(args, fun.symbol.info.paramTypes, ctx)
-
+
ctx1.bb.emit(CALL_METHOD(fun.symbol, invokeStyle), tree.pos)
generatedType =
if (fun.symbol.isConstructor) UNIT
@@ -815,7 +814,7 @@ abstract class GenICode extends SubComponent {
val ctor = fun.symbol
debugassert(ctor.isClassConstructor,
"'new' call to non-constructor: " + ctor.name)
-
+
generatedType = toTypeKind(tpt.tpe)
debugassert(generatedType.isReferenceType || generatedType.isArrayType,
"Non reference type cannot be instantiated: " + generatedType)
@@ -861,7 +860,7 @@ abstract class GenICode extends SubComponent {
ctx1
}
ctx2
-
+
case _ =>
abort("Cannot instantiate " + tpt + " of kind: " + generatedType)
}
@@ -902,7 +901,7 @@ abstract class GenICode extends SubComponent {
ctx1
case app @ Apply(fun @ Select(qual, _), args)
- if !ctx.method.symbol.isStaticConstructor
+ if !ctx.method.symbol.isStaticConstructor
&& fun.symbol.isAccessor && fun.symbol.accessed.hasStaticAnnotation
&& qual.tpe.typeSymbol.orElse(fun.symbol.owner).companionClass != NoSymbol =>
// bypass the accessor to the companion object and load the static field directly
@@ -941,11 +940,11 @@ abstract class GenICode extends SubComponent {
}
}
genLoadApply5
-
+
case app @ Apply(fun, args) =>
def genLoadApply6 = {
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
@@ -982,7 +981,7 @@ abstract class GenICode extends SubComponent {
Static(true)
else
Dynamic
-
+
var ctx1 =
if (invokeStyle.hasInstance) {
if (forMSIL && !(invokeStyle.isInstanceOf[SuperCall]) && msil_IsValuetypeInstMethod(sym))
@@ -990,24 +989,26 @@ abstract class GenICode extends SubComponent {
else
genLoadQualifier(fun, ctx)
} else ctx
-
+
ctx1 = genLoadArguments(args, sym.info.paramTypes, ctx1)
val cm = CALL_METHOD(sym, invokeStyle)
-
+
/** In a couple cases, squirrel away a little extra information in the
* CALL_METHOD for use by GenJVM.
*/
fun match {
case Select(qual, _) =>
val qualSym = findHostClass(qual.tpe, sym)
-
- if (qualSym == ArrayClass) cm setTargetTypeKind toTypeKind(qual.tpe)
- else cm setHostClass qualSym
-
- log(
- if (qualSym == ArrayClass) "Stored target type kind " + toTypeKind(qual.tpe) + " for " + sym.fullName
- else s"Set more precise host class for ${sym.fullName} hostClass: $qualSym"
- )
+ if (qualSym == ArrayClass) {
+ val kind = toTypeKind(qual.tpe)
+ cm setTargetTypeKind kind
+ log(s"Stored target type kind for {$sym.fullName} as $kind")
+ }
+ else {
+ cm setHostClass qualSym
+ if (qual.tpe.typeSymbol != qualSym)
+ log(s"Precisified host class for $sym from ${qual.tpe.typeSymbol.fullName} to ${qualSym.fullName}")
+ }
case _ =>
}
ctx1.bb.emit(cm, tree.pos)
@@ -1143,7 +1144,7 @@ abstract class GenICode extends SubComponent {
val elmKind = toTypeKind(tpt.tpe)
generatedType = ARRAY(elmKind)
val elems = _elems.toIndexedSeq
-
+
ctx1.bb.emit(CONSTANT(new Constant(elems.length)), tree.pos)
ctx1.bb.emit(CREATE_ARRAY(elmKind, 1))
// inline array literals
@@ -1166,7 +1167,7 @@ abstract class GenICode extends SubComponent {
val afterCtx = ctx1.newBlock
var caseCtx: Context = null
generatedType = toTypeKind(tree.tpe)
-
+
var targets: List[BasicBlock] = Nil
var tags: List[Int] = Nil
var default: BasicBlock = afterCtx.bb
@@ -1193,7 +1194,7 @@ abstract class GenICode extends SubComponent {
abort("Invalid case statement in switch-like pattern match: " +
tree + " at: " + (tree.pos))
}
-
+
caseCtx = genLoad(body, tmpCtx, generatedType)
// close the block unless it's already been closed by the body, which closes the block if it ends in a jump (which is emitted to have alternatives share their body)
caseCtx.bb.closeWith(JUMP(afterCtx.bb) setPos caze.pos)
diff --git a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala
index d1d8e4a385..df158a29ea 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/analysis/CopyPropagation.scala
@@ -200,7 +200,7 @@ abstract class CopyPropagation {
in(b) = lattice.bottom
out(b) = lattice.bottom
assert(out.contains(b), out)
- log("Added point: " + b)
+ debuglog("CopyAnalysis added point: " + b)
}
m.exh foreach { e =>
in(e.startBlock) = new copyLattice.State(copyLattice.emptyBinding, copyLattice.exceptionHandlerStack);
@@ -531,11 +531,11 @@ abstract class CopyPropagation {
case 0 => ()
case 1 if ctor.tpe.paramTypes.head == ctor.owner.rawowner.tpe =>
// it's an unused outer
- log("considering unused outer at position 0 in " + ctor.tpe.paramTypes)
+ debuglog("considering unused outer at position 0 in " + ctor.tpe.paramTypes)
paramTypes = paramTypes.tail
values = values.tail
case _ =>
- log("giving up on " + ctor + "(diff: " + diff + ")")
+ debuglog("giving up on " + ctor + "(diff: " + diff + ")")
return bindings
}
@@ -566,7 +566,7 @@ abstract class CopyPropagation {
method.blocks map { b =>
"\nIN(%s):\t Bindings: %s".format(b.label, in(b).bindings) +
"\nIN(%s):\t Stack: %s".format(b.label, in(b).stack)
- }
+ }
).mkString
} /* class CopyAnalysis */
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index b57f5e86a3..ea0a0148e4 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -154,8 +154,10 @@ abstract class GenASM extends SubComponent with BytecodeWriters {
inform("[running phase " + name + " on icode]")
if (settings.Xdce.value)
- for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym))
+ for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) {
+ log(s"Optimizer eliminated ${sym.fullNameString}")
icodes.classes -= sym
+ }
// For predictably ordered error messages.
var sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
index 930791d88d..0f64a55d70 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
@@ -122,8 +122,10 @@ abstract class GenJVM extends SubComponent with GenJVMUtil with GenAndroid with
inform("[running phase " + name + " on icode]")
if (settings.Xdce.value)
- for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym))
+ for ((sym, cls) <- icodes.classes if inliner.isClosureClass(sym) && !deadCode.liveClosures(sym)) {
+ log(s"Optimizer eliminated ${sym.fullNameString}")
icodes.classes -= sym
+ }
// For predictably ordered error messages.
val sortedClasses = classes.values.toList sortBy ("" + _.symbol.fullName)
diff --git a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
index 7772ccbdd5..eb2da72401 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/ClosureElimination.scala
@@ -35,7 +35,7 @@ abstract class ClosureElimination extends SubComponent {
case (STORE_LOCAL(x), LOAD_LOCAL(y)) if (x == y) =>
var liveOut = liveness.out(bb)
if (!liveOut(x)) {
- log("store/load to a dead local? " + x)
+ debuglog("store/load to a dead local? " + x)
val instrs = bb.getArray
var idx = instrs.length - 1
while (idx > 0 && (instrs(idx) ne i2)) {
@@ -43,7 +43,7 @@ abstract class ClosureElimination extends SubComponent {
idx -= 1
}
if (!liveOut(x)) {
- log("removing dead store/load " + x)
+ log("Removing dead store/load of " + x.sym.initialize.defString)
Some(Nil)
} else None
} else
@@ -84,6 +84,7 @@ abstract class ClosureElimination extends SubComponent {
*/
class ClosureElim {
def analyzeClass(cls: IClass): Unit = if (settings.Xcloselim.value) {
+ log(s"Analyzing ${cls.methods.size} methods in $cls.")
cls.methods foreach { m =>
analyzeMethod(m)
peephole(m)
@@ -95,7 +96,6 @@ abstract class ClosureElimination extends SubComponent {
/* Some embryonic copy propagation. */
def analyzeMethod(m: IMethod): Unit = try {if (m.hasCode) {
- log("Analyzing " + m)
cpp.init(m)
cpp.run
@@ -110,23 +110,20 @@ abstract class ClosureElimination extends SubComponent {
t match {
case Deref(This) | Const(_) =>
bb.replaceInstruction(i, valueToInstruction(t));
- log("replaced " + i + " with " + t)
+ debuglog(s"replaced $i with $t")
case _ =>
- bb.replaceInstruction(i, LOAD_LOCAL(info.getAlias(l)))
- log("replaced " + i + " with " + info.getAlias(l))
-
+ val t = info.getAlias(l)
+ bb.replaceInstruction(i, LOAD_LOCAL(t))
+ debuglog(s"replaced $i with $t")
}
case LOAD_FIELD(f, false) /* if accessible(f, m.symbol) */ =>
def replaceFieldAccess(r: Record) {
val Record(cls, bindings) = r
- info.getFieldNonRecordValue(r, f) match {
- case Some(v) =>
- bb.replaceInstruction(i,
- DROP(REFERENCE(cls)) :: valueToInstruction(v) :: Nil);
- log("Replaced " + i + " with " + info.getFieldNonRecordValue(r, f));
- case None =>
+ info.getFieldNonRecordValue(r, f) foreach { v =>
+ bb.replaceInstruction(i, DROP(REFERENCE(cls)) :: valueToInstruction(v) :: Nil)
+ debuglog(s"replaced $i with $v")
}
}
@@ -157,14 +154,14 @@ abstract class ClosureElimination extends SubComponent {
value match {
case Boxed(LocalVar(loc2)) =>
bb.replaceInstruction(i, DROP(icodes.ObjectReference) :: valueToInstruction(info.getBinding(loc2)) :: Nil)
- log("replaced " + i + " with " + info.getBinding(loc2))
+ debuglog("replaced " + i + " with " + info.getBinding(loc2))
case _ =>
()
}
case Boxed(LocalVar(loc1)) :: _ =>
val loc2 = info.getAlias(loc1)
bb.replaceInstruction(i, DROP(icodes.ObjectReference) :: valueToInstruction(Deref(LocalVar(loc2))) :: Nil)
- log("replaced " + i + " with " + LocalVar(loc2))
+ debuglog("replaced " + i + " with " + LocalVar(loc2))
case _ =>
}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
index fd949576e1..36a5d61cfb 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/DeadCodeElimination.scala
@@ -44,6 +44,7 @@ abstract class DeadCodeElimination extends SubComponent {
class DeadCode {
def analyzeClass(cls: IClass) {
+ log(s"Analyzing ${cls.methods.size} methods in $cls.")
cls.methods.foreach { m =>
this.method = m
dieCodeDie(m)
@@ -73,7 +74,7 @@ abstract class DeadCodeElimination extends SubComponent {
def dieCodeDie(m: IMethod) {
if (m.hasCode) {
- log("dead code elimination on " + m);
+ debuglog("dead code elimination on " + m);
dropOf.clear()
m.code.blocks.clear()
accessedLocals = m.params.reverse
@@ -82,8 +83,10 @@ abstract class DeadCodeElimination extends SubComponent {
mark
sweep(m)
accessedLocals = accessedLocals.distinct
- if ((m.locals diff accessedLocals).nonEmpty) {
- log("Removed dead locals: " + (m.locals diff accessedLocals))
+ val diff = m.locals diff accessedLocals
+ if (diff.nonEmpty) {
+ val msg = diff.map(_.sym.name)mkString(", ")
+ log(s"Removed ${diff.size} dead locals: $msg")
m.locals = accessedLocals.reverse
}
}
@@ -126,7 +129,7 @@ abstract class DeadCodeElimination extends SubComponent {
case RETURN(_) | JUMP(_) | CJUMP(_, _, _, _) | CZJUMP(_, _, _, _) | STORE_FIELD(_, _) |
THROW(_) | LOAD_ARRAY_ITEM(_) | STORE_ARRAY_ITEM(_) | SCOPE_ENTER(_) | SCOPE_EXIT(_) | STORE_THIS(_) |
LOAD_EXCEPTION(_) | SWITCH(_, _) | MONITOR_ENTER() | MONITOR_EXIT() => worklist += ((bb, idx))
- case CALL_METHOD(m1, _) if isSideEffecting(m1) => worklist += ((bb, idx)); log("marking " + m1)
+ case CALL_METHOD(m1, _) if isSideEffecting(m1) => worklist += ((bb, idx)); debuglog("marking " + m1)
case CALL_METHOD(m1, SuperCall(_)) =>
worklist += ((bb, idx)) // super calls to constructor
case DROP(_) =>
@@ -173,7 +176,7 @@ abstract class DeadCodeElimination extends SubComponent {
instr match {
case LOAD_LOCAL(l1) =>
for ((l2, bb1, idx1) <- defs((bb, idx)) if l1 == l2; if !useful(bb1)(idx1)) {
- log("\tAdding " + bb1(idx1))
+ debuglog("\tAdding " + bb1(idx1))
worklist += ((bb1, idx1))
}
@@ -197,7 +200,7 @@ abstract class DeadCodeElimination extends SubComponent {
case _ =>
for ((bb1, idx1) <- rdef.findDefs(bb, idx, instr.consumed) if !useful(bb1)(idx1)) {
- log("\tAdding " + bb1(idx1))
+ debuglog("\tAdding " + bb1(idx1))
worklist += ((bb1, idx1))
}
}
@@ -232,7 +235,7 @@ abstract class DeadCodeElimination extends SubComponent {
} else {
i match {
case NEW(REFERENCE(sym)) =>
- log("skipped object creation: " + sym + "inside " + m)
+ log(s"Eliminated instantation of $sym inside $m")
case _ => ()
}
debuglog("Skipped: bb_" + bb + ": " + idx + "( " + i + ")")
@@ -240,7 +243,7 @@ abstract class DeadCodeElimination extends SubComponent {
}
if (bb.nonEmpty) bb.close
- else log("empty block encountered")
+ else log(s"empty block encountered in $m")
}
}
@@ -252,7 +255,7 @@ abstract class DeadCodeElimination extends SubComponent {
foreachWithIndex(bb.toList) { (i, idx) =>
if (!useful(bb)(idx)) {
foreachWithIndex(i.consumedTypes.reverse) { (consumedType, depth) =>
- log("Finding definitions of: " + i + "\n\t" + consumedType + " at depth: " + depth)
+ debuglog("Finding definitions of: " + i + "\n\t" + consumedType + " at depth: " + depth)
val defs = rdef.findDefs(bb, idx, 1, depth)
for (d <- defs) {
val (bb, idx) = d
diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala
index f1f597322e..98120f0614 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala
@@ -93,10 +93,9 @@ abstract class InlineExceptionHandlers extends SubComponent {
val startTime = System.currentTimeMillis
currentClass = c
- log("Starting " + c)
+ debuglog("Starting InlineExceptionHandlers on " + c)
c.methods foreach applyMethod
-
- log("Finished " + c + "... " + (System.currentTimeMillis - startTime) + "ms")
+ debuglog("Finished InlineExceptionHandlers on " + c + "... " + (System.currentTimeMillis - startTime) + "ms")
currentClass = null
}
diff --git a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
index 5464b6fc3b..62bb23c3a7 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/Inliners.scala
@@ -44,7 +44,7 @@ abstract class Inliners extends SubComponent {
import definitions.{
NullClass, NothingClass, ObjectClass,
PredefModule, RuntimePackage, ScalaInlineClass, ScalaNoInlineClass,
- isFunctionType
+ isFunctionType, isByNameParamType
}
val phaseName = "inliner"
@@ -143,7 +143,6 @@ abstract class Inliners extends SubComponent {
}
def isBottomType(sym: Symbol) = sym == NullClass || sym == NothingClass
- def posToStr(pos: scala.reflect.internal.util.Position) = if (pos.isDefined) pos.point.toString else "<nopos>"
/** Is the given class a closure? */
def isClosureClass(cls: Symbol): Boolean =
@@ -194,6 +193,27 @@ abstract class Inliners extends SubComponent {
private var currentIClazz: IClass = _
private def warn(pos: Position, msg: String) = currentIClazz.cunit.inlinerWarning(pos, msg)
+ private def ownedName(sym: Symbol): String = afterUncurry {
+ val count = (
+ if (!sym.isMethod) 1
+ else if (sym.owner.isAnonymousFunction) 3
+ else 2
+ )
+ (sym.ownerChain take count filterNot (_.isPackageClass)).reverseMap(_.nameString).mkString(".")
+ }
+ private def inlineLog(what: String, main: => String, comment: => String) {
+ def cstr = comment match {
+ case "" => ""
+ case str => " // " + str
+ }
+ val width = if (currentIClazz eq null) 40 else currentIClazz.symbol.enclosingPackage.fullName.length + 25
+ val fmt = "%8s %-" + width + "s" + cstr
+ log(fmt.format(what, main))
+ }
+ private def inlineLog(what: String, main: Symbol, comment: => String) {
+ inlineLog(what, ownedName(main), comment)
+ }
+
val recentTFAs = mutable.Map.empty[Symbol, Tuple2[Boolean, analysis.MethodTFA]]
private def getRecentTFA(incm: IMethod, forceable: Boolean): (Boolean, analysis.MethodTFA) = {
@@ -244,14 +264,15 @@ abstract class Inliners extends SubComponent {
def analyzeClass(cls: IClass): Unit =
if (settings.inline.value) {
- debuglog("Analyzing " + cls)
+ inlineLog("class", s"${cls.symbol.decodedName}", s"analyzing ${cls.methods.size} methods in $cls")
this.currentIClazz = cls
val ms = cls.methods sorted imethodOrdering
ms foreach { im =>
- if(hasInline(im.symbol)) {
- log("Not inlining into " + im.symbol.originalName.decode + " because it is marked @inline.")
- } else if(im.hasCode && !im.symbol.isBridge) {
+ if (hasInline(im.symbol)) {
+ inlineLog("skip", im.symbol, "no inlining into @inline methods")
+ }
+ else if(im.hasCode && !im.symbol.isBridge) {
analyzeMethod(im)
}
}
@@ -296,6 +317,8 @@ abstract class Inliners extends SubComponent {
* */
def analyzeMethod(m: IMethod): Unit = {
// m.normalize
+ if (settings.debug.value)
+ inlineLog("caller", ownedName(m.symbol), "in " + m.symbol.owner.fullName)
var sizeBeforeInlining = m.code.blockCount
var instrBeforeInlining = m.code.instructionCount
@@ -306,8 +329,8 @@ abstract class Inliners extends SubComponent {
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)
+ def analyzeMessage = s"Analyzing ${caller.length} blocks of $m for inlining sites."
def preInline(isFirstRound: Boolean): Int = {
val inputBlocks = caller.m.linearizedBlocks()
@@ -354,15 +377,17 @@ abstract class Inliners extends SubComponent {
*/
def analyzeInc(i: CALL_METHOD, bb: BasicBlock, receiver: Symbol, stackLength: Int, concreteMethod: Symbol): Boolean = {
assert(bb.toList contains i, "Candidate callsite does not belong to BasicBlock.")
-
- var inlined = false
val shouldWarn = hasInline(i.method)
- def warnNoInline(reason: String) = {
- if (shouldWarn) {
- warn(i.pos, "Could not inline required method %s because %s.".format(i.method.originalName.decode, reason))
- }
- }
+ def warnNoInline(reason: String): Boolean = {
+ def msg = "Could not inline required method %s because %s.".format(i.method.originalName.decode, reason)
+ if (settings.debug.value)
+ inlineLog("fail", i.method.fullName, reason)
+ if (shouldWarn)
+ warn(i.pos, msg)
+
+ false
+ }
var isAvailable = icodes available concreteMethod.enclClass
@@ -378,92 +403,69 @@ abstract class Inliners extends SubComponent {
isAvailable = icodes.load(concreteMethod.enclClass)
}
- def isCandidate = (
- isClosureClass(receiver)
- || concreteMethod.isEffectivelyFinal
- || receiver.isEffectivelyFinal
- )
+ def isCandidate = (
+ isClosureClass(receiver)
+ || concreteMethod.isEffectivelyFinal
+ || receiver.isEffectivelyFinal
+ )
- def isApply = concreteMethod.name == nme.apply
+ def isApply = concreteMethod.name == nme.apply
- def isCountable = !(
- isClosureClass(receiver)
- || isApply
- || isMonadicMethod(concreteMethod)
- || receiver.enclosingPackage == definitions.RuntimePackage
- ) // only count non-closures
+ def isCountable = !(
+ isClosureClass(receiver)
+ || isApply
+ || isMonadicMethod(concreteMethod)
+ || receiver.enclosingPackage == definitions.RuntimePackage
+ ) // only count non-closures
debuglog("Treating " + i
+ "\n\treceiver: " + receiver
+ "\n\ticodes.available: " + isAvailable
+ "\n\tconcreteMethod.isEffectivelyFinal: " + concreteMethod.isEffectivelyFinal)
- if (isAvailable && isCandidate) {
- lookupIMethod(concreteMethod, receiver) match {
-
- case Some(callee) if callee.hasCode =>
- val inc = new IMethodInfo(callee)
- val pair = new CallerCalleeInfo(caller, inc, fresh, inlinedMethodCount)
-
- if(inc.hasHandlers && (stackLength == -1)) {
- // no inlining is done, yet don't warn about it, stackLength == -1 indicates we're trying to inlineWithoutTFA.
- // Shortly, a TFA will be computed and an error message reported if indeed inlining not possible.
- return false
- }
-
- (pair isStampedForInlining stackLength) match {
-
- case inlInfo if inlInfo.isSafe =>
-
- (inlInfo: @unchecked) match {
-
- case FeasibleInline(accessNeeded, toBecomePublic) =>
- for(f <- toBecomePublic) {
- debuglog("Making public (synthetic) field-symbol: " + f)
- f setFlag Flags.notPRIVATE
- f setFlag Flags.notPROTECTED
- }
- // only add to `knownSafe` after all `toBecomePublic` fields actually made public.
- if(accessNeeded == NonPublicRefs.Public) { tfa.knownSafe += inc.sym }
-
- case InlineableAtThisCaller => ()
-
- }
-
- retry = true
- inlined = true
- if (isCountable) { count += 1 };
+ if (!isCandidate) warnNoInline("it can be overridden")
+ else if (!isAvailable) warnNoInline("bytecode unavailable")
+ else lookupIMethod(concreteMethod, receiver) filter (callee => callee.hasCode || warnNoInline("callee has no code")) exists { callee =>
+ val inc = new IMethodInfo(callee)
+ val pair = new CallerCalleeInfo(caller, inc, fresh, inlinedMethodCount)
- pair.doInline(bb, i)
- if (!pair.isInlineForced || inc.isMonadic) { caller.inlinedCalls += 1 };
- inlinedMethodCount(inc.sym) += 1
-
- // Remove the caller from the cache (this inlining might have changed its calls-private relation).
- usesNonPublics -= m
- recentTFAs -= m.symbol
-
-
- case DontInlineHere(msg) =>
- debuglog("inline failed, reason: " + msg)
- warnNoInline(msg)
-
- case NeverSafeToInline => ()
- }
-
- case Some(callee) =>
- assert(!callee.hasCode, "The case clause right before this one should have handled this case.")
- warnNoInline("callee (" + callee + ") has no code")
- ()
+ if (inc.hasHandlers && (stackLength == -1)) {
+ // no inlining is done, yet don't warn about it, stackLength == -1 indicates we're trying to inlineWithoutTFA.
+ // Shortly, a TFA will be computed and an error message reported if indeed inlining not possible.
+ false
+ }
+ else {
+ val isSafe = pair isStampedForInlining stackLength match {
+ case DontInlineHere(msg) => warnNoInline(msg)
+ case NeverSafeToInline => false
+ case InlineableAtThisCaller => true
+ case inl @ FeasibleInline(_, _) if !inl.isSafe => false
+ case FeasibleInline(required, toPublicize) =>
+ for (f <- toPublicize) {
+ inlineLog("access", f, "making public")
+ f setFlag Flags.notPRIVATE
+ f setFlag Flags.notPROTECTED
+ }
+ // only add to `knownSafe` after all `toPublicize` fields actually made public.
+ if (required == NonPublicRefs.Public)
+ tfa.knownSafe += inc.sym
- case None =>
- warnNoInline("bytecode was not available")
- debuglog("could not find icode\n\treceiver: " + receiver + "\n\tmethod: " + concreteMethod)
+ true
+ }
+ isSafe && {
+ retry = true
+ if (isCountable) count += 1
+ pair.doInline(bb, i)
+ if (!pair.isInlineForced || inc.isMonadic) caller.inlinedCalls += 1
+ inlinedMethodCount(inc.sym) += 1
+
+ // Remove the caller from the cache (this inlining might have changed its calls-private relation).
+ usesNonPublics -= m
+ recentTFAs -= m.symbol
+ true
+ }
}
- } else {
- warnNoInline(if(!isAvailable) "bytecode was not available" else "it can be overridden")
}
-
- inlined
}
/* Pre-inlining consists in invoking the usual inlining subroutine with (receiver class, concrete method) pairs as input
@@ -485,7 +487,7 @@ abstract class Inliners extends SubComponent {
do {
retry = false
- log("Analyzing " + m + " count " + count + " with " + caller.length + " blocks")
+ debuglog(analyzeMessage)
/* it's important not to inline in unreachable basic blocks. linearizedBlocks() returns only reachable ones. */
tfa.callerLin = caller.m.linearizedBlocks()
@@ -567,9 +569,16 @@ abstract class Inliners extends SubComponent {
m.normalize
if (sizeBeforeInlining > 0) {
val instrAfterInlining = m.code.instructionCount
- val prefix = if ((instrAfterInlining > 2 * instrBeforeInlining) && (instrAfterInlining > 200)) " !! " else ""
- log(prefix + " %s blocks before inlining: %d (%d) after: %d (%d)".format(
- m.symbol.fullName, sizeBeforeInlining, instrBeforeInlining, m.code.blockCount, instrAfterInlining))
+ val prefix = if ((instrAfterInlining > 2 * instrBeforeInlining) && (instrAfterInlining > 200)) "!!" else ""
+ val inlinings = caller.inlinedCalls
+ if (inlinings > 0) {
+ val s1 = s"instructions $instrBeforeInlining -> $instrAfterInlining"
+ val s2 = if (sizeBeforeInlining == m.code.blockCount) "" else s", blocks $sizeBeforeInlining -> ${m.code.blockCount}"
+ val callees = inlinedMethodCount.toList map { case (k, v) => k.fullNameString + ( if (v == 1) "" else "/" + v ) }
+
+ inlineLog("inlined", m.symbol.fullName, callees.sorted.mkString(inlinings + " inlined: ", ", ", ""))
+ inlineLog("<<tldr>>", m.symbol.fullName, s"${m.symbol.nameString}: $s1$s2")
+ }
}
}
@@ -589,6 +598,8 @@ abstract class Inliners extends SubComponent {
}
class IMethodInfo(val m: IMethod) {
+ override def toString = m.toString
+
val sym = m.symbol
val name = sym.name
def owner = sym.owner
@@ -608,10 +619,11 @@ abstract class Inliners extends SubComponent {
def instructions = m.code.instructions
// def linearized = linearizer linearize m
- def isSmall = (length <= SMALL_METHOD_SIZE) && blocks(0).length < 10
- def isLarge = length > MAX_INLINE_SIZE
- def isRecursive = m.recursive
- def hasHandlers = handlers.nonEmpty || m.bytecodeHasEHs
+ def isSmall = (length <= SMALL_METHOD_SIZE) && blocks(0).length < 10
+ def isLarge = length > MAX_INLINE_SIZE
+ def isRecursive = m.recursive
+ def hasHandlers = handlers.nonEmpty || m.bytecodeHasEHs
+ def hasClosureParam = paramTypes exists (tp => isByNameParamType(tp) || isFunctionType(tp))
def isSynchronized = sym.hasFlag(Flags.SYNCHRONIZED)
def hasNonFinalizerHandler = handlers exists {
@@ -661,9 +673,9 @@ abstract class Inliners extends SubComponent {
*
* TODO handle more robustly the case of a trait var changed at the source-level from public to private[this]
* (eg by having ICodeReader use unpickler, see SI-5442).
-
+
DISABLED
-
+
def potentiallyPublicized(f: Symbol): Boolean = {
(m.sourceFile eq NoSourceFile) && f.name.containsChar('$')
}
@@ -687,7 +699,7 @@ abstract class Inliners extends SubComponent {
val i = iter.next()
getAccess(i) match {
case Private =>
- log("instruction " + i + " requires private access.")
+ inlineLog("access", s"instruction $i requires private access", "pos=" + i.pos)
toBecomePublic = Nil
seen = Private
case Protected => seen = Protected
@@ -764,11 +776,10 @@ abstract class Inliners extends SubComponent {
tfa.warnIfInlineFails.remove(instr)
val targetPos = instr.pos
- log("Inlining " + inc.m + " in " + caller.m + " at pos: " + posToStr(targetPos))
def blockEmit(i: Instruction) = block.emit(i, targetPos)
def newLocal(baseName: String, kind: TypeKind) =
- new Local(caller.sym.newVariable(freshName(baseName), targetPos), kind, false)
+ new Local(caller.sym.newVariable(freshName(baseName), targetPos) setInfo kind.toType, kind, false)
val (hasRETURN, a) = getRecentTFA(inc.m, isInlineForced)
@@ -955,6 +966,7 @@ abstract class Inliners extends SubComponent {
if(reasonWhyNever != null) {
tfa.knownNever += inc.sym
+ inlineLog("never", inc.sym, reasonWhyNever)
// next time around NeverSafeToInline is returned, thus skipping (duplicate) msg, this is intended.
return DontInlineHere(inc.m + " " + reasonWhyNever)
}
@@ -977,10 +989,15 @@ abstract class Inliners extends SubComponent {
* As a result of (b), some synthetic private members can be chosen to become public.
*/
- if(!isInlineForced && !isScoreOK) {
+ val score = inlinerScore
+ val scoreStr = if (score > 0) "+" + score else "" + score
+ val what = if (score > 0) "ok to" else "don't"
+ inlineLog(scoreStr, inc.m.symbol, s"$what inline into ${ownedName(caller.m.symbol)}")
+
+ if (!isInlineForced && score <= 0) {
// During inlining retry, a previous caller-callee pair that scored low may pass.
// Thus, adding the callee to tfa.knownUnsafe isn't warranted.
- return DontInlineHere("too low score (heuristics)")
+ return DontInlineHere(s"inliner heuristic")
}
if(inc.hasHandlers && (stackLength > inc.minimumStack)) {
@@ -999,7 +1016,9 @@ abstract class Inliners extends SubComponent {
val accReq = inc.accessRequirements
if(!canAccess(accReq.accessNeeded)) {
tfa.knownUnsafe += inc.sym
- return DontInlineHere("access level required by callee not matched by caller")
+ val msg = "access level required by callee not matched by caller"
+ inlineLog("fail", inc.sym, msg)
+ return DontInlineHere(msg)
}
FeasibleInline(accReq.accessNeeded, accReq.toBecomePublic)
@@ -1021,9 +1040,7 @@ abstract class Inliners extends SubComponent {
* - it's good to inline closures functions.
* - it's bad (useless) to inline inside bridge methods
*/
- def isScoreOK: Boolean = {
- debuglog("shouldInline: " + caller.m + " , callee:" + inc.m)
-
+ def inlinerScore: Int = {
var score = 0
// better not inline inside closures, but hope that the closure itself is repeatedly inlined
@@ -1031,21 +1048,19 @@ abstract class Inliners extends SubComponent {
else if (caller.inlinedCalls < 1) score -= 1 // only monadic methods can trigger the first inline
if (inc.isSmall) score += 1;
+ // if (inc.hasClosureParam) score += 2
if (inc.isLarge) score -= 1;
if (caller.isSmall && isLargeSum) {
score -= 1
- debuglog("shouldInline: score decreased to " + score + " because small " + caller + " would become large")
+ debuglog(s"inliner score decreased to $score because small caller $caller would become large")
}
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)
-
- score > 0
+ if (inc.isInClosure) score += 2
+ if (inlinedMethodCount(inc.sym) > 2) score -= 2
+ score
}
}
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 96d7dadbd7..a2df1494ef 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -387,8 +387,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
newSym <- req.definedSymbols get name
oldSym <- oldReq.definedSymbols get name.companionName
} {
- replwarn("warning: previously defined %s is not a companion to %s.".format(
- stripString("" + oldSym), stripString("" + newSym)))
+ afterTyper(replwarn(s"warning: previously defined $oldSym is not a companion to $newSym."))
replwarn("Companions must be defined together; you may wish to use :paste mode for this.")
}
diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
index 116eed0f31..2ff81ae603 100644
--- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala
@@ -536,7 +536,7 @@ class MutableSettings(val errorFn: String => Unit)
}
override def tryToSetColon(args: List[String]) = tryToSet(args)
override def tryToSetFromPropertyValue(s: String) = tryToSet(s.trim.split(',').toList)
- def unparse: List[String] = name :: value
+ def unparse: List[String] = value map (name + ":" + _)
withHelpSyntax(name + ":<" + arg + ">")
}
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
index 437a5e1434..175c322786 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
@@ -37,7 +37,7 @@ abstract class ICodeReader extends ClassfileParser {
cls.info // ensure accurate type information
isScalaModule = cls.isModule && !cls.isJavaDefined
- log("Reading class: " + cls + " isScalaModule?: " + isScalaModule)
+ log("ICodeReader reading " + cls)
val name = cls.javaClassName
classPath.findSourceFile(name) match {
@@ -99,11 +99,9 @@ abstract class ICodeReader extends ClassfileParser {
if (sym == NoSymbol)
sym = owner.info.findMember(newTermName(name + nme.LOCAL_SUFFIX_STRING), 0, 0, false).suchThat(_.tpe =:= tpe)
if (sym == NoSymbol) {
- log("Could not find symbol for " + name + ": " + tpe)
- log(owner.info.member(name).tpe + " : " + tpe)
sym = if (field) owner.newValue(name, owner.pos, toScalaFieldFlags(jflags)) else dummySym
sym setInfoAndEnter tpe
- log("added " + sym + ": " + sym.tpe)
+ log(s"ICodeReader could not locate ${name.decode} in $owner. Created ${sym.defString}.")
}
(jflags, sym)
}
@@ -172,10 +170,7 @@ abstract class ICodeReader extends ClassfileParser {
}
else if (nme.isModuleName(name)) {
val strippedName = nme.stripModuleSuffix(name)
- val sym = forceMangledName(newTermName(strippedName.decode), true)
-
- if (sym == NoSymbol) rootMirror.getModule(strippedName)
- else sym
+ forceMangledName(newTermName(strippedName.decode), true) orElse rootMirror.getModule(strippedName)
}
else {
forceMangledName(name, false)
@@ -956,7 +951,7 @@ abstract class ICodeReader extends ClassfileParser {
case None =>
checkValidIndex
val l = freshLocal(idx, kind, false)
- log("Added new local for idx " + idx + ": " + kind)
+ debuglog("Added new local for idx " + idx + ": " + kind)
locals += (idx -> List((l, kind)))
l
}
diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
index a8cdee7154..4a668d4c61 100644
--- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
+++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
@@ -79,12 +79,11 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
// -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 " + implSym)
iface.owner.info.decls unlink implSym
NoSymbol
}
else {
- log("not unlinking existing " + implSym + " as the impl class is not visible on the classpath.")
+ log(s"not unlinking $iface's existing implClass ${implSym.name} because it is not on the classpath.")
implSym
}
}
@@ -113,9 +112,10 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
iface.info
implClassMap.getOrElse(iface, atPhase(implClassPhase) {
- log("Creating implClass for " + iface)
- if (iface.implClass ne NoSymbol)
- log("%s.implClass already exists: %s".format(iface, iface.implClass))
+ if (iface.implClass eq NoSymbol)
+ debuglog(s"${iface.fullLocationString} has no implClass yet, creating it now.")
+ else
+ log(s"${iface.fullLocationString} impl class is ${iface.implClass.nameString}")
newImplClass(iface)
})
@@ -137,7 +137,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
* given the decls ifaceDecls of its interface.
*/
private def implDecls(implClass: Symbol, ifaceDecls: Scope): Scope = {
- log("LazyImplClassType calculating decls for " + implClass)
+ debuglog("LazyImplClassType calculating decls for " + implClass)
val decls = newScope
if ((ifaceDecls lookup nme.MIXIN_CONSTRUCTOR) == NoSymbol) {
@@ -152,16 +152,16 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
for (sym <- ifaceDecls) {
if (isInterfaceMember(sym)) {
if (needsImplMethod(sym)) {
- log("Cloning " + sym + " for implementation method in " + implClass)
val clone = sym.cloneSymbol(implClass).resetFlag(lateDEFERRED)
if (currentRun.compiles(implClass)) implMethodMap(sym) = clone
decls enter clone
sym setFlag lateDEFERRED
+ if (!sym.isSpecialized)
+ log(s"Cloned ${sym.name} from ${sym.owner} into implClass ${implClass.fullName}")
}
- else log(sym + " needs no implementation method in " + implClass)
}
else {
- log("Destructively modifying owner of %s from %s to %s".format(sym, sym.owner, implClass))
+ log(s"Destructively modifying owner of $sym from ${sym.owner} to $implClass")
sym.owner = implClass
// note: OK to destructively modify the owner here,
// because symbol will not be accessible from outside the sourcefile.
@@ -174,7 +174,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
}
override def complete(implSym: Symbol) {
- log("LazyImplClassType completing " + implSym)
+ debuglog("LazyImplClassType completing " + implSym)
/** If `tp` refers to a non-interface trait, return a
* reference to its implementation class. Otherwise return `tp`.
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index bfede7a690..ae1a2c7e8e 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -490,7 +490,7 @@ abstract class Erasure extends AddInterfaces
@inline private def box(tree: Tree, target: => String): Tree = {
val result = box1(tree)
- log("boxing "+tree+":"+tree.tpe+" to "+target+" = "+result+":"+result.tpe)
+ log(s"boxing ${tree.summaryString}: ${tree.tpe} into $target: ${result.tpe}")
result
}
@@ -525,7 +525,7 @@ abstract class Erasure extends AddInterfaces
* fields (see TupleX). (ID)
*/
case Apply(boxFun, List(arg)) if isUnbox(tree.symbol) && safeToRemoveUnbox(arg.tpe.typeSymbol) =>
- log("boxing an unbox: " + tree + "/" + tree.symbol + " and replying with " + arg + " of type " + arg.tpe)
+ log(s"boxing an unbox: ${tree.symbol} -> ${arg.tpe}")
arg
case _ =>
(REF(boxMethod(x)) APPLY tree) setPos (tree.pos) setType ObjectClass.tpe
@@ -537,7 +537,7 @@ abstract class Erasure extends AddInterfaces
private def unbox(tree: Tree, pt: Type): Tree = {
val result = unbox1(tree, pt)
- log("unboxing "+tree+":"+tree.tpe+" to "+pt+" = "+result+":"+result.tpe)
+ log(s"unboxing ${tree.summaryString}: ${tree.tpe} with pt=$pt as type ${result.tpe}")
result
}
@@ -614,7 +614,7 @@ abstract class Erasure extends AddInterfaces
* @return the adapted tree
*/
private def adaptToType(tree: Tree, pt: Type): Tree = {
- //if (settings.debug.value && pt != WildcardType)
+ if (settings.debug.value && pt != WildcardType)
log("adapting " + tree + ":" + tree.tpe + " : " + tree.tpe.parents + " to " + pt)//debug
if (tree.tpe <:< pt)
tree
@@ -1081,8 +1081,8 @@ abstract class Erasure extends AddInterfaces
case New(tpt) if name == nme.CONSTRUCTOR && tpt.tpe.typeSymbol.isDerivedValueClass =>
// println("inject derived: "+arg+" "+tpt.tpe)
val List(arg) = args
- InjectDerivedValue(arg) addAttachment //@@@ setSymbol tpt.tpe.typeSymbol
- new TypeRefAttachment(tree.tpe.asInstanceOf[TypeRef])
+ val attachment = new TypeRefAttachment(tree.tpe.asInstanceOf[TypeRef])
+ InjectDerivedValue(arg) addAttachment attachment
case _ =>
preEraseNormalApply(tree)
}
diff --git a/src/compiler/scala/tools/nsc/transform/Flatten.scala b/src/compiler/scala/tools/nsc/transform/Flatten.scala
index 94eaba67d7..3bbf429fc2 100644
--- a/src/compiler/scala/tools/nsc/transform/Flatten.scala
+++ b/src/compiler/scala/tools/nsc/transform/Flatten.scala
@@ -22,12 +22,14 @@ abstract class Flatten extends InfoTransform {
*/
private def replaceSymbolInCurrentScope(sym: Symbol): Symbol = afterFlatten {
val scope = sym.owner.info.decls
- val old = scope lookup sym.name
- if (old ne NoSymbol)
- scope unlink old
-
+ val old = scope lookup sym.name andAlso scope.unlink
scope enter sym
- log("lifted " + sym.fullLocationString)
+
+ if (old eq NoSymbol)
+ log(s"lifted ${sym.fullLocationString}")
+ else
+ log(s"lifted ${sym.fullLocationString} after unlinking existing $old from scope.")
+
old
}
@@ -35,9 +37,7 @@ abstract class Flatten extends InfoTransform {
if (!sym.isLifted) {
sym setFlag LIFTED
debuglog("re-enter " + sym.fullLocationString)
- val old = replaceSymbolInCurrentScope(sym)
- if (old ne NoSymbol)
- log("unlinked " + old.fullLocationString + " after lifting " + sym)
+ replaceSymbolInCurrentScope(sym)
}
}
private def liftSymbol(sym: Symbol) {
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index fc9e611d20..5097ecc3fe 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -436,7 +436,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
val sClassMap = anyrefSpecCache.getOrElseUpdate(sClass, mutable.Map[Symbol, Symbol]())
sClassMap.getOrElseUpdate(tparam,
- tparam.cloneSymbol(sClass, tparam.flags, (tparam.name append tpnme.SPECIALIZED_SUFFIX).asInstanceOf[Name]) // [Eugene] why do we need this cast?
+ tparam.cloneSymbol(sClass, tparam.flags, tparam.name append tpnme.SPECIALIZED_SUFFIX)
modifyInfo (info => TypeBounds(info.bounds.lo, AnyRefClass.tpe))
).tpe
}
@@ -811,12 +811,17 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
specializingOn = specializingOn filterNot (unusedStvars contains)
}
for (env0 <- specializations(specializingOn) if needsSpecialization(env0, sym)) yield {
+ // !!! Can't this logic be structured so that the new symbol's name is
+ // known when the symbol is cloned? It is much cleaner not to be mutating
+ // names after the fact. And it adds about a billion lines of
+ // "Renaming value _1 in class Tuple2 to _1$mcZ$sp" to obscure the small
+ // number of other (important) actual symbol renamings.
val tps = survivingParams(sym.info.typeParams, env0)
- val specMember = sym.cloneSymbol(owner, (sym.flags | SPECIALIZED) & ~DEFERRED)
+ val specMember = sym.cloneSymbol(owner, (sym.flags | SPECIALIZED) & ~DEFERRED) // <-- this needs newName = ...
val env = mapAnyRefsInSpecSym(env0, sym, specMember)
val (keys, vals) = env.toList.unzip
- specMember setName specializedName(sym, env)
+ specMember setName specializedName(sym, env) // <-- but the name is calculated based on the cloned symbol
// debuglog("%s normalizes to %s%s".format(sym, specMember,
// if (tps.isEmpty) "" else " with params " + tps.mkString(", ")))
@@ -897,7 +902,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
private def specializedOverload(owner: Symbol, sym: Symbol, env: TypeEnv): Symbol = {
val newFlags = (sym.flags | SPECIALIZED) & ~(DEFERRED | CASEACCESSOR)
// this method properly duplicates the symbol's info
- ( sym.cloneSymbol(owner, newFlags, specializedName(sym, env))
+ ( sym.cloneSymbol(owner, newFlags, newName = specializedName(sym, env))
modifyInfo (info => subst(env, info.asSeenFrom(owner.thisType, sym.owner)))
)
}
@@ -912,7 +917,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
*
* this method will return List('apply$mcII$sp')
*/
- private def specialOverrides(clazz: Symbol) = logResultIf[List[Symbol]]("specialOverrides(" + clazz + ")", _.nonEmpty) {
+ private def specialOverrides(clazz: Symbol) = logResultIf[List[Symbol]]("specialized overrides in " + clazz, _.nonEmpty) {
/** Return the overridden symbol in syms that needs a specialized overriding symbol,
* together with its specialization environment. The overridden symbol may not be
* the closest to 'overriding', in a given hierarchy.
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 181463657b..5bff653499 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -484,11 +484,7 @@ abstract class UnCurry extends InfoTransform
arg setType functionType(Nil, arg.tpe)
}
else {
- log("byname | %s | %s | %s".format(
- arg.pos.source.path + ":" + arg.pos.line, fun.fullName,
- if (fun.isPrivate) "private" else "")
- )
-
+ log(s"Argument '$arg' at line ${arg.pos.safeLine} is $formal from ${fun.fullName}")
arg match {
// don't add a thunk for by-name argument if argument already is an application of
// a Function0. We can then remove the application and use the existing Function0.
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 4f597f97c9..83740f1658 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -369,7 +369,7 @@ trait MethodSynthesis {
}
/** A synthetic method which performs the implicit conversion implied by
- * the declaration of an implicit class. Yet to be written.
+ * the declaration of an implicit class.
*/
case class ImplicitClassWrapper(tree: ClassDef) extends DerivedFromClassDef {
def completer(sym: Symbol): Type = ??? // not needed
@@ -377,7 +377,7 @@ trait MethodSynthesis {
def derivedSym: Symbol = {
// Only methods will do! Don't want to pick up any stray
// companion objects of the same name.
- val result = enclClass.info decl name suchThat (_.isMethod)
+ val result = enclClass.info decl name suchThat (x => x.isMethod && x.isSynthetic)
assert(result != NoSymbol, "not found: "+name+" in "+enclClass+" "+enclClass.info.decls)
result
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 166bb2d18c..d515934c58 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -430,6 +430,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
overrideError("cannot override a macro")
} else {
checkOverrideTypes()
+ checkOverrideDeprecated()
if (settings.warnNullaryOverride.value) {
if (other.paramss.isEmpty && !member.paramss.isEmpty) {
unit.warning(member.pos, "non-nullary method overrides nullary method")
@@ -508,6 +509,14 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
}
}
}
+
+ def checkOverrideDeprecated() {
+ if (other.hasDeprecatedOverridingAnnotation) {
+ val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse ""
+ val msg = s"overriding ${other.fullLocationString} is deprecated$suffix"
+ unit.deprecationWarning(member.pos, msg)
+ }
+ }
}
val opc = new overridingPairs.Cursor(clazz)
@@ -1197,6 +1206,23 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
case _ =>
}
+ // SI-6276 warn for `def foo = foo` or `val bar: X = bar`, which come up more frequently than you might think.
+ def checkInfiniteLoop(valOrDef: ValOrDefDef) {
+ def callsSelf = valOrDef.rhs match {
+ case t @ (Ident(_) | Select(This(_), _)) =>
+ t hasSymbolWhich (_.accessedOrSelf == valOrDef.symbol)
+ case _ => false
+ }
+ val trivialInifiniteLoop = (
+ !valOrDef.isErroneous
+ && !valOrDef.symbol.isValueParameter
+ && valOrDef.symbol.paramss.isEmpty
+ && callsSelf
+ )
+ if (trivialInifiniteLoop)
+ unit.warning(valOrDef.rhs.pos, s"${valOrDef.symbol.fullLocationString} does nothing other than call itself recursively")
+ }
+
// Transformation ------------------------------------------------------------
/* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */
@@ -1640,6 +1666,7 @@ abstract class RefChecks extends InfoTransform with reflect.internal.transform.R
case ValDef(_, _, _, _) | DefDef(_, _, _, _, _, _) =>
checkDeprecatedOvers(tree)
+ checkInfiniteLoop(tree.asInstanceOf[ValOrDefDef])
if (settings.warnNullaryUnit.value)
checkNullaryMethodReturnType(sym)
if (settings.warnInaccessible.value) {
diff --git a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
index d227f485c2..67afb0c118 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala
@@ -63,7 +63,7 @@ trait SyntheticMethods extends ast.TreeDSL {
// in the original order.
def accessors = clazz.caseFieldAccessors sortBy { acc =>
originalAccessors indexWhere { orig =>
- (acc.name == orig.name) || (acc.name startsWith (orig.name append "$").asInstanceOf[Name]) // [Eugene] why do we need this cast?
+ (acc.name == orig.name) || (acc.name startsWith (orig.name append "$"))
}
}
val arity = accessors.size
@@ -87,7 +87,7 @@ trait SyntheticMethods extends ast.TreeDSL {
)
def forwardToRuntime(method: Symbol): Tree =
- forwardMethod(method, getMember(ScalaRunTimeModule, (method.name prepend "_").asInstanceOf[Name]))(mkThis :: _) // [Eugene] why do we need this cast?
+ forwardMethod(method, getMember(ScalaRunTimeModule, (method.name prepend "_")))(mkThis :: _)
def callStaticsMethod(name: String)(args: Tree*): Tree = {
val method = termMember(RuntimeStaticsModule, name)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index a224c546e4..d90141b732 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1577,6 +1577,12 @@ trait Typers extends Modes with Adaptations with Tags {
if (psym.isFinal)
pending += ParentFinalInheritanceError(parent, psym)
+ if (psym.hasDeprecatedInheritanceAnnotation) {
+ val suffix = psym.deprecatedInheritanceMessage map (": " + _) getOrElse ""
+ val msg = s"inheritance from ${psym.fullLocationString} is deprecated$suffix"
+ unit.deprecationWarning(parent.pos, msg)
+ }
+
if (psym.isSealed && !phase.erasedTypes)
if (context.unit.source.file == psym.sourceFile)
psym addChild context.owner
@@ -1871,7 +1877,7 @@ trait Typers extends Modes with Adaptations with Tags {
* @param rhs ...
*/
def computeParamAliases(clazz: Symbol, vparamss: List[List[ValDef]], rhs: Tree) {
- log("computing param aliases for "+clazz+":"+clazz.primaryConstructor.tpe+":"+rhs)//debug
+ debuglog(s"computing param aliases for $clazz:${clazz.primaryConstructor.tpe}:$rhs")
def decompose(call: Tree): (Tree, List[Tree]) = call match {
case Apply(fn, args) =>
val (superConstr, args1) = decompose(fn)
@@ -3438,7 +3444,7 @@ trait Typers extends Modes with Adaptations with Tags {
}
if (hasError) annotationError
- else AnnotationInfo(annType, List(), nvPairs map {p => (p._1.asInstanceOf[Name], p._2.get)}).setOriginal(Apply(typedFun, args).setPos(ann.pos)) // [Eugene] why do we need this cast?
+ else AnnotationInfo(annType, List(), nvPairs map {p => (p._1, p._2.get)}).setOriginal(Apply(typedFun, args).setPos(ann.pos))
}
} else if (requireJava) {
reportAnnotationError(NestedAnnotationError(ann, annType))
diff --git a/src/library/scala/collection/GenIterableViewLike.scala b/src/library/scala/collection/GenIterableViewLike.scala
index 9e3927eaf4..142561df20 100644
--- a/src/library/scala/collection/GenIterableViewLike.scala
+++ b/src/library/scala/collection/GenIterableViewLike.scala
@@ -25,6 +25,7 @@ self =>
def iterator: Iterator[B]
override def foreach[U](f: B => U): Unit = iterator foreach f
override def toString = viewToString
+ override def isEmpty = !iterator.hasNext
}
trait EmptyView extends Transformed[Nothing] with super.EmptyView {
diff --git a/src/library/scala/concurrent/Awaitable.scala b/src/library/scala/concurrent/Awaitable.scala
index 99bdfbc5a9..655115349a 100644
--- a/src/library/scala/concurrent/Awaitable.scala
+++ b/src/library/scala/concurrent/Awaitable.scala
@@ -16,15 +16,34 @@ import scala.concurrent.util.Duration
trait Awaitable[+T] {
/**
- * Should throw [[scala.concurrent.TimeoutException]] if it times out
+ * Await the "resolved" state of this Awaitable.
* This method should not be called directly.
+ *
+ * @param atMost
+ * maximum wait time, which may be negative (no waiting is done),
+ * [[Duration.Inf]] for unbounded waiting, or a finite positive
+ * duration
+ * @return the Awaitable itself
+ * @throws InterruptedException if the wait call was interrupted
+ * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready
+ * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]]
*/
@throws(classOf[TimeoutException])
+ @throws(classOf[InterruptedException])
def ready(atMost: Duration)(implicit permit: CanAwait): this.type
/**
- * Throws exceptions if it cannot produce a T within the specified time.
+ * Await and return the result of this Awaitable, which is either of type T or a thrown exception (any Throwable).
* This method should not be called directly.
+ *
+ * @param atMost
+ * maximum wait time, which may be negative (no waiting is done),
+ * [[Duration.Inf]] for unbounded waiting, or a finite positive
+ * duration
+ * @return the value if the Awaitable was successful within the specific maximum wait time
+ * @throws InterruptedException if the wait call was interrupted
+ * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready
+ * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]]
*/
@throws(classOf[Exception])
def result(atMost: Duration)(implicit permit: CanAwait): T
diff --git a/src/library/scala/concurrent/BlockContext.scala b/src/library/scala/concurrent/BlockContext.scala
index 640560a174..83333a9e94 100644
--- a/src/library/scala/concurrent/BlockContext.scala
+++ b/src/library/scala/concurrent/BlockContext.scala
@@ -8,9 +8,6 @@
package scala.concurrent
-import java.lang.Thread
-import scala.concurrent.util.Duration
-
/**
* A context to be notified by `scala.concurrent.blocking` when
* a thread is about to block. In effect this trait provides
diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala
index 1be6050303..844ec14241 100644
--- a/src/library/scala/concurrent/ExecutionContext.scala
+++ b/src/library/scala/concurrent/ExecutionContext.scala
@@ -10,7 +10,6 @@ package scala.concurrent
import java.util.concurrent.{ ExecutorService, Executor }
-import scala.concurrent.util.Duration
import scala.annotation.implicitNotFound
import scala.util.Try
diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala
index bc0b437a33..a5d9cdd5d1 100644
--- a/src/library/scala/concurrent/Future.scala
+++ b/src/library/scala/concurrent/Future.scala
@@ -16,7 +16,6 @@ import java.lang.{ Iterable => JIterable }
import java.util.{ LinkedList => JLinkedList }
import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean }
-import scala.concurrent.util.Duration
import scala.util.control.NonFatal
import scala.Option
import scala.util.{Try, Success, Failure}
diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala
index 875a558887..c517a05a81 100644
--- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala
+++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala
@@ -14,7 +14,6 @@ import java.util.concurrent.{ LinkedBlockingQueue, Callable, Executor, ExecutorS
import java.util.Collection
import scala.concurrent.forkjoin._
import scala.concurrent.{ BlockContext, ExecutionContext, Awaitable, CanAwait, ExecutionContextExecutor, ExecutionContextExecutorService }
-import scala.concurrent.util.Duration
import scala.util.control.NonFatal
diff --git a/src/library/scala/concurrent/impl/Promise.scala b/src/library/scala/concurrent/impl/Promise.scala
index b19bed004b..9228872f2b 100644
--- a/src/library/scala/concurrent/impl/Promise.scala
+++ b/src/library/scala/concurrent/impl/Promise.scala
@@ -12,7 +12,7 @@ package scala.concurrent.impl
import java.util.concurrent.TimeUnit.NANOSECONDS
import scala.concurrent.{ ExecutionContext, CanAwait, OnCompleteRunnable, TimeoutException, ExecutionException }
-import scala.concurrent.util.Duration
+import scala.concurrent.util.{ Duration, Deadline, FiniteDuration }
import scala.annotation.tailrec
import scala.util.control.NonFatal
import scala.util.{ Try, Success, Failure }
@@ -64,30 +64,40 @@ private[concurrent] object Promise {
protected final def tryAwait(atMost: Duration): Boolean = {
@tailrec
- def awaitUnsafe(waitTimeNanos: Long): Boolean = {
- if (value.isEmpty && waitTimeNanos > 0) {
- val ms = NANOSECONDS.toMillis(waitTimeNanos)
- val ns = (waitTimeNanos % 1000000l).toInt // as per object.wait spec
- val start = System.nanoTime()
- try {
- synchronized {
- if (!isCompleted) wait(ms, ns) // previously - this was a `while`, ending up in an infinite loop
- }
- } catch {
- case e: InterruptedException =>
- }
+ def awaitUnsafe(deadline: Deadline, nextWait: FiniteDuration): Boolean = {
+ if (!isCompleted && nextWait > Duration.Zero) {
+ val ms = nextWait.toMillis
+ val ns = (nextWait.toNanos % 1000000l).toInt // as per object.wait spec
+
+ synchronized { if (!isCompleted) wait(ms, ns) }
- awaitUnsafe(waitTimeNanos - (System.nanoTime() - start))
+ awaitUnsafe(deadline, deadline.timeLeft)
} else
isCompleted
}
- awaitUnsafe(if (atMost.isFinite) atMost.toNanos else Long.MaxValue)
+ @tailrec
+ def awaitUnbounded(): Boolean = {
+ if (isCompleted) true
+ else {
+ synchronized { if (!isCompleted) wait() }
+ awaitUnbounded()
+ }
+ }
+
+ import Duration.Undefined
+ atMost match {
+ case u if u eq Undefined => throw new IllegalArgumentException("cannot wait for Undefined period")
+ case Duration.Inf => awaitUnbounded
+ case Duration.MinusInf => isCompleted
+ case f: FiniteDuration => if (f > Duration.Zero) awaitUnsafe(f.fromNow, f) else isCompleted
+ }
}
@throws(classOf[TimeoutException])
+ @throws(classOf[InterruptedException])
def ready(atMost: Duration)(implicit permit: CanAwait): this.type =
if (isCompleted || tryAwait(atMost)) this
- else throw new TimeoutException("Futures timed out after [" + atMost.toMillis + "] milliseconds")
+ else throw new TimeoutException("Futures timed out after [" + atMost + "]")
@throws(classOf[Exception])
def result(atMost: Duration)(implicit permit: CanAwait): T =
diff --git a/src/library/scala/concurrent/package.scala b/src/library/scala/concurrent/package.scala
index a2ef42fac8..1d06341d4d 100644
--- a/src/library/scala/concurrent/package.scala
+++ b/src/library/scala/concurrent/package.scala
@@ -67,26 +67,39 @@ package concurrent {
*/
object Await {
/**
+ * Await the "resolved" state of this Awaitable.
* 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.
+ * @param awaitable
+ * the `Awaitable` on which `ready` is to be called
+ * @param atMost
+ * maximum wait time, which may be negative (no waiting is done),
+ * [[Duration.Inf]] for unbounded waiting, or a finite positive
+ * duration
+ * @return the awaitable itself
+ * @throws InterruptedException if the wait call was interrupted
+ * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready
+ * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]]
*/
@throws(classOf[TimeoutException])
+ @throws(classOf[InterruptedException])
def ready[T](awaitable: Awaitable[T], atMost: Duration): awaitable.type =
blocking(awaitable.ready(atMost)(AwaitPermission))
/**
+ * Await and return the result of this Awaitable, which is either of type T or a thrown exception (any Throwable).
* 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`
+ * @param awaitable
+ * the `Awaitable` on which `result` is to be called
+ * @param atMost
+ * maximum wait time, which may be negative (no waiting is done),
+ * [[Duration.Inf]] for unbounded waiting, or a finite positive
+ * duration
+ * @return the value if the Awaitable was successful within the specific maximum wait time
+ * @throws InterruptedException if the wait call was interrupted
+ * @throws TimeoutException if after waiting for the specified time this Awaitable is still not ready
+ * @throws IllegalArgumentException if `atMost` is [[Duration.Undefined]]
*/
@throws(classOf[Exception])
def result[T](awaitable: Awaitable[T], atMost: Duration): T =
diff --git a/src/library/scala/concurrent/util/Duration.scala b/src/library/scala/concurrent/util/Duration.scala
index bf0f3006f1..1a4bc1323d 100644
--- a/src/library/scala/concurrent/util/Duration.scala
+++ b/src/library/scala/concurrent/util/Duration.scala
@@ -10,36 +10,121 @@ package scala.concurrent.util
import java.util.concurrent.TimeUnit
import TimeUnit._
-import java.lang.{ Double => JDouble }
+import java.lang.{ Double => JDouble, Long => JLong }
import language.implicitConversions
-case class Deadline private (time: Duration) {
- def +(other: Duration): Deadline = copy(time = time + other)
- def -(other: Duration): Deadline = copy(time = time - other)
- def -(other: Deadline): Duration = time - other.time
- def timeLeft: Duration = this - Deadline.now
+/**
+ * This class stores a deadline, as obtained via `Deadline.now` or the
+ * duration DSL:
+ *
+ * {{{
+ * import scala.concurrent.util.duration._
+ * 3.seconds.fromNow
+ * }}}
+ *
+ * Its main purpose is to manage repeated attempts to achieve something (like
+ * awaiting a condition) by offering the methods `hasTimeLeft` and `timeLeft`. All
+ * durations are measured according to `System.nanoTime` aka wall-time; this
+ * does not take into account changes to the system clock (such as leap
+ * seconds).
+ */
+case class Deadline private (time: FiniteDuration) extends Ordered[Deadline] {
+ /**
+ * Return a deadline advanced (i.e. moved into the future) by the given duration.
+ */
+ def +(other: FiniteDuration): Deadline = copy(time = time + other)
+ /**
+ * Return a deadline moved backwards (i.e. towards the past) by the given duration.
+ */
+ def -(other: FiniteDuration): Deadline = copy(time = time - other)
+ /**
+ * Calculate time difference between this and the other deadline, where the result is directed (i.e. may be negative).
+ */
+ def -(other: Deadline): FiniteDuration = time - other.time
+ /**
+ * Calculate time difference between this duration and now; the result is negative if the deadline has passed.
+ *
+ * '''''Note that on some systems this operation is costly because it entails a system call.'''''
+ * Check `System.nanoTime` for your platform.
+ */
+ def timeLeft: FiniteDuration = this - Deadline.now
+ /**
+ * Determine whether the deadline still lies in the future at the point where this method is called.
+ *
+ * '''''Note that on some systems this operation is costly because it entails a system call.'''''
+ * Check `System.nanoTime` for your platform.
+ */
def hasTimeLeft(): Boolean = !isOverdue()
+ /**
+ * Determine whether the deadline lies in the past at the point where this method is called.
+ *
+ * '''''Note that on some systems this operation is costly because it entails a system call.'''''
+ * Check `System.nanoTime` for your platform.
+ */
def isOverdue(): Boolean = (time.toNanos - System.nanoTime()) < 0
+ /**
+ * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration.
+ */
+ def compare(other: Deadline) = time compare other.time
}
object Deadline {
+ /**
+ * Construct a deadline due exactly at the point where this method is called. Useful for then
+ * advancing it to obtain a future deadline, or for sampling the current time exactly once and
+ * then comparing it to multiple deadlines (using subtraction).
+ */
def now: Deadline = Deadline(Duration(System.nanoTime, NANOSECONDS))
+
+ /**
+ * The natural ordering for deadline is determined by the natural order of the underlying (finite) duration.
+ */
+ implicit object DeadlineIsOrdered extends Ordering[Deadline] {
+ def compare(a: Deadline, b: Deadline) = a compare b
+ }
}
-// TODO: "Inf", "PlusInf", "MinusInf", where did these names come from?
-// TODO: Duration.create(n, DAYS) == Duration(Long.MaxValue, NANOSECONDS) forall (n: Double) >= 106752d
object Duration {
+ /**
+ * This implicit conversion allows the use of a Deadline in place of a Duration, which will
+ * insert the time left until the deadline in its place.
+ */
implicit def timeLeft(implicit d: Deadline): Duration = d.timeLeft
- def apply(length: Double, unit: TimeUnit): FiniteDuration = fromNanos(unit.toNanos(1) * length)
- def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit)
- def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit))
+ /**
+ * Construct a Duration from the given length and unit. Observe that nanosecond precision may be lost if
+ *
+ * - the unit is NANOSECONDS
+ * - and the length has an absolute value greater than 2^53
+ *
+ * Infinite inputs (and NaN) are converted into [[Duration.Inf]], [[Duration.MinusInf]] and [[Duration.Undefined]], respectively.
+ *
+ * @throws IllegalArgumentException if the length was finite but the resulting duration cannot be expressed as a [[FiniteDuration]]
+ */
+ def apply(length: Double, unit: TimeUnit): Duration = fromNanos(unit.toNanos(1) * length)
+ /**
+ * Construct a finite duration from the given length and time unit. The unit given is retained
+ * throughout calculations as long as possible, so that it can be retrieved later.
+ */
+ def apply(length: Long, unit: TimeUnit): FiniteDuration = new FiniteDuration(length, unit)
+ /**
+ * Construct a finite duration from the given length and time unit, where the latter is
+ * looked up in a list of string representation. Valid choices are:
+ *
+ * `d, day, h, hour, min, minute, s, sec, second, ms, milli, millisecond, µs, micro, microsecond, ns, nano, nanosecond`
+ * and their pluralized forms (for every but the first mentioned form of each unit, i.e. no "ds", but "days").
+ */
+ def apply(length: Long, unit: String): FiniteDuration = new FiniteDuration(length, Duration.timeUnit(unit))
+
+ // Double stores 52 bits mantissa, but there is an implied '1' in front, making the limit 2^53
+ final val maxPreciseDouble = 9007199254740992d
/**
* Parse String into Duration. Format is `"<length><unit>"`, where
* whitespace is allowed before, between and after the parts. Infinities are
* designated by `"Inf"`, `"PlusInf"`, `"+Inf"` and `"-Inf"` or `"MinusInf"`.
- * Throws exception if format is not parseable.
+ *
+ * @throws NumberFormatException if format is not parseable
*/
def apply(s: String): Duration = {
val s1: String = s filterNot (_.isWhitespace)
@@ -48,21 +133,24 @@ object Duration {
case "MinusInf" | "-Inf" => MinusInf
case _ =>
val unitName = s1.reverse takeWhile (_.isLetter) reverse;
- def length = JDouble.parseDouble(s1 dropRight unitName.length)
timeUnit get unitName match {
- case Some(unit) => Duration(length, unit)
- case _ => sys.error("format error " + s)
+ case Some(unit) =>
+ val valueStr = s1 dropRight unitName.length
+ val valueD = JDouble.parseDouble(valueStr)
+ if (valueD >= -maxPreciseDouble && valueD <= maxPreciseDouble) Duration(valueD, unit)
+ else Duration(JLong.parseLong(valueStr), unit)
+ case _ => throw new NumberFormatException("format error " + s)
}
}
}
// "ms milli millisecond" -> List("ms", "milli", "millis", "millisecond", "milliseconds")
- private def words(s: String) = (s.trim split "\\s+").toList
- private def expandLabels(labels: String): List[String] = {
+ private[this] def words(s: String) = (s.trim split "\\s+").toList
+ private[this] def expandLabels(labels: String): List[String] = {
val hd :: rest = words(labels)
hd :: rest.flatMap(s => List(s, s + "s"))
}
- private val timeUnitLabels = List(
+ private[this] val timeUnitLabels = List(
DAYS -> "d day",
HOURS -> "h hour",
MINUTES -> "min minute",
@@ -80,124 +168,218 @@ object Duration {
protected[util] val timeUnit: Map[String, TimeUnit] =
timeUnitLabels flatMap { case (unit, names) => expandLabels(names) map (_ -> unit) } toMap
+ /**
+ * Extract length and time unit out of a string, where the format must match the description for [[Duration$.apply(String):Duration apply(String)]].
+ * The extractor will not match for malformed strings or non-finite durations.
+ */
def unapply(s: String): Option[(Long, TimeUnit)] =
( try Some(apply(s)) catch { case _: RuntimeException => None } ) flatMap unapply
+ /**
+ * Extract length and time unit out of a duration, if it is finite.
+ */
def unapply(d: Duration): Option[(Long, TimeUnit)] =
if (d.isFinite) Some((d.length, d.unit)) else None
- def fromNanos(nanos: Double): FiniteDuration =
- fromNanos((nanos + 0.5).toLong)
+ /**
+ * Construct a possibly infinite or undefined Duration from the given number of nanoseconds.
+ *
+ * - `Double.PositiveInfinity` is mapped to [[Duration.Inf]]
+ * - `Double.NegativeInfinity` is mapped to [[Duration.MinusInf]]
+ * - `Double.NaN` is mapped to [[Duration.Undefined]]
+ * - `-0d` is mapped to [[Duration.Zero]] (exactly like `0d`)
+ *
+ * The semantics of the resulting Duration objects matches the semantics of their Double
+ * counterparts with respect to arithmetic operations.
+ *
+ * @throws IllegalArgumentException if the length was finite but the resulting duration cannot be expressed as a [[FiniteDuration]]
+ */
+ def fromNanos(nanos: Double): Duration = {
+ if (nanos.isInfinite)
+ if (nanos > 0) Inf else MinusInf
+ else if (nanos.isNaN)
+ Undefined
+ else if (nanos > Long.MaxValue || nanos < Long.MinValue)
+ throw new IllegalArgumentException("trying to construct too large duration with " + nanos + "ns")
+ else
+ fromNanos((nanos + 0.5).toLong)
+ }
+
+ private[this] final val µs_per_ns = 1000L
+ private[this] final val ms_per_ns = µs_per_ns * 1000
+ private[this] final val s_per_ns = ms_per_ns * 1000
+ private[this] final val min_per_ns = s_per_ns * 60
+ private[this] final val h_per_ns = min_per_ns * 60
+ private[this] final val d_per_ns = h_per_ns * 24
+ /**
+ * Construct a finite duration from the given number of nanoseconds. The
+ * result will have the coarsest possible time unit which can exactly express
+ * this duration.
+ *
+ * @throws IllegalArgumentException for `Long.MinValue` since that would lead to inconsistent behavior afterwards (cannot be negated)
+ */
def fromNanos(nanos: Long): FiniteDuration = {
- if (nanos % 86400000000000L == 0) {
- Duration(nanos / 86400000000000L, DAYS)
- } else if (nanos % 3600000000000L == 0) {
- Duration(nanos / 3600000000000L, HOURS)
- } else if (nanos % 60000000000L == 0) {
- Duration(nanos / 60000000000L, MINUTES)
- } else if (nanos % 1000000000L == 0) {
- Duration(nanos / 1000000000L, SECONDS)
- } else if (nanos % 1000000L == 0) {
- Duration(nanos / 1000000L, MILLISECONDS)
- } else if (nanos % 1000L == 0) {
- Duration(nanos / 1000L, MICROSECONDS)
- } else {
- Duration(nanos, NANOSECONDS)
- }
+ if (nanos % d_per_ns == 0) Duration(nanos / d_per_ns, DAYS)
+ else if (nanos % h_per_ns == 0) Duration(nanos / h_per_ns, HOURS)
+ else if (nanos % min_per_ns == 0) Duration(nanos / min_per_ns, MINUTES)
+ else if (nanos % s_per_ns == 0) Duration(nanos / s_per_ns, SECONDS)
+ else if (nanos % ms_per_ns == 0) Duration(nanos / ms_per_ns, MILLISECONDS)
+ else if (nanos % µs_per_ns == 0) Duration(nanos / µs_per_ns, MICROSECONDS)
+ else Duration(nanos, NANOSECONDS)
}
/**
- * Parse TimeUnit from string representation.
+ * Preconstructed value of `0.days`.
*/
+ // unit as coarse as possible to keep (_ + Zero) sane unit-wise
+ val Zero: FiniteDuration = new FiniteDuration(0, DAYS)
- val Zero: FiniteDuration = new FiniteDuration(0, NANOSECONDS)
-
- object Undefined extends Infinite {
- private def fail(what: String) = throw new IllegalArgumentException(s"cannot $what Undefined duration")
-
+ /**
+ * The Undefined value corresponds closely to Double.NaN:
+ *
+ * - it is the result of otherwise invalid operations
+ * - it does not equal itself (according to `equals()`)
+ * - it compares greater than any other Duration apart from itself (for which `compare` returns 0)
+ *
+ * The particular comparison semantics mirror those of Double.NaN.
+ *
+ * '''''Use `eq` when checking an input of a method against this value.'''''
+ */
+ val Undefined: Infinite = new Infinite {
override def toString = "Duration.Undefined"
- override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
- override def +(other: Duration): Duration = fail("add")
- override def -(other: Duration): Duration = fail("subtract")
- override def *(factor: Double): Duration = fail("multiply")
- override def /(factor: Double): Duration = fail("divide")
- override def /(other: Duration): Double = fail("divide")
- def compare(other: Duration) = fail("compare")
- def unary_- : Duration = fail("negate")
+ override def equals(other: Any) = false
+ override def +(other: Duration): Duration = this
+ override def -(other: Duration): Duration = this
+ override def *(factor: Double): Duration = this
+ override def /(factor: Double): Duration = this
+ override def /(other: Duration): Double = Double.NaN
+ def compare(other: Duration) = if (other eq this) 0 else 1
+ def unary_- : Duration = this
+ def toUnit(unit: TimeUnit): Double = Double.NaN
}
sealed abstract class Infinite extends Duration {
def +(other: Duration): Duration = other match {
- case x: Infinite if x ne this => throw new IllegalArgumentException("illegal addition of infinities")
+ case x if x eq Undefined => Undefined
+ case x: Infinite if x ne this => Undefined
+ case _ => this
+ }
+ def -(other: Duration): Duration = other match {
+ case x if x eq Undefined => Undefined
+ case x: Infinite if x eq this => Undefined
case _ => this
}
- // Is this really intended to throw if the argument is "this" but otherwise return this?
- def -(other: Duration): Duration =
- if (other ne this) this
- else throw new IllegalArgumentException("illegal subtraction of infinities")
- def *(factor: Double): Duration = this
- def /(factor: Double): Duration = this
+ def *(factor: Double): Duration =
+ if (factor == 0d || factor.isNaN) Undefined
+ else if (factor < 0d) -this
+ else this
+ def /(factor: Double): Duration =
+ if (factor.isNaN || factor.isInfinite) Undefined
+ else if ((factor compare 0d) < 0) -this
+ else this
def /(other: Duration): Double = other match {
- case _: Infinite => throw new IllegalArgumentException("illegal division of infinities")
- // maybe questionable but pragmatic: Inf / 0 => Inf
+ case _: Infinite => Double.NaN
case x => Double.PositiveInfinity * (if ((this > Zero) ^ (other >= Zero)) -1 else 1)
}
final def isFinite() = false
- private def fail(what: String) = throw new IllegalArgumentException(s"$what not allowed on infinite Durations")
- def length: Long = fail("length")
- def toNanos: Long = fail("toNanos")
- def toMicros: Long = fail("toMicros")
- def toMillis: Long = fail("toMillis")
- def toSeconds: Long = fail("toSeconds")
- def toMinutes: Long = fail("toMinutes")
- def toHours: Long = fail("toHours")
- def toDays: Long = fail("toDays")
-
- def unit: TimeUnit = fail("unit")
- def toUnit(unit: TimeUnit): Double = fail("toUnit")
+ private[this] def fail(what: String) = throw new IllegalArgumentException(s"$what not allowed on infinite Durations")
+ final def length: Long = fail("length")
+ final def unit: TimeUnit = fail("unit")
+ final def toNanos: Long = fail("toNanos")
+ final def toMicros: Long = fail("toMicros")
+ final def toMillis: Long = fail("toMillis")
+ final def toSeconds: Long = fail("toSeconds")
+ final def toMinutes: Long = fail("toMinutes")
+ final def toHours: Long = fail("toHours")
+ final def toDays: Long = fail("toDays")
}
/**
- * Infinite duration: greater than any other and not equal to any other,
- * including itself.
+ * Infinite duration: greater than any other (apart from Undefined) and not equal to any other
+ * but itself. This value closely corresponds to Double.PositiveInfinity,
+ * matching its semantics in arithmetic operations.
*/
val Inf: Infinite = new Infinite {
override def toString = "Duration.Inf"
- def compare(other: Duration) = if (other eq this) 0 else 1
+ def compare(other: Duration) = other match {
+ case x if x eq Undefined => -1 // Undefined != Undefined
+ case x if x eq this => 0 // `case Inf` will include null checks in the byte code
+ case _ => 1
+ }
def unary_- : Duration = MinusInf
+ def toUnit(unit: TimeUnit): Double = Double.PositiveInfinity
}
/**
- * Infinite negative duration: lesser than any other and not equal to any other,
- * including itself.
+ * Infinite duration: less than any other and not equal to any other
+ * but itself. This value closely corresponds to Double.NegativeInfinity,
+ * matching its semantics in arithmetic operations.
*/
val MinusInf: Infinite = new Infinite {
override def toString = "Duration.MinusInf"
def compare(other: Duration) = if (other eq this) 0 else -1
def unary_- : Duration = Inf
+ def toUnit(unit: TimeUnit): Double = Double.NegativeInfinity
}
// Java Factories
- def create(length: Long, unit: TimeUnit): FiniteDuration = apply(length, unit)
- def create(length: Double, unit: TimeUnit): FiniteDuration = apply(length, unit)
- def create(length: Long, unit: String): FiniteDuration = apply(length, unit)
- def create(s: String): Duration = apply(s)
+ /**
+ * Construct a finite duration from the given length and time unit. The unit given is retained
+ * throughout calculations as long as possible, so that it can be retrieved later.
+ */
+ def create(length: Long, unit: TimeUnit): FiniteDuration = apply(length, unit)
+ /**
+ * Construct a Duration from the given length and unit. Observe that nanosecond precision may be lost if
+ *
+ * - the unit is NANOSECONDS
+ * - and the length has an absolute value greater than 2^53
+ *
+ * Infinite inputs (and NaN) are converted into [[Duration.Inf]], [[Duration.MinusInf]] and [[Duration.Undefined]], respectively.
+ *
+ * @throws IllegalArgumentException if the length was finite but the resulting duration cannot be expressed as a [[FiniteDuration]]
+ */
+ def create(length: Double, unit: TimeUnit): Duration = apply(length, unit)
+ /**
+ * Construct a finite duration from the given length and time unit, where the latter is
+ * looked up in a list of string representation. Valid choices are:
+ *
+ * `d, day, h, hour, min, minute, s, sec, second, ms, milli, millisecond, µs, micro, microsecond, ns, nano, nanosecond`
+ * and their pluralized forms (for every but the first mentioned form of each unit, i.e. no "ds", but "days").
+ */
+ def create(length: Long, unit: String): FiniteDuration = apply(length, unit)
+ /**
+ * Parse String into Duration. Format is `"<length><unit>"`, where
+ * whitespace is allowed before, between and after the parts. Infinities are
+ * designated by `"Inf"`, `"PlusInf"`, `"+Inf"` and `"-Inf"` or `"MinusInf"`.
+ *
+ * @throws NumberFormatException if format is not parseable
+ */
+ def create(s: String): Duration = apply(s)
+
+ /**
+ * The natural ordering of durations matches the natural ordering for Double, including non-finite values.
+ */
implicit object DurationIsOrdered extends Ordering[Duration] {
def compare(a: Duration, b: Duration) = a compare b
}
}
/**
- * Utility for working with java.util.concurrent.TimeUnit durations.
+ * <h2>Utility for working with java.util.concurrent.TimeUnit durations.</h2>
+ *
+ * '''''This class is not meant as a general purpose representation of time, it is
+ * optimized for the needs of `scala.concurrent`.'''''
+ *
+ * <h2>Basic Usage</h2>
*
* <p/>
* Examples:
- * <pre>
+ * {{{
* import scala.concurrent.util.Duration
* import java.util.concurrent.TimeUnit
*
@@ -207,57 +389,212 @@ object Duration {
* duration.toNanos
* duration < 1.second
* duration <= Duration.Inf
- * </pre>
+ * }}}
+ *
+ * '''''Invoking inexpressible conversions (like calling `toSeconds` on an infinite duration) will throw an IllegalArgumentException.'''''
*
* <p/>
* Implicits are also provided for Int, Long and Double. Example usage:
- * <pre>
+ * {{{
* import scala.concurrent.util.Duration._
*
* val duration = 100 millis
- * </pre>
+ * }}}
+ *
+ * '''''The DSL provided by the implicit conversions always allows construction of finite durations, even for infinite Double inputs; use Duration.Inf instead.'''''
*
* Extractors, parsing and arithmetic are also included:
- * <pre>
+ * {{{
* val d = Duration("1.2 µs")
* val Duration(length, unit) = 5 millis
* val d2 = d * 2.5
* val d3 = d2 + 1.millisecond
- * </pre>
+ * }}}
+ *
+ * <h2>Handling of Time Units</h2>
+ *
+ * Calculations performed on finite durations always retain the more precise unit of either operand, no matter
+ * whether a coarser unit would be able to exactly express the same duration. This means that Duration can be
+ * used as a lossless container for a (length, unit) pair if it is constructed using the corresponding methods
+ * and no arithmetic is performed on it; adding/subtracting durations should in that case be done with care.
+ *
+ * <h2>Correspondence to Double Semantics</h2>
+ *
+ * The semantics of arithmetic operations on Duration are two-fold:
+ *
+ * - exact addition/subtraction with nanosecond resolution for finite durations, independent of the summands' magnitude
+ * - isomorphic to `java.lang.Double` when it comes to infinite or undefined values
+ *
+ * The conversion between Duration and Double is done using [[Duration.toUnit]] (with unit NANOSECONDS)
+ * and [[Duration$.fromNanos(Double):Duration Duration.fromNanos(Double)]].
+ *
+ * <h2>Ordering</h2>
+ *
+ * The default ordering is consistent with the ordering of Double numbers, which means that Undefined is
+ * considered greater than all other durations, including [[Duration.Inf]].
+ *
+ * @define exc @throws IllegalArgumentException when invoked on a non-finite duration
+ *
+ * @define ovf @throws IllegalArgumentException in case of a finite overflow: the range of a finite duration is +-(2^63-1)ns, and no conversion to infinite durations takes place.
*/
-abstract class Duration extends Serializable with Ordered[Duration] {
+sealed abstract class Duration extends Serializable with Ordered[Duration] {
+ /**
+ * Obtain the length of this Duration measured in the unit obtained by the `unit` method.
+ *
+ * $exc
+ */
def length: Long
+ /**
+ * Obtain the time unit in which the length of this duration is measured.
+ *
+ * $exc
+ */
def unit: TimeUnit
+ /**
+ * Return the length of this duration measured in whole nanoseconds, rounding towards zero.
+ *
+ * $exc
+ */
def toNanos: Long
+ /**
+ * Return the length of this duration measured in whole microseconds, rounding towards zero.
+ *
+ * $exc
+ */
def toMicros: Long
+ /**
+ * Return the length of this duration measured in whole milliseconds, rounding towards zero.
+ *
+ * $exc
+ */
def toMillis: Long
+ /**
+ * Return the length of this duration measured in whole seconds, rounding towards zero.
+ *
+ * $exc
+ */
def toSeconds: Long
+ /**
+ * Return the length of this duration measured in whole minutes, rounding towards zero.
+ *
+ * $exc
+ */
def toMinutes: Long
+ /**
+ * Return the length of this duration measured in whole hours, rounding towards zero.
+ *
+ * $exc
+ */
def toHours: Long
+ /**
+ * Return the length of this duration measured in whole days, rounding towards zero.
+ *
+ * $exc
+ */
def toDays: Long
+ /**
+ * Return the number of nanoseconds as floating point number, scaled down to the given unit.
+ * The result may not precisely represent this duration due to the Double datatype's inherent
+ * limitations (mantissa size effectively 53 bits). Non-finite durations are represented as
+ * - [[Duration.Undefined]] is mapped to Double.NaN
+ * - [[Duration.Inf]] is mapped to Double.PositiveInfinity
+ * - [[Duration.MinusInf]] is mapped to Double.NegativeInfinity
+ */
def toUnit(unit: TimeUnit): Double
+ /**
+ * Return the sum of that duration and this. When involving non-finite summands the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def +(other: Duration): Duration
+ /**
+ * Return the difference of that duration and this. When involving non-finite summands the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def -(other: Duration): Duration
+ /**
+ * Return this duration multiplied by the scalar factor. When involving non-finite factors the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def *(factor: Double): Duration
+ /**
+ * Return this duration divided by the scalar factor. When involving non-finite factors the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def /(factor: Double): Duration
+ /**
+ * Return the quotient of this and that duration as floating-point number. The semantics are
+ * determined by Double as if calculating the quotient of the nanosecond lengths of both factors.
+ */
def /(other: Duration): Double
+ /**
+ * Negate this duration. The only two values which are mapped to themselves are [[Duration.Zero]] and [[Duration.Undefined]].
+ */
def unary_- : Duration
+ /**
+ * This method returns whether this duration is finite, which is not the same as
+ * `!isInfinite` for Double because this method also returns `false` for [[Duration.Undefined]].
+ */
def isFinite(): Boolean
+ /**
+ * Return the smaller of this and that duration as determined by the natural ordering.
+ */
def min(other: Duration): Duration = if (this < other) this else other
+ /**
+ * Return the larger of this and that duration as determined by the natural ordering.
+ */
def max(other: Duration): Duration = if (this > other) this else other
- def fromNow: Deadline = Deadline.now + this
// Java API
+
+ /**
+ * Return this duration divided by the scalar factor. When involving non-finite factors the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def div(factor: Double) = this / factor
+ /**
+ * Return the quotient of this and that duration as floating-point number. The semantics are
+ * determined by Double as if calculating the quotient of the nanosecond lengths of both factors.
+ */
def div(other: Duration) = this / other
def gt(other: Duration) = this > other
def gteq(other: Duration) = this >= other
def lt(other: Duration) = this < other
def lteq(other: Duration) = this <= other
+ /**
+ * Return the difference of that duration and this. When involving non-finite summands the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def minus(other: Duration) = this - other
+ /**
+ * Return this duration multiplied by the scalar factor. When involving non-finite factors the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def mul(factor: Double) = this * factor
+ /**
+ * Negate this duration. The only two values which are mapped to themselves are [[Duration.Zero]] and [[Duration.Undefined]].
+ */
def neg() = -this
+ /**
+ * Return the sum of that duration and this. When involving non-finite summands the semantics match those
+ * of Double.
+ *
+ * $ovf
+ */
def plus(other: Duration) = this + other
}
@@ -268,11 +605,43 @@ object FiniteDuration {
def apply(length: Long, unit: TimeUnit) = new FiniteDuration(length, unit)
def apply(length: Long, unit: String) = new FiniteDuration(length, Duration.timeUnit(unit))
+
+ // limit on abs. value of durations in their units
+ private final val max_ns = Long.MaxValue
+ private final val max_µs = max_ns / 1000
+ private final val max_ms = max_µs / 1000
+ private final val max_s = max_ms / 1000
+ private final val max_min= max_s / 60
+ private final val max_h = max_min / 60
+ private final val max_d = max_h / 24
}
-class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
+/**
+ * This class represents a finite duration. Its addition and subtraction operators are overloaded to retain
+ * this guarantee statically. The range of this class is limited to +-(2^63-1)ns, which is roughly 292 years.
+ */
+final class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
+ import FiniteDuration._
import Duration._
+ private[this] def bounded(max: Long) = -max <= length && length <= max
+
+ require(unit match {
+ /*
+ * enforce the 2^63-1 ns limit, must be pos/neg symmetrical because of unary_-
+ */
+ case NANOSECONDS ⇒ bounded(max_ns)
+ case MICROSECONDS ⇒ bounded(max_µs)
+ case MILLISECONDS ⇒ bounded(max_ms)
+ case SECONDS ⇒ bounded(max_s)
+ case MINUTES ⇒ bounded(max_min)
+ case HOURS ⇒ bounded(max_h)
+ case DAYS ⇒ bounded(max_d)
+ case _ ⇒
+ val v = DAYS.convert(length, unit)
+ -max_d <= v && v <= max_d
+ }, "Duration is limited to +-(2^63-1)ns (ca. 292 years)")
+
def toNanos = unit.toNanos(length)
def toMicros = unit.toMicros(length)
def toMillis = unit.toMillis(length)
@@ -282,27 +651,68 @@ class FiniteDuration(val length: Long, val unit: TimeUnit) extends Duration {
def toDays = unit.toDays(length)
def toUnit(u: TimeUnit) = toNanos.toDouble / NANOSECONDS.convert(1, u)
- private def unitString = timeUnitName(unit) + ( if (length == 1) "" else "s" )
+ /**
+ * Construct a [[Deadline]] from this duration by adding it to the current instant `Deadline.now`.
+ */
+ def fromNow: Deadline = Deadline.now + this
+
+ private[this] def unitString = timeUnitName(unit) + ( if (length == 1) "" else "s" )
override def toString = "" + length + " " + unitString
def compare(other: Duration) = other match {
case x: FiniteDuration => toNanos compare x.toNanos
case _ => -(other compare this)
}
+
+ // see https://www.securecoding.cert.org/confluence/display/java/NUM00-J.+Detect+or+prevent+integer+overflow
+ private[this] def safeAdd(a: Long, b: Long): Long = {
+ if ((b > 0) && (a > Long.MaxValue - b) ||
+ (b < 0) && (a < Long.MinValue - b)) throw new IllegalArgumentException("integer overflow")
+ a + b
+ }
+ private[this] def add(otherLength: Long, otherUnit: TimeUnit): FiniteDuration = {
+ val commonUnit = if (otherUnit.convert(1, unit) == 0) unit else otherUnit
+ val totalLength = safeAdd(commonUnit.convert(length, unit), commonUnit.convert(otherLength, otherUnit))
+ new FiniteDuration(totalLength, commonUnit)
+ }
+
def +(other: Duration) = other match {
- case x: FiniteDuration => fromNanos(toNanos + x.toNanos)
+ case x: FiniteDuration => add(x.length, x.unit)
case _ => other
}
def -(other: Duration) = other match {
- case x: FiniteDuration => fromNanos(toNanos - x.toNanos)
+ case x: FiniteDuration => add(-x.length, x.unit)
case _ => other
}
- def *(factor: Double) = fromNanos(toNanos.toDouble * factor)
-
- def /(factor: Double) = fromNanos(toNanos.toDouble / factor)
-
- def /(other: Duration) = if (other.isFinite) toNanos.toDouble / other.toNanos else 0
+ def *(factor: Double) =
+ if (!factor.isInfinite) fromNanos(toNanos * factor)
+ else if (factor.isNaN) Undefined
+ else if ((factor > 0) ^ (this < Zero)) Inf
+ else MinusInf
+
+ def /(factor: Double) =
+ if (!factor.isInfinite) fromNanos(toNanos / factor)
+ else if (factor.isNaN) Undefined
+ else Zero
+
+ // if this is made a constant, then scalac will elide the conditional and always return +0.0, SI-6331
+ private[this] def minusZero = -0d
+ def /(other: Duration): Double =
+ if (other.isFinite) toNanos.toDouble / other.toNanos
+ else if (other eq Undefined) Double.NaN
+ else if ((length < 0) ^ (other > Zero)) 0d
+ else minusZero
+
+ // overridden methods taking FiniteDurations, so that you can calculate while statically staying finite
+ def +(other: FiniteDuration) = add(other.length, other.unit)
+ def -(other: FiniteDuration) = add(-other.length, other.unit)
+ def plus(other: FiniteDuration) = this + other
+ def minus(other: FiniteDuration) = this - other
+ override def div(factor: Double) = this / factor
+ override def mul(factor: Double) = this * factor
+ def min(other: FiniteDuration) = if (this < other) this else other
+ def max(other: FiniteDuration) = if (this > other) this else other
def unary_- = Duration(-length, unit)
@@ -383,5 +793,9 @@ final class DurationLong(val n: Long) extends AnyVal with DurationConversions {
}
final class DurationDouble(val d: Double) extends AnyVal with DurationConversions {
- override protected def durationIn(unit: TimeUnit): FiniteDuration = Duration(d, unit)
+ override protected def durationIn(unit: TimeUnit): FiniteDuration =
+ Duration(d, unit) match {
+ case f: FiniteDuration => f
+ case _ => throw new IllegalArgumentException("Duration DSL not applicable to " + d)
+ }
}
diff --git a/src/library/scala/deprecatedInheritance.scala b/src/library/scala/deprecatedInheritance.scala
new file mode 100644
index 0000000000..ca1b586223
--- /dev/null
+++ b/src/library/scala/deprecatedInheritance.scala
@@ -0,0 +1,22 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2012, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+/** An annotation that designates that inheriting from a class is deprecated.
+ *
+ * This is usually done to warn about a non-final class being made final in a future version.
+ * Sub-classing such a class then generates a warning.
+ *
+ * @param message the message to print during compilation if the class was sub-classed
+ * @param since a string identifying the first version in which inheritance was deprecated
+ * @since 2.10
+ * @see [[scala.deprecatedOverriding]]
+ */
+private[scala] // for now, this needs to be generalized to communicate other modifier deltas
+class deprecatedInheritance(message: String = "", since: String = "") extends annotation.StaticAnnotation
diff --git a/src/library/scala/deprecatedOverriding.scala b/src/library/scala/deprecatedOverriding.scala
new file mode 100644
index 0000000000..566cb59431
--- /dev/null
+++ b/src/library/scala/deprecatedOverriding.scala
@@ -0,0 +1,21 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2012, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala
+
+/** An annotation that designates that overriding a member is deprecated.
+ *
+ * Overriding such a member in a sub-class then generates a warning.
+ *
+ * @param message the message to print during compilation if the member was overridden
+ * @param since a string identifying the first version in which overriding was deprecated
+ * @since 2.10
+ * @see [[scala.deprecatedInheritance]]
+ */
+private[scala] // for the same reasons as deprecatedInheritance
+class deprecatedOverriding(message: String = "", since: String = "") extends annotation.StaticAnnotation
diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala
index 8669b2e2e8..a475d663f4 100644
--- a/src/library/scala/math/BigDecimal.scala
+++ b/src/library/scala/math/BigDecimal.scala
@@ -159,6 +159,7 @@ object BigDecimal {
* @author Stephane Micheloud
* @version 1.0
*/
+@deprecatedInheritance("This class will me made final.", "2.10.0")
class BigDecimal(
val bigDecimal: BigDec,
val mc: MathContext)
diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala
index 09e8ae2026..e354117e14 100644
--- a/src/library/scala/math/BigInt.scala
+++ b/src/library/scala/math/BigInt.scala
@@ -114,6 +114,7 @@ object BigInt {
* @author Martin Odersky
* @version 1.0, 15/07/2003
*/
+@deprecatedInheritance("This class will me made final.", "2.10.0")
class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericConversions with Serializable {
/** Returns the hash code for this BigInt. */
override def hashCode(): Int =
diff --git a/src/partest/scala/tools/partest/TestUtil.scala b/src/partest/scala/tools/partest/TestUtil.scala
index b86a8e2c7f..146e6fc69f 100644
--- a/src/partest/scala/tools/partest/TestUtil.scala
+++ b/src/partest/scala/tools/partest/TestUtil.scala
@@ -1,5 +1,7 @@
package scala.tools.partest
+import reflect.{ classTag, ClassTag }
+
trait TestUtil {
/** Given function and block of code, evaluates code block,
* calls function with nanoseconds elapsed, and returns block result.
@@ -29,8 +31,16 @@ trait TestUtil {
assert(mult <= acceptableMultiple, "Performance difference too great: multiple = " + mult)
}
+
+ def intercept[T <: Exception : ClassTag](code: => Unit): Unit =
+ try {
+ code
+ assert(false, "did not throw " + classTag[T])
+ } catch {
+ case ex: Exception if classTag[T].runtimeClass isInstance ex =>
+ }
}
object TestUtil extends TestUtil {
-} \ No newline at end of file
+}
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 4e44b76607..15b74058b7 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -941,6 +941,8 @@ trait Definitions extends api.StandardDefinitions {
lazy val CloneableAttr = requiredClass[scala.annotation.cloneable]
lazy val DeprecatedAttr = requiredClass[scala.deprecated]
lazy val DeprecatedNameAttr = requiredClass[scala.deprecatedName]
+ lazy val DeprecatedInheritanceAttr = requiredClass[scala.deprecatedInheritance]
+ lazy val DeprecatedOverridingAttr = requiredClass[scala.deprecatedOverriding]
lazy val NativeAttr = requiredClass[scala.native]
lazy val RemoteAttr = requiredClass[scala.remote]
lazy val ScalaInlineClass = requiredClass[scala.inline]
diff --git a/src/reflect/scala/reflect/internal/Names.scala b/src/reflect/scala/reflect/internal/Names.scala
index 2fdf27d847..3f85db0f54 100644
--- a/src/reflect/scala/reflect/internal/Names.scala
+++ b/src/reflect/scala/reflect/internal/Names.scala
@@ -149,11 +149,15 @@ trait Names extends api.Names with LowPriorityNames {
type ThisNameType >: Null <: Name
protected[this] def thisName: ThisNameType
+ // Note that "Name with ThisNameType" should be redundant
+ // because ThisNameType <: Name, but due to SI-6161 the
+ // compile loses track of this fact.
+
/** Index into name table */
def start: Int = index
/** The next name in the same hash bucket. */
- def next: ThisNameType
+ def next: Name with ThisNameType
/** The length of this name. */
final def length: Int = len
@@ -169,13 +173,13 @@ trait Names extends api.Names with LowPriorityNames {
def bothNames: List[Name] = List(toTermName, toTypeName)
/** Return the subname with characters from from to to-1. */
- def subName(from: Int, to: Int): ThisNameType
+ def subName(from: Int, to: Int): Name with ThisNameType
/** Return a new name of the same variety. */
- def newName(str: String): ThisNameType
+ def newName(str: String): Name with ThisNameType
/** Return a new name based on string transformation. */
- def mapName(f: String => String): ThisNameType = newName(f(toString))
+ def mapName(f: String => String): Name with ThisNameType = newName(f(toString))
/** Copy bytes of this name to buffer cs, starting at position `offset`. */
final def copyChars(cs: Array[Char], offset: Int) =
diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala
index 4100e97cdd..2424e75949 100644
--- a/src/reflect/scala/reflect/internal/SymbolTable.scala
+++ b/src/reflect/scala/reflect/internal/SymbolTable.scala
@@ -47,6 +47,8 @@ abstract class SymbolTable extends macros.Universe
def globalError(msg: String): Unit = abort(msg)
def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg))
+ def shouldLogAtThisPhase = false
+
@deprecated("Give us a reason", "2.10.0")
def abort(): Nothing = abort("unknown error")
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 113500fe12..b1e81de037 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -114,7 +114,12 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
// with the proper specific type.
def rawname: NameType
def name: NameType
- def name_=(n: Name): Unit
+ def name_=(n: Name): Unit = {
+ if (shouldLogAtThisPhase) {
+ val msg = s"Renaming $fullLocationString to $n"
+ if (isSpecialized) debuglog(msg) else log(msg)
+ }
+ }
def asNameType(n: Name): NameType
private[this] var _rawowner = initOwner // Syncnote: need not be protected, as only assignment happens in owner_=, which is not exposed to api
@@ -697,6 +702,18 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def deprecationMessage = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 0)
def deprecationVersion = getAnnotation(DeprecatedAttr) flatMap (_ stringArg 1)
def deprecatedParamName = getAnnotation(DeprecatedNameAttr) flatMap (_ symbolArg 0)
+ def hasDeprecatedInheritanceAnnotation
+ = hasAnnotation(DeprecatedInheritanceAttr)
+ def deprecatedInheritanceMessage
+ = getAnnotation(DeprecatedInheritanceAttr) flatMap (_ stringArg 0)
+ def deprecatedInheritanceVersion
+ = getAnnotation(DeprecatedInheritanceAttr) flatMap (_ stringArg 1)
+ def hasDeprecatedOverridingAnnotation
+ = hasAnnotation(DeprecatedOverridingAttr)
+ def deprecatedOverridingMessage
+ = getAnnotation(DeprecatedOverridingAttr) flatMap (_ stringArg 0)
+ def deprecatedOverridingVersion
+ = getAnnotation(DeprecatedOverridingAttr) flatMap (_ stringArg 1)
// !!! when annotation arguments are not literal strings, but any sort of
// assembly of strings, there is a fair chance they will turn up here not as
@@ -954,7 +971,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
/** If this symbol has an expanded name, its original name, otherwise its name itself.
* @see expandName
*/
- def originalName: Name = nme.originalName(name)
+ def originalName: Name = nme.originalName(nme.dropLocalSuffix(name))
/** The name of the symbol before decoding, e.g. `\$eq\$eq` instead of `==`.
*/
@@ -999,7 +1016,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
private def fullNameInternal(separator: Char): Name = (
if (isRoot || isRootPackage || this == NoSymbol) name
else if (owner.isEffectiveRoot) name
- else effectiveOwner.enclClass.fullNameAsName(separator) append separator append name
+ else ((effectiveOwner.enclClass.fullNameAsName(separator) append separator): Name) append name
)
def fullNameAsName(separator: Char): Name = nme.dropLocalSuffix(fullNameInternal(separator))
@@ -2187,10 +2204,10 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
* If settings.Yshowsymkinds, adds abbreviated symbol kind.
*/
def nameString: String = (
- if (!settings.uniqid.value && !settings.Yshowsymkinds.value) "" + decodedName
- else if (settings.uniqid.value && !settings.Yshowsymkinds.value) decodedName + "#" + id
- else if (!settings.uniqid.value && settings.Yshowsymkinds.value) decodedName + "#" + abbreviatedKindString
- else decodedName + "#" + id + "#" + abbreviatedKindString
+ if (!settings.uniqid.value && !settings.Yshowsymkinds.value) "" + originalName.decode
+ else if (settings.uniqid.value && !settings.Yshowsymkinds.value) originalName.decode + "#" + id
+ else if (!settings.uniqid.value && settings.Yshowsymkinds.value) originalName.decode + "#" + abbreviatedKindString
+ else originalName.decode + "#" + id + "#" + abbreviatedKindString
)
def fullNameString: String = {
@@ -2300,9 +2317,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
if (Statistics.hotEnabled) Statistics.incCounter(nameCount)
_rawname
}
- def name_=(name: Name) {
+ override def name_=(name: Name) {
if (name != rawname) {
- log("Renaming %s %s %s to %s".format(shortSymbolClass, debugFlagString, rawname, name))
+ super.name_=(name) // logging
changeNameInOwners(name)
_rawname = name.toTermName
}
@@ -2603,9 +2620,9 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
// TODO - don't allow names to be renamed in this unstructured a fashion.
// Rename as little as possible. Enforce invariants on all renames.
- def name_=(name: Name) {
+ override def name_=(name: Name) {
if (name != rawname) {
- log("Renaming %s %s %s to %s".format(shortSymbolClass, debugFlagString, rawname, name))
+ super.name_=(name) // logging
changeNameInOwners(name)
_rawname = name.toTypeName
}
@@ -3076,7 +3093,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def asNameType(n: Name) = n.toTermName
def rawname = nme.NO_NAME
def name = nme.NO_NAME
- def name_=(n: Name) = abort("Cannot set NoSymbol's name to " + n)
+ override def name_=(n: Name) = abort("Cannot set NoSymbol's name to " + n)
synchronized {
setInfo(NoType)
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index daf4588438..df44cf234e 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -3454,9 +3454,9 @@ trait Types extends api.Types { self: SymbolTable =>
}
/** A temporary type representing the erasure of a user-defined value type.
- * Created during phase reasure, eliminated again in posterasure.
- * @param sym The value class symbol
- * @param underlying The underlying type before erasure
+ * Created during phase erasure, eliminated again in posterasure.
+ *
+ * @param original The underlying type before erasure
*/
abstract case class ErasedValueType(original: TypeRef) extends UniqueType {
override def safeToString = "ErasedValueType("+original+")"
diff --git a/test/ant/test-basic/build.xml b/test/ant/test-basic/build.xml
new file mode 100644
index 0000000000..acc210806f
--- /dev/null
+++ b/test/ant/test-basic/build.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="test-simple" default="compile">
+ <description>Super simple test for Scala</description>
+
+ <target name="init">
+ <!-- Define project CLASSPATH. -->
+ <property name="base.dir" value="../../.."/>
+ <property name="pack.dir" value="${base.dir}/build/pack/"/>
+ <property name="build.dir" value="classes"/>
+ <property name="src.dir" value="src"/>
+ <property name="jvmargs" value=""/>
+ <path id="scala.classpath">
+ <fileset dir="${pack.dir}/lib/"> <include name="*.jar" /> </fileset>
+ </path>
+
+ <!-- Define scala compiler, scaladoc, etc command -->
+ <taskdef resource="scala/tools/ant/antlib.xml">
+ <classpath refid="scala.classpath" />
+ </taskdef>
+ </target>
+
+ <target name="compile" depends="init">
+ <mkdir dir="${build.dir}"/>
+
+ <scalac srcdir="${src.dir}" destdir="${build.dir}"
+ classpathref="scala.classpath" fork="true" target="jvm-1.5"
+ deprecation="no" addparams="-no-specialization"
+ jvmargs="${jvmargs} -XX:+UseConcMarkSweepGC">
+ <include name="**/*.scala"/>
+ </scalac>
+ </target>
+</project>
diff --git a/test/ant/test-basic/src/test-1.scala b/test/ant/test-basic/src/test-1.scala
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/ant/test-basic/src/test-1.scala
diff --git a/test/files/jvm/duration-java.check b/test/files/jvm/duration-java.check
index 7ae257dcc0..49d06fbe93 100644
--- a/test/files/jvm/duration-java.check
+++ b/test/files/jvm/duration-java.check
@@ -201,7 +201,7 @@
6.0E7 seconds => 1000000 minutes
1.0E8 seconds => 100000000 seconds
1.0E9 seconds => 1000000000 seconds
- 1.0E12 seconds => 9223372036854775807 nanoseconds
+ 1.0E12 seconds => class java.lang.IllegalArgumentException
0.0 minutes => 0 days
1.0 minutes => 1 minute
7.0 minutes => 7 minutes
@@ -251,8 +251,8 @@
3.0E7 minutes => 500000 hours
6.0E7 minutes => 1000000 hours
1.0E8 minutes => 100000000 minutes
- 1.0E9 minutes => 9223372036854775807 nanoseconds
- 1.0E12 minutes => 9223372036854775807 nanoseconds
+ 1.0E9 minutes => class java.lang.IllegalArgumentException
+ 1.0E12 minutes => class java.lang.IllegalArgumentException
0.0 hours => 0 days
1.0 hours => 1 hour
7.0 hours => 7 hours
@@ -295,15 +295,15 @@
60000.0 hours => 2500 days
100000.0 hours => 100000 hours
1000000.0 hours => 1000000 hours
- 7000000.0 hours => 9223372036854775807 nanoseconds
- 1.0E7 hours => 9223372036854775807 nanoseconds
- 1.2E7 hours => 9223372036854775807 nanoseconds
- 2.4E7 hours => 9223372036854775807 nanoseconds
- 3.0E7 hours => 9223372036854775807 nanoseconds
- 6.0E7 hours => 9223372036854775807 nanoseconds
- 1.0E8 hours => 9223372036854775807 nanoseconds
- 1.0E9 hours => 9223372036854775807 nanoseconds
- 1.0E12 hours => 9223372036854775807 nanoseconds
+ 7000000.0 hours => class java.lang.IllegalArgumentException
+ 1.0E7 hours => class java.lang.IllegalArgumentException
+ 1.2E7 hours => class java.lang.IllegalArgumentException
+ 2.4E7 hours => class java.lang.IllegalArgumentException
+ 3.0E7 hours => class java.lang.IllegalArgumentException
+ 6.0E7 hours => class java.lang.IllegalArgumentException
+ 1.0E8 hours => class java.lang.IllegalArgumentException
+ 1.0E9 hours => class java.lang.IllegalArgumentException
+ 1.0E12 hours => class java.lang.IllegalArgumentException
0.0 days => 0 days
1.0 days => 1 day
7.0 days => 7 days
@@ -345,16 +345,18 @@
30000.0 days => 30000 days
60000.0 days => 60000 days
100000.0 days => 100000 days
- 1000000.0 days => 9223372036854775807 nanoseconds
- 7000000.0 days => 9223372036854775807 nanoseconds
- 1.0E7 days => 9223372036854775807 nanoseconds
- 1.2E7 days => 9223372036854775807 nanoseconds
- 2.4E7 days => 9223372036854775807 nanoseconds
- 3.0E7 days => 9223372036854775807 nanoseconds
- 6.0E7 days => 9223372036854775807 nanoseconds
- 1.0E8 days => 9223372036854775807 nanoseconds
- 1.0E9 days => 9223372036854775807 nanoseconds
- 1.0E12 days => 9223372036854775807 nanoseconds
+ 1000000.0 days => class java.lang.IllegalArgumentException
+ 7000000.0 days => class java.lang.IllegalArgumentException
+ 1.0E7 days => class java.lang.IllegalArgumentException
+ 1.2E7 days => class java.lang.IllegalArgumentException
+ 2.4E7 days => class java.lang.IllegalArgumentException
+ 3.0E7 days => class java.lang.IllegalArgumentException
+ 6.0E7 days => class java.lang.IllegalArgumentException
+ 1.0E8 days => class java.lang.IllegalArgumentException
+ 1.0E9 days => class java.lang.IllegalArgumentException
+ 1.0E12 days => class java.lang.IllegalArgumentException
+10000000000000001 nanoseconds => 10000000000000001 nanoseconds
+10000000000000002 nanoseconds => 10000000000000002 nanoseconds
Inf => Duration.Inf
-Inf => Duration.MinusInf
+Inf => Duration.Inf
diff --git a/test/files/jvm/duration-java/Test.java b/test/files/jvm/duration-java/Test.java
index 02feb522b8..1c53ccb266 100644
--- a/test/files/jvm/duration-java/Test.java
+++ b/test/files/jvm/duration-java/Test.java
@@ -26,10 +26,18 @@ public class Test {
for (TimeUnit t : TimeUnit.values()) {
for (Double n: makeNumbers()) {
String s = "" + n + " " + t.toString().toLowerCase();
- Duration d = Duration.create(n, t);
- p(String.format("%25s => %s", s, d));
+ String result;
+ try {
+ Duration d = Duration.create(n, t);
+ result = d.toString();
+ } catch(Exception e) {
+ result = e.getClass().toString();
+ }
+ p(String.format("%25s => %s", s, result));
}
}
+ for (String s: new String[] {"10000000000000001 nanoseconds", "10000000000000002 nanoseconds"})
+ p(String.format("%25s => %s", s, Duration.create(s)));
for (String s: Arrays.asList("Inf", "-Inf", "+Inf", "PlusInf", "MinusInf")) {
Duration d = Duration.create(s);
p(String.format("%25s => %s", s, d));
diff --git a/test/files/jvm/duration-tck.scala b/test/files/jvm/duration-tck.scala
new file mode 100644
index 0000000000..0947e84004
--- /dev/null
+++ b/test/files/jvm/duration-tck.scala
@@ -0,0 +1,186 @@
+/**
+ * Copyright (C) 2012 Typesafe Inc. <http://www.typesafe.com>
+ */
+
+import scala.concurrent.util._
+import duration._
+import scala.reflect._
+import java.util.concurrent.TimeUnit._
+import scala.tools.partest.TestUtil.intercept
+
+object Test extends App {
+
+ implicit class Assert(val left: Any) extends AnyVal {
+ import Duration.Undefined
+ def mustBe(right: Any) = right match {
+ case r: Double if r.isNaN => assert(left.asInstanceOf[Double].isNaN, s"$left was not NaN")
+ case r: Double if r == 0 && r.compareTo(0) == -1 => assert(left == 0 && left.asInstanceOf[Double].compareTo(0) == -1, s"$left was not -0.0")
+ case Undefined => assert(left.asInstanceOf[AnyRef] eq Undefined, s"$left was not Undefined")
+ case _ => assert(left == right, s"$left was not equal to $right")
+ }
+ }
+
+ val zero = 0 seconds
+ val one = 1 second
+ val two = one + one
+ val three = 3 * one
+ val inf = Duration.Inf
+ val minf = Duration.MinusInf
+ val undef = Duration.Undefined
+ val inputs = List(zero, one, inf, minf, undef)
+ val nan = Double.NaN
+
+ // test field ops
+ one.isFinite mustBe true
+ 0 * one mustBe zero
+ 2 * one mustBe two
+ three - two mustBe one
+ three / 3 mustBe one
+ two / one mustBe 2
+ one + zero mustBe one
+ one / 1000000 mustBe 1.micro
+
+
+ // test infinities
+
+ inf.isFinite mustBe false
+ minf.isFinite mustBe false
+
+ inf mustBe inf
+ minf mustBe minf
+ -inf mustBe minf
+ -minf mustBe inf
+
+ minf + inf mustBe undef
+ inf - inf mustBe undef
+ inf + minf mustBe undef
+ minf - minf mustBe undef
+
+ inf + inf mustBe inf
+ inf - minf mustBe inf
+ minf - inf mustBe minf
+ minf + minf mustBe minf
+
+ inf.compareTo(inf) mustBe 0
+ inf.compareTo(one) mustBe 1
+ inf.compareTo(minf) mustBe 1
+ minf.compareTo(minf) mustBe 0
+ minf.compareTo(one) mustBe -1
+ minf.compareTo(inf) mustBe -1
+
+ assert(inf != minf)
+ assert(minf != inf)
+ assert(one != inf)
+ assert(minf != one)
+
+ inf mustBe (minf * -1d)
+ inf mustBe (minf / -1d)
+
+ one / inf mustBe 0d
+ -one / inf mustBe -0d
+ one / minf mustBe -0d
+ -one / minf mustBe 0d
+
+ inputs filterNot (_.isFinite) foreach (x => x / zero mustBe x.toUnit(DAYS))
+ inputs filterNot (_.isFinite) foreach (_ * 0d mustBe undef)
+ inputs filterNot (_.isFinite) foreach (_ * -0d mustBe undef)
+ inputs filterNot (_.isFinite) foreach (x => x * Double.PositiveInfinity mustBe x)
+ inputs filterNot (_.isFinite) foreach (x => x * Double.NegativeInfinity mustBe -x)
+
+ inf.toUnit(SECONDS) mustBe Double.PositiveInfinity
+ minf.toUnit(MINUTES) mustBe Double.NegativeInfinity
+ Duration.fromNanos(Double.PositiveInfinity) mustBe inf
+ Duration.fromNanos(Double.NegativeInfinity) mustBe minf
+
+
+ // test undefined & NaN
+
+ undef.isFinite mustBe false
+ -undef mustBe undef
+ assert(undef != undef)
+ assert(undef eq undef)
+
+ inputs foreach (_ + undef mustBe undef)
+ inputs foreach (_ - undef mustBe undef)
+ inputs foreach (_ / undef mustBe nan)
+ inputs foreach (_ / nan mustBe undef)
+ inputs foreach (_ * nan mustBe undef)
+ inputs foreach (undef + _ mustBe undef)
+ inputs foreach (undef - _ mustBe undef)
+ inputs foreach (undef / _ mustBe nan)
+ undef / 1 mustBe undef
+ undef / nan mustBe undef
+ undef * 1 mustBe undef
+ undef * nan mustBe undef
+ inputs foreach (x => x / zero mustBe x.toUnit(SECONDS) / 0d)
+ inputs foreach (x => x / 0d mustBe Duration.fromNanos(x.toUnit(NANOSECONDS) / 0d))
+ inputs foreach (x => x / -0d mustBe Duration.fromNanos(x.toUnit(NANOSECONDS) / -0d))
+
+ inputs filterNot (_ eq undef) foreach (_ compareTo undef mustBe -1)
+ inputs filterNot (_ eq undef) foreach (undef compareTo _ mustBe 1)
+ undef compare undef mustBe 0
+
+ undef.toUnit(DAYS) mustBe nan
+ Duration.fromNanos(nan) mustBe undef
+
+
+ // test overflow protection
+ for (unit ← Seq(DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS)) {
+ val x = unit.convert(Long.MaxValue, NANOSECONDS)
+ val dur = Duration(x, unit)
+ val mdur = Duration(-x, unit)
+ -mdur mustBe (dur)
+ intercept[IllegalArgumentException] { Duration(x + 10000000d, unit) }
+ intercept[IllegalArgumentException] { Duration(-x - 10000000d, unit) }
+ if (unit != NANOSECONDS) {
+ intercept[IllegalArgumentException] { Duration(x + 1, unit) }
+ intercept[IllegalArgumentException] { Duration(-x - 1, unit) }
+ }
+ intercept[IllegalArgumentException] { dur + 1.day }
+ intercept[IllegalArgumentException] { mdur - 1.day }
+ intercept[IllegalArgumentException] { dur * 1.1 }
+ intercept[IllegalArgumentException] { mdur * 1.1 }
+ intercept[IllegalArgumentException] { dur * 2.1 }
+ intercept[IllegalArgumentException] { mdur * 2.1 }
+ intercept[IllegalArgumentException] { dur / 0.9 }
+ intercept[IllegalArgumentException] { mdur / 0.9 }
+ intercept[IllegalArgumentException] { dur / 0.4 }
+ intercept[IllegalArgumentException] { mdur / 0.4 }
+ Duration(x + unit.toString.toLowerCase)
+ Duration("-" + x + unit.toString.toLowerCase)
+ intercept[IllegalArgumentException] { Duration("%.0f".format(x + 10000000d) + unit.toString.toLowerCase) }
+ intercept[IllegalArgumentException] { Duration("-%.0f".format(x + 10000000d) + unit.toString.toLowerCase) }
+ }
+ intercept[IllegalArgumentException] { Duration.fromNanos(1e20) }
+ intercept[IllegalArgumentException] { Duration.fromNanos(-1e20) }
+
+
+ // test precision
+ 1.second + 1.millisecond mustBe 1001.milliseconds
+ 100000.days + 1.nanosecond mustBe 8640000000000000001L.nanoseconds
+ 1.5.seconds.toSeconds mustBe 1
+ (-1.5).seconds.toSeconds mustBe -1
+
+
+ // test unit stability
+ 1000.millis.unit mustBe MILLISECONDS
+ (1000.millis + 0.days).unit mustBe MILLISECONDS
+ 1.second.unit mustBe SECONDS
+ (1.second + 1.millisecond).unit mustBe MILLISECONDS
+
+
+ // test Deadline
+ val dead = 2.seconds.fromNow
+ val dead2 = 2 seconds fromNow
+ assert(dead.timeLeft > 1.second)
+ assert(dead2.timeLeft > 1.second)
+ Thread.sleep(1.second.toMillis)
+ assert(dead.timeLeft < 1.second)
+ assert(dead2.timeLeft < 1.second)
+
+
+ // check statically retaining finite-ness
+ val finiteDuration: FiniteDuration = 1.second plus 3.seconds minus 1.millisecond min 1.second max 1.second
+
+
+}
diff --git a/test/files/jvm/scala-concurrent-tck.scala b/test/files/jvm/scala-concurrent-tck.scala
index ffb5608fd2..0e76b711de 100644
--- a/test/files/jvm/scala-concurrent-tck.scala
+++ b/test/files/jvm/scala-concurrent-tck.scala
@@ -11,6 +11,8 @@ import scala.concurrent.{
import scala.concurrent.{ future, promise, blocking }
import scala.util.{ Try, Success, Failure }
import scala.concurrent.util.Duration
+import scala.reflect.{ classTag, ClassTag }
+import scala.tools.partest.TestUtil.intercept
trait TestBase {
@@ -19,7 +21,7 @@ trait TestBase {
body(() => sv put true)
sv.take(2000)
}
-
+
// def assert(cond: => Boolean) {
// try {
// Predef.assert(cond)
@@ -663,6 +665,29 @@ trait FutureProjections extends TestBase {
case nsee: NoSuchElementException => done()
}
}
+
+ def testAwaitPositiveDuration(): Unit = once { done =>
+ val p = Promise[Int]()
+ val f = p.future
+ future {
+ intercept[IllegalArgumentException] { Await.ready(f, Duration.Undefined) }
+ p.success(0)
+ Await.ready(f, Duration.Zero)
+ Await.ready(f, Duration(500, "ms"))
+ Await.ready(f, Duration.Inf)
+ done()
+ } onFailure { case x => throw x }
+ }
+
+ def testAwaitNegativeDuration(): Unit = once { done =>
+ val f = Promise().future
+ future {
+ intercept[TimeoutException] { Await.ready(f, Duration.Zero) }
+ intercept[TimeoutException] { Await.ready(f, Duration.MinusInf) }
+ intercept[TimeoutException] { Await.ready(f, Duration(-500, "ms")) }
+ done()
+ } onFailure { case x => throw x }
+ }
testFailedFailureOnComplete()
testFailedFailureOnSuccess()
@@ -670,6 +695,8 @@ trait FutureProjections extends TestBase {
testFailedSuccessOnFailure()
testFailedFailureAwait()
testFailedSuccessAwait()
+ testAwaitPositiveDuration()
+ testAwaitNegativeDuration()
}
diff --git a/test/files/neg/deadline-inf-illegal.check b/test/files/neg/deadline-inf-illegal.check
new file mode 100644
index 0000000000..2b9b25e48e
--- /dev/null
+++ b/test/files/neg/deadline-inf-illegal.check
@@ -0,0 +1,15 @@
+deadline-inf-illegal.scala:5: error: value fromNow is not a member of scala.concurrent.util.Duration
+ d.fromNow
+ ^
+deadline-inf-illegal.scala:6: error: type mismatch;
+ found : scala.concurrent.util.Duration
+ required: scala.concurrent.util.FiniteDuration
+ Deadline.now + d
+ ^
+deadline-inf-illegal.scala:7: error: overloaded method value - with alternatives:
+ (other: scala.concurrent.util.Deadline)scala.concurrent.util.FiniteDuration <and>
+ (other: scala.concurrent.util.FiniteDuration)scala.concurrent.util.Deadline
+ cannot be applied to (scala.concurrent.util.Duration)
+ Deadline.now - d
+ ^
+three errors found
diff --git a/test/files/neg/deadline-inf-illegal.scala b/test/files/neg/deadline-inf-illegal.scala
new file mode 100644
index 0000000000..161089bfee
--- /dev/null
+++ b/test/files/neg/deadline-inf-illegal.scala
@@ -0,0 +1,8 @@
+import concurrent.util.{ Deadline, Duration }
+
+class T {
+ val d: Duration = Duration.Zero
+ d.fromNow
+ Deadline.now + d
+ Deadline.now - d
+}
diff --git a/test/files/neg/override.check b/test/files/neg/override.check
index fc152cb3b1..8be98bf4d0 100644
--- a/test/files/neg/override.check
+++ b/test/files/neg/override.check
@@ -1,5 +1,5 @@
override.scala:9: error: overriding type T in trait A with bounds >: Int <: Int;
type T in trait B with bounds >: String <: String has incompatible type
- lazy val x : A with B = x
+ lazy val x : A with B = {println(""); x}
^
one error found
diff --git a/test/files/neg/override.scala b/test/files/neg/override.scala
index 3e589b52e3..7975516061 100755
--- a/test/files/neg/override.scala
+++ b/test/files/neg/override.scala
@@ -6,7 +6,7 @@ trait X {
trait Y extends X {
trait B { type T >: String <: String }
- lazy val x : A with B = x
+ lazy val x : A with B = {println(""); x}
n = "foo"
}
diff --git a/test/files/neg/t6162-inheritance.check b/test/files/neg/t6162-inheritance.check
new file mode 100644
index 0000000000..a7d3cc3238
--- /dev/null
+++ b/test/files/neg/t6162-inheritance.check
@@ -0,0 +1,10 @@
+t6162-inheritance.scala:6: error: inheritance from class Foo in package t6126 is deprecated: `Foo` will be made final in a future version.
+class SubFoo extends Foo
+ ^
+t6162-inheritance.scala:11: error: inheritance from trait T in package t6126 is deprecated
+object SubT extends T
+ ^
+t6162-inheritance.scala:17: error: inheritance from trait S in package t6126 is deprecated
+ new S {
+ ^
+three errors found
diff --git a/test/files/neg/t6162-inheritance.flags b/test/files/neg/t6162-inheritance.flags
new file mode 100644
index 0000000000..65faf53579
--- /dev/null
+++ b/test/files/neg/t6162-inheritance.flags
@@ -0,0 +1 @@
+-Xfatal-warnings -deprecation \ No newline at end of file
diff --git a/test/files/neg/t6162-inheritance.scala b/test/files/neg/t6162-inheritance.scala
new file mode 100644
index 0000000000..7b47b9285a
--- /dev/null
+++ b/test/files/neg/t6162-inheritance.scala
@@ -0,0 +1,19 @@
+package scala.t6126
+
+@deprecatedInheritance("`Foo` will be made final in a future version.", "2.10.0")
+class Foo
+
+class SubFoo extends Foo
+
+@deprecatedInheritance()
+trait T
+
+object SubT extends T
+
+@deprecatedInheritance()
+trait S
+
+object O {
+ new S {
+ }
+}
diff --git a/test/files/neg/t6162-overriding.check b/test/files/neg/t6162-overriding.check
new file mode 100644
index 0000000000..e774888d36
--- /dev/null
+++ b/test/files/neg/t6162-overriding.check
@@ -0,0 +1,7 @@
+t6162-overriding.scala:14: error: overriding method bar in class Bar is deprecated: `bar` will be made private in a future version.
+ override def bar = 43
+ ^
+t6162-overriding.scala:15: error: overriding method baz in class Bar is deprecated
+ override def baz = 43
+ ^
+two errors found
diff --git a/test/files/neg/t6162-overriding.flags b/test/files/neg/t6162-overriding.flags
new file mode 100644
index 0000000000..65faf53579
--- /dev/null
+++ b/test/files/neg/t6162-overriding.flags
@@ -0,0 +1 @@
+-Xfatal-warnings -deprecation \ No newline at end of file
diff --git a/test/files/neg/t6162-overriding.scala b/test/files/neg/t6162-overriding.scala
new file mode 100644
index 0000000000..4cab0c2dee
--- /dev/null
+++ b/test/files/neg/t6162-overriding.scala
@@ -0,0 +1,17 @@
+package scala.t6162
+
+class Bar {
+ @deprecatedOverriding("`bar` will be made private in a future version.", "2.10.0")
+ def bar = 42
+
+ @deprecatedOverriding()
+ def baz = 42
+
+ def baz(a: Any) = 0
+}
+
+class SubBar extends Bar {
+ override def bar = 43
+ override def baz = 43
+ override def baz(a: Any) = 43 // okay
+}
diff --git a/test/files/neg/t6276.check b/test/files/neg/t6276.check
new file mode 100644
index 0000000000..0b3dfa5531
--- /dev/null
+++ b/test/files/neg/t6276.check
@@ -0,0 +1,19 @@
+t6276.scala:4: error: method a in class C does nothing other than call itself recursively
+ def a: Any = a // warn
+ ^
+t6276.scala:5: error: value b in class C does nothing other than call itself recursively
+ val b: Any = b // warn
+ ^
+t6276.scala:7: error: method c in class C does nothing other than call itself recursively
+ def c: Any = this.c // warn
+ ^
+t6276.scala:8: error: method d in class C does nothing other than call itself recursively
+ def d: Any = C.this.d // warn
+ ^
+t6276.scala:13: error: method a does nothing other than call itself recursively
+ def a: Any = a // warn
+ ^
+t6276.scala:22: error: method a does nothing other than call itself recursively
+ def a = a // warn
+ ^
+6 errors found
diff --git a/test/files/neg/t6276.flags b/test/files/neg/t6276.flags
new file mode 100644
index 0000000000..85d8eb2ba2
--- /dev/null
+++ b/test/files/neg/t6276.flags
@@ -0,0 +1 @@
+-Xfatal-warnings
diff --git a/test/files/neg/t6276.scala b/test/files/neg/t6276.scala
new file mode 100644
index 0000000000..bd0a473f71
--- /dev/null
+++ b/test/files/neg/t6276.scala
@@ -0,0 +1,44 @@
+object Test {
+ def foo(a: Int, b: Int, c: Int) {
+ class C {
+ def a: Any = a // warn
+ val b: Any = b // warn
+
+ def c: Any = this.c // warn
+ def d: Any = C.this.d // warn
+ }
+
+ def method {
+ // method local
+ def a: Any = a // warn
+ }
+
+ trait T {
+ def a: Any
+ }
+
+ new T {
+ // inherited return type
+ def a = a // warn
+ }
+
+ // no warnings below
+ new {
+ def a: Any = {println(""); a}
+ val b: Any = {println(""); b}
+ def c(i: Int): Any = c(i - 0)
+ }
+
+ class D {
+ def other: D = null
+ def foo: Any = other.foo
+ }
+
+ class E {
+ def foo: Any = 0
+ class D extends E {
+ override def foo: Any = E.this.foo
+ }
+ }
+ }
+}
diff --git a/test/files/neg/t6335.check b/test/files/neg/t6335.check
new file mode 100644
index 0000000000..1727a05eb2
--- /dev/null
+++ b/test/files/neg/t6335.check
@@ -0,0 +1,9 @@
+t6335.scala:6: error: method Z is defined twice
+ conflicting symbols both originated in file 't6335.scala'
+ implicit class Z[A](val i: A) { def zz = i }
+ ^
+t6335.scala:3: error: method X is defined twice
+ conflicting symbols both originated in file 't6335.scala'
+ implicit class X(val x: Int) { def xx = x }
+ ^
+two errors found
diff --git a/test/files/neg/t6335.scala b/test/files/neg/t6335.scala
new file mode 100644
index 0000000000..5c41e81ef5
--- /dev/null
+++ b/test/files/neg/t6335.scala
@@ -0,0 +1,7 @@
+object ImplicitClass {
+ def X(i: Int) {}
+ implicit class X(val x: Int) { def xx = x }
+
+ def Z[A](i: A) {}
+ implicit class Z[A](val i: A) { def zz = i }
+} \ No newline at end of file
diff --git a/test/files/pos/t6335.scala b/test/files/pos/t6335.scala
new file mode 100644
index 0000000000..50e34092d1
--- /dev/null
+++ b/test/files/pos/t6335.scala
@@ -0,0 +1,25 @@
+object E extends Z {
+ def X = 3
+ implicit class X(val i: Int) {
+ def xx = i
+ }
+
+ def Y(a: Any) = 0
+ object Y
+ implicit class Y(val i: String) { def yy = i }
+
+ implicit class Z(val i: Boolean) { def zz = i }
+}
+
+trait Z {
+ def Z = 0
+}
+
+object Test {
+ import E._
+ 0.xx
+
+ "".yy
+
+ true.zz
+}
diff --git a/test/files/run/compiler-asSeenFrom.check b/test/files/run/compiler-asSeenFrom.check
index 96e257d303..47d40b0331 100644
--- a/test/files/run/compiler-asSeenFrom.check
+++ b/test/files/run/compiler-asSeenFrom.check
@@ -269,8 +269,8 @@ value jZ { // after parser
value jZ { // after explicitouter
protected val $outer: D.this.type
- val ll$D$J$$$outer(): D.this.type
- val ll$C$I$$$outer(): C.this.type
+ val $outer(): D.this.type
+ val $outer(): C.this.type
def thisI(): I.this.type
def thisC(): C.this.type
def t2(): T2
@@ -279,9 +279,9 @@ value jZ { // after explicitouter
value jZ { // after erasure
protected val $outer: ll.D
- val ll$D$J$$$outer(): ll.D
+ val $outer(): ll.D
protected val $outer: ll.C
- val ll$C$I$$$outer(): ll.C
+ val $outer(): ll.C
def thisI(): ll.C#I
def thisC(): ll.C
def t2(): Object
@@ -290,9 +290,9 @@ value jZ { // after erasure
value jZ { // after flatten
protected val $outer: ll.D
- val ll$D$J$$$outer(): ll.D
+ val $outer(): ll.D
protected val $outer: ll.C
- val ll$C$I$$$outer(): ll.C
+ val $outer(): ll.C
def thisI(): ll.C#C$I
def thisC(): ll.C
def t2(): Object
diff --git a/test/files/run/t6271.scala b/test/files/run/t6271.scala
new file mode 100644
index 0000000000..8ebf7ad8b5
--- /dev/null
+++ b/test/files/run/t6271.scala
@@ -0,0 +1,32 @@
+object Test extends App {
+ def filterIssue = {
+ val viewed : Iterable[Iterable[Int]] = List(List(0).view).view
+ val filtered = viewed flatMap { x => List( x filter (_ > 0) ) }
+ filtered.iterator.toIterable.flatten
+ }
+ def takenIssue = {
+ val viewed : Iterable[Iterable[Int]] = List(List(0).view).view
+ val filtered = viewed flatMap { x => List( x take 0 ) }
+ filtered.iterator.toIterable.flatten
+ }
+ def droppedIssue = {
+ val viewed : Iterable[Iterable[Int]] = List(List(0).view).view
+ val filtered = viewed flatMap { x => List( x drop 1 ) }
+ filtered.iterator.toIterable.flatten
+ }
+ def flatMappedIssue = {
+ val viewed : Iterable[Iterable[Int]] = List(List(0).view).view
+ val filtered = viewed flatMap { x => List( x flatMap (_ => List()) ) }
+ filtered.iterator.toIterable.flatten
+ }
+ def slicedIssue = {
+ val viewed : Iterable[Iterable[Int]] = List(List(0).view).view
+ val filtered = viewed flatMap { x => List( x slice (2,3) ) }
+ filtered.iterator.toIterable.flatten
+ }
+ filterIssue
+ takenIssue
+ droppedIssue
+ flatMappedIssue
+ slicedIssue
+}
diff --git a/test/files/pos/t2868.cmds b/test/flaky/pos/t2868.cmds
index ed8124a9e0..ed8124a9e0 100644
--- a/test/files/pos/t2868.cmds
+++ b/test/flaky/pos/t2868.cmds
diff --git a/test/files/pos/t2868/Jann.java b/test/flaky/pos/t2868/Jann.java
index f5b68de7b0..f5b68de7b0 100644
--- a/test/files/pos/t2868/Jann.java
+++ b/test/flaky/pos/t2868/Jann.java
diff --git a/test/files/pos/t2868/Nest.java b/test/flaky/pos/t2868/Nest.java
index 53652291ad..53652291ad 100644
--- a/test/files/pos/t2868/Nest.java
+++ b/test/flaky/pos/t2868/Nest.java
diff --git a/test/files/pos/t2868/pick_1.scala b/test/flaky/pos/t2868/pick_1.scala
index a211687432..a211687432 100644
--- a/test/files/pos/t2868/pick_1.scala
+++ b/test/flaky/pos/t2868/pick_1.scala
diff --git a/test/files/pos/t2868/t2868_src_2.scala b/test/flaky/pos/t2868/t2868_src_2.scala
index f11ef0fae2..f11ef0fae2 100644
--- a/test/files/pos/t2868/t2868_src_2.scala
+++ b/test/flaky/pos/t2868/t2868_src_2.scala