diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 5 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 55 | ||||
-rw-r--r-- | test/files/neg/t0764.check | 7 | ||||
-rw-r--r-- | test/files/neg/t0764.scala | 14 | ||||
-rw-r--r-- | test/files/neg/t0764b.check | 63 | ||||
-rw-r--r-- | test/files/pos/t0764.scala | 27 | ||||
-rw-r--r-- | test/files/pos/t0764b.scala (renamed from test/files/neg/t0764b.scala) | 7 | ||||
-rw-r--r-- | test/files/pos/t8177.scala | 12 | ||||
-rw-r--r-- | test/files/pos/t8177a.scala | 9 | ||||
-rw-r--r-- | test/files/pos/t8177b.scala | 13 | ||||
-rw-r--r-- | test/files/pos/t8177d.scala | 12 | ||||
-rw-r--r-- | test/files/pos/t8177e.scala | 3 | ||||
-rw-r--r-- | test/files/pos/t8177g.scala | 11 | ||||
-rw-r--r-- | test/files/run/t8177f.scala | 20 |
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"))) +} |