summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-12-15 16:38:10 -0800
committerPaul Phillips <paulp@improving.org>2012-12-15 19:40:05 -0800
commit8aae61180c3edab97c653dbc7096a439f5c3da12 (patch)
tree83e392fdefa9de398f42b0aedb57376423ff0332
parente14917528e1c080a7f10785e21de36f3a7769718 (diff)
downloadscala-8aae61180c3edab97c653dbc7096a439f5c3da12.tar.gz
scala-8aae61180c3edab97c653dbc7096a439f5c3da12.tar.bz2
scala-8aae61180c3edab97c653dbc7096a439f5c3da12.zip
Deskolemize type skolems before pickling.
Lex Spoon noticed what appeared to be duplicate symbols in methods read from classfiles. The duplicates turned out to be type skolems, which because they're not supposed to be pickled in the first place (right?) are unpickled without turning back into skolems. Now pickler is careful to deskolemize before writing anything down. The effort implied by test case is more than can possibly be justified for this obscure compiler corner, but I'll chalk it up to reflection exploration.
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala35
-rw-r--r--test/files/run/no-pickle-skolems.check1
-rw-r--r--test/files/run/no-pickle-skolems/Source_1.scala5
-rw-r--r--test/files/run/no-pickle-skolems/Test_2.scala37
4 files changed, 76 insertions, 2 deletions
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
index caa45ea6db..efe7519d5e 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/Pickler.scala
@@ -148,9 +148,34 @@ abstract class Pickler extends SubComponent {
true
}
+ /** If the symbol is a type skolem, deskolemize and log it.
+ * If we fail to deskolemize, in a method like
+ * trait Trait[+A] { def f[CC[X]] : CC[A] }
+ * the applied type CC[A] will hold a different CC symbol
+ * than the type-constructor type-parameter CC.
+ */
+ private def deskolemize(sym: Symbol) = {
+ if (sym.isTypeSkolem) {
+ val sym1 = sym.deSkolemize
+ log({
+ val what0 = sym.defString
+ val what = sym1.defString match {
+ case `what0` => what0
+ case other => what0 + "->" + other
+ }
+ val where = sym.enclMethod.fullLocationString
+ s"deskolemizing $what in $where"
+ })
+ sym1
+ }
+ else sym
+ }
+
/** Store symbol in index. If symbol is local, also store everything it references.
*/
- def putSymbol(sym: Symbol) {
+ def putSymbol(sym0: Symbol) {
+ val sym = deskolemize(sym0)
+
if (putEntry(sym)) {
if (isLocal(sym)) {
putEntry(sym.name)
@@ -503,7 +528,13 @@ abstract class Pickler extends SubComponent {
/** Write a reference to object, i.e., the object's number in the map index.
*/
- private def writeRef(ref: AnyRef) { writeNat(index(ref)) }
+ private def writeRef(ref0: AnyRef) {
+ val ref = ref0 match {
+ case sym: Symbol => deskolemize(sym)
+ case _ => ref0
+ }
+ writeNat(index(ref))
+ }
private def writeRefs(refs: List[AnyRef]) { refs foreach writeRef }
private def writeRefsWithLength(refs: List[AnyRef]) {
writeNat(refs.length)
diff --git a/test/files/run/no-pickle-skolems.check b/test/files/run/no-pickle-skolems.check
new file mode 100644
index 0000000000..d64066171a
--- /dev/null
+++ b/test/files/run/no-pickle-skolems.check
@@ -0,0 +1 @@
+OK!
diff --git a/test/files/run/no-pickle-skolems/Source_1.scala b/test/files/run/no-pickle-skolems/Source_1.scala
new file mode 100644
index 0000000000..1b4cbfa788
--- /dev/null
+++ b/test/files/run/no-pickle-skolems/Source_1.scala
@@ -0,0 +1,5 @@
+package s
+
+trait Foo { def to[CC[X]](implicit cc: CC[Int]): Unit }
+
+class Bar extends Foo { def to[CC[X]](implicit cc: CC[Int]): Unit = ??? }
diff --git a/test/files/run/no-pickle-skolems/Test_2.scala b/test/files/run/no-pickle-skolems/Test_2.scala
new file mode 100644
index 0000000000..90bb4c4f88
--- /dev/null
+++ b/test/files/run/no-pickle-skolems/Test_2.scala
@@ -0,0 +1,37 @@
+import scala.reflect.runtime.universe._
+
+object Test {
+ /** Collects symbols by the given name, even if they're not
+ * named CC.
+ */
+ def collectSymbols[T: TypeTag](inMethod: TermName, name: String): List[String] = {
+ val m = typeOf[T] member inMethod typeSignatureIn typeOf[T]
+ var buf: List[Symbol] = Nil
+ var seen: Set[Symbol] = Set()
+ def id(s: Symbol): Int = s.asInstanceOf[{ def id: Int }].id
+
+ def check(s: Symbol) {
+ if (!seen(s)) {
+ seen += s
+ if (s.name.toString == name) buf ::= s
+ }
+ }
+ def loop(t: Type) {
+ t match {
+ case TypeRef(pre, sym, args) => loop(pre) ; check(sym) ; args foreach loop
+ case PolyType(tparams, restpe) => tparams foreach { tp => check(tp) ; check(tp.owner) ; loop(tp.typeSignature) } ; loop(restpe)
+ case MethodType(params, restpe) => params foreach { p => check(p) ; loop(p.typeSignature) } ; loop(restpe)
+ case _ =>
+ }
+ }
+ loop(m)
+
+ buf.reverse.distinct map (s => s.name + "#" + id(s))
+ }
+
+ def main(args: Array[String]): Unit = {
+ val syms = collectSymbols[s.Bar]("to", "CC")
+ assert(syms.size == 1, syms)
+ println("OK!")
+ }
+}