summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala27
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala12
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala23
-rw-r--r--src/library/scala/concurrent/ExecutionContext.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala35
6 files changed, 72 insertions, 35 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 19cdcd2590..3712745590 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -1397,13 +1397,38 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
}
clasz.fields foreach genField
- clasz.methods foreach { im => genMethod(im, c.symbol.isInterface) }
+ clasz.methods foreach { im =>
+ if (im.symbol.isBridge && isRedundantBridge(im, clasz))
+ // We can't backport the erasure fix of SI-7120 to 2.10.x, but we can detect and delete
+ // bridge methods with identical signatures to their targets.
+ //
+ // NOTE: this backstop only implemented here in the ASM backend, and is not implemented in the FJBG backend.
+ debugwarn(s"Discarding redundant bridge method: ${im.symbol.debugLocationString}. See SI-8114.")
+ else
+ genMethod(im, c.symbol.isInterface)
+ }
addInnerClasses(clasz.symbol, jclass)
jclass.visitEnd()
writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol)
}
+ private def isRedundantBridge(bridge: IMethod, owner: IClass): Boolean = {
+ def lastCalledMethod: Option[Symbol] = bridge.code.instructions.reverseIterator.collectFirst {
+ case CALL_METHOD(meth, _) => meth
+ }
+ def hasSameSignatureAsBridge(targetMethod: Symbol): Boolean = {
+ val targetIMethod = clasz.methods find (m => m.symbol == targetMethod)
+ // Important to compare the IMethod#paramss, rather then the erased MethodTypes, as
+ // due to the bug SI-7120, these are out of sync. For example, in the `applyOrElse`
+ // method in run/t8114.scala, the method symbol info has a parameter of type `Long`,
+ // but the IMethod parameter has type `Object`. The latter comes from the info of the
+ // symbol representing the parameter ValDef in the tree, which is incorrectly erased.
+ targetIMethod exists (m => bridge.matchesSignature(m))
+ }
+ lastCalledMethod exists hasSameSignatureAsBridge
+ }
+
/**
* @param owner internal name of the enclosing class of the class.
*
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 430129aaff..e61b35a621 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -878,11 +878,13 @@ abstract class UnCurry extends InfoTransform
case Packed(param, tempVal) => (param, tempVal)
}.unzip
- val rhs1 = localTyper.typedPos(rhs.pos) {
- // Patch the method body to refer to the temp vals
- val rhsSubstituted = rhs.substituteSymbols(packedParams map (_.symbol), tempVals map (_.symbol))
- // The new method body: { val p$1 = p.asInstanceOf[<dependent type>]; ...; <rhsSubstituted> }
- Block(tempVals, rhsSubstituted)
+ val rhs1 = if (tempVals.isEmpty) rhs else {
+ localTyper.typedPos(rhs.pos) {
+ // Patch the method body to refer to the temp vals
+ val rhsSubstituted = rhs.substituteSymbols(packedParams map (_.symbol), tempVals map (_.symbol))
+ // The new method body: { val p$1 = p.asInstanceOf[<dependent type>]; ...; <rhsSubstituted> }
+ Block(tempVals, rhsSubstituted)
+ }
}
(allParams :: Nil, rhs1)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 081f7a8696..fea234dd14 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -909,11 +909,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// case DeBruijnIndex(_, _) =>
case SingleType(pre, sym) =>
validateVariance(pre, variance)
+ case TypeRef(_, sym, _) if sym.isAliasType =>
+ // okay to ignore pre/args here. In 2.10.3 we used to check them in addition to checking
+ // the normalized type, which led to exponential time type checking, see pos/t8152-performance.scala
+ validateVariance(tp.normalize, variance)
case TypeRef(pre, sym, args) =>
// println("validate "+sym+" at "+relativeVariance(sym))
- if (sym.isAliasType/* && relativeVariance(sym) == AnyVariance*/)
- validateVariance(tp.normalize, variance)
- else if (sym.variance != NoVariance) {
+ if (sym.variance != NoVariance) {
val v = relativeVariance(sym)
if (v != AnyVariance && sym.variance != v * variance) {
//Console.println("relativeVariance(" + base + "," + sym + ") = " + v);//DEBUG
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 40313bdb5d..e09a509839 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3269,6 +3269,28 @@ trait Typers extends Modes with Adaptations with Tags {
// calls to the default getters. Example:
// foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a))
checkNotMacro()
+
+ // SI-8111 transformNamedApplication eagerly shuffles around the application to preserve
+ // evaluation order. During this process, it calls `changeOwner` on symbols that
+ // are transplanted underneath synthetic temporary vals.
+ //
+ // Here, we keep track of the symbols owned by `context.owner` to enable us to
+ // rollback, so that we don't end up with "orphaned" symbols.
+ //
+ // TODO: Find a better way!
+ //
+ // Note that duplicating trees would not be enough to fix this problem, we would also need to
+ // clone local symbols in the duplicated tree to truly isolate things (in the spirit of BodyDuplicator),
+ // or, better yet, disentangle the logic in `transformNamedApplication` so that we could
+ // determine whether names/defaults is viable *before* transforming trees.
+ def ownerOf(sym: Symbol) = if (sym == null || sym == NoSymbol) NoSymbol else sym.owner
+ val symsOwnedByContextOwner = tree.collect {
+ case t @ (_: DefTree | _: Function) if ownerOf(t.symbol) == context.owner => t.symbol
+ }
+ def rollbackNamesDefaultsOwnerChanges() {
+ symsOwnedByContextOwner foreach (_.owner = context.owner)
+ }
+
val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x)
if (fun1.isErroneous) duplErrTree
else {
@@ -3297,6 +3319,7 @@ trait Typers extends Modes with Adaptations with Tags {
if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic
doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)
} else {
+ rollbackNamesDefaultsOwnerChanges()
tryTupleApply getOrElse duplErrorTree(NotEnoughArgsError(tree, fun, missing))
}
}
diff --git a/src/library/scala/concurrent/ExecutionContext.scala b/src/library/scala/concurrent/ExecutionContext.scala
index 8928724b34..b4af161c3c 100644
--- a/src/library/scala/concurrent/ExecutionContext.scala
+++ b/src/library/scala/concurrent/ExecutionContext.scala
@@ -16,7 +16,7 @@ import scala.util.Try
/**
* An `ExecutionContext` is an abstraction over an entity that can execute program logic.
*/
-@implicitNotFound("Cannot find an implicit ExecutionContext, either require one yourself or import ExecutionContext.Implicits.global")
+@implicitNotFound("Cannot find an implicit ExecutionContext, either import scala.concurrent.ExecutionContext.Implicits.global or use a custom one")
trait ExecutionContext {
/** Runs a block of code on this execution context.
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index c684f4d690..2f49995030 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -5230,30 +5230,15 @@ trait Types extends api.Types { self: SymbolTable =>
}
}
- class SubTypePair(val tp1: Type, val tp2: Type) {
- override def hashCode = tp1.hashCode * 41 + tp2.hashCode
- override def equals(other: Any) = other match {
- case stp: SubTypePair =>
- // suspend TypeVars in types compared by =:=,
- // since we don't want to mutate them simply to check whether a subtype test is pending
- // in addition to making subtyping "more correct" for type vars,
- // it should avoid the stackoverflow that's been plaguing us (https://groups.google.com/d/topic/scala-internals/2gHzNjtB4xA/discussion)
- // this method is only called when subtyping hits a recursion threshold (subsametypeRecursions >= LogPendingSubTypesThreshold)
- def suspend(tp: Type) =
- if (tp.isGround) null else suspendTypeVarsInType(tp)
- def revive(suspension: List[TypeVar]) =
- if (suspension ne null) suspension foreach (_.suspended = false)
-
- val suspensions = Array(tp1, stp.tp1, tp2, stp.tp2) map suspend
-
- val sameTypes = (tp1 =:= stp.tp1) && (tp2 =:= stp.tp2)
-
- suspensions foreach revive
-
- sameTypes
- case _ =>
- false
- }
+ final case class SubTypePair(tp1: Type, tp2: Type) {
+ // SI-8146 we used to implement equality here in terms of pairwise =:=.
+ // But, this was inconsistent with hashCode, which was based on the
+ // Type#hashCode, based on the structure of types, not the meaning.
+ // Now, we use `Type#{equals,hashCode}` as the (consistent) basis for
+ // detecting cycles (aka keeping subtyping decidable.)
+ //
+ // I added tests to show that we detect the cycle: neg/t8146-no-finitary*
+
override def toString = tp1+" <:<? "+tp2
}
@@ -5819,7 +5804,7 @@ trait Types extends api.Types { self: SymbolTable =>
if (subsametypeRecursions >= LogPendingSubTypesThreshold) {
val p = new SubTypePair(tp1, tp2)
if (pendingSubTypes(p))
- false
+ false // see neg/t8146-no-finitary*
else
try {
pendingSubTypes += p