From 3e6b82c59c75e7ea925a015e33aaac95f7a56b60 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Sat, 5 Oct 2013 17:06:27 -0700 Subject: Extract isStable and isVolatile from Type. As with finalResultType, this is easier to control and WAY easier to understand. Look, all the relevant code fits on half a screenful rather than being spread out over 5000 lines and you have to be constantly conscious of what is overriding what. Truly it would be hard to damn the indiscriminate use of subtype polymorphism any more soundly than does this, by way of contrast: def isStable(tp: Type): Boolean = tp match { case _: SingletonType => true case NoPrefix => true case TypeRef(_, NothingClass | SingletonClass, _) => true case TypeRef(_, sym, _) if sym.isAbstractType => tp.bounds.hi.typeSymbol isSubClass SingletonClass case TypeRef(pre, sym, _) if sym.isModuleClass => isStable(pre) case TypeRef(_, _, _) if tp ne tp.dealias => isStable(tp.dealias) case TypeVar(origin, _) => isStable(origin) case AnnotatedType(_, atp, _) => isStable(atp) // Really? case _: SimpleTypeProxy => isStable(tp.underlying) case _ => false } That's all of it! If there are bugs in it (of course there are) some might even be found now. --- .../scala/reflect/internal/Definitions.scala | 75 +++++++++++++++++++ src/reflect/scala/reflect/internal/Types.scala | 85 +++------------------- .../scala/reflect/internal/tpe/TypeComparers.scala | 2 +- 3 files changed, 85 insertions(+), 77 deletions(-) diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala index 7b2e40b59c..4d8ae73bcc 100644 --- a/src/reflect/scala/reflect/internal/Definitions.scala +++ b/src/reflect/scala/reflect/internal/Definitions.scala @@ -709,7 +709,82 @@ trait Definitions extends api.StandardDefinitions { case NullaryMethodType(restpe) => finalResultType(restpe) case _ => tp } + /** Similarly, putting all the isStable logic in one place. + * This makes it like 1000x easier to see the overall logic + * of the method. + */ + def isStable(tp: Type): Boolean = tp match { + case _: SingletonType => true + case NoPrefix => true + case TypeRef(_, NothingClass | SingletonClass, _) => true + case TypeRef(_, sym, _) if sym.isAbstractType => tp.bounds.hi.typeSymbol isSubClass SingletonClass + case TypeRef(pre, sym, _) if sym.isModuleClass => isStable(pre) + case TypeRef(_, _, _) if tp ne tp.dealias => isStable(tp.dealias) + case TypeVar(origin, _) => isStable(origin) + case AnnotatedType(_, atp, _) => isStable(atp) // Really? + case _: SimpleTypeProxy => isStable(tp.underlying) + case _ => false + } + def isVolatile(tp: Type): Boolean = { + // need to be careful not to fall into an infinite recursion here + // because volatile checking is done before all cycles are detected. + // the case to avoid is an abstract type directly or + // indirectly upper-bounded by itself. See #2918 + def isVolatileAbstractType: Boolean = { + def sym = tp.typeSymbol + def volatileUpperBound = isVolatile(tp.bounds.hi) + def safeIsVolatile = ( + if (volatileRecursions < TypeConstants.LogVolatileThreshold) + volatileUpperBound + // we can return true when pendingVolatiles contains sym, because + // a cycle will be detected afterwards and an error will result anyway. + else pendingVolatiles(sym) || { + pendingVolatiles += sym + try volatileUpperBound finally pendingVolatiles -= sym + } + ) + volatileRecursions += 1 + try safeIsVolatile finally volatileRecursions -= 1 + } + /** A refined type P1 with ... with Pn { decls } is volatile if + * one of the parent types Pi is an abstract type, and + * either i > 1, or decls or a following parent Pj, j > 1, contributes + * an abstract member. + * A type contributes an abstract member if it has an abstract member which + * is also a member of the whole refined type. A scope `decls` contributes + * an abstract member if it has an abstract definition which is also + * a member of the whole type. + */ + def isVolatileRefinedType: Boolean = { + val RefinedType(parents, decls) = tp + def isVisibleDeferred(m: Symbol) = m.isDeferred && ((tp nonPrivateMember m.name).alternatives contains m) + def contributesAbstractMembers(p: Type) = p.deferredMembers exists isVisibleDeferred + def dropConcreteParents = parents dropWhile (p => !p.typeSymbol.isAbstractType) + + (parents exists isVolatile) || { + dropConcreteParents match { + case Nil => false + case ps => (ps ne parents) || (ps.tail exists contributesAbstractMembers) || (decls exists isVisibleDeferred) + } + } + } + + tp match { + case ThisType(_) => false + case SingleType(_, sym) => isVolatile(tp.underlying) && (sym.hasVolatileType || !sym.isStable) + case NullaryMethodType(restpe) => isVolatile(restpe) + case PolyType(_, restpe) => isVolatile(restpe) + case TypeRef(_, _, _) if tp ne tp.dealias => isVolatile(tp.dealias) + case TypeRef(_, sym, _) if sym.isAbstractType => isVolatileAbstractType + case RefinedType(_, _) => isVolatileRefinedType + case TypeVar(origin, _) => isVolatile(origin) + case _: SimpleTypeProxy => isVolatile(tp.underlying) + case _ => false + } + } + private[this] var volatileRecursions: Int = 0 + private[this] val pendingVolatiles = mutable.HashSet[Symbol]() def abstractFunctionForFunctionType(tp: Type) = { assert(isFunctionType(tp), tp) abstractFunctionType(tp.typeArgs.init, tp.typeArgs.last) diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index 4367e51041..85dfa037ec 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -18,6 +18,7 @@ import util.Statistics import util.ThreeValues._ import Variance._ import Depth._ +import TypeConstants._ /* A standard type pattern match: case ErrorType => @@ -90,10 +91,6 @@ trait Types private var explainSwitch = false private final val emptySymbolSet = immutable.Set.empty[Symbol] - protected[internal] final val DefaultLogThreshhold = 50 - private final val LogPendingBaseTypesThreshold = DefaultLogThreshhold - private final val LogVolatileThreshold = DefaultLogThreshhold - private final val traceTypeVars = sys.props contains "scalac.debug.tvar" private final val breakCycles = settings.breakCycles.value /** In case anyone wants to turn off type parameter bounds being used @@ -142,8 +139,6 @@ trait Types override def typeConstructor: Type = underlying.typeConstructor override def isError = underlying.isError override def isErroneous = underlying.isErroneous - override def isStable: Boolean = underlying.isStable - override def isVolatile = underlying.isVolatile override def paramSectionCount = underlying.paramSectionCount override def paramss = underlying.paramss override def params = underlying.params @@ -270,7 +265,7 @@ trait Types def takesTypeArgs: Boolean = this.isHigherKinded /** Does this type denote a stable reference (i.e. singleton type)? */ - def isStable: Boolean = false + final def isStable: Boolean = definitions isStable this /** Is this type dangerous (i.e. it might contain conflicting * type information when empty, so that it can be constructed @@ -278,7 +273,7 @@ trait Types * type of the form T_1 with T_n { decls }, where one of the * T_i (i > 1) is an abstract type. */ - def isVolatile: Boolean = false + final def isVolatile: Boolean = definitions isVolatile this /** Is this type a structural refinement type (it ''refines'' members that have not been inherited) */ def isStructuralRefinement: Boolean = false @@ -1221,8 +1216,6 @@ trait Types abstract class SingletonType extends SubType with SimpleTypeProxy { def supertype = underlying override def isTrivial = false - override def isStable = true - override def isVolatile = underlying.isVolatile override def widen: Type = underlying.widen override def baseTypeSeq: BaseTypeSeq = { if (Statistics.canEnable) Statistics.incCounter(singletonBaseTypeSeqCount) @@ -1300,7 +1293,6 @@ trait Types /** An object representing a non-existing prefix */ case object NoPrefix extends Type { override def isTrivial: Boolean = true - override def isStable: Boolean = true override def prefixString = "" override def safeToString: String = "" override def kind = "NoPrefixType" @@ -1318,7 +1310,6 @@ trait Types override def isTrivial: Boolean = sym.isPackageClass override def typeSymbol = sym override def underlying: Type = sym.typeOfThis - override def isVolatile = false override def isHigherKinded = sym.isRefinementClass && underlying.isHigherKinded override def prefixString = if (settings.debug) sym.nameString + ".this." @@ -1367,8 +1358,6 @@ trait Types // more precise conceptually, but causes cyclic errors: (paramss exists (_ contains sym)) override def isImmediatelyDependent = (sym ne NoSymbol) && (sym.owner.isMethod && sym.isValueParameter) - - override def isVolatile : Boolean = underlying.isVolatile && (sym.hasVolatileType || !sym.isStable) /* override def narrow: Type = { if (phase.erasedTypes) this @@ -1764,33 +1753,6 @@ trait Types typeSymbol)) } else super.normalize } - - /** A refined type P1 with ... with Pn { decls } is volatile if - * one of the parent types Pi is an abstract type, and - * either i > 1, or decls or a following parent Pj, j > 1, contributes - * an abstract member. - * A type contributes an abstract member if it has an abstract member which - * is also a member of the whole refined type. A scope `decls` contributes - * an abstract member if it has an abstract definition which is also - * a member of the whole type. - */ - override def isVolatile = { - def isVisible(m: Symbol) = - this.nonPrivateMember(m.name).alternatives contains m - def contributesAbstractMembers(p: Type) = - p.deferredMembers exists isVisible - - ((parents exists (_.isVolatile)) - || - (parents dropWhile (! _.typeSymbol.isAbstractType) match { - case ps @ (_ :: ps1) => - (ps ne parents) || - (ps1 exists contributesAbstractMembers) || - (decls.iterator exists (m => m.isDeferred && isVisible(m))) - case _ => - false - })) - } override def kind = "RefinedType" } @@ -2026,7 +1988,6 @@ trait Types class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { require(sym.isModuleClass, sym) private[this] var narrowedCache: Type = _ - override def isStable = pre.isStable override def narrow = { if (narrowedCache eq null) narrowedCache = singleType(pre, sym.sourceModule) @@ -2041,7 +2002,6 @@ trait Types } class PackageTypeRef(pre0: Type, sym0: Symbol) extends ModuleTypeRef(pre0, sym0) { require(sym.isPackageClass, sym) - override def isStable = true override protected def finishPrefix(rest: String) = packagePrefix + rest } class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef { @@ -2148,8 +2108,6 @@ trait Types require(sym.isAliasType, sym) override def dealias = if (typeParamsMatchArgs) betaReduce.dealias else super.dealias - override def isStable = normalize.isStable - override def isVolatile = normalize.isVolatile override def narrow = normalize.narrow override def thisInfo = normalize override def prefix = if (this ne normalize) normalize.prefix else pre @@ -2204,30 +2162,6 @@ trait Types private var symInfoCache: Type = _ private var thisInfoCache: Type = _ - override def isVolatile = { - // need to be careful not to fall into an infinite recursion here - // because volatile checking is done before all cycles are detected. - // the case to avoid is an abstract type directly or - // indirectly upper-bounded by itself. See #2918 - try { - volatileRecursions += 1 - if (volatileRecursions < LogVolatileThreshold) - bounds.hi.isVolatile - else if (pendingVolatiles(sym)) - true // we can return true here, because a cycle will be detected - // here afterwards and an error will result anyway. - else - try { - pendingVolatiles += sym - bounds.hi.isVolatile - } finally { - pendingVolatiles -= sym - } - } finally { - volatileRecursions -= 1 - } - } - override def thisInfo = { val symInfo = sym.info if (thisInfoCache == null || (symInfo ne symInfoCache)) { @@ -2242,7 +2176,6 @@ trait Types } thisInfoCache } - override def isStable = bounds.hi.typeSymbol isSubClass SingletonClass override def bounds = thisInfo.bounds override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this override def kind = "AbstractTypeRef" @@ -2328,7 +2261,6 @@ trait Types override def baseClasses = thisInfo.baseClasses override def baseTypeSeqDepth = baseTypeSeq.maxDepth - override def isStable = (sym eq NothingClass) || (sym eq SingletonClass) override def prefix = pre override def termSymbol = super.termSymbol override def termSymbolDirect = super.termSymbol @@ -2595,7 +2527,6 @@ trait Types override def baseClasses: List[Symbol] = resultType.baseClasses override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) override def boundSyms = resultType.boundSyms - override def isVolatile = resultType.isVolatile override def safeToString: String = "=> "+ resultType override def kind = "NullaryMethodType" } @@ -2634,7 +2565,6 @@ trait Types override def baseClasses: List[Symbol] = resultType.baseClasses override def baseType(clazz: Symbol): Type = resultType.baseType(clazz) override def narrow: Type = resultType.narrow - override def isVolatile = resultType.isVolatile /** @M: typeDefSig wraps a TypeBounds in a PolyType * to represent a higher-kinded type parameter @@ -2679,7 +2609,6 @@ trait Types override protected def rewrap(newtp: Type) = existentialAbstraction(quantified, newtp) override def isTrivial = false - override def isStable: Boolean = false override def bounds = TypeBounds(maybeRewrap(underlying.bounds.lo), maybeRewrap(underlying.bounds.hi)) override def parents = underlying.parents map maybeRewrap override def boundSyms = quantified.toSet @@ -3239,8 +3168,6 @@ trait Types else super.normalize ) override def typeSymbol = origin.typeSymbol - override def isStable = origin.isStable - override def isVolatile = origin.isVolatile private def tparamsOfSym(sym: Symbol) = sym.info match { case PolyType(tparams, _) if tparams.nonEmpty => @@ -4651,6 +4578,12 @@ trait Types } +object TypeConstants { + final val DefaultLogThreshhold = 50 + final val LogPendingBaseTypesThreshold = DefaultLogThreshhold + final val LogVolatileThreshold = DefaultLogThreshhold +} + object TypesStats { import BaseTypeSeqsStats._ val rawTypeCount = Statistics.newCounter ("#raw type creations") diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index 152c689c56..6532bce9f0 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -12,7 +12,7 @@ trait TypeComparers { import definitions._ import TypesStats._ - private final val LogPendingSubTypesThreshold = DefaultLogThreshhold + private final val LogPendingSubTypesThreshold = TypeConstants.DefaultLogThreshhold private val pendingSubTypes = new mutable.HashSet[SubTypePair] -- cgit v1.2.3