summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/Types.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/reflect/scala/reflect/internal/Types.scala')
-rw-r--r--src/reflect/scala/reflect/internal/Types.scala782
1 files changed, 445 insertions, 337 deletions
diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala
index 28b16eeb1a..dc12ef9352 100644
--- a/src/reflect/scala/reflect/internal/Types.scala
+++ b/src/reflect/scala/reflect/internal/Types.scala
@@ -7,7 +7,7 @@ package scala
package reflect
package internal
-import scala.collection.{ mutable, immutable, generic }
+import scala.collection.{ mutable, immutable }
import scala.ref.WeakReference
import mutable.ListBuffer
import Flags._
@@ -91,7 +91,6 @@ trait Types
private var explainSwitch = false
private final val emptySymbolSet = immutable.Set.empty[Symbol]
- private final val traceTypeVars = sys.props contains "scalac.debug.tvar"
private final val breakCycles = settings.breakCycles.value
/** In case anyone wants to turn on type parameter bounds being used
* to seed type constraints.
@@ -99,8 +98,6 @@ trait Types
private final val propagateParameterBoundsToTypeVars = sys.props contains "scalac.debug.prop-constraints"
private final val sharperSkolems = sys.props contains "scalac.experimental.sharper-skolems"
- protected val enableTypeVarExperimentals = settings.Xexperimental.value
-
/** Caching the most recent map has a 75-90% hit rate. */
private object substTypeMapCache {
private[this] var cached: SubstTypeMap = new SubstTypeMap(Nil, Nil)
@@ -172,11 +169,16 @@ trait Types
trait RewrappingTypeProxy extends SimpleTypeProxy {
protected def maybeRewrap(newtp: Type) = (
if (newtp eq underlying) this
- // BoundedWildcardTypes reach here during erroneous compilation: neg/t6258
- // Higher-kinded exclusion is because [x]CC[x] compares =:= to CC: pos/t3800
- // Otherwise, if newtp =:= underlying, don't rewrap it.
- else if (!newtp.isWildcard && !newtp.isHigherKinded && (newtp =:= underlying)) this
- else rewrap(newtp)
+ else {
+ // - BoundedWildcardTypes reach here during erroneous compilation: neg/t6258
+ // - Higher-kinded exclusion is because [x]CC[x] compares =:= to CC: pos/t3800
+ // - Avoid reusing the existing Wrapped(RefinedType) when we've be asked to wrap an =:= RefinementTypeRef, the
+ // distinction is important in base type sequences. See TypesTest.testExistentialRefinement
+ // - Otherwise, if newtp =:= underlying, don't rewrap it.
+ val hasSpecialMeaningBeyond_=:= = newtp.isWildcard || newtp.isHigherKinded || newtp.isInstanceOf[RefinementTypeRef]
+ if (!hasSpecialMeaningBeyond_=:= && (newtp =:= underlying)) this
+ else rewrap(newtp)
+ }
)
protected def rewrap(newtp: Type): Type
@@ -307,6 +309,9 @@ trait Types
/** Is this type completed (i.e. not a lazy type)? */
def isComplete: Boolean = true
+ /** Should this be printed as an infix type (@showAsInfix class &&[T, U])? */
+ def isShowAsInfixType: Boolean = false
+
/** If this is a lazy type, assign a new type to `sym`. */
def complete(sym: Symbol) {}
@@ -467,7 +472,7 @@ trait Types
* the empty list for all other types */
def boundSyms: immutable.Set[Symbol] = emptySymbolSet
- /** Replace formal type parameter symbols with actual type arguments.
+ /** Replace formal type parameter symbols with actual type arguments. ErrorType on arity mismatch.
*
* Amounts to substitution except for higher-kinded types. (See overridden method in TypeRef) -- @M
*/
@@ -594,7 +599,12 @@ trait Types
def nonPrivateMembersAdmitting(admit: Long): Scope = membersBasedOnFlags(BridgeAndPrivateFlags & ~admit, 0)
/** A list of all implicit symbols of this type (defined or inherited) */
- def implicitMembers: Scope = membersBasedOnFlags(BridgeFlags, IMPLICIT)
+ def implicitMembers: Scope = {
+ typeSymbolDirect match {
+ case sym: ModuleClassSymbol => sym.implicitMembers
+ case _ => membersBasedOnFlags(BridgeFlags, IMPLICIT)
+ }
+ }
/** A list of all deferred symbols of this type (defined or inherited) */
def deferredMembers: Scope = membersBasedOnFlags(BridgeFlags, DEFERRED)
@@ -611,6 +621,8 @@ trait Types
def nonPrivateMember(name: Name): Symbol =
memberBasedOnName(name, BridgeAndPrivateFlags)
+ def packageObject: Symbol = member(nme.PACKAGE)
+
/** The non-private member with given name, admitting members with given flags `admit`.
* "Admitting" refers to the fact that members with a PRIVATE, BRIDGE, or VBRIDGE
* flag are usually excluded from findMember results, but supplying any of those flags
@@ -664,7 +676,7 @@ trait Types
)
if (trivial) this
else {
- val m = newAsSeenFromMap(pre.normalize, clazz)
+ val m = new AsSeenFromMap(pre.normalize, clazz)
val tp = m(this)
val tp1 = existentialAbstraction(m.capturedParams, tp)
@@ -684,23 +696,21 @@ trait Types
* }}}
*/
def memberInfo(sym: Symbol): Type = {
- require(sym ne NoSymbol, this)
+// assert(sym ne NoSymbol, this)
sym.info.asSeenFrom(this, sym.owner)
}
/** The type of `sym`, seen as a member of this type. */
- def memberType(sym: Symbol): Type = sym match {
- case meth: MethodSymbol =>
- meth.typeAsMemberOf(this)
- case _ =>
- computeMemberType(sym)
- }
-
- def computeMemberType(sym: Symbol): Type = sym.tpeHK match { //@M don't prematurely instantiate higher-kinded types, they will be instantiated by transform, typedTypeApply, etc. when really necessary
- case OverloadedType(_, alts) =>
- OverloadedType(this, alts)
+ def memberType(sym: Symbol): Type = sym.tpeHK match {
+ case OverloadedType(_, alts) => OverloadedType(this, alts)
case tp =>
- if (sym eq NoSymbol) NoType else tp.asSeenFrom(this, sym.owner)
+ // Correct caching is nearly impossible because `sym.tpeHK.asSeenFrom(pre, sym.owner)`
+ // may have different results even for reference-identical `sym.tpeHK` and `pre` (even in the same period).
+ // For example, `pre` could be a `ThisType`. For such a type, `tpThen eq tpNow` does not imply
+ // `tpThen` and `tpNow` mean the same thing, because `tpThen.typeSymbol.info` could have been different
+ // from what it is now, and the cache won't know simply by looking at `pre`.
+ if (sym eq NoSymbol) NoType
+ else tp.asSeenFrom(this, sym.owner)
}
/** Substitute types `to` for occurrences of references to
@@ -957,6 +967,8 @@ trait Types
*/
def directObjectString = safeToString
+ def nameAndArgsString = typeSymbol.name.toString
+
/** A test whether a type contains any unification type variables.
* Overridden with custom logic except where trivially true.
*/
@@ -1194,7 +1206,6 @@ trait Types
object ThisType extends ThisTypeExtractor {
def apply(sym: Symbol): Type = (
if (!phase.erasedTypes) unique(new UniqueThisType(sym))
- else if (sym.isImplClass) sym.typeOfThis
else sym.tpe_*
)
}
@@ -1212,6 +1223,10 @@ trait Types
private[reflect] var underlyingCache: Type = NoType
private[reflect] var underlyingPeriod = NoPeriod
+ private[Types] def invalidateSingleTypeCaches(): Unit = {
+ underlyingCache = NoType
+ underlyingPeriod = NoPeriod
+ }
override def underlying: Type = {
val cache = underlyingCache
if (underlyingPeriod == currentPeriod && cache != null) cache
@@ -1352,6 +1367,12 @@ trait Types
private[reflect] var baseTypeSeqPeriod = NoPeriod
private[reflect] var baseClassesCache: List[Symbol] = _
private[reflect] var baseClassesPeriod = NoPeriod
+ private[Types] def invalidatedCompoundTypeCaches() {
+ baseTypeSeqCache = null
+ baseTypeSeqPeriod = NoPeriod
+ baseClassesCache = null
+ baseClassesPeriod = NoPeriod
+ }
override def baseTypeSeq: BaseTypeSeq = {
val cached = baseTypeSeqCache
@@ -1580,13 +1601,11 @@ trait Types
*/
case class RefinedType(override val parents: List[Type],
override val decls: Scope) extends CompoundType with RefinedTypeApi {
-
override def isHigherKinded = (
parents.nonEmpty &&
(parents forall typeIsHigherKinded) &&
!phase.erasedTypes
)
-
override def typeParams =
if (isHigherKinded) firstParent.typeParams
else super.typeParams
@@ -1605,7 +1624,14 @@ trait Types
private var normalized: Type = _
private def normalizeImpl = {
// TODO see comments around def intersectionType and def merge
- def flatten(tps: List[Type]): List[Type] = tps flatMap { case RefinedType(parents, ds) if ds.isEmpty => flatten(parents) case tp => List(tp) }
+ // SI-8575 The dealias is needed here to keep subtyping transitive, example in run/t8575b.scala
+ def flatten(tps: List[Type]): List[Type] = {
+ def dealiasRefinement(tp: Type) = if (tp.dealias.isInstanceOf[RefinedType]) tp.dealias else tp
+ tps map dealiasRefinement flatMap {
+ case RefinedType(parents, ds) if ds.isEmpty => flatten(parents)
+ case tp => List(tp)
+ }
+ }
val flattened = flatten(parents).distinct
if (decls.isEmpty && hasLength(flattened, 1)) {
flattened.head
@@ -1848,53 +1874,13 @@ trait Types
override def isHigherKinded = false
override def typeParams = Nil
- override def transform(tp: Type): Type = {
- // This situation arises when a typevar is encountered for which
- // too little information is known to determine its kind, and
- // it later turns out not to have kind *. See SI-4070. Only
- // logging it for now.
- val tparams = sym.typeParams
- if (tparams.size != args.size)
- devWarning(s"$this.transform($tp), but tparams.isEmpty and args=$args")
- def asSeenFromInstantiated(tp: Type) =
- asSeenFromOwner(tp).instantiateTypeParams(tparams, args)
- // If we're called with a poly type, and we were to run the `asSeenFrom`, over the entire
- // type, we can end up with new symbols for the type parameters (clones from TypeMap).
- // The subsequent substitution of type arguments would fail. This problem showed up during
- // the fix for SI-8046, however the solution taken there wasn't quite right, and led to
- // SI-8170.
- //
- // Now, we detect the PolyType before both the ASF *and* the substitution, and just operate
- // on the result type.
- //
- // TODO: Revisit this and explore the questions raised:
- //
- // AM: I like this better than the old code, but is there any way the tparams would need the ASF treatment as well?
- // JZ: I think its largely irrelevant, as they are no longer referred to in the result type.
- // In fact, you can get away with returning a type of kind * here and the sky doesn't fall:
- // `case PolyType(`tparams`, result) => asSeenFromInstantiated(result)`
- // But I thought it was better to retain the kind.
- // AM: I've been experimenting with apply-type-args-then-ASF, but running into cycles.
- // In general, it seems iffy the tparams can never occur in the result
- // then we might as well represent the type as a no-arg typeref.
- // AM: I've also been trying to track down uses of transform (pretty generic name for something that
- // does not seem that widely applicable).
- // It's kind of a helper for computing baseType (since it tries to propagate our type args to some
- // other type, which has to be related to this type for that to make sense).
- //
- tp match {
- case PolyType(`tparams`, result) => PolyType(tparams, asSeenFromInstantiated(result))
- case _ => asSeenFromInstantiated(tp)
- }
- }
-
// note: does not go through typeRef. There's no need to because
// neither `pre` nor `sym` changes. And there's a performance
// advantage to call TypeRef directly.
override def typeConstructor = TypeRef(pre, sym, Nil)
}
- class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef {
+ class ModuleTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) {
require(sym.isModuleClass, sym)
private[this] var narrowedCache: Type = _
override def narrow = {
@@ -1903,6 +1889,10 @@ trait Types
narrowedCache
}
+ override private[Types] def invalidateTypeRefCaches(): Unit = {
+ super.invalidateTypeRefCaches()
+ narrowedCache = null
+ }
override protected def finishPrefix(rest: String) = objectPrefix + rest
override def directObjectString = super.safeToString
override def toLongString = toString
@@ -1913,12 +1903,12 @@ trait Types
require(sym.isPackageClass, sym)
override protected def finishPrefix(rest: String) = packagePrefix + rest
}
- class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) with ClassTypeRef {
+ class RefinementTypeRef(pre0: Type, sym0: Symbol) extends NoArgsTypeRef(pre0, sym0) {
require(sym.isRefinementClass, sym)
// I think this is okay, but see #1241 (r12414), #2208, and typedTypeConstructor in Typers
- override protected def normalizeImpl: Type = sym.info.normalize
- override protected def finishPrefix(rest: String) = "" + thisInfo
+ override protected def normalizeImpl: Type = pre.memberInfo(sym).normalize
+ override protected def finishPrefix(rest: String) = "" + sym.info
}
class NoArgsTypeRef(pre0: Type, sym0: Symbol) extends TypeRef(pre0, sym0, Nil) {
@@ -1929,7 +1919,6 @@ trait Types
// represented as existential types.
override def isHigherKinded = (typeParams ne Nil)
override def typeParams = if (isDefinitionsInitialized) sym.typeParams else sym.unsafeTypeParams
- private def isRaw = !phase.erasedTypes && isRawIfWithoutArgs(sym)
override def instantiateTypeParams(formals: List[Symbol], actuals: List[Type]): Type =
if (isHigherKinded) {
@@ -1942,17 +1931,6 @@ trait Types
else
super.instantiateTypeParams(formals, actuals)
- override def transform(tp: Type): Type = {
- val res = asSeenFromOwner(tp)
- if (isHigherKinded && !isRaw)
- res.instantiateTypeParams(typeParams, dummyArgs)
- else
- res
- }
-
- override def transformInfo(tp: Type): Type =
- appliedType(asSeenFromOwner(tp), dummyArgs)
-
override def narrow =
if (sym.isModuleClass) singleType(pre, sym.sourceModule)
else super.narrow
@@ -1964,65 +1942,75 @@ trait Types
if (isHigherKinded) etaExpand else super.normalizeImpl
}
- trait ClassTypeRef extends TypeRef {
- // !!! There are scaladoc-created symbols arriving which violate this require.
- // require(sym.isClass, sym)
-
- override def baseType(clazz: Symbol): Type =
- if (sym == clazz) this
- else transform(sym.info.baseType(clazz))
- }
-
trait NonClassTypeRef extends TypeRef {
require(sym.isNonClassType, sym)
- /* Syncnote: These are pure caches for performance; no problem to evaluate these
- * several times. Hence, no need to protected with synchronized in a multi-threaded
- * usage scenario.
- */
+ /** Syncnote: These are pure caches for performance; no problem to evaluate these
+ * several times. Hence, no need to protected with synchronized in a multi-threaded
+ * usage scenario.
+ */
private var relativeInfoCache: Type = _
- private var relativeInfoPeriod: Period = NoPeriod
+ private var relativeInfoCacheValidForPeriod: Period = NoPeriod
+ private var relativeInfoCacheValidForSymInfo: Type = _
+
+ override private[Types] def invalidateTypeRefCaches(): Unit = {
+ super.invalidateTypeRefCaches()
+ relativeInfoCache = NoType
+ relativeInfoCacheValidForPeriod = NoPeriod
+ relativeInfoCacheValidForSymInfo = null
+ }
+
+ final override protected def relativeInfo = {
+ val symInfo = sym.info
+ if ((relativeInfoCache eq null) || (relativeInfoCacheValidForSymInfo ne symInfo) || (relativeInfoCacheValidForPeriod != currentPeriod)) {
+ relativeInfoCache = super.relativeInfo
+
+ if (this.isInstanceOf[AbstractTypeRef]) validateRelativeInfo()
- private[Types] def relativeInfo = /*trace(s"relativeInfo(${safeToString}})")*/{
- if (relativeInfoPeriod != currentPeriod) {
- val memberInfo = pre.memberInfo(sym)
- relativeInfoCache = transformInfo(memberInfo)
- relativeInfoPeriod = currentPeriod
+ relativeInfoCacheValidForSymInfo = symInfo
+ relativeInfoCacheValidForPeriod = currentPeriod
}
relativeInfoCache
}
- override def baseType(clazz: Symbol): Type =
- if (sym == clazz) this else baseTypeOfNonClassTypeRef(this, clazz)
+ private def validateRelativeInfo(): Unit = relativeInfoCache match {
+ // If a subtyping cycle is not detected here, we'll likely enter an infinite
+ // loop before a sensible error can be issued. SI-5093 is one example.
+ case x: SubType if x.supertype eq this =>
+ relativeInfoCache = null
+ throw new RecoverableCyclicReference(sym)
+ case _ =>
+ }
}
- protected def baseTypeOfNonClassTypeRef(tpe: NonClassTypeRef, clazz: Symbol) = try {
- basetypeRecursions += 1
- if (basetypeRecursions < LogPendingBaseTypesThreshold)
- tpe.relativeInfo.baseType(clazz)
- else if (pendingBaseTypes contains tpe)
- if (clazz == AnyClass) clazz.tpe else NoType
- else
- try {
- pendingBaseTypes += tpe
- tpe.relativeInfo.baseType(clazz)
- } finally {
- pendingBaseTypes -= tpe
- }
- } finally {
- basetypeRecursions -= 1
- }
trait AliasTypeRef extends NonClassTypeRef {
require(sym.isAliasType, sym)
override def dealias = if (typeParamsMatchArgs) betaReduce.dealias else super.dealias
override def narrow = normalize.narrow
- override def thisInfo = normalize
override def prefix = if (this ne normalize) normalize.prefix else pre
override def termSymbol = if (this ne normalize) normalize.termSymbol else super.termSymbol
override def typeSymbol = if (this ne normalize) normalize.typeSymbol else sym
+ override protected[Types] def parentsImpl: List[Type] = normalize.parents map relativize
+
+ // `baseClasses` is sensitive to type args when referencing type members
+ // consider `type foo[x] = x`, `typeOf[foo[String]].baseClasses` should be the same as `typeOf[String].baseClasses`,
+ // which would be lost by looking at `sym.info` without propagating args
+ // since classes cannot be overridden, the prefix can be ignored
+ // (in fact, taking the prefix into account by replacing `normalize`
+ // with `relativeInfo` breaks pos/t8177g.scala, which is probably a bug, but a tricky one...
+ override def baseClasses = normalize.baseClasses
+
+ // similar reasoning holds here as for baseClasses
+ // as another example, consider the type alias `Foo` in `class O { o => type Foo = X { val bla: o.Bar }; type Bar }`
+ // o1.Foo and o2.Foo have different decls `val bla: o1.Bar` versus `val bla: o2.Bar`
+ // In principle, you should only call `sym.info.decls` when you know `sym.isClass`,
+ // and you should `relativize` the infos of the resulting members.
+ // The latter is certainly violated in multiple spots in the codebase (the members are usually transformed correctly, though).
+ override def decls: Scope = normalize.decls
+
// beta-reduce, but don't do partial application -- cycles have been checked in typeRef
override protected def normalizeImpl =
if (typeParamsMatchArgs) betaReduce.normalize
@@ -2045,7 +2033,7 @@ trait Types
//
// this crashes pos/depmet_implicit_tpbetareduce.scala
// appliedType(sym.info, typeArgs).asSeenFrom(pre, sym.owner)
- override def betaReduce = transform(sym.info.resultType)
+ override def betaReduce = relativize(sym.info.resultType)
/** 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).
@@ -2095,27 +2083,13 @@ trait Types
trait AbstractTypeRef extends NonClassTypeRef {
require(sym.isAbstractType, sym)
- /** Syncnote: Pure performance caches; no need to synchronize in multi-threaded environment
- */
- private var symInfoCache: Type = _
- private var thisInfoCache: Type = _
+ override def baseClasses = relativeInfo.baseClasses
+ override def decls = relativeInfo.decls
+ override def bounds = relativeInfo.bounds
+
+ override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = bounds.hi.baseTypeSeq prepend this
+ override protected[Types] def parentsImpl: List[Type] = relativeInfo.parents
- override def thisInfo = {
- val symInfo = sym.info
- if (thisInfoCache == null || (symInfo ne symInfoCache)) {
- symInfoCache = symInfo
- thisInfoCache = transformInfo(symInfo) match {
- // If a subtyping cycle is not detected here, we'll likely enter an infinite
- // loop before a sensible error can be issued. SI-5093 is one example.
- case x: SubType if x.supertype eq this =>
- throw new RecoverableCyclicReference(sym)
- case tp => tp
- }
- }
- thisInfoCache
- }
- override def bounds = thisInfo.bounds
- override protected[Types] def baseTypeSeqImpl: BaseTypeSeq = transform(bounds.hi).baseTypeSeq prepend this
override def kind = "AbstractTypeRef"
}
@@ -2133,9 +2107,21 @@ trait Types
trivial = fromBoolean(!sym.isTypeParameter && pre.isTrivial && areTrivialTypes(args))
toBoolean(trivial)
}
- private[scala] def invalidateCaches(): Unit = {
+
+ /* It only makes sense to show 2-ary type constructors infix.
+ * By default we do only if it's a symbolic name. */
+ override def isShowAsInfixType: Boolean =
+ hasLength(args, 2) &&
+ sym.getAnnotation(ShowAsInfixAnnotationClass)
+ .map(_ booleanArg 0 getOrElse true)
+ .getOrElse(!Character.isUnicodeIdentifierStart(sym.decodedName.head))
+
+ private[Types] def invalidateTypeRefCaches(): Unit = {
+ parentsCache = null
parentsPeriod = NoPeriod
+ baseTypeSeqCache = null
baseTypeSeqPeriod = NoPeriod
+ normalized = null
}
private[reflect] var parentsCache: List[Type] = _
private[reflect] var parentsPeriod = NoPeriod
@@ -2156,11 +2142,91 @@ trait Types
finalizeHash(h, 2)
}
+ // interpret symbol's info in terms of the type's prefix and type args
+ protected def relativeInfo: Type = appliedType(sym.info.asSeenFrom(pre, sym.owner), argsOrDummies)
+
// @M: propagate actual type params (args) to `tp`, by replacing
// formal type parameters with actual ones. If tp is higher kinded,
// the "actual" type arguments are types that simply reference the
// corresponding type parameters (unbound type variables)
- def transform(tp: Type): Type
+ //
+ // NOTE: for performance, as well as correctness, we do not attempt
+ // to reframe trivial types in terms of our prefix and args.
+ // asSeenFrom, by construction, is the identity for trivial types,
+ // and substitution cannot change them either (abstract types are non-trivial, specifically because they may need to be replaced)
+ // For correctness, the result for `tp == NoType` must be `NoType`,
+ // if we don't shield against this, and apply instantiateTypeParams to it,
+ // this would result in an ErrorType, which behaves differently during subtyping
+ // (and thus on recursion, subtyping would go from false -- since a NoType is involved --
+ // to true, as ErrorType is always a sub/super type....)
+ final def relativize(tp: Type): Type =
+ if (tp.isTrivial) tp
+ else if (args.isEmpty && (phase.erasedTypes || !isHigherKinded || isRawIfWithoutArgs(sym))) tp.asSeenFrom(pre, sym.owner)
+ else {
+ // The type params and type args should always match in length,
+ // though a mismatch can arise when a typevar is encountered for which
+ // too little information is known to determine its kind, and
+ // it later turns out not to have kind *. See SI-4070.
+ val formals = sym.typeParams
+
+ // If we're called with a poly type, and we were to run the `asSeenFrom`, over the entire
+ // type, we can end up with new symbols for the type parameters (clones from TypeMap).
+ // The subsequent substitution of type arguments would fail. This problem showed up during
+ // the fix for SI-8046, however the solution taken there wasn't quite right, and led to
+ // SI-8170.
+ //
+ // Now, we detect the PolyType before both the ASF *and* the substitution, and just operate
+ // on the result type.
+ //
+ // TODO: Revisit this and explore the questions raised:
+ //
+ // AM: I like this better than the old code, but is there any way the tparams would need the ASF treatment as well?
+ // JZ: I think its largely irrelevant, as they are no longer referred to in the result type.
+ // In fact, you can get away with returning a type of kind * here and the sky doesn't fall:
+ // `case PolyType(`tparams`, result) => asSeenFromInstantiated(result)`
+ // But I thought it was better to retain the kind.
+ // AM: I've been experimenting with apply-type-args-then-ASF, but running into cycles.
+ // In general, it seems iffy the tparams can never occur in the result
+ // then we might as well represent the type as a no-arg typeref.
+ // AM: I've also been trying to track down uses of transform (pretty generic name for something that
+ // does not seem that widely applicable).
+ // It's kind of a helper for computing baseType (since it tries to propagate our type args to some
+ // other type, which has to be related to this type for that to make sense).
+ //
+ def seenFromOwnerInstantiated(tp: Type): Type =
+ tp.asSeenFrom(pre, sym.owner).instantiateTypeParams(formals, argsOrDummies)
+
+ tp match {
+ case PolyType(`formals`, result) => PolyType(formals, seenFromOwnerInstantiated(result))
+ case _ => seenFromOwnerInstantiated(tp)
+ }
+ }
+
+ private def argsOrDummies = if (args.isEmpty) dummyArgs else args
+
+ final override def baseType(clazz: Symbol): Type =
+ if (clazz eq sym) this
+ // NOTE: this first goes to requested base type, *then* does asSeenFrom prefix & instantiates args
+ else if (sym.isClass) relativize(sym.info.baseType(clazz))
+ else baseTypeOfNonClassTypeRef(clazz)
+
+ // two differences with class type basetype:
+ // (1) first relativize the type, then go to the requested base type
+ // (2) cache for cycle robustness
+ private def baseTypeOfNonClassTypeRef(clazz: Symbol) =
+ try {
+ basetypeRecursions += 1
+ if (basetypeRecursions >= LogPendingBaseTypesThreshold) baseTypeOfNonClassTypeRefLogged(clazz)
+ else relativeInfo.baseType(clazz)
+ } finally basetypeRecursions -= 1
+
+ private def baseTypeOfNonClassTypeRefLogged(clazz: Symbol) =
+ if (pendingBaseTypes add this) try relativeInfo.baseType(clazz) finally { pendingBaseTypes remove this }
+ // TODO: is this optimization for AnyClass worth it? (or is it playing last-ditch cycle defense?)
+ // NOTE: for correctness, it only applies for non-class types
+ // (e.g., a package class should not get AnyTpe as its supertype, ever)
+ else if (clazz eq AnyClass) AnyTpe
+ else NoType
// eta-expand, subtyping relies on eta-expansion of higher-kinded types
protected def normalizeImpl: Type = if (isHigherKinded) etaExpand else super.normalize
@@ -2193,21 +2259,16 @@ trait Types
// (they are allowed to be rebound more liberally)
def coevolveSym(pre1: Type): Symbol = sym
- //@M! use appliedType on the polytype that represents the bounds (or if aliastype, the rhs)
- def transformInfo(tp: Type): Type = appliedType(asSeenFromOwner(tp), args)
-
- def thisInfo = sym.info
def initializedTypeParams = sym.info.typeParams
def typeParamsMatchArgs = sameLength(initializedTypeParams, args)
- def asSeenFromOwner(tp: Type) = tp.asSeenFrom(pre, sym.owner)
- override def baseClasses = thisInfo.baseClasses
+
override def baseTypeSeqDepth = baseTypeSeq.maxDepth
override def prefix = pre
override def termSymbol = super.termSymbol
override def termSymbolDirect = super.termSymbol
override def typeArgs = args
- override def typeOfThis = transform(sym.typeOfThis)
+ override def typeOfThis = relativize(sym.typeOfThis)
override def typeSymbol = sym
override def typeSymbolDirect = sym
@@ -2220,22 +2281,26 @@ trait Types
}
}
- override def decls: Scope = {
- sym.info match {
- case TypeRef(_, sym1, _) =>
- assert(sym1 != sym, this) // @MAT was != typeSymbol
- case _ =>
- }
- thisInfo.decls
- }
+ protected[Types] def parentsImpl: List[Type] = sym.info.parents map relativize
+
+ // Since type parameters cannot occur in super types, no need to relativize before looking at base *classes*.
+ // Similarly, our prefix can occur in super class types, but it cannot influence which classes those types resolve to.
+ // For example, `class Outer { outer => class Inner extends outer.Foo; class Foo }`
+ // `outer`'s value has no impact on which `Foo` is selected, since classes cannot be overridden.
+ // besides being faster, we can't use relativeInfo because it causes cycles
+ override def baseClasses = sym.info.baseClasses
+
+ // in principle, we should use `relativeInfo.decls`, but I believe all uses of `decls` will correctly `relativize` the individual members
+ override def decls: Scope = sym.info.decls
+
protected[Types] def baseTypeSeqImpl: BaseTypeSeq =
if (sym.info.baseTypeSeq exists (_.typeSymbolDirect.isAbstractType))
// SI-8046 base type sequence might have more elements in a subclass, we can't map it element wise.
- transform(sym.info).baseTypeSeq
+ relativize(sym.info).baseTypeSeq
else
// Optimization: no abstract types, we can compute the BTS of this TypeRef as an element-wise map
// of the BTS of the referenced symbol.
- sym.info.baseTypeSeq map transform
+ sym.info.baseTypeSeq map relativize
override def baseTypeSeq: BaseTypeSeq = {
val cache = baseTypeSeqCache
@@ -2258,6 +2323,8 @@ trait Types
private def preString = if (needsPreString) pre.prefixString else ""
private def argsString = if (args.isEmpty) "" else args.mkString("[", ",", "]")
+ override def nameAndArgsString = typeSymbol.name.toString + argsString
+
private def refinementDecls = fullyInitializeScope(decls) filter (sym => sym.isPossibleInRefinement && sym.isPublic)
private def refinementString = (
if (sym.isStructuralRefinement)
@@ -2266,15 +2333,32 @@ trait Types
)
protected def finishPrefix(rest: String) = (
if (sym.isInitialized && sym.isAnonymousClass && !phase.erasedTypes)
- parentsString(thisInfo.parents) + refinementString
+ parentsString(sym.info.parents) + refinementString
else rest
- )
+ )
+
private def noArgsString = finishPrefix(preString + sym.nameString)
private def tupleTypeString: String = args match {
case Nil => noArgsString
case arg :: Nil => s"($arg,)"
case _ => args.mkString("(", ", ", ")")
}
+ private def infixTypeString: String = {
+ /* SLS 3.2.8: all infix types have the same precedence.
+ * In A op B op' C, op and op' need the same associativity.
+ * Therefore, if op is left associative, anything on its right
+ * needs to be parenthesized if it's an infix type, and vice versa. */
+ // we should only get here after `isShowInfixType` says we have 2 args
+ val l :: r :: Nil = args
+
+ val isRightAssoc = typeSymbol.decodedName endsWith ":"
+
+ val lstr = if (isRightAssoc && l.isShowAsInfixType) s"($l)" else l.toString
+
+ val rstr = if (!isRightAssoc && r.isShowAsInfixType) s"($r)" else r.toString
+
+ s"$lstr ${sym.decodedName} $rstr"
+ }
private def customToString = sym match {
case RepeatedParamClass | JavaRepeatedParamClass => args.head + "*"
case ByNameParamClass => "=> " + args.head
@@ -2298,6 +2382,8 @@ trait Types
xs.init.mkString("(", ", ", ")") + " => " + xs.last
}
}
+ else if (isShowAsInfixType)
+ infixTypeString
else if (isTupleTypeDirect(this))
tupleTypeString
else if (sym.isAliasType && prefixChain.exists(_.termSymbol.isSynthetic) && (this ne dealias))
@@ -2330,10 +2416,10 @@ trait Types
// No longer defined as anonymous classes in `object TypeRef` to avoid an unnecessary outer pointer.
private final class AliasArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AliasTypeRef
private final class AbstractArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with AbstractTypeRef
- private final class ClassArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args) with ClassTypeRef
+ private final class ClassArgsTypeRef(pre: Type, sym: Symbol, args: List[Type]) extends ArgsTypeRef(pre, sym, args)
private final class AliasNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AliasTypeRef
private final class AbstractNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with AbstractTypeRef
- private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym) with ClassTypeRef
+ private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym)
object TypeRef extends TypeRefExtractor {
def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({
@@ -2358,7 +2444,7 @@ trait Types
if (period != currentPeriod) {
tpe.parentsPeriod = currentPeriod
if (!isValidForBaseClasses(period)) {
- tpe.parentsCache = tpe.thisInfo.parents map tpe.transform
+ tpe.parentsCache = tpe.parentsImpl
} else if (tpe.parentsCache == null) { // seems this can happen if things are corrupted enough, see #2641
tpe.parentsCache = List(AnyTpe)
}
@@ -2413,7 +2499,6 @@ trait Types
def isImplicit = (params ne Nil) && params.head.isImplicit
def isJava = false // can we do something like for implicits? I.e. do Java methods without parameters need to be recognized?
- //assert(paramTypes forall (pt => !pt.typeSymbol.isImplClass))//DEBUG
override def paramSectionCount: Int = resultType.paramSectionCount + 1
override def paramss: List[List[Symbol]] = params :: resultType.paramss
@@ -2463,6 +2548,8 @@ trait Types
override def isJava = true
}
+ // TODO: rename so it's more appropriate for the type that is for a method without argument lists
+ // ("nullary" erroneously implies it has an argument list with zero arguments, it actually has zero argument lists)
case class NullaryMethodType(override val resultType: Type) extends Type with NullaryMethodTypeApi {
override def isTrivial = resultType.isTrivial && (resultType eq resultType.withoutAnnotations)
override def prefix: Type = resultType.prefix
@@ -2645,6 +2732,19 @@ trait Types
arg.toString
}
+ override def nameAndArgsString: String = underlying match {
+ case TypeRef(_, sym, args) if !settings.debug && isRepresentableWithWildcards =>
+ sym.name + wildcardArgsString(quantified.toSet, args).mkString("[", ",", "]")
+ case TypeRef(_, sym, args) =>
+ sym.name + args.mkString("[", ",", "]") + existentialClauses
+ case _ => underlying.typeSymbol.name + existentialClauses
+ }
+
+ private def existentialClauses = {
+ val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }")
+ if (settings.explaintypes) "(" + str + ")" else str
+ }
+
/** An existential can only be printed with wildcards if:
* - the underlying type is a typeref
* - every quantified variable appears at most once as a type argument and
@@ -2656,13 +2756,14 @@ trait Types
def isRepresentableWithWildcards = {
val qset = quantified.toSet
underlying match {
+ case _: RefinementTypeRef => false
case TypeRef(pre, sym, args) =>
def isQuantified(tpe: Type): Boolean = {
(tpe exists (t => qset contains t.typeSymbol)) ||
tpe.typeSymbol.isRefinementClass && (tpe.parents exists isQuantified)
}
val (wildcardArgs, otherArgs) = args partition (arg => qset contains arg.typeSymbol)
- wildcardArgs.distinct == wildcardArgs &&
+ wildcardArgs.toSet.size == wildcardArgs.size &&
!(otherArgs exists (arg => isQuantified(arg))) &&
!(wildcardArgs exists (arg => isQuantified(arg.typeSymbol.info.bounds))) &&
!(qset contains sym) &&
@@ -2672,17 +2773,13 @@ trait Types
}
override def safeToString: String = {
- def clauses = {
- val str = quantified map (_.existentialToString) mkString (" forSome { ", "; ", " }")
- if (settings.explaintypes) "(" + str + ")" else str
- }
underlying match {
case TypeRef(pre, sym, args) if !settings.debug && isRepresentableWithWildcards =>
"" + TypeRef(pre, sym, Nil) + wildcardArgsString(quantified.toSet, args).mkString("[", ", ", "]")
case MethodType(_, _) | NullaryMethodType(_) | PolyType(_, _) =>
- "(" + underlying + ")" + clauses
+ "(" + underlying + ")" + existentialClauses
case _ =>
- "" + underlying + clauses
+ "" + underlying + existentialClauses
}
}
@@ -2771,13 +2868,13 @@ trait Types
// now, pattern-matching returns the most recent constr
object TypeVar {
@inline final def trace[T](action: String, msg: => String)(value: T): T = {
- if (traceTypeVars) {
- val s = msg match {
- case "" => ""
- case str => "( " + str + " )"
- }
- Console.err.println("[%10s] %-25s%s".format(action, value, s))
- }
+ // Uncomment the following for a compiler that has some diagnostics about type inference
+ // I doubt this is ever useful in the wild, so a recompile will be needed
+// val s = msg match {
+// case "" => ""
+// case str => "( " + str + " )"
+// }
+// Console.err.println("[%10s] %-25s%s".format(action, value, s))
value
}
@@ -2798,7 +2895,9 @@ trait Types
val exclude = bounds.isEmptyBounds || (bounds exists typeIsNonClassType)
if (exclude) new TypeConstraint
- else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(new TypeConstraint(bounds))
+ else TypeVar.trace("constraint", "For " + tparam.fullLocationString)(
+ new TypeConstraint(bounds)
+ )
}
else new TypeConstraint
}
@@ -2827,7 +2926,9 @@ trait Types
else throw new Error("Invalid TypeVar construction: " + ((origin, constr, args, params)))
)
- trace("create", "In " + tv.originLocation)(tv)
+ trace("create", "In " + tv.originLocation)(
+ tv
+ )
}
private def createTypeVar(tparam: Symbol, untouchable: Boolean): TypeVar =
createTypeVar(tparam.tpeHK, deriveConstraint(tparam), Nil, tparam.typeParams, untouchable)
@@ -2931,7 +3032,9 @@ trait Types
else if (newArgs.size == params.size) {
val tv = TypeVar(origin, constr, newArgs, params)
tv.linkSuspended(this)
- TypeVar.trace("applyArgs", "In " + originLocation + ", apply args " + newArgs.mkString(", ") + " to " + originName)(tv)
+ TypeVar.trace("applyArgs", s"In $originLocation, apply args ${newArgs.mkString(", ")} to $originName")(
+ tv
+ )
}
else
TypeVar(typeSymbol).setInst(ErrorType)
@@ -2950,31 +3053,20 @@ trait Types
// only one of them is in the set of tvars that need to be solved, but
// they share the same TypeConstraint instance
- // When comparing to types containing skolems, remember the highest level
- // of skolemization. If that highest level is higher than our initial
- // skolemizationLevel, we can't re-use those skolems as the solution of this
- // typevar, which means we'll need to repack our inst into a fresh existential.
- // were we compared to skolems at a higher skolemizationLevel?
- // EXPERIMENTAL: value will not be considered unless enableTypeVarExperimentals is true
- // see SI-5729 for why this is still experimental
- private var encounteredHigherLevel = false
- private def shouldRepackType = enableTypeVarExperimentals && encounteredHigherLevel
-
// <region name="constraint mutators + undoLog">
// invariant: before mutating constr, save old state in undoLog
// (undoLog is used to reset constraints to avoid piling up unrelated ones)
- def setInst(tp: Type): this.type = {
- if (tp eq this) {
+ def setInst(tp: Type): this.type =
+ if (tp ne this) {
+ undoLog record this
+ constr.inst = TypeVar.trace("setInst", s"In $originLocation, $originName=$tp")(
+ tp
+ )
+ this
+ } else {
log(s"TypeVar cycle: called setInst passing $this to itself.")
- return this
+ this
}
- undoLog record this
- // if we were compared against later typeskolems, repack the existential,
- // because skolems are only compatible if they were created at the same level
- val res = if (shouldRepackType) repackExistential(tp) else tp
- constr.inst = TypeVar.trace("setInst", "In " + originLocation + ", " + originName + "=" + res)(res)
- this
- }
def addLoBound(tp: Type, isNumericBound: Boolean = false) {
assert(tp != this, tp) // implies there is a cycle somewhere (?)
@@ -3199,19 +3291,13 @@ trait Types
case ts: TypeSkolem => ts.level > level
case _ => false
}
- // side-effects encounteredHigherLevel
- private def containsSkolemAboveLevel(tp: Type) =
- (tp exists isSkolemAboveLevel) && { encounteredHigherLevel = true ; true }
- /** Can this variable be related in a constraint to type `tp`?
+
+ /** Can this variable be related in a constraint to type `tp`?
* This is not the case if `tp` contains type skolems whose
* skolemization level is higher than the level of this variable.
*/
- def isRelatable(tp: Type) = (
- shouldRepackType // short circuit if we already know we've seen higher levels
- || !containsSkolemAboveLevel(tp) // side-effects tracking boolean
- || enableTypeVarExperimentals // -Xexperimental: always say we're relatable, track consequences
- )
+ def isRelatable(tp: Type) = !(tp exists isSkolemAboveLevel)
override def normalize: Type = (
if (instValid) inst
@@ -3259,7 +3345,7 @@ trait Types
// to never be resumed with the current implementation
assert(!suspended, this)
TypeVar.trace("clone", originLocation)(
- TypeVar(origin, constr.cloneInternal, typeArgs, params) // @M TODO: clone args/params?
+ TypeVar(origin, constr.cloneInternal, typeArgs, params)
)
}
}
@@ -3421,10 +3507,10 @@ trait Types
if (!sym.isOverridableMember || sym.owner == pre.typeSymbol) sym
else pre.nonPrivateMember(sym.name).suchThat { sym =>
// SI-7928 `isModuleNotMethod` is here to avoid crashing with spuriously "overloaded" module accessor and module symbols.
- // These appear after refchecks eliminates ModuleDefs that implement an interface.
+ // These appear after the fields phase eliminates ModuleDefs that implement an interface.
// Here, we exclude the module symbol, which allows us to bind to the accessor.
- // SI-8054 We must only do this after refchecks, otherwise we exclude the module symbol which does not yet have an accessor!
- val isModuleWithAccessor = phase.refChecked && sym.isModuleNotMethod
+ // SI-8054 We must only do this after fields, otherwise we exclude the module symbol which does not yet have an accessor!
+ val isModuleWithAccessor = phase.assignsFields && sym.isModuleNotMethod
sym.isType || (!isModuleWithAccessor && sym.isStable && !sym.hasVolatileType)
} orElse sym
}
@@ -3473,7 +3559,9 @@ trait Types
if ((parents eq original.parents) && (decls eq original.decls)) original
else {
val owner = original.typeSymbol.owner
- val result = refinedType(parents, owner)
+ val result =
+ if (isIntersectionTypeForLazyBaseType(original)) intersectionTypeForLazyBaseType(parents)
+ else refinedType(parents, owner)
val syms1 = decls.toList
for (sym <- syms1)
result.decls.enter(sym.cloneSymbol(result.typeSymbol))
@@ -3548,6 +3636,14 @@ trait Types
case tp :: Nil => tp
case _ => refinedType(tps, commonOwner(tps))
}
+ def intersectionTypeForLazyBaseType(tps: List[Type]) = tps match {
+ case tp :: Nil => tp
+ case _ => RefinedType(tps, newScope, tps.head.typeSymbolDirect)
+ }
+ def isIntersectionTypeForLazyBaseType(tp: RefinedType) = tp.parents match {
+ case head :: _ => tp.typeSymbolDirect eq head.typeSymbolDirect
+ case _ => false
+ }
/**** This implementation to merge parents was checked in in commented-out
form and has languished unaltered for five years. I think we should
@@ -3797,7 +3893,7 @@ trait Types
case _ => false
})
- @deprecated("Use isRawType", "2.10.1") // presently used by sbt
+ @deprecated("use isRawType", "2.10.1") // presently used by sbt
def isRaw(sym: Symbol, args: List[Type]) = (
!phase.erasedTypes
&& args.isEmpty
@@ -3912,6 +4008,8 @@ trait Types
et.withTypeVars(isConsistent(_, tp2))
case (_, et: ExistentialType) =>
et.withTypeVars(isConsistent(tp1, _))
+ case (_, _) =>
+ throw new MatchError((tp1, tp2))
}
def check(tp1: Type, tp2: Type) = (
@@ -3924,54 +4022,15 @@ trait Types
check(tp1, tp2) && check(tp2, tp1)
}
- /** Does a pattern of type `patType` need an outer test when executed against
- * selector type `selType` in context defined by `currentOwner`?
- */
- def needsOuterTest(patType: Type, selType: Type, currentOwner: Symbol) = {
- def createDummyClone(pre: Type): Type = {
- val dummy = currentOwner.enclClass.newValue(nme.ANYname).setInfo(pre.widen)
- singleType(ThisType(currentOwner.enclClass), dummy)
- }
- def maybeCreateDummyClone(pre: Type, sym: Symbol): Type = pre match {
- case SingleType(pre1, sym1) =>
- if (sym1.isModule && sym1.isStatic) {
- NoType
- } else if (sym1.isModule && sym.owner == sym1.moduleClass) {
- val pre2 = maybeCreateDummyClone(pre1, sym1)
- if (pre2 eq NoType) pre2
- else singleType(pre2, sym1)
- } else {
- createDummyClone(pre)
- }
- case ThisType(clazz) =>
- if (clazz.isModuleClass)
- maybeCreateDummyClone(clazz.typeOfThis, sym)
- else if (sym.owner == clazz && (sym.hasFlag(PRIVATE) || sym.privateWithin == clazz))
- NoType
- else
- createDummyClone(pre)
- case _ =>
- NoType
- }
- // See the test for SI-7214 for motivation for dealias. Later `treeCondStrategy#outerTest`
- // generates an outer test based on `patType.prefix` with automatically dealises.
- patType.dealias match {
- case TypeRef(pre, sym, args) =>
- val pre1 = maybeCreateDummyClone(pre, sym)
- (pre1 ne NoType) && isPopulated(copyTypeRef(patType, pre1, sym, args), selType)
- case _ =>
- false
- }
- }
-
- def normalizePlus(tp: Type) = (
+ def normalizePlus(tp: Type): Type = {
if (isRawType(tp)) rawToExistential(tp)
else tp.normalize match {
- // Unify the two representations of module classes
- case st @ SingleType(_, sym) if sym.isModule => st.underlying.normalize
- case _ => tp.normalize
+ // Unify the representations of module classes
+ case st@SingleType(_, sym) if sym.isModule => st.underlying.normalize
+ case st@ThisType(sym) if sym.isModuleClass => normalizePlus(st.underlying)
+ case _ => tp.normalize
}
- )
+ }
/*
todo: change to:
@@ -4136,7 +4195,7 @@ trait Types
* The specification-enumerated non-value types are method types, polymorphic
* method types, and type constructors. Supplements to the specified set of
* non-value types include: types which wrap non-value symbols (packages
- * abd statics), overloaded types. Varargs and by-name types T* and (=>T) are
+ * and statics), overloaded types. Varargs and by-name types T* and (=>T) are
* not designated non-value types because there is code which depends on using
* them as type arguments, but their precise status is unclear.
*/
@@ -4235,7 +4294,7 @@ trait Types
case mt1 @ MethodType(params1, res1) =>
tp2 match {
case mt2 @ MethodType(params2, res2) =>
- // sameLength(params1, params2) was used directly as pre-screening optimization (now done by matchesQuantified -- is that ok, performancewise?)
+ // sameLength(params1, params2) was used directly as pre-screening optimization (now done by matchesQuantified -- is that ok, performance-wise?)
mt1.isImplicit == mt2.isImplicit &&
matchingParams(params1, params2, mt1.isJava, mt2.isJava) &&
matchesQuantified(params1, params2, res1, res2)
@@ -4392,89 +4451,123 @@ trait Types
finally foreach2(tvs, saved)(_.suspended = _)
}
+ final def stripExistentialsAndTypeVars(ts: List[Type], expandLazyBaseType: Boolean = false): (List[Type], List[Symbol]) = {
+ val needsStripping = ts.exists {
+ case _: RefinedType | _: TypeVar | _: ExistentialType => true
+ case _ => false
+ }
+ if (!needsStripping) (ts, Nil) // fast path for common case
+ else {
+ val tparams = mutable.ListBuffer[Symbol]()
+ val stripped = mutable.ListBuffer[Type]()
+ def stripType(tp: Type): Unit = tp match {
+ case rt: RefinedType if isIntersectionTypeForLazyBaseType(rt) =>
+ if (expandLazyBaseType)
+ rt.parents foreach stripType
+ else {
+ devWarning(s"Unexpected RefinedType in stripExistentialsAndTypeVars $ts, not expanding")
+ stripped += tp
+ }
+ case ExistentialType(qs, underlying) =>
+ tparams ++= qs
+ stripType(underlying)
+ case tv@TypeVar(_, constr) =>
+ if (tv.instValid) stripType(constr.inst)
+ else if (tv.untouchable) stripped += tv
+ else abort("trying to do lub/glb of typevar " + tv)
+ case tp => stripped += tp
+ }
+ ts foreach stripType
+ (stripped.toList, tparams.toList)
+ }
+ }
+
/** Compute lub (if `variance == Covariant`) or glb (if `variance == Contravariant`) of given list
* of types `tps`. All types in `tps` are typerefs or singletypes
* with the same symbol.
* Return `x` if the computation succeeds with result `x`.
* Return `NoType` if the computation fails.
*/
- def mergePrefixAndArgs(tps: List[Type], variance: Variance, depth: Depth): Type = tps match {
- case tp :: Nil => tp
- case TypeRef(_, sym, _) :: rest =>
- val pres = tps map (_.prefix) // prefix normalizes automatically
+ def mergePrefixAndArgs(tps0: List[Type], variance: Variance, depth: Depth): Type = {
+ val (tps, tparams) = stripExistentialsAndTypeVars(tps0, expandLazyBaseType = true)
+
+ val merged = tps match {
+ case tp :: Nil => tp
+ case TypeRef(_, sym, _) :: rest =>
+ val pres = tps map (_.prefix) // prefix normalizes automatically
val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth)
- val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments
+ val argss = tps map (_.normalize.typeArgs) // symbol equality (of the tp in tps) was checked using typeSymbol, which normalizes, so should normalize before retrieving arguments
val capturedParams = new ListBuffer[Symbol]
- try {
- if (sym == ArrayClass && phase.erasedTypes) {
- // special treatment for lubs of array types after erasure:
- // if argss contain one value type and some other type, the lub is Object
- // if argss contain several reference types, the lub is an array over lub of argtypes
- if (argss exists typeListIsEmpty) {
- NoType // something is wrong: an array without a type arg.
- }
- else {
- val args = argss map (_.head)
- if (args.tail forall (_ =:= args.head)) typeRef(pre, sym, List(args.head))
- else if (args exists (arg => isPrimitiveValueClass(arg.typeSymbol))) ObjectTpe
- else typeRef(pre, sym, List(lub(args)))
+ try {
+ if (sym == ArrayClass && phase.erasedTypes) {
+ // special treatment for lubs of array types after erasure:
+ // if argss contain one value type and some other type, the lub is Object
+ // if argss contain several reference types, the lub is an array over lub of argtypes
+ if (argss exists typeListIsEmpty) {
+ NoType // something is wrong: an array without a type arg.
+ }
+ else {
+ val args = argss map (_.head)
+ if (args.tail forall (_ =:= args.head)) typeRef(pre, sym, List(args.head))
+ else if (args exists (arg => isPrimitiveValueClass(arg.typeSymbol))) ObjectTpe
+ else typeRef(pre, sym, List(lub(args)))
+ }
}
- }
- else transposeSafe(argss) match {
- case None =>
- // transpose freaked out because of irregular argss
- // catching just in case (shouldn't happen, but also doesn't cost us)
- // [JZ] It happens: see SI-5683.
- debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss")
- NoType
- case Some(argsst) =>
- val args = map2(sym.typeParams, argsst) { (tparam, as0) =>
- val as = as0.distinct
- if (as.size == 1) as.head
- else if (depth.isZero) {
- log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString))
- // Don't return "Any" (or "Nothing") when we have to give up due to
- // recursion depth. Return NoType, which prevents us from poisoning
- // lublist's results. It can recognize the recursion and deal with it, but
- // only if we aren't returning invalid types.
- NoType
- }
- else {
- if (tparam.variance == variance) lub(as, depth.decr)
- else if (tparam.variance == variance.flip) glb(as, depth.decr)
+ else transposeSafe(argss) match {
+ case None =>
+ // transpose freaked out because of irregular argss
+ // catching just in case (shouldn't happen, but also doesn't cost us)
+ // [JZ] It happens: see SI-5683.
+ debuglog(s"transposed irregular matrix!? tps=$tps argss=$argss")
+ NoType
+ case Some(argsst) =>
+ var capturedParamIds = 0
+ val args = map2(sym.typeParams, argsst) { (tparam, as0) =>
+ val as = as0.distinct
+ if (as.size == 1) as.head
+ else if (depth.isZero) {
+ log("Giving up merging args: can't unify %s under %s".format(as.mkString(", "), tparam.fullLocationString))
+ // Don't return "Any" (or "Nothing") when we have to give up due to
+ // recursion depth. Return NoType, which prevents us from poisoning
+ // lublist's results. It can recognize the recursion and deal with it, but
+ // only if we aren't returning invalid types.
+ NoType
+ }
else {
- val l = lub(as, depth.decr)
- val g = glb(as, depth.decr)
- if (l <:< g) l
- else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we
- // just err on the conservative side, i.e. with a bound that is too high.
- // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251
-
- val qvar = commonOwner(as) freshExistential "" setInfo TypeBounds(g, l)
- capturedParams += qvar
- qvar.tpe
+ if (tparam.variance == variance) lub(as, depth.decr)
+ else if (tparam.variance == variance.flip) glb(as, depth.decr)
+ else {
+ val l = lub(as, depth.decr)
+ val g = glb(as, depth.decr)
+ if (l <:< g) l
+ else { // Martin: I removed this, because incomplete. Not sure there is a good way to fix it. For the moment we
+ // just err on the conservative side, i.e. with a bound that is too high.
+ // if(!(tparam.info.bounds contains tparam)) //@M can't deal with f-bounds, see #2251
+ capturedParamIds += 1
+ val capturedParamId = capturedParamIds
+
+ val qvar = commonOwner(as).freshExistential("", capturedParamId) setInfo TypeBounds(g, l)
+ capturedParams += qvar
+ qvar.tpe
+ }
}
}
}
- }
- if (args contains NoType) NoType
- else existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))
+ if (args contains NoType) NoType
+ else existentialAbstraction(capturedParams.toList, typeRef(pre, sym, args))
+ }
+ } catch {
+ case ex: MalformedType => NoType
}
- } catch {
- case ex: MalformedType => NoType
- }
- case SingleType(_, sym) :: rest =>
- val pres = tps map (_.prefix)
- val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth)
- try singleType(pre, sym)
- catch { case ex: MalformedType => NoType }
- case ExistentialType(tparams, quantified) :: rest =>
- mergePrefixAndArgs(quantified :: rest, variance, depth) match {
- case NoType => NoType
- case tpe => existentialAbstraction(tparams, tpe)
- }
- case _ =>
- abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps")
+ case SingleType(_, sym) :: rest =>
+ val pres = tps map (_.prefix)
+ val pre = if (variance.isPositive) lub(pres, depth) else glb(pres, depth)
+ try singleType(pre, sym)
+ catch { case ex: MalformedType => NoType }
+ case _ =>
+ abort(s"mergePrefixAndArgs($tps, $variance, $depth): unsupported tps")
+ }
+ existentialAbstraction(tparams, merged)
}
def addMember(thistp: Type, tp: Type, sym: Symbol): Unit = addMember(thistp, tp, sym, AnyDepth)
@@ -4590,6 +4683,21 @@ trait Types
if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyTpe
else tp
+ def invalidateTreeTpeCaches(tree: Tree, updatedSyms: List[Symbol]) = if (updatedSyms.nonEmpty)
+ for (t <- tree if t.tpe != null)
+ for (tp <- t.tpe) {
+ invalidateCaches(tp, updatedSyms)
+ }
+
+ def invalidateCaches(t: Type, updatedSyms: List[Symbol]) =
+ t match {
+ case st: SingleType if updatedSyms.contains(st.sym) => st.invalidateSingleTypeCaches()
+ case tr: TypeRef if updatedSyms.contains(tr.sym) => tr.invalidateTypeRefCaches()
+ case ct: CompoundType if ct.baseClasses.exists(updatedSyms.contains) => ct.invalidatedCompoundTypeCaches()
+ case _ =>
+ }
+
+
val shorthands = Set(
"scala.collection.immutable.List",
"scala.collection.immutable.Nil",
@@ -4631,7 +4739,7 @@ trait Types
case _ => Depth(1)
}
- //OPT replaced with tailrecursive function to save on #closures
+ //OPT replaced with tail recursive function to save on #closures
// was:
// var d = 0
// for (tp <- tps) d = d max by(tp) //!!!OPT!!!