diff options
author | Paul Phillips <paulp@improving.org> | 2012-12-21 15:11:29 -0800 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@typesafe.com> | 2012-12-28 15:16:49 -0800 |
commit | 3bf51189f979eb0dd41744ca844fd12dfdaa0dee (patch) | |
tree | a5317cbc2cafad735cd2d3fa27c28d99785361e7 | |
parent | 422f461578ae0547181afe6d2c0c52ea1071d37b (diff) | |
download | scala-3bf51189f979eb0dd41744ca844fd12dfdaa0dee.tar.gz scala-3bf51189f979eb0dd41744ca844fd12dfdaa0dee.tar.bz2 scala-3bf51189f979eb0dd41744ca844fd12dfdaa0dee.zip |
Cleaning up type alias usage.
I determined that many if not most of the calls to .normalize
have no intent beyond dealiasing the type. In light of this I
went call site to call site knocking on doors and asking why
exactly they were calling any of
.normalize
.widen.normalize
.normalize.widen
and if I didn't like their answers they found themselves
introduced to 'dropAliasesAndSingleTypes', the recursive widener
and dealiaser which I concluded is necessary after all.
Discovered that the object called 'deAlias' actually depends
upon calling 'normalize', not 'dealias'. Decided this was
sufficient cause to rename it to 'normalizeAliases'.
Created dealiasWiden and dealiasWidenChain.
Dropped dropAliasesAndSingleTypes in favor of methods
on Type alongside dealias and widen (Type#dealiasWiden).
These should reduce the number of "hey, the type alias doesn't work" bugs.
7 files changed, 65 insertions, 45 deletions
diff --git a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala index c647ef6f51..c5bb8494ce 100644 --- a/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala +++ b/src/compiler/scala/tools/nsc/interpreter/CompletionOutput.scala @@ -37,7 +37,7 @@ trait CompletionOutput { val pkg = method.ownerChain find (_.isPackageClass) map (_.fullName) getOrElse "" def relativize(str: String): String = quietString(str stripPrefix (pkg + ".")) - def relativize(tp: Type): String = relativize(tp.normalize.toString) + def relativize(tp: Type): String = relativize(tp.dealiasWiden.toString) def braceList(tparams: List[String]) = if (tparams.isEmpty) "" else (tparams map relativize).mkString("[", ", ", "]") def parenList(params: List[Any]) = params.mkString("(", ", ", ")") @@ -55,8 +55,8 @@ trait CompletionOutput { } ) - def tupleString(tp: Type) = parenList(tp.normalize.typeArgs map relativize) - def functionString(tp: Type) = tp.normalize.typeArgs match { + def tupleString(tp: Type) = parenList(tp.dealiasWiden.typeArgs map relativize) + def functionString(tp: Type) = tp.dealiasWiden.typeArgs match { case List(t, r) => t + " => " + r case xs => parenList(xs.init) + " => " + xs.last } @@ -64,7 +64,7 @@ trait CompletionOutput { def tparamsString(tparams: List[Symbol]) = braceList(tparams map (_.defString)) def paramsString(params: List[Symbol]) = { def paramNameString(sym: Symbol) = if (sym.isSynthetic) "" else sym.nameString + ": " - def paramString(sym: Symbol) = paramNameString(sym) + typeToString(sym.info.normalize) + def paramString(sym: Symbol) = paramNameString(sym) + typeToString(sym.info.dealiasWiden) val isImplicit = params.nonEmpty && params.head.isImplicit val strs = (params map paramString) match { diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala index 91e909b1f1..36f012229e 100644 --- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala +++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala @@ -523,7 +523,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends // normalize non-public types so we don't see protected aliases like Self def normalizeNonPublic(tp: Type) = tp match { - case TypeRef(_, sym, _) if sym.isAliasType && !sym.isPublic => tp.normalize + case TypeRef(_, sym, _) if sym.isAliasType && !sym.isPublic => tp.dealias case _ => tp } diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index 8d6e0c3b85..ed1e6d01e8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -439,8 +439,8 @@ trait Implicits { val start = if (Statistics.canEnable) Statistics.startTimer(matchesPtNanos) else null val result = normSubType(tp, pt) || isView && { pt match { - case TypeRef(_, Function1.Sym, args) => - matchesPtView(tp, args.head, args.tail.head, undet) + case TypeRef(_, Function1.Sym, arg1 :: arg2 :: Nil) => + matchesPtView(tp, arg1, arg2, undet) case _ => false } @@ -484,7 +484,7 @@ trait Implicits { loop(restpe, pt) else pt match { case tr @ TypeRef(pre, sym, args) => - if (sym.isAliasType) loop(tp, pt.normalize) + if (sym.isAliasType) loop(tp, pt.dealias) else if (sym.isAbstractType) loop(tp, pt.bounds.lo) else { val len = args.length - 1 @@ -528,18 +528,15 @@ trait Implicits { * to a final true or false. */ private def isPlausiblySubType(tp1: Type, tp2: Type) = !isImpossibleSubType(tp1, tp2) - private def isImpossibleSubType(tp1: Type, tp2: Type) = tp1.normalize.widen match { - case tr1 @ TypeRef(_, sym1, _) => - // We can only rule out a subtype relationship if the left hand - // side is a class, else we may not know enough. - sym1.isClass && (tp2.normalize.widen match { - case TypeRef(_, sym2, _) => - sym2.isClass && !(sym1 isWeakSubClass sym2) - case RefinedType(parents, decls) => - decls.nonEmpty && - tr1.member(decls.head.name) == NoSymbol - case _ => false - }) + private def isImpossibleSubType(tp1: Type, tp2: Type) = tp1.dealiasWiden match { + // We can only rule out a subtype relationship if the left hand + // side is a class, else we may not know enough. + case tr1 @ TypeRef(_, sym1, _) if sym1.isClass => + tp2.dealiasWiden match { + case TypeRef(_, sym2, _) => sym2.isClass && !(sym1 isWeakSubClass sym2) + case RefinedType(parents, decls) => decls.nonEmpty && tr1.member(decls.head.name) == NoSymbol + case _ => false + } case _ => false } @@ -1010,7 +1007,7 @@ trait Implicits { args foreach (getParts(_)) } } else if (sym.isAliasType) { - getParts(tp.normalize) + getParts(tp.dealias) } else if (sym.isAbstractType) { getParts(tp.bounds.hi) } @@ -1168,7 +1165,7 @@ trait Implicits { implicit def wrapResult(tree: Tree): SearchResult = if (tree == EmptyTree) SearchFailure else new SearchResult(tree, if (from.isEmpty) EmptyTreeTypeSubstituter else new TreeTypeSubstituter(from, to)) - val tp1 = tp0.normalize + val tp1 = tp0.dealias tp1 match { case ThisType(_) | SingleType(_, _) => // can't generate a reference to a value that's abstracted over by an existential diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 2693fcfd27..a43dbae1fa 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -43,7 +43,7 @@ trait Infer extends Checkable { case formal => formal } else formals if (isVarArgTypes(formals1) && (removeRepeated || formals.length != nargs)) { - val ft = formals1.last.normalize.typeArgs.head + val ft = formals1.last.dealiasWiden.typeArgs.head formals1.init ::: (for (i <- List.range(formals1.length - 1, nargs)) yield ft) } else formals1 } @@ -1437,9 +1437,9 @@ trait Infer extends Checkable { } object approximateAbstracts extends TypeMap { - def apply(tp: Type): Type = tp.normalize match { + def apply(tp: Type): Type = tp.dealiasWiden match { case TypeRef(pre, sym, _) if sym.isAbstractType => WildcardType - case _ => mapOver(tp) + case _ => mapOver(tp) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index abc5baff72..b5f456d1ae 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -13,7 +13,7 @@ package scala.tools.nsc package typechecker import scala.collection.mutable -import scala.reflect.internal.util.{ BatchSourceFile, Statistics } +import scala.reflect.internal.util.{ BatchSourceFile, Statistics, shortClassOfInstance } import mutable.ListBuffer import symtab.Flags._ @@ -227,7 +227,7 @@ trait Typers extends Modes with Adaptations with Tags { case ExistentialType(tparams, tpe) => new SubstWildcardMap(tparams).apply(tp) case TypeRef(_, sym, _) if sym.isAliasType => - val tp0 = tp.normalize + val tp0 = tp.dealias val tp1 = dropExistential(tp0) if (tp1 eq tp0) tp else tp1 case _ => tp @@ -413,7 +413,7 @@ trait Typers extends Modes with Adaptations with Tags { if (!hiddenSymbols.isEmpty && hiddenSymbols.head == sym && sym.isAliasType && sameLength(sym.typeParams, args)) { hiddenSymbols = hiddenSymbols.tail - t.normalize + t.dealias } else t case SingleType(_, sym) => checkNoEscape(sym) @@ -1033,9 +1033,9 @@ trait Typers extends Modes with Adaptations with Tags { adapt(tree setType restpe, mode, pt, original) case TypeRef(_, ByNameParamClass, List(arg)) if ((mode & EXPRmode) != 0) => // (2) adapt(tree setType arg, mode, pt, original) - case tr @ TypeRef(_, sym, _) if sym.isAliasType && tr.normalize.isInstanceOf[ExistentialType] && + case tr @ TypeRef(_, sym, _) if sym.isAliasType && tr.dealias.isInstanceOf[ExistentialType] && ((mode & (EXPRmode | LHSmode)) == EXPRmode) => - adapt(tree setType tr.normalize.skolemizeExistential(context.owner, tree), mode, pt, original) + adapt(tree setType tr.dealias.skolemizeExistential(context.owner, tree), mode, pt, original) case et @ ExistentialType(_, _) if ((mode & (EXPRmode | LHSmode)) == EXPRmode) => adapt(tree setType et.skolemizeExistential(context.owner, tree), mode, pt, original) case PolyType(tparams, restpe) if inNoModes(mode, TAPPmode | PATTERNmode | HKmode) => // (3) @@ -1105,7 +1105,7 @@ trait Typers extends Modes with Adaptations with Tags { if (tree1.tpe <:< pt) adapt(tree1, mode, pt, original) else { if (inExprModeButNot(mode, FUNmode)) { - pt.normalize match { + pt.dealias match { case TypeRef(_, sym, _) => // note: was if (pt.typeSymbol == UnitClass) but this leads to a potentially // infinite expansion if pt is constant type () @@ -1251,7 +1251,7 @@ trait Typers extends Modes with Adaptations with Tags { def adaptToMember(qual: Tree, searchTemplate: Type, reportAmbiguous: Boolean = true, saveErrors: Boolean = true): Tree = { if (isAdaptableWithView(qual)) { - qual.tpe.widen.normalize match { + qual.tpe.dealiasWiden match { case et: ExistentialType => qual setType et.skolemizeExistential(context.owner, qual) // open the existential case _ => @@ -1766,7 +1766,7 @@ trait Typers extends Modes with Adaptations with Tags { _.typedTemplate(cdef.impl, parentTypes(cdef.impl)) } val impl2 = finishMethodSynthesis(impl1, clazz, context) - if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.normalize.typeSymbol == AnyClass) + if (clazz.isTrait && clazz.info.parents.nonEmpty && clazz.info.firstParent.typeSymbol == AnyClass) checkEphemeral(clazz, impl2.body) if ((clazz != ClassfileAnnotationClass) && (clazz isNonBottomSubClass ClassfileAnnotationClass)) @@ -3675,7 +3675,7 @@ trait Typers extends Modes with Adaptations with Tags { val normalizeLocals = new TypeMap { def apply(tp: Type): Type = tp match { case TypeRef(pre, sym, args) => - if (sym.isAliasType && containsLocal(tp)) apply(tp.normalize) + if (sym.isAliasType && containsLocal(tp)) apply(tp.dealias) else { if (pre.isVolatile) InferTypeWithVolatileTypeSelectionError(tree, pre) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 282d7e18ac..7a63699259 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -563,6 +563,26 @@ trait Types extends api.Types { self: SymbolTable => /** Expands type aliases. */ def dealias = this + /** Repeatedly apply widen and dealias until they have no effect. + * This compensates for the fact that type aliases can hide beneath + * singleton types and singleton types can hide inside type aliases. + */ + def dealiasWiden: Type = ( + if (this ne widen) widen.dealiasWiden + else if (this ne dealias) dealias.dealiasWiden + else this + ) + + /** All the types encountered in the course of dealiasing/widening, + * including each intermediate beta reduction step (whereas calling + * dealias applies as many as possible.) + */ + def dealiasWidenChain: List[Type] = this :: ( + if (this ne widen) widen.dealiasWidenChain + else if (this ne betaReduce) betaReduce.dealiasWidenChain + else Nil + ) + def etaExpand: Type = this /** Performs a single step of beta-reduction on types. @@ -3236,7 +3256,7 @@ trait Types extends api.Types { self: SymbolTable => if (constr.instValid) constr.inst // get here when checking higher-order subtyping of the typevar by itself // TODO: check whether this ever happens? - else if (isHigherKinded) typeFun(params, applyArgs(params map (_.typeConstructor))) + else if (isHigherKinded) logResult("Normalizing HK $this")(typeFun(params, applyArgs(params map (_.typeConstructor)))) else super.normalize ) override def typeSymbol = origin.typeSymbol @@ -3663,7 +3683,7 @@ trait Types extends api.Types { self: SymbolTable => def existentialAbstraction(tparams: List[Symbol], tpe0: Type): Type = if (tparams.isEmpty) tpe0 else { - val tpe = deAlias(tpe0) + val tpe = normalizeAliases(tpe0) val tpe1 = new ExistentialExtrapolation(tparams) extrapolate tpe var tparams0 = tparams var tparams1 = tparams0 filter tpe1.contains @@ -3677,13 +3697,16 @@ trait Types extends api.Types { self: SymbolTable => newExistentialType(tparams1, tpe1) } - /** Remove any occurrences of type aliases from this type */ - object deAlias extends TypeMap { - def apply(tp: Type): Type = mapOver { - tp match { - case TypeRef(pre, sym, args) if sym.isAliasType => tp.normalize - case _ => tp - } + /** Normalize any type aliases within this type (@see Type#normalize). + * Note that this depends very much on the call to "normalize", not "dealias", + * so it is no longer carries the too-stealthy name "deAlias". + */ + object normalizeAliases extends TypeMap { + def apply(tp: Type): Type = tp match { + case TypeRef(_, sym, _) if sym.isAliasType => + def msg = if (tp.isHigherKinded) s"Normalizing type alias function $tp" else s"Dealiasing type alias $tp" + mapOver(logResult(msg)(tp.normalize)) + case _ => mapOver(tp) } } diff --git a/test/files/run/existentials3-old.check b/test/files/run/existentials3-old.check index 72abfac637..36a458dacc 100644 --- a/test/files/run/existentials3-old.check +++ b/test/files/run/existentials3-old.check @@ -5,7 +5,7 @@ Object with Test$ToS Object with Test$ToS scala.Function0[Object with Test$ToS] scala.Function0[Object with Test$ToS] -_ <: Object with _ <: Object with Test$ToS +_ <: Object with _ <: Object with Object with Test$ToS _ <: Object with _ <: Object with _ <: Object with Test$ToS scala.collection.immutable.List[Object with scala.collection.Seq[Int]] scala.collection.immutable.List[Object with scala.collection.Seq[_ <: Int]] @@ -16,7 +16,7 @@ Object with Test$ToS Object with Test$ToS scala.Function0[Object with Test$ToS] scala.Function0[Object with Test$ToS] -_ <: Object with _ <: Object with Test$ToS +_ <: Object with _ <: Object with Object with Test$ToS _ <: Object with _ <: Object with _ <: Object with Test$ToS scala.collection.immutable.List[Object with scala.collection.Seq[Int]] scala.collection.immutable.List[Object with scala.collection.Seq[_ <: Int]] |