summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala3
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala49
-rw-r--r--test/files/pos/t8177.scala11
3 files changed, 50 insertions, 13 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 2125e281f0..2b5eed7102 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -467,6 +467,9 @@ 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.
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..0ef5d60a10 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -2017,22 +2017,45 @@ 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 = tp.widen match {
+ case RefinedType(_, decls) => decls lookup name
+ case _ => NoSymbol
+ }
+
trait AbstractTypeRef extends NonClassTypeRef {
require(sym.isAbstractType, sym)
diff --git a/test/files/pos/t8177.scala b/test/files/pos/t8177.scala
new file mode 100644
index 0000000000..9bdf80b1bb
--- /dev/null
+++ b/test/files/pos/t8177.scala
@@ -0,0 +1,11 @@
+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 }