summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2012-11-11 14:02:24 +0100
committerJason Zaugg <jzaugg@gmail.com>2012-11-14 09:01:50 +0100
commit1587a77eca46b265dc677b9be2536f1f60503f65 (patch)
tree7423adb560cefdd7231140c921d7fb80812f95c6
parent8b598436f64ca4e980c8a38f642085b4d23e2327 (diff)
downloadscala-1587a77eca46b265dc677b9be2536f1f60503f65.tar.gz
scala-1587a77eca46b265dc677b9be2536f1f60503f65.tar.bz2
scala-1587a77eca46b265dc677b9be2536f1f60503f65.zip
SI-6648 copyAttrs must preserve TypeTree#wasEmpty
This field tracks whether the type is an inferred on, subject to removal in `resetAttrs`, or an explicit type, which must remain. In ae5ff662, `ResetAttrs` was modified to duplicate trees, rather than mutate trees in place. But the tree copier didn't pass `wasEmpty` on to the new tree, which in turn meant that the subsequent typing run on the tree would not re-infer the types. If the type refers to a local class, e.g. the anonymous function in the enclosed test case, the reference to the old symbol would persist. This commit overrides `copyAttrs` in TypeTree to copy `wasEmpty`. We might consider representing this as a tree attachment, but this would need to be validated for the performance impact.
-rw-r--r--src/reflect/scala/reflect/internal/Trees.scala13
-rw-r--r--test/files/pos/t6648.scala24
2 files changed, 37 insertions, 0 deletions
diff --git a/src/reflect/scala/reflect/internal/Trees.scala b/src/reflect/scala/reflect/internal/Trees.scala
index 53b40da8f6..b8f5e73acb 100644
--- a/src/reflect/scala/reflect/internal/Trees.scala
+++ b/src/reflect/scala/reflect/internal/Trees.scala
@@ -482,6 +482,10 @@ trait Trees extends api.Trees { self: SymbolTable =>
case class TypeTree() extends TypTree with TypeTreeContextApi {
private var orig: Tree = null
+ /** Was this type tree originally empty? That is, does it now contain
+ * an inferred type that must be forgotten in `resetAttrs` to
+ * enable retyping.
+ */
private[scala] var wasEmpty: Boolean = false
override def symbol = typeTreeSymbol(this) // if (tpe == null) null else tpe.typeSymbol
@@ -502,6 +506,15 @@ trait Trees extends api.Trees { self: SymbolTable =>
wasEmpty = isEmpty
setType(tp)
}
+
+ override private[scala] def copyAttrs(tree: Tree) = {
+ super.copyAttrs(tree)
+ tree match {
+ case other: TypeTree => wasEmpty = other.wasEmpty // SI-6648 Critical for correct operation of `resetAttrs`.
+ case _ =>
+ }
+ this
+ }
}
object TypeTree extends TypeTreeExtractor
diff --git a/test/files/pos/t6648.scala b/test/files/pos/t6648.scala
new file mode 100644
index 0000000000..9593ebfee9
--- /dev/null
+++ b/test/files/pos/t6648.scala
@@ -0,0 +1,24 @@
+abstract class Node extends NodeSeq
+trait NodeSeq extends Seq[Node]
+object NodeSeq {
+ implicit def seqToNodeSeq(ns: Seq[Node]): NodeSeq = ???
+ def foo[B, That](f: Seq[B])(implicit bf: scala.collection.generic.CanBuildFrom[Seq[Int], B, That]): That = ???
+}
+
+class Transformer {
+ def apply(nodes: Any): Any = ???
+}
+
+object transformer1 extends Transformer {
+ // Adding explicit type arguments, or making the impilcit view
+ // seqToNodeSeq explicit avoids the crash
+ NodeSeq.foo {
+ // These both avoid the crash:
+ // val t = new Transformer {}; t.apply(null)
+ // new Transformer().apply(null)
+ new Transformer {}.apply(null)
+
+ null: NodeSeq
+ }: NodeSeq
+}
+