diff options
author | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-06 17:59:14 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2014-02-12 10:25:12 -0800 |
commit | 7ea7a3b89b2a597f7aa22eefe39e3bae7244882f (patch) | |
tree | 3f56600fac2ca94000969f383b45d1e4d88e9b52 /src | |
parent | 668966be53da2b9b8602098625180192438345b1 (diff) | |
download | scala-7ea7a3b89b2a597f7aa22eefe39e3bae7244882f.tar.gz scala-7ea7a3b89b2a597f7aa22eefe39e3bae7244882f.tar.bz2 scala-7ea7a3b89b2a597f7aa22eefe39e3bae7244882f.zip |
SI-8177 co-evolve more than just RefinedTypes
`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 a 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
`TypeRef` wasn't co-evolved (so it still referred to the `A` in `{ type A = AIn }`
underlying the old prefix).
`coEvolveSym` used to only look at prefixes that were directly `RefinedType`s,
but this bug shows they could also be `SingleType`s with an underlying `RefinedType`.
Diffstat (limited to 'src')
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/RefChecks.scala | 3 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 49 |
2 files changed, 39 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) |