diff options
Diffstat (limited to 'src')
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 |