diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/doc/model/LinkTo.scala (renamed from src/compiler/scala/tools/nsc/doc/model/Links.scala) | 0 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala | 50 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Typers.scala | 19 | ||||
-rw-r--r-- | src/library/scala/reflect/ClassManifestDeprecatedApis.scala (renamed from src/library/scala/reflect/ClassManifest.scala) | 0 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala | 29 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/TwoWayCache.scala | 38 | ||||
-rw-r--r-- | test/files/neg/t4440.check | 13 | ||||
-rw-r--r-- | test/files/neg/t4440.flags | 1 | ||||
-rw-r--r-- | test/files/neg/t4440.scala | 19 | ||||
-rw-r--r-- | test/files/pos/t6117.scala | 19 |
10 files changed, 136 insertions, 52 deletions
diff --git a/src/compiler/scala/tools/nsc/doc/model/Links.scala b/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala index b76dee0f14..b76dee0f14 100644 --- a/src/compiler/scala/tools/nsc/doc/model/Links.scala +++ b/src/compiler/scala/tools/nsc/doc/model/LinkTo.scala diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala index afbe528b1f..1f7c34b8ad 100644 --- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala +++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala @@ -377,6 +377,7 @@ abstract class ExplicitOuter extends InfoTransform } } + // requires settings.XoldPatmat.value def matchTranslation(tree: Match) = { val Match(selector, cases) = tree var nselector = transform(selector) @@ -519,33 +520,32 @@ abstract class ExplicitOuter extends InfoTransform super.transform(treeCopy.Apply(tree, sel, outerVal :: args)) // entry point for pattern matcher translation - case mch: Match if (!opt.virtPatmat) => // don't use old pattern matcher as fallback when the user wants the virtualizing one - matchTranslation(mch) - - case _ => - if (opt.virtPatmat) { // this turned out to be expensive, hence the hacky `if` and `return` - tree match { - // for patmatvirtualiser - // base.<outer>.eq(o) --> base.$outer().eq(o) if there's an accessor, else the whole tree becomes TRUE - // TODO remove the synthetic `<outer>` method from outerFor?? - case Apply(eqsel@Select(eqapp@Apply(sel@Select(base, outerAcc), Nil), eq), args) if outerAcc == nme.OUTER_SYNTH => - val outerFor = sel.symbol.owner.toInterface // TODO: toInterface necessary? - val acc = outerAccessor(outerFor) - if(acc == NoSymbol) { - // println("WARNING: no outer for "+ outerFor) - return transform(TRUE) // urgh... drop condition if there's no accessor - } else { - // println("(base, acc)= "+(base, acc)) - val outerSelect = localTyper typed Apply(Select(base, acc), Nil) - // achieves the same as: localTyper typed atPos(tree.pos)(outerPath(base, base.tpe.typeSymbol, outerFor.outerClass)) - // println("(b, tpsym, outerForI, outerFor, outerClass)= "+ (base, base.tpe.typeSymbol, outerFor, sel.symbol.owner, outerFor.outerClass)) - // println("outerSelect = "+ outerSelect) - return transform(treeCopy.Apply(tree, treeCopy.Select(eqsel, outerSelect, eq), args)) - } - case _ => - } + case m: Match if settings.XoldPatmat.value => // the new pattern matcher runs in its own phase right after typer + matchTranslation(m) + + // for the new pattern matcher + // base.<outer>.eq(o) --> base.$outer().eq(o) if there's an accessor, else the whole tree becomes TRUE + // TODO remove the synthetic `<outer>` method from outerFor?? + case Apply(eqsel@Select(eqapp@Apply(sel@Select(base, nme.OUTER_SYNTH), Nil), eq), args) if !settings.XoldPatmat.value => + val outerFor = sel.symbol.owner.toInterface // TODO: toInterface necessary? + val acc = outerAccessor(outerFor) + + if (acc == NoSymbol || + // since we can't fix SI-4440 properly (we must drop the outer accessors of final classes when there's no immediate reference to them in sight) + // at least don't crash... this duplicates maybeOmittable from constructors + (acc.owner.isEffectivelyFinal && !acc.isOverridingSymbol)) { + unit.uncheckedWarning(tree.pos, "The outer reference in this type test cannot be checked at run time.") + return transform(TRUE) // urgh... drop condition if there's no accessor (or if it may disappear after constructors) + } else { + // println("(base, acc)= "+(base, acc)) + val outerSelect = localTyper typed Apply(Select(base, acc), Nil) + // achieves the same as: localTyper typed atPos(tree.pos)(outerPath(base, base.tpe.typeSymbol, outerFor.outerClass)) + // println("(b, tpsym, outerForI, outerFor, outerClass)= "+ (base, base.tpe.typeSymbol, outerFor, sel.symbol.owner, outerFor.outerClass)) + // println("outerSelect = "+ outerSelect) + return transform(treeCopy.Apply(tree, treeCopy.Select(eqsel, outerSelect, eq), args)) } + case _ => if (settings.Xmigration28.value) tree match { case TypeApply(fn @ Select(qual, _), args) if fn.symbol == Object_isInstanceOf || fn.symbol == Any_isInstanceOf => if (isArraySeqTest(qual.tpe, args.head.tpe)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 80e7d0d474..51753baa4f 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4661,15 +4661,28 @@ trait Typers extends Modes with Adaptations with Tags { // If the ambiguous name is a monomorphic type, we can relax this far. def mt1 = t1 memberType impSym def mt2 = t2 memberType impSym1 + def characterize = List( + s"types: $t1 =:= $t2 ${t1 =:= t2} members: ${mt1 =:= mt2}", + s"member type 1: $mt1", + s"member type 2: $mt2", + s"$impSym == $impSym1 ${impSym == impSym1}", + s"${impSym.debugLocationString} ${impSym.getClass}", + s"${impSym1.debugLocationString} ${impSym1.getClass}" + ).mkString("\n ") + + // The symbol names are checked rather than the symbols themselves because + // each time an overloaded member is looked up it receives a new symbol. + // So foo.member("x") != foo.member("x") if x is overloaded. This seems + // likely to be the cause of other bugs too... + if (t1 =:= t2 && impSym.name == impSym1.name) + log(s"Suppressing ambiguous import: $t1 =:= $t2 && $impSym == $impSym1") // Monomorphism restriction on types is in part because type aliases could have the // same target type but attach different variance to the parameters. Maybe it can be // relaxed, but doesn't seem worth it at present. - if (t1 =:= t2 && impSym == impSym1) - log(s"Suppressing ambiguous import: $t1 =:= $t2 && $impSym == $impSym1") else if (mt1 =:= mt2 && name.isTypeName && impSym.isMonomorphicType && impSym1.isMonomorphicType) log(s"Suppressing ambiguous import: $mt1 =:= $mt2 && $impSym and $impSym1 are equivalent") else { - log(s"Import is genuinely ambiguous: !($t1 =:= $t2)") + log(s"Import is genuinely ambiguous:\n " + characterize) ambiguousError(s"it is imported twice in the same scope by\n${imports.head}\nand ${imports1.head}") } } diff --git a/src/library/scala/reflect/ClassManifest.scala b/src/library/scala/reflect/ClassManifestDeprecatedApis.scala index d226e43e77..d226e43e77 100644 --- a/src/library/scala/reflect/ClassManifest.scala +++ b/src/library/scala/reflect/ClassManifestDeprecatedApis.scala diff --git a/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala index 5fbeb5f576..cecf8e4658 100644 --- a/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala +++ b/src/reflect/scala/reflect/internal/util/TraceSymbolActivity.scala @@ -8,7 +8,8 @@ trait TraceSymbolActivity { val global: SymbolTable import global._ - if (traceSymbolActivity && global.isCompilerUniverse) + private[this] var enabled = traceSymbolActivity + if (enabled && global.isCompilerUniverse) scala.sys addShutdownHook showAllSymbols() private type Set[T] = scala.collection.immutable.Set[T] @@ -21,23 +22,26 @@ trait TraceSymbolActivity { val allTrees = mutable.Set[Tree]() def recordSymbolsInTree(tree: Tree) { - allTrees += tree + if (enabled) + allTrees += tree } def recordNewSymbol(sym: Symbol) { - if (sym.id > 1) { + if (enabled && sym.id > 1) { allSymbols(sym.id) = sym allChildren(sym.owner.id) ::= sym.id } } def recordNewSymbolOwner(sym: Symbol, newOwner: Symbol) { - val sid = sym.id - val oid = sym.owner.id - val nid = newOwner.id - - prevOwners(sid) ::= (oid -> phase) - allChildren(oid) = allChildren(oid) filterNot (_ == sid) - allChildren(nid) ::= sid + if (enabled) { + val sid = sym.id + val oid = sym.owner.id + val nid = newOwner.id + + prevOwners(sid) ::= (oid -> phase) + allChildren(oid) = allChildren(oid) filterNot (_ == sid) + allChildren(nid) ::= sid + } } /** TODO. @@ -86,7 +90,7 @@ trait TraceSymbolActivity { def prefix = (" " * (sym.ownerChain.length - 1)) + sym.id try println("%s#%s %s".format(prefix, sym.accurateKindString, sym.name.decode)) catch { - case x => println(prefix + " failed: " + x) + case x: Throwable => println(prefix + " failed: " + x) } allChildren(sym.id).sorted foreach showIdAndRemove } @@ -128,7 +132,8 @@ trait TraceSymbolActivity { private def runBeforeErasure[T](body: => T): T = atPhase(findErasurePhase)(body) def showAllSymbols() { - if (!traceSymbolActivity) return + if (!enabled) return + enabled = false allSymbols(1) = NoSymbol println("" + allSymbols.size + " symbols created.") diff --git a/src/reflect/scala/reflect/runtime/TwoWayCache.scala b/src/reflect/scala/reflect/runtime/TwoWayCache.scala index c7bfb3435d..e2bf5773d2 100644 --- a/src/reflect/scala/reflect/runtime/TwoWayCache.scala +++ b/src/reflect/scala/reflect/runtime/TwoWayCache.scala @@ -1,27 +1,40 @@ package scala.reflect package runtime +import collection.mutable.WeakHashMap +import java.lang.ref.WeakReference + /** A cache that maintains a bijection between Java reflection type `J` * and Scala reflection type `S`. + * + * The cache is two-way weak (i.e. is powered by weak references), + * so that neither Java artifacts prevent Scala artifacts from being garbage collected, + * nor the other way around. */ -import collection.mutable.HashMap - private[runtime] class TwoWayCache[J, S] { - private val toScalaMap = new HashMap[J, S] - private val toJavaMap = new HashMap[S, J] + private val toScalaMap = new WeakHashMap[J, WeakReference[S]] + private val toJavaMap = new WeakHashMap[S, WeakReference[J]] def enter(j: J, s: S) = synchronized { // debugInfo("cached: "+j+"/"+s) - toScalaMap(j) = s - toJavaMap(s) = j + toScalaMap(j) = new WeakReference(s) + toJavaMap(s) = new WeakReference(j) + } + + private object SomeRef { + def unapply[T](optRef: Option[WeakReference[T]]): Option[T] = + if (optRef.nonEmpty) { + val result = optRef.get.get + if (result != null) Some(result) else None + } else None } def toScala(key: J)(body: => S): S = synchronized { toScalaMap get key match { - case Some(v) => + case SomeRef(v) => v - case none => + case _ => val result = body enter(key, result) result @@ -30,9 +43,9 @@ private[runtime] class TwoWayCache[J, S] { def toJava(key: S)(body: => J): J = synchronized { toJavaMap get key match { - case Some(v) => + case SomeRef(v) => v - case none => + case _ => val result = body enter(result, key) result @@ -41,11 +54,12 @@ private[runtime] class TwoWayCache[J, S] { def toJavaOption(key: S)(body: => Option[J]): Option[J] = synchronized { toJavaMap get key match { - case None => + case SomeRef(v) => + Some(v) + case _ => val result = body for (value <- result) enter(value, key) result - case some => some } } } diff --git a/test/files/neg/t4440.check b/test/files/neg/t4440.check new file mode 100644 index 0000000000..2861dc3040 --- /dev/null +++ b/test/files/neg/t4440.check @@ -0,0 +1,13 @@ +t4440.scala:12: error: The outer reference in this type test cannot be checked at run time. + case _: b.Inner => println("b") + ^ +t4440.scala:13: error: The outer reference in this type test cannot be checked at run time. + case _: a.Inner => println("a") // this is the case we want + ^ +t4440.scala:16: error: The outer reference in this type test cannot be checked at run time. + case _: a.Inner => println("a") + ^ +t4440.scala:17: error: The outer reference in this type test cannot be checked at run time. + case _: b.Inner => println("b") // this is the case we want + ^ +four errors found diff --git a/test/files/neg/t4440.flags b/test/files/neg/t4440.flags new file mode 100644 index 0000000000..779916d58f --- /dev/null +++ b/test/files/neg/t4440.flags @@ -0,0 +1 @@ +-unchecked -Xfatal-warnings
\ No newline at end of file diff --git a/test/files/neg/t4440.scala b/test/files/neg/t4440.scala new file mode 100644 index 0000000000..383b141edd --- /dev/null +++ b/test/files/neg/t4440.scala @@ -0,0 +1,19 @@ +// constructors used to drop outer fields when they were not accessed +// however, how can you know (respecting separate compilation) that they're not accessed!? +class Outer { final class Inner } + +// the matches below require Inner's outer pointer +// until SI-4440 is fixed properly, we can't make this a run test +// in principle, the output should be "a\nb", but without outer checks it's "b\na" +object Test extends App { + val a = new Outer + val b = new Outer + (new a.Inner: Any) match { + case _: b.Inner => println("b") + case _: a.Inner => println("a") // this is the case we want + } + (new b.Inner: Any) match { + case _: a.Inner => println("a") + case _: b.Inner => println("b") // this is the case we want + } +} diff --git a/test/files/pos/t6117.scala b/test/files/pos/t6117.scala new file mode 100644 index 0000000000..6aca84f72c --- /dev/null +++ b/test/files/pos/t6117.scala @@ -0,0 +1,19 @@ +package test + +trait ImportMe { + def foo(i: Int) = 1 + def foo(s: String) = 2 +} + +class Test(val importMe: ImportMe) { + import importMe._ + import importMe._ + + // A.scala:12: error: reference to foo is ambiguous; + // it is imported twice in the same scope by + // import importMe._ + // and import importMe._ + // println(foo(1)) + // ^ + println(foo(1)) +} |