summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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.scala50
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala19
-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.scala29
-rw-r--r--src/reflect/scala/reflect/runtime/TwoWayCache.scala38
-rw-r--r--test/files/neg/t4440.check13
-rw-r--r--test/files/neg/t4440.flags1
-rw-r--r--test/files/neg/t4440.scala19
-rw-r--r--test/files/pos/t6117.scala19
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))
+}