summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@typesafe.com>2014-02-12 17:47:54 -0800
committerAdriaan Moors <adriaan.moors@typesafe.com>2014-02-12 17:47:54 -0800
commit9c4a6e3ed7624892f46948c1c0fb57d7d5b3346e (patch)
treeb4b6d8fcedd45a8304d44c0dca3fe39a07fae065
parent2240464dea5b13a487938f66878e3e84b180376a (diff)
parent427b82648422e4118c68f34e81c94deca3755deb (diff)
downloadscala-9c4a6e3ed7624892f46948c1c0fb57d7d5b3346e.tar.gz
scala-9c4a6e3ed7624892f46948c1c0fb57d7d5b3346e.tar.bz2
scala-9c4a6e3ed7624892f46948c1c0fb57d7d5b3346e.zip
Merge pull request #3516 from adriaanm/t8177
SI-8177 co-evolve more than just RefinedTypes
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala5
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala55
-rw-r--r--test/files/neg/t0764.check7
-rw-r--r--test/files/neg/t0764.scala14
-rw-r--r--test/files/neg/t0764b.check63
-rw-r--r--test/files/pos/t0764.scala27
-rw-r--r--test/files/pos/t0764b.scala (renamed from test/files/neg/t0764b.scala)7
-rw-r--r--test/files/pos/t8177.scala12
-rw-r--r--test/files/pos/t8177a.scala9
-rw-r--r--test/files/pos/t8177b.scala13
-rw-r--r--test/files/pos/t8177d.scala12
-rw-r--r--test/files/pos/t8177e.scala3
-rw-r--r--test/files/pos/t8177g.scala11
-rw-r--r--test/files/run/t8177f.scala20
14 files changed, 156 insertions, 102 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 2125e281f0..02dd63f011 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -467,6 +467,11 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
// overrideError("may not override parameterized type");
// @M: substSym
def checkOverrideAlias() {
+ // Important: first check the pair has the same kind, since the substitution
+ // carries high's type parameter's bounds over to low, so that
+ // type equality doesn't consider potentially different bounds on low/high's type params.
+ // In b781e25afe this went from using memberInfo to memberType (now lowType/highType), tested by neg/override.scala.
+ // TODO: was that the right fix? it seems type alias's RHS should be checked by looking at the symbol's info
if (pair.sameKind && lowType.substSym(low.typeParams, high.typeParams) =:= highType) ()
else overrideTypeError() // (1.6)
}
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index cf405ade03..218ad28a03 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -2017,22 +2017,51 @@ trait Types
// appliedType(sym.info, typeArgs).asSeenFrom(pre, sym.owner)
override def betaReduce = transform(sym.info.resultType)
- // #3731: return sym1 for which holds: pre bound sym.name to sym and
- // pre1 now binds sym.name to sym1, conceptually exactly the same
- // symbol as sym. The selection of sym on pre must be updated to the
- // selection of sym1 on pre1, since sym's info was probably updated
- // by the TypeMap to yield a new symbol, sym1 with transformed info.
- // @returns sym1
- override def coevolveSym(pre1: Type): Symbol =
- if (pre eq pre1) sym else (pre, pre1) match {
- // don't look at parents -- it would be an error to override alias types anyway
- case (RefinedType(_, _), RefinedType(_, decls1)) => decls1 lookup sym.name
- // TODO: is there another way a typeref's symbol can refer to a symbol defined in its pre?
- case _ => sym
- }
+ /** SI-3731, SI-8177: when prefix is changed to `newPre`, maintain consistency of prefix and sym
+ * (where the symbol refers to a declaration "embedded" in the prefix).
+ *
+ * @returns newSym so that `newPre` binds `sym.name` to `newSym`,
+ * to remain consistent with `pre` previously binding `sym.name` to `sym`.
+ *
+ * `newSym` and `sym` are conceptually the same symbols, but some change to our `prefix`
+ * got them out of whack. (Usually triggered by substitution or `asSeenFrom`.)
+ * The only kind of "binds" we consider is where `prefix` (or its underlying type)
+ * is a refined type that declares `sym` (since the old prefix was discarded,
+ * the old symbol is now stale and we should update it, like in `def rebind`,
+ * except this is not for overriding symbols -- a vertical move -- but a "lateral" change.)
+ *
+ * The reason for this hack is that substitution and asSeenFrom clone RefinedTypes and
+ * their members, without updating the potential references to those members -- here, we aim to patch
+ * this up, so that: when changing a TypeRef(pre, sym, args) to a TypeRef(pre', sym', args'), and pre
+ * embeds a symbol sym (pre is a RefinedType(_, Scope(..., sym,...)) or a SingleType with such an
+ * underlying type), make sure that we update sym' to compensate for the change of pre -> pre' (which may
+ * have created a new symbol for the one the original sym referred to)
+ */
+ override def coevolveSym(newPre: Type): Symbol =
+ if ((pre ne newPre) && embeddedSymbol(pre, sym.name) == sym) {
+ val newSym = embeddedSymbol(newPre, sym.name)
+ debuglog(s"co-evolve: ${pre} -> ${newPre}, $sym : ${sym.info} -> $newSym : ${newSym.info}")
+ // To deal with erroneous `preNew`, fallback via `orElse sym`, in case `preNew` does not have a decl named `sym.name`.
+ newSym orElse sym
+ } else sym
+
override def kind = "AliasTypeRef"
}
+ // Return the symbol named `name` that's "embedded" in tp
+ // This is the case if `tp` is a `T{...; type/val $name ; ...}`,
+ // or a singleton type with such an underlying type.
+ private def embeddedSymbol(tp: Type, name: Name): Symbol =
+ // normalize to flatten nested RefinedTypes
+ // don't check whether tp is a RefinedType -- it may be a ThisType of one, for example
+ // TODO: check the resulting symbol is owned by the refinement class? likely an invariant...
+ if (tp.typeSymbol.isRefinementClass) tp.normalize.decls lookup name
+ else {
+ debuglog(s"no embedded symbol $name found in ${showRaw(tp)} --> ${tp.normalize.decls lookup name}")
+ NoSymbol
+ }
+
+
trait AbstractTypeRef extends NonClassTypeRef {
require(sym.isAbstractType, sym)
diff --git a/test/files/neg/t0764.check b/test/files/neg/t0764.check
deleted file mode 100644
index 6156b52712..0000000000
--- a/test/files/neg/t0764.check
+++ /dev/null
@@ -1,7 +0,0 @@
-t0764.scala:13: error: type mismatch;
- found : Node{type T = _1.type} where val _1: Node{type T = NextType}
- required: Node{type T = Main.this.AType}
- (which expands to) Node{type T = Node{type T = NextType}}
- new Main[AType]( (value: AType).prepend )
- ^
-one error found
diff --git a/test/files/neg/t0764.scala b/test/files/neg/t0764.scala
deleted file mode 100644
index f2cc65cf7d..0000000000
--- a/test/files/neg/t0764.scala
+++ /dev/null
@@ -1,14 +0,0 @@
-class Top[A] {
- type AType = A
-}
-
-trait Node { outer =>
- type T <: Node
- def prepend = new Node { type T = outer.type }
-}
-
-class Main[NextType <: Node](value: Node { type T = NextType })
- extends Top[Node { type T = NextType }] {
-
- new Main[AType]( (value: AType).prepend )
-}
diff --git a/test/files/neg/t0764b.check b/test/files/neg/t0764b.check
deleted file mode 100644
index d74a9efbfe..0000000000
--- a/test/files/neg/t0764b.check
+++ /dev/null
@@ -1,63 +0,0 @@
-t0764b.scala:27: error: type mismatch;
- found : p1.t0764.Node{type T = p1.t0764.<refinement>.type}
- required: p1.t0764.NodeAlias[p1.t0764.NodeAlias[A]]
- (which expands to) p1.t0764.Node{type T = p1.t0764.Node{type T = A}}
- private[this] def f1 = new Main1(v.prepend) // fail
- ^
-t0764b.scala:28: error: type mismatch;
- found : p1.t0764.Node{type T = p1.t0764.<refinement>.type}
- required: p1.t0764.NodeAlias[p1.t0764.NodeAlias[A]]
- (which expands to) p1.t0764.Node{type T = p1.t0764.Node{type T = A}}
- private[this] def f2 = new Main1[NodeAlias[A]](v.prepend) // fail
- ^
-t0764b.scala:29: error: type mismatch;
- found : p1.t0764.Node{type T = p1.t0764.<refinement>.type}
- required: p1.t0764.NodeAlias[p1.t0764.Node{type T = A}]
- (which expands to) p1.t0764.Node{type T = p1.t0764.Node{type T = A}}
- private[this] def f3 = new Main1[Node { type T = A }](v.prepend) // fail
- ^
-t0764b.scala:34: error: type mismatch;
- found : p1.t0764.Node{type T = p1.t0764.<refinement>.type}
- required: p1.t0764.Node{type T = p1.t0764.Node{type T = A}}
- private[this] def f1 = new Main2(v.prepend) // fail
- ^
-t0764b.scala:35: error: type mismatch;
- found : p1.t0764.Node{type T = p1.t0764.<refinement>.type}
- required: p1.t0764.Node{type T = p1.t0764.NodeAlias[A]}
- (which expands to) p1.t0764.Node{type T = p1.t0764.Node{type T = A}}
- private[this] def f2 = new Main2[NodeAlias[A]](v.prepend) // fail
- ^
-t0764b.scala:36: error: type mismatch;
- found : p1.t0764.Node{type T = p1.t0764.<refinement>.type}
- required: p1.t0764.Node{type T = p1.t0764.Node{type T = A}}
- private[this] def f3 = new Main2[Node { type T = A }](v.prepend) // fail
- ^
-t0764b.scala:52: error: type mismatch;
- found : p2.t0764.Node{type T = p2.t0764.<refinement>.type}
- required: p2.t0764.NodeAlias[p2.t0764.NodeAlias[A]]
- (which expands to) p2.t0764.Node{type T = p2.t0764.Node{type T = A}}
- private[this] def f2 = new Main1[NodeAlias[A]](v.prepend) // fail
- ^
-t0764b.scala:53: error: type mismatch;
- found : p2.t0764.Node{type T = p2.t0764.<refinement>.type}
- required: p2.t0764.NodeAlias[p2.t0764.Node{type T = A}]
- (which expands to) p2.t0764.Node{type T = p2.t0764.Node{type T = A}}
- private[this] def f3 = new Main1[Node { type T = A }](v.prepend) // fail
- ^
-t0764b.scala:58: error: type mismatch;
- found : p2.t0764.Node{type T = p2.t0764.<refinement>.type}
- required: p2.t0764.Node{type T = p2.t0764.Node{type T = A}}
- private[this] def f1 = new Main2(v.prepend) // fail
- ^
-t0764b.scala:59: error: type mismatch;
- found : p2.t0764.Node{type T = p2.t0764.<refinement>.type}
- required: p2.t0764.Node{type T = p2.t0764.NodeAlias[A]}
- (which expands to) p2.t0764.Node{type T = p2.t0764.Node{type T = A}}
- private[this] def f2 = new Main2[NodeAlias[A]](v.prepend) // fail
- ^
-t0764b.scala:60: error: type mismatch;
- found : p2.t0764.Node{type T = p2.t0764.<refinement>.type}
- required: p2.t0764.Node{type T = p2.t0764.Node{type T = A}}
- private[this] def f3 = new Main2[Node { type T = A }](v.prepend) // fail
- ^
-11 errors found
diff --git a/test/files/pos/t0764.scala b/test/files/pos/t0764.scala
new file mode 100644
index 0000000000..f1084f5ff8
--- /dev/null
+++ b/test/files/pos/t0764.scala
@@ -0,0 +1,27 @@
+class Top[A] {
+ type AType = A
+}
+
+trait Node { outer =>
+ type T <: Node
+ def prepend = new Node { type T = outer.type }
+}
+
+class Main[NextType <: Node](value: Node { type T = NextType })
+ extends Top[Node { type T = NextType }] {
+
+ new Main[AType]( (value: AType).prepend )
+}
+
+/* this used to be a neg test, even though it should've compiled
+SI-8177 fixed this.
+
+Behold the equivalent program which type checks without the fix for SI-8177.
+(Expand type alias, convert type member to type param;
+note the covariance to encode subtyping on type members.)
+
+class Node[+T <: Node[_]] { def prepend = new Node[this.type] }
+class Main[NextType <: Node[_]](value: Node[NextType]) {
+ new Main(value.prepend)
+}
+*/ \ No newline at end of file
diff --git a/test/files/neg/t0764b.scala b/test/files/pos/t0764b.scala
index 4ad5ecdc03..6ae3c105c9 100644
--- a/test/files/neg/t0764b.scala
+++ b/test/files/pos/t0764b.scala
@@ -1,8 +1,3 @@
-/** Note that this should compile! It's a neg test to track the
-behavior. If you have broken this test by making it compile, that
-means you have fixed it and it should be moved to pos.
-**/
-
// In all cases when calling "prepend" the receiver 'v'
// has static type NodeAlias[A] or (equivalently) Node { type T = A }.
// Since prepend explicitly returns the singleton type of the receiver,
@@ -15,6 +10,8 @@ means you have fixed it and it should be moved to pos.
// new Main[Node { type T = A }](v.prepend)
// new Main(v.prepend)
+// the `fail` comments below denote what didn't compile before SI-8177 fixed all of them
+
package p1 {
object t0764 {
type NodeAlias[A] = Node { type T = A }
diff --git a/test/files/pos/t8177.scala b/test/files/pos/t8177.scala
new file mode 100644
index 0000000000..fe265f8d0a
--- /dev/null
+++ b/test/files/pos/t8177.scala
@@ -0,0 +1,12 @@
+// exercise coevolveSym: SingleType with an underlying RefinedType
+trait Thing { type A }
+object IntThing extends Thing { type A = Int }
+
+// The following erroneously failed with error: method f overrides nothing.
+// because asSeenFrom produced a typeref of the shape T'#A where A referred to a symbol defined in a T of times past
+// More precisely, the TypeRef case of TypeMap's mapOver correctly modified prefix
+// from having an underlying type of { type A = Ain } to { type A = Int }, with a new symbol for A (now with info Int),
+// but the symbol in the outer type ref wasn't co-evolved (so it still referred to the { type A = AIn } underlying the old prefix)
+// coEvolveSym used to only look at prefixes that were directly RefinedTypes, but they could also be SingleTypes with an underlying RefinedType
+class View[AIn](val in: Thing { type A = AIn }) { def f(p: in.A): in.A = p }
+class SubView extends View[Int](IntThing) { override def f(p: in.A): in.A = p }
diff --git a/test/files/pos/t8177a.scala b/test/files/pos/t8177a.scala
new file mode 100644
index 0000000000..7e2cfb386c
--- /dev/null
+++ b/test/files/pos/t8177a.scala
@@ -0,0 +1,9 @@
+// exercise coevolveSym
+trait Thing { type A; var p: A = _ }
+class AA[T](final val x: Thing { type A = T }) {
+ def foo: x.A = ???
+}
+
+class B extends AA[Int](null) {
+ override def foo: B.this.x.A = super.foo
+}
diff --git a/test/files/pos/t8177b.scala b/test/files/pos/t8177b.scala
new file mode 100644
index 0000000000..b7ed9342a3
--- /dev/null
+++ b/test/files/pos/t8177b.scala
@@ -0,0 +1,13 @@
+// exercise coevolveSym: SingleType with an underlying RefinedType, via a type alias
+trait Thing { type A }
+object IntThing extends Thing { type A = Int }
+object ThingHolder { type Alias[AIn] = Thing { type A = AIn } }
+
+// The following erroneously failed with error: method f overrides nothing.
+// because asSeenFrom produced a typeref of the shape T'#A where A referred to a symbol defined in a T of times past
+// More precisely, the TypeRef case of TypeMap's mapOver correctly modified prefix
+// from having an underlying type of { type A = Ain } to { type A = Int }, with a new symbol for A (now with info Int),
+// but the symbol in the outer type ref wasn't co-evolved (so it still referred to the { type A = AIn } underlying the old prefix)
+// coEvolveSym used to only look at prefixes that were directly RefinedTypes, but they could also be SingleTypes with an underlying RefinedType
+class View[AIn](val in: ThingHolder.Alias[AIn]) { def f(p: in.A): in.A = p }
+class SubView extends View[Int](IntThing) { override def f(p: in.A): in.A = p } \ No newline at end of file
diff --git a/test/files/pos/t8177d.scala b/test/files/pos/t8177d.scala
new file mode 100644
index 0000000000..d15a05a359
--- /dev/null
+++ b/test/files/pos/t8177d.scala
@@ -0,0 +1,12 @@
+// exercise coevolveSym
+trait HasElem { type A }
+trait View[AIn] {
+ val tc: HasElem { type A = AIn }
+ def f2(p: tc.A): tc.A = p
+}
+
+object Test {
+ val view: View[Int] = null
+
+ view f2 5 // fails
+}
diff --git a/test/files/pos/t8177e.scala b/test/files/pos/t8177e.scala
new file mode 100644
index 0000000000..cb1136ff11
--- /dev/null
+++ b/test/files/pos/t8177e.scala
@@ -0,0 +1,3 @@
+// exercise coevolveSym
+trait T[A] { val foo: { type B = A } = ???; def bar(b: foo.B) = () }
+object O extends T[Int] { bar(0) }
diff --git a/test/files/pos/t8177g.scala b/test/files/pos/t8177g.scala
new file mode 100644
index 0000000000..bb66d32021
--- /dev/null
+++ b/test/files/pos/t8177g.scala
@@ -0,0 +1,11 @@
+// exercise coevolveSym: ThisType
+trait HasA { type A }
+class AA[T] {
+ type HasAT[T] = HasA{ type A = T }
+ val x: HasAT[T] = ???
+ def foo: x.A = ???
+}
+
+class B extends AA[Int] {
+ override def foo: B.this.x.A = super.foo
+} \ No newline at end of file
diff --git a/test/files/run/t8177f.scala b/test/files/run/t8177f.scala
new file mode 100644
index 0000000000..f50a5d98d6
--- /dev/null
+++ b/test/files/run/t8177f.scala
@@ -0,0 +1,20 @@
+trait Thing { type A; var p: A = _ }
+class A[T](final val x: Thing { type A = T }) {
+ type Q = T
+
+ def x1: T = x.p
+ def x2: Q = x.p
+ def x3: x.A = x.p
+}
+// all result types should be inferred as Int
+class B extends A[Int](null) {
+ def y1 = x1
+ def y2 = x2
+ val y3 = x3 // before SI-8177, this lead to a signature that erased to java.lang.Object
+}
+
+
+object Test extends App {
+ val methods = classOf[B].getDeclaredMethods.sortBy(_.getName)
+ assert(methods.forall(_.toGenericString.startsWith("public int")))
+}