aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/Types.scala
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core/Types.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/core/Types.scala3865
1 files changed, 3865 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala
new file mode 100644
index 000000000..89bc21929
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/core/Types.scala
@@ -0,0 +1,3865 @@
+package dotty.tools
+package dotc
+package core
+
+import util.common._
+import Symbols._
+import Flags._
+import Names._
+import StdNames._, NameOps._
+import Scopes._
+import Constants._
+import Contexts._
+import Annotations._
+import SymDenotations._
+import Decorators._
+import Denotations._
+import Periods._
+import util.Positions.Position
+import util.Stats._
+import util.{DotClass, SimpleMap}
+import ast.tpd._
+import ast.TreeTypeMap
+import printing.Texts._
+import ast.untpd
+import dotty.tools.dotc.transform.Erasure
+import printing.Printer
+import Hashable._
+import Uniques._
+import collection.{mutable, Seq, breakOut}
+import config.Config
+import annotation.tailrec
+import Flags.FlagSet
+import language.implicitConversions
+import scala.util.hashing.{ MurmurHash3 => hashing }
+import config.Printers.{core, typr, cyclicErrors}
+
+object Types {
+
+ @sharable private var nextId = 0
+
+ implicit def eqType: Eq[Type, Type] = Eq
+
+ /** The class of types.
+ * The principal subclasses and sub-objects are as follows:
+ *
+ * Type -+- ProxyType --+- NamedType ----+--- TypeRef
+ * | | \
+ * | +- SingletonType-+-+- TermRef
+ * | | |
+ * | | +--- ThisType
+ * | | +--- SuperType
+ * | | +--- ConstantType
+ * | | +--- MethodParam
+ * | | +----RecThis
+ * | | +--- SkolemType
+ * | +- PolyParam
+ * | +- RefinedOrRecType -+-- RefinedType
+ * | | -+-- RecType
+ * | +- HKApply
+ * | +- TypeBounds
+ * | +- ExprType
+ * | +- AnnotatedType
+ * | +- TypeVar
+ * | +- PolyType
+ * |
+ * +- GroundType -+- AndType
+ * +- OrType
+ * +- MethodType -----+- ImplicitMethodType
+ * | +- JavaMethodType
+ * +- ClassInfo
+ * |
+ * +- NoType
+ * +- NoPrefix
+ * +- ErrorType
+ * +- WildcardType
+ *
+ * Note: please keep in sync with copy in `docs/docs/internals/type-system.md`.
+ */
+ abstract class Type extends DotClass with Hashable with printing.Showable {
+
+// ----- Tests -----------------------------------------------------
+
+ // debug only: a unique identifier for a type
+ val uniqId = {
+ nextId = nextId + 1
+// if (nextId == 19555)
+// println("foo")
+ nextId
+ }
+
+ /** Is this type different from NoType? */
+ def exists: Boolean = true
+
+ /** This type, if it exists, otherwise `that` type */
+ def orElse(that: => Type) = if (exists) this else that
+
+ /** Is this type a value type? */
+ final def isValueType: Boolean = this.isInstanceOf[ValueType]
+
+ /** Is the is value type or type lambda? */
+ final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[PolyType]
+
+ /** Does this type denote a stable reference (i.e. singleton type)? */
+ final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
+ case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable
+ case _: SingletonType | NoPrefix => true
+ case tp: RefinedOrRecType => tp.parent.isStable
+ case _ => false
+ }
+
+ /** Is this type a (possibly refined or applied or aliased) type reference
+ * to the given type symbol?
+ * @sym The symbol to compare to. It must be a class symbol or abstract type.
+ * It makes no sense for it to be an alias type because isRef would always
+ * return false in that case.
+ */
+ def isRef(sym: Symbol)(implicit ctx: Context): Boolean = stripAnnots.stripTypeVar match {
+ case this1: TypeRef =>
+ this1.info match { // see comment in Namer#typeDefSig
+ case TypeAlias(tp) => tp.isRef(sym)
+ case _ => this1.symbol eq sym
+ }
+ case this1: RefinedOrRecType => this1.parent.isRef(sym)
+ case this1: HKApply => this1.superType.isRef(sym)
+ case _ => false
+ }
+
+ /** Is this type a (neither aliased nor applied) reference to class `sym`? */
+ def isDirectRef(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match {
+ case this1: TypeRef =>
+ this1.name == sym.name && // avoid forcing info if names differ
+ (this1.symbol eq sym)
+ case _ =>
+ false
+ }
+
+ /** Does this type refer exactly to class symbol `sym`, instead of to a subclass of `sym`?
+ * Implemented like `isRef`, but follows more types: all type proxies as well as and- and or-types
+ */
+ private[Types] def isTightPrefix(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match {
+ case tp: NamedType => tp.info.isTightPrefix(sym)
+ case tp: ClassInfo => tp.cls eq sym
+ case tp: Types.ThisType => tp.cls eq sym
+ case tp: TypeProxy => tp.underlying.isTightPrefix(sym)
+ case tp: AndType => tp.tp1.isTightPrefix(sym) && tp.tp2.isTightPrefix(sym)
+ case tp: OrType => tp.tp1.isTightPrefix(sym) || tp.tp2.isTightPrefix(sym)
+ case _ => false
+ }
+
+ /** Is this type an instance of a non-bottom subclass of the given class `cls`? */
+ final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = {
+ def loop(tp: Type) = tp match {
+ case tp: TypeRef =>
+ val sym = tp.symbol
+ if (sym.isClass) sym.derivesFrom(cls) else tp.superType.derivesFrom(cls)
+ case tp: TypeProxy =>
+ tp.underlying.derivesFrom(cls)
+ case tp: AndType =>
+ tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls)
+ case tp: OrType =>
+ tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls)
+ case tp: JavaArrayType =>
+ cls == defn.ObjectClass
+ case _ =>
+ false
+ }
+ cls == defn.AnyClass || loop(this)
+ }
+
+ /** Is this type guaranteed not to have `null` as a value?
+ * For the moment this is only true for modules, but it could
+ * be refined later.
+ */
+ final def isNotNull(implicit ctx: Context): Boolean =
+ classSymbol is ModuleClass
+
+ /** Is this type produced as a repair for an error? */
+ final def isError(implicit ctx: Context): Boolean = stripTypeVar match {
+ case ErrorType => true
+ case tp => (tp.typeSymbol is Erroneous) || (tp.termSymbol is Erroneous)
+ }
+
+ /** Is some part of this type produced as a repair for an error? */
+ final def isErroneous(implicit ctx: Context): Boolean = existsPart(_.isError, forceLazy = false)
+
+ /** Does the type carry an annotation that is an instance of `cls`? */
+ final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = stripTypeVar match {
+ case AnnotatedType(tp, annot) => (annot matches cls) || (tp hasAnnotation cls)
+ case _ => false
+ }
+
+ /** Does this type occur as a part of type `that`? */
+ final def occursIn(that: Type)(implicit ctx: Context): Boolean =
+ that existsPart (this == _)
+
+ /** Is this a type of a repeated parameter? */
+ def isRepeatedParam(implicit ctx: Context): Boolean =
+ typeSymbol eq defn.RepeatedParamClass
+
+ /** Does this type carry an UnsafeNonvariant annotation? */
+ final def isUnsafeNonvariant(implicit ctx: Context): Boolean = this match {
+ case AnnotatedType(_, annot) => annot.symbol == defn.UnsafeNonvariantAnnot
+ case _ => false
+ }
+
+ /** Does this type have an UnsafeNonvariant annotation on one of its parts? */
+ final def hasUnsafeNonvariant(implicit ctx: Context): Boolean =
+ new HasUnsafeNonAccumulator().apply(false, this)
+
+ /** Is this the type of a method that has a repeated parameter type as
+ * last parameter type?
+ */
+ def isVarArgsMethod(implicit ctx: Context): Boolean = this match {
+ case tp: PolyType => tp.resultType.isVarArgsMethod
+ case MethodType(_, paramTypes) => paramTypes.nonEmpty && paramTypes.last.isRepeatedParam
+ case _ => false
+ }
+
+ /** Is this an alias TypeBounds? */
+ def isAlias: Boolean = this.isInstanceOf[TypeAlias]
+
+// ----- Higher-order combinators -----------------------------------
+
+ /** Returns true if there is a part of this type that satisfies predicate `p`.
+ */
+ final def existsPart(p: Type => Boolean, forceLazy: Boolean = true)(implicit ctx: Context): Boolean =
+ new ExistsAccumulator(p, forceLazy).apply(false, this)
+
+ /** Returns true if all parts of this type satisfy predicate `p`.
+ */
+ final def forallParts(p: Type => Boolean)(implicit ctx: Context): Boolean =
+ !existsPart(!p(_))
+
+ /** Performs operation on all parts of this type */
+ final def foreachPart(p: Type => Unit, stopAtStatic: Boolean = false)(implicit ctx: Context): Unit =
+ new ForeachAccumulator(p, stopAtStatic).apply((), this)
+
+ /** The parts of this type which are type or term refs */
+ final def namedParts(implicit ctx: Context): collection.Set[NamedType] =
+ namedPartsWith(alwaysTrue)
+
+ /** The parts of this type which are type or term refs and which
+ * satisfy predicate `p`.
+ *
+ * @param p The predicate to satisfy
+ * @param excludeLowerBounds If set to true, the lower bounds of abstract
+ * types will be ignored.
+ */
+ def namedPartsWith(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
+ (implicit ctx: Context): collection.Set[NamedType] =
+ new NamedPartsAccumulator(p, excludeLowerBounds).apply(mutable.LinkedHashSet(), this)
+
+ /** Map function `f` over elements of an AndType, rebuilding with function `g` */
+ def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match {
+ case AndType(tp1, tp2) => g(tp1.mapReduceAnd(f)(g), tp2.mapReduceAnd(f)(g))
+ case _ => f(this)
+ }
+
+ /** Map function `f` over elements of an OrType, rebuilding with function `g` */
+ final def mapReduceOr[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match {
+ case OrType(tp1, tp2) => g(tp1.mapReduceOr(f)(g), tp2.mapReduceOr(f)(g))
+ case _ => f(this)
+ }
+
+// ----- Associated symbols ----------------------------------------------
+
+ /** The type symbol associated with the type */
+ final def typeSymbol(implicit ctx: Context): Symbol = this match {
+ case tp: TypeRef => tp.symbol
+ case tp: ClassInfo => tp.cls
+// case ThisType(cls) => cls // needed?
+ case tp: SingletonType => NoSymbol
+ case tp: TypeProxy => tp.underlying.typeSymbol
+ case _ => NoSymbol
+ }
+
+ /** The least class or trait of which this type is a subtype or parameterized
+ * instance, or NoSymbol if none exists (either because this type is not a
+ * value type, or because superclasses are ambiguous).
+ */
+ final def classSymbol(implicit ctx: Context): Symbol = this match {
+ case ConstantType(constant) =>
+ constant.tpe.classSymbol
+ case tp: TypeRef =>
+ val sym = tp.symbol
+ if (sym.isClass) sym else tp.superType.classSymbol
+ case tp: ClassInfo =>
+ tp.cls
+ case tp: SingletonType =>
+ NoSymbol
+ case tp: TypeProxy =>
+ tp.underlying.classSymbol
+ case AndType(l, r) =>
+ val lsym = l.classSymbol
+ val rsym = r.classSymbol
+ if (lsym isSubClass rsym) lsym
+ else if (rsym isSubClass lsym) rsym
+ else NoSymbol
+ case OrType(l, r) => // TODO does not conform to spec
+ val lsym = l.classSymbol
+ val rsym = r.classSymbol
+ if (lsym isSubClass rsym) rsym
+ else if (rsym isSubClass lsym) lsym
+ else NoSymbol
+ case _ =>
+ NoSymbol
+ }
+
+ /** The least (wrt <:<) set of class symbols of which this type is a subtype
+ */
+ final def classSymbols(implicit ctx: Context): List[ClassSymbol] = this match {
+ case tp: ClassInfo =>
+ tp.cls :: Nil
+ case tp: TypeRef =>
+ val sym = tp.symbol
+ if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols
+ case tp: TypeProxy =>
+ tp.underlying.classSymbols
+ case AndType(l, r) =>
+ l.classSymbols union r.classSymbols
+ case OrType(l, r) =>
+ l.classSymbols intersect r.classSymbols // TODO does not conform to spec
+ case _ =>
+ Nil
+ }
+
+ /** The term symbol associated with the type */
+ final def termSymbol(implicit ctx: Context): Symbol = this match {
+ case tp: TermRef => tp.symbol
+ case tp: TypeProxy => tp.underlying.termSymbol
+ case _ => NoSymbol
+ }
+
+ /** The base classes of this type as determined by ClassDenotation
+ * in linearization order, with the class itself as first element.
+ * For AndTypes/OrTypes, the union/intersection of the operands' baseclasses.
+ * Inherited by all type proxies. `Nil` for all other types.
+ */
+ final def baseClasses(implicit ctx: Context): List[ClassSymbol] = track("baseClasses") {
+ this match {
+ case tp: TypeProxy =>
+ tp.underlying.baseClasses
+ case tp: ClassInfo =>
+ tp.cls.baseClasses
+ case AndType(tp1, tp2) =>
+ tp1.baseClasses union tp2.baseClasses
+ case OrType(tp1, tp2) =>
+ tp1.baseClasses intersect tp2.baseClasses
+ case _ => Nil
+ }
+ }
+
+// ----- Member access -------------------------------------------------
+
+ /** The scope of all declarations of this type.
+ * Defined by ClassInfo, inherited by type proxies.
+ * Empty scope for all other types.
+ */
+ final def decls(implicit ctx: Context): Scope = this match {
+ case tp: ClassInfo =>
+ tp.decls
+ case tp: TypeProxy =>
+ tp.underlying.decls
+ case _ =>
+ EmptyScope
+ }
+
+ /** A denotation containing the declaration(s) in this type with the given name.
+ * The result is either a SymDenotation or a MultiDenotation of SymDenotations.
+ * The info(s) are the original symbol infos, no translation takes place.
+ */
+ final def decl(name: Name)(implicit ctx: Context): Denotation = track("decl") {
+ findDecl(name, EmptyFlags)
+ }
+
+ /** A denotation containing the non-private declaration(s) in this type with the given name */
+ final def nonPrivateDecl(name: Name)(implicit ctx: Context): Denotation = track("nonPrivateDecl") {
+ findDecl(name, Private)
+ }
+
+ /** A denotation containing the declaration(s) in this type with the given
+ * name, as seen from prefix type `pre`. Declarations that have a flag
+ * in `excluded` are omitted.
+ */
+ final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match {
+ case tp: ClassInfo =>
+ tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix)
+ case tp: TypeProxy =>
+ tp.underlying.findDecl(name, excluded)
+ case ErrorType =>
+ ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name)
+ case _ =>
+ NoDenotation
+ }
+
+ /** The member of this type with the given name */
+ final def member(name: Name)(implicit ctx: Context): Denotation = /*>|>*/ track("member") /*<|<*/ {
+ memberExcluding(name, EmptyFlags)
+ }
+
+ /** The non-private member of this type with the given name. */
+ final def nonPrivateMember(name: Name)(implicit ctx: Context): Denotation = track("nonPrivateMember") {
+ memberExcluding(name, Flags.Private)
+ }
+
+ final def memberExcluding(name: Name, excluding: FlagSet)(implicit ctx: Context): Denotation = {
+ // We need a valid prefix for `asSeenFrom`
+ val pre = this match {
+ case tp: ClassInfo =>
+ tp.typeRef
+ case _ =>
+ widenIfUnstable
+ }
+ findMember(name, pre, excluding)
+ }
+
+ /** Find member of this type with given name and
+ * produce a denotation that contains the type of the member
+ * as seen from given prefix `pre`. Exclude all members that have
+ * flags in `excluded` from consideration.
+ */
+ final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = {
+ @tailrec def go(tp: Type): Denotation = tp match {
+ case tp: RefinedType =>
+ if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
+ case tp: ThisType =>
+ goThis(tp)
+ case tp: TypeRef =>
+ tp.denot.findMember(name, pre, excluded)
+ case tp: TermRef =>
+ go (tp.underlying match {
+ case mt: MethodType
+ if mt.paramTypes.isEmpty && (tp.symbol is Stable) => mt.resultType
+ case tp1 => tp1
+ })
+ case tp: PolyParam =>
+ goParam(tp)
+ case tp: RecType =>
+ goRec(tp)
+ case tp: HKApply =>
+ goApply(tp)
+ case tp: TypeProxy =>
+ go(tp.underlying)
+ case tp: ClassInfo =>
+ tp.cls.findMember(name, pre, excluded)
+ case AndType(l, r) =>
+ goAnd(l, r)
+ case tp: OrType =>
+ // we need to keep the invariant that `pre <: tp`. Branch `union-types-narrow-prefix`
+ // achieved that by narrowing `pre` to each alternative, but it led to merge errors in
+ // lots of places. The present strategy is instead of widen `tp` using `join` to be a
+ // supertype of `pre`.
+ go(tp.join)
+ case tp: JavaArrayType =>
+ defn.ObjectType.findMember(name, pre, excluded)
+ case ErrorType =>
+ ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name)
+ case _ =>
+ NoDenotation
+ }
+ def goRec(tp: RecType) =
+ if (tp.parent == null) NoDenotation
+ else {
+ //println(s"find member $pre . $name in $tp")
+
+ // We have to be careful because we might open the same (wrt eq) recursive type
+ // twice during findMember which risks picking the wrong prefix in the `substRecThis(rt, pre)`
+ // call below. To avoid this problem we do a defensive copy of the recursive
+ // type first. But if we do this always we risk being inefficient and we ran into
+ // stackoverflows when compiling pos/hk.scala under the refinement encoding
+ // of hk-types. So we only do a copy if the type
+ // is visited again in a recursive call to `findMember`, as tracked by `tp.opened`.
+ // Furthermore, if this happens we mark the original recursive type with `openedTwice`
+ // which means that we always defensively copy the type in the future. This second
+ // measure is necessary because findMember calls might be cached, so do not
+ // necessarily appear in nested order.
+ // Without the defensive copy, Typer.scala fails to compile at the line
+ //
+ // untpd.rename(lhsCore, setterName).withType(setterType), WildcardType)
+ //
+ // because the subtype check
+ //
+ // ThisTree[Untyped]#ThisTree[Typed] <: Tree[Typed]
+ //
+ // fails (in fact it thinks the underlying type of the LHS is `Tree[Untyped]`.)
+ //
+ // Without the `openedTwice` trick, Typer.scala fails to Ycheck
+ // at phase resolveSuper.
+ val rt =
+ if (tp.opened) { // defensive copy
+ tp.openedTwice = true
+ RecType(rt => tp.parent.substRecThis(tp, RecThis(rt)))
+ } else tp
+ rt.opened = true
+ try go(rt.parent).mapInfo(_.substRecThis(rt, pre))
+ finally {
+ if (!rt.openedTwice) rt.opened = false
+ }
+ }
+
+ def goRefined(tp: RefinedType) = {
+ val pdenot = go(tp.parent)
+ val rinfo = tp.refinedInfo
+ if (name.isTypeName) { // simplified case that runs more efficiently
+ val jointInfo =
+ if (rinfo.isAlias) rinfo
+ else if (pdenot.info.isAlias) pdenot.info
+ else if (ctx.pendingMemberSearches.contains(name)) pdenot.info safe_& rinfo
+ else
+ try pdenot.info & rinfo
+ catch {
+ case ex: CyclicReference =>
+ // happens for tests/pos/sets.scala. findMember is called from baseTypeRef.
+ // The & causes a subtype check which calls baseTypeRef again with the same
+ // superclass. In the observed case, the superclass was Any, and
+ // the special shortcut for Any in derivesFrom was as yet absent. To reproduce,
+ // remove the special treatment of Any in derivesFrom and compile
+ // sets.scala.
+ pdenot.info safe_& rinfo
+ }
+ pdenot.asSingleDenotation.derivedSingleDenotation(pdenot.symbol, jointInfo)
+ } else {
+ pdenot & (
+ new JointRefDenotation(NoSymbol, rinfo, Period.allInRun(ctx.runId)),
+ pre,
+ safeIntersection = ctx.pendingMemberSearches.contains(name))
+ }
+ }
+
+ def goApply(tp: HKApply) = tp.tycon match {
+ case tl: PolyType =>
+ go(tl.resType).mapInfo(info =>
+ tl.derivedLambdaAbstraction(tl.paramNames, tl.paramBounds, info).appliedTo(tp.args))
+ case _ =>
+ go(tp.superType)
+ }
+
+ def goThis(tp: ThisType) = {
+ val d = go(tp.underlying)
+ if (d.exists)
+ if ((pre eq tp) && d.symbol.is(NamedTypeParam) && (d.symbol.owner eq tp.cls))
+ // If we look for a named type parameter `P` in `C.this.P`, looking up
+ // the fully applied self type of `C` will give as an info the alias type
+ // `P = this.P`. We need to return a denotation with the underlying bounds instead.
+ d.symbol.denot
+ else d
+ else
+ // There is a special case to handle:
+ // trait Super { this: Sub => private class Inner {} println(this.Inner) }
+ // class Sub extends Super
+ // When resolving Super.this.Inner, the normal logic goes to the self type and
+ // looks for Inner from there. But this fails because Inner is private.
+ // We fix the problem by having the following fallback case, which links up the
+ // member in Super instead of Sub.
+ // As an example of this in the wild, see
+ // loadClassWithPrivateInnerAndSubSelf in ShowClassTests
+ go(tp.cls.typeRef) orElse d
+ }
+ def goParam(tp: PolyParam) = {
+ val next = tp.underlying
+ ctx.typerState.constraint.entry(tp) match {
+ case bounds: TypeBounds if bounds ne next =>
+ ctx.typerState.ephemeral = true
+ go(bounds.hi)
+ case _ =>
+ go(next)
+ }
+ }
+ def goAnd(l: Type, r: Type) = {
+ go(l) & (go(r), pre, safeIntersection = ctx.pendingMemberSearches.contains(name))
+ }
+
+ { val recCount = ctx.findMemberCount + 1
+ ctx.findMemberCount = recCount
+ if (recCount >= Config.LogPendingFindMemberThreshold)
+ ctx.pendingMemberSearches = name :: ctx.pendingMemberSearches
+ }
+
+ //assert(ctx.findMemberCount < 20)
+ try go(this)
+ catch {
+ case ex: Throwable =>
+ core.println(i"findMember exception for $this member $name, pre = $pre")
+ throw ex // DEBUG
+ }
+ finally {
+ val recCount = ctx.findMemberCount
+ if (recCount >= Config.LogPendingFindMemberThreshold)
+ ctx.pendingMemberSearches = ctx.pendingMemberSearches.tail
+ ctx.findMemberCount = recCount - 1
+ }
+ }
+
+ /** The set of names of members of this type that pass the given name filter
+ * when seen as members of `pre`. More precisely, these are all
+ * of members `name` such that `keepOnly(pre, name)` is `true`.
+ * @note: OK to use a Set[Name] here because Name hashcodes are replayable,
+ * hence the Set will always give the same names in the same order.
+ */
+ final def memberNames(keepOnly: NameFilter, pre: Type = this)(implicit ctx: Context): Set[Name] = this match {
+ case tp: ClassInfo =>
+ tp.cls.memberNames(keepOnly) filter (keepOnly(pre, _))
+ case tp: RefinedType =>
+ val ns = tp.parent.memberNames(keepOnly, pre)
+ if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns
+ case tp: TypeProxy =>
+ tp.underlying.memberNames(keepOnly, pre)
+ case tp: AndType =>
+ tp.tp1.memberNames(keepOnly, pre) | tp.tp2.memberNames(keepOnly, pre)
+ case tp: OrType =>
+ tp.tp1.memberNames(keepOnly, pre) & tp.tp2.memberNames(keepOnly, pre)
+ case _ =>
+ Set()
+ }
+
+ def memberDenots(keepOnly: NameFilter, f: (Name, mutable.Buffer[SingleDenotation]) => Unit)(implicit ctx: Context): Seq[SingleDenotation] = {
+ val buf = mutable.ArrayBuffer[SingleDenotation]()
+ for (name <- memberNames(keepOnly)) f(name, buf)
+ buf
+ }
+
+ /** The set of abstract term members of this type. */
+ final def abstractTermMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTermMembers") {
+ memberDenots(abstractTermNameFilter,
+ (name, buf) => buf ++= nonPrivateMember(name).altsWith(_ is Deferred))
+ }
+
+ /** The set of abstract type members of this type. */
+ final def abstractTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("abstractTypeMembers") {
+ memberDenots(abstractTypeNameFilter,
+ (name, buf) => buf += nonPrivateMember(name).asSingleDenotation)
+ }
+
+ /** The set of abstract type members of this type. */
+ final def nonClassTypeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("nonClassTypeMembers") {
+ memberDenots(nonClassTypeNameFilter,
+ (name, buf) => buf += member(name).asSingleDenotation)
+ }
+
+ /** The set of type members of this type */
+ final def typeMembers(implicit ctx: Context): Seq[SingleDenotation] = track("typeMembers") {
+ memberDenots(typeNameFilter,
+ (name, buf) => buf += member(name).asSingleDenotation)
+ }
+
+ /** The set of implicit members of this type */
+ final def implicitMembers(implicit ctx: Context): List[TermRef] = track("implicitMembers") {
+ memberDenots(implicitFilter,
+ (name, buf) => buf ++= member(name).altsWith(_ is Implicit))
+ .toList.map(d => TermRef.withSig(this, d.symbol.asTerm))
+ }
+
+ /** The set of member classes of this type */
+ final def memberClasses(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") {
+ memberDenots(typeNameFilter,
+ (name, buf) => buf ++= member(name).altsWith(x => x.isClass))
+ }
+
+ final def fields(implicit ctx: Context): Seq[SingleDenotation] = track("fields") {
+ memberDenots(fieldFilter,
+ (name, buf) => buf ++= member(name).altsWith(x => !x.is(Method)))
+ }
+
+ /** The set of members of this type having at least one of `requiredFlags` but none of `excludedFlags` set */
+ final def membersBasedOnFlags(requiredFlags: FlagSet, excludedFlags: FlagSet)(implicit ctx: Context): Seq[SingleDenotation] = track("implicitMembers") {
+ memberDenots(takeAllFilter,
+ (name, buf) => buf ++= memberExcluding(name, excludedFlags).altsWith(x => x.is(requiredFlags)))
+ }
+
+ /** The info of `sym`, seen as a member of this type. */
+ final def memberInfo(sym: Symbol)(implicit ctx: Context): Type =
+ sym.info.asSeenFrom(this, sym.owner)
+
+ /** This type seen as if it were the type of a member of prefix type `pre`
+ * declared in class `cls`.
+ */
+ final def asSeenFrom(pre: Type, cls: Symbol)(implicit ctx: Context): Type = track("asSeenFrom") {
+ if (!cls.membersNeedAsSeenFrom(pre)) this
+ else ctx.asSeenFrom(this, pre, cls)
+ }
+
+// ----- Subtype-related --------------------------------------------
+
+ /** Is this type a subtype of that type? */
+ final def <:<(that: Type)(implicit ctx: Context): Boolean = track("<:<") {
+ ctx.typeComparer.topLevelSubType(this, that)
+ }
+
+ /** Is this type a subtype of that type? */
+ final def frozen_<:<(that: Type)(implicit ctx: Context): Boolean = track("frozen_<:<") {
+ ctx.typeComparer.isSubTypeWhenFrozen(this, that)
+ }
+
+ /** Is this type the same as that type?
+ * This is the case iff `this <:< that` and `that <:< this`.
+ */
+ final def =:=(that: Type)(implicit ctx: Context): Boolean = track("=:=") {
+ ctx.typeComparer.isSameType(this, that)
+ }
+
+ /** Is this type a primitive value type which can be widened to the primitive value type `that`? */
+ def isValueSubType(that: Type)(implicit ctx: Context) = widen match {
+ case self: TypeRef if self.symbol.isPrimitiveValueClass =>
+ that.widenExpr match {
+ case that: TypeRef if that.symbol.isPrimitiveValueClass =>
+ defn.isValueSubClass(self.symbol, that.symbol)
+ case _ =>
+ false
+ }
+ case _ =>
+ false
+ }
+
+ def relaxed_<:<(that: Type)(implicit ctx: Context) =
+ (this <:< that) || (this isValueSubType that)
+
+ /** Is this type a legal type for a member that overrides another
+ * member of type `that`? This is the same as `<:<`, except that
+ * the types ()T and => T are identified, and T is seen as overriding
+ * either type.
+ */
+ final def overrides(that: Type)(implicit ctx: Context) = {
+ def result(tp: Type): Type = tp match {
+ case ExprType(_) | MethodType(Nil, _) => tp.resultType
+ case _ => tp
+ }
+ (this frozen_<:< that) || {
+ val rthat = result(that)
+ (rthat ne that) && (result(this) frozen_<:< rthat)
+ }
+ }
+
+ /** Is this type close enough to that type so that members
+ * with the two types would override each other?
+ * This means:
+ * - Either both types are polytypes with the same number of
+ * type parameters and their result types match after renaming
+ * corresponding type parameters
+ * - Or both types are method types with =:=-equivalent(*) parameter types
+ * and matching result types after renaming corresponding parameter types
+ * if the method types are dependent.
+ * - Or both types are =:=-equivalent
+ * - Or phase.erasedTypes is false, and neither type takes
+ * term or type parameters.
+ *
+ * (*) when matching with a Java method, we also regard Any and Object as equivalent
+ * parameter types.
+ */
+ def matches(that: Type)(implicit ctx: Context): Boolean = track("matches") {
+ ctx.typeComparer.matchesType(this, that, relaxed = !ctx.phase.erasedTypes)
+ }
+
+ /** This is the same as `matches` except that it also matches => T with T and
+ * vice versa.
+ */
+ def matchesLoosely(that: Type)(implicit ctx: Context): Boolean =
+ (this matches that) || {
+ val thisResult = this.widenExpr
+ val thatResult = that.widenExpr
+ (this eq thisResult) != (that eq thatResult) && (thisResult matchesLoosely thatResult)
+ }
+
+ /** The basetype TypeRef of this type with given class symbol,
+ * but without including any type arguments
+ */
+ final def baseTypeRef(base: Symbol)(implicit ctx: Context): Type = /*ctx.traceIndented(s"$this baseTypeRef $base")*/ /*>|>*/ track("baseTypeRef") /*<|<*/ {
+ base.denot match {
+ case classd: ClassDenotation => classd.baseTypeRefOf(this)
+ case _ => NoType
+ }
+ }
+
+ def & (that: Type)(implicit ctx: Context): Type = track("&") {
+ ctx.typeComparer.glb(this, that)
+ }
+
+ /** Safer version of `&`.
+ *
+ * This version does not simplify the upper bound of the intersection of
+ * two TypeBounds. The simplification done by `&` requires subtyping checks
+ * which may end up calling `&` again, in most cases this should be safe
+ * but because of F-bounded types, this can result in an infinite loop
+ * (which will be masked unless `-Yno-deep-subtypes` is enabled).
+ */
+ def safe_& (that: Type)(implicit ctx: Context): Type = (this, that) match {
+ case (TypeBounds(lo1, hi1), TypeBounds(lo2, hi2)) => TypeBounds(lo1 | lo2, AndType(hi1, hi2))
+ case _ => this & that
+ }
+
+ def | (that: Type)(implicit ctx: Context): Type = track("|") {
+ ctx.typeComparer.lub(this, that)
+ }
+
+// ----- Unwrapping types -----------------------------------------------
+
+ /** Map a TypeVar to either its instance if it is instantiated, or its origin,
+ * if not, until the result is no longer a TypeVar. Identity on all other types.
+ */
+ def stripTypeVar(implicit ctx: Context): Type = this
+
+ /** Remove all AnnotatedTypes wrapping this type.
+ */
+ def stripAnnots(implicit ctx: Context): Type = this
+
+ /** Widen from singleton type to its underlying non-singleton
+ * base type by applying one or more `underlying` dereferences,
+ * Also go from => T to T.
+ * Identity for all other types. Example:
+ *
+ * class Outer { class C ; val x: C }
+ * def o: Outer
+ * <o.x.type>.widen = o.C
+ */
+ final def widen(implicit ctx: Context): Type = widenSingleton match {
+ case tp: ExprType => tp.resultType.widen
+ case tp => tp
+ }
+
+ /** Widen from singleton type to its underlying non-singleton
+ * base type by applying one or more `underlying` dereferences.
+ */
+ final def widenSingleton(implicit ctx: Context): Type = stripTypeVar match {
+ case tp: SingletonType if !tp.isOverloaded => tp.underlying.widenSingleton
+ case _ => this
+ }
+
+ /** Widen from TermRef to its underlying non-termref
+ * base type, while also skipping Expr types.
+ */
+ final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match {
+ case tp: TermRef if !tp.isOverloaded => tp.underlying.widenExpr.widenTermRefExpr
+ case _ => this
+ }
+
+ /** Widen from ExprType type to its result type.
+ * (Note: no stripTypeVar needed because TypeVar's can't refer to ExprTypes.)
+ */
+ final def widenExpr: Type = this match {
+ case tp: ExprType => tp.resType
+ case _ => this
+ }
+
+ /** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */
+ final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match {
+ case tp: ExprType => tp.resultType.widenIfUnstable
+ case tp: TermRef if !tp.symbol.isStable => tp.underlying.widenIfUnstable
+ case _ => this
+ }
+
+ /** If this is a skolem, its underlying type, otherwise the type itself */
+ final def widenSkolem(implicit ctx: Context): Type = this match {
+ case tp: SkolemType => tp.underlying
+ case _ => this
+ }
+
+ /** Eliminate anonymous classes */
+ final def deAnonymize(implicit ctx: Context): Type = this match {
+ case tp:TypeRef if tp.symbol.isAnonymousClass =>
+ tp.symbol.asClass.typeRef.asSeenFrom(tp.prefix, tp.symbol.owner)
+ case tp => tp
+ }
+
+ private def dealias(keepAnnots: Boolean)(implicit ctx: Context): Type = this match {
+ case tp: TypeRef =>
+ if (tp.symbol.isClass) tp
+ else tp.info match {
+ case TypeAlias(tp) => tp.dealias(keepAnnots)
+ case _ => tp
+ }
+ case tp: TypeVar =>
+ val tp1 = tp.instanceOpt
+ if (tp1.exists) tp1.dealias(keepAnnots) else tp
+ case tp: AnnotatedType =>
+ val tp1 = tp.tpe.dealias(keepAnnots)
+ if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1
+ case tp: LazyRef =>
+ tp.ref.dealias(keepAnnots)
+ case app @ HKApply(tycon, args) =>
+ val tycon1 = tycon.dealias(keepAnnots)
+ if (tycon1 ne tycon) app.superType.dealias(keepAnnots)
+ else this
+ case _ => this
+ }
+
+ /** Follow aliases and dereferences LazyRefs and instantiated TypeVars until type
+ * is no longer alias type, LazyRef, or instantiated type variable.
+ * Goes through annotated types and rewraps annotations on the result.
+ */
+ final def dealiasKeepAnnots(implicit ctx: Context): Type =
+ dealias(keepAnnots = true)
+
+ /** Follow aliases and dereferences LazyRefs, annotated types and instantiated
+ * TypeVars until type is no longer alias type, annotated type, LazyRef,
+ * or instantiated type variable.
+ */
+ final def dealias(implicit ctx: Context): Type =
+ dealias(keepAnnots = false)
+
+ /** Perform successive widenings and dealiasings until none can be applied anymore */
+ final def widenDealias(implicit ctx: Context): Type = {
+ val res = this.widen.dealias
+ if (res eq this) res else res.widenDealias
+ }
+
+ /** Widen from constant type to its underlying non-constant
+ * base type.
+ */
+ final def deconst(implicit ctx: Context): Type = stripTypeVar match {
+ case tp: ConstantType => tp.value.tpe
+ case _ => this
+ }
+
+ /** If this is a (possibly aliased, annotated, and/or parameterized) reference to
+ * a class, the class type ref, otherwise NoType.
+ * @param refinementOK If `true` we also skip non-parameter refinements.
+ */
+ def underlyingClassRef(refinementOK: Boolean)(implicit ctx: Context): Type = dealias match {
+ case tp: TypeRef =>
+ if (tp.symbol.isClass) tp
+ else if (tp.symbol.isAliasType) tp.underlying.underlyingClassRef(refinementOK)
+ else NoType
+ case tp: AnnotatedType =>
+ tp.underlying.underlyingClassRef(refinementOK)
+ case tp: RefinedType =>
+ def isParamName = tp.classSymbol.typeParams.exists(_.name == tp.refinedName)
+ if (refinementOK || isParamName) tp.underlying.underlyingClassRef(refinementOK)
+ else NoType
+ case tp: RecType =>
+ tp.underlying.underlyingClassRef(refinementOK)
+ case _ =>
+ NoType
+ }
+
+ /** The iterator of underlying types as long as type is a TypeProxy.
+ * Useful for diagnostics
+ */
+ def underlyingIterator(implicit ctx: Context): Iterator[Type] = new Iterator[Type] {
+ var current = Type.this
+ var hasNext = true
+ def next = {
+ val res = current
+ hasNext = current.isInstanceOf[TypeProxy]
+ if (hasNext) current = current.asInstanceOf[TypeProxy].underlying
+ res
+ }
+ }
+
+ /** A prefix-less refined this or a termRef to a new skolem symbol
+ * that has the given type as info.
+ */
+ def narrow(implicit ctx: Context): TermRef =
+ TermRef(NoPrefix, ctx.newSkolem(this))
+
+ /** Useful for diagnostics: The underlying type if this type is a type proxy,
+ * otherwise NoType
+ */
+ def underlyingIfProxy(implicit ctx: Context) = this match {
+ case this1: TypeProxy => this1.underlying
+ case _ => NoType
+ }
+
+ /** If this is a FunProto or PolyProto, WildcardType, otherwise this. */
+ def notApplied: Type = this
+
+ // ----- Normalizing typerefs over refined types ----------------------------
+
+ /** If this normalizes* to a refinement type that has a refinement for `name` (which might be followed
+ * by other refinements), and the refined info is a type alias, return the alias,
+ * otherwise return NoType. Used to reduce types of the form
+ *
+ * P { ... type T = / += / -= U ... } # T
+ *
+ * to just U. Does not perform the reduction if the resulting type would contain
+ * a reference to the "this" of the current refined type, except in the following situation
+ *
+ * (1) The "this" reference can be avoided by following an alias. Example:
+ *
+ * P { type T = String, type R = P{...}.T } # R --> String
+ *
+ * (*) normalizes means: follow instantiated typevars and aliases.
+ */
+ def lookupRefined(name: Name)(implicit ctx: Context): Type = {
+ def loop(pre: Type): Type = pre.stripTypeVar match {
+ case pre: RefinedType =>
+ pre.refinedInfo match {
+ case TypeAlias(alias) =>
+ if (pre.refinedName ne name) loop(pre.parent) else alias
+ case _ => loop(pre.parent)
+ }
+ case pre: RecType =>
+ val candidate = loop(pre.parent)
+ if (candidate.exists && !pre.isReferredToBy(candidate)) {
+ //println(s"lookupRefined ${this.toString} . $name, pre: $pre ---> $candidate / ${candidate.toString}")
+ candidate
+ }
+ else NoType
+ case SkolemType(tp) =>
+ tp.lookupRefined(name)
+ case pre: WildcardType =>
+ WildcardType
+ case pre: TypeRef =>
+ pre.info match {
+ case TypeAlias(alias) => loop(alias)
+ case _ => NoType
+ }
+ case _ =>
+ NoType
+ }
+
+ loop(this)
+ }
+
+ /** The type <this . name> , reduced if possible */
+ def select(name: Name)(implicit ctx: Context): Type = name match {
+ case name: TermName => TermRef.all(this, name)
+ case name: TypeName => TypeRef(this, name).reduceProjection
+ }
+
+ /** The type <this . name> , reduced if possible, with given denotation if unreduced */
+ def select(name: Name, denot: Denotation)(implicit ctx: Context): Type = name match {
+ case name: TermName => TermRef(this, name, denot)
+ case name: TypeName => TypeRef(this, name, denot).reduceProjection
+ }
+
+ /** The type <this . name> with given symbol, reduced if possible */
+ def select(sym: Symbol)(implicit ctx: Context): Type =
+ if (sym.isTerm) TermRef(this, sym.asTerm)
+ else TypeRef(this, sym.asType).reduceProjection
+
+// ----- Access to parts --------------------------------------------
+
+ /** The normalized prefix of this type is:
+ * For an alias type, the normalized prefix of its alias
+ * For all other named type and class infos: the prefix.
+ * Inherited by all other type proxies.
+ * `NoType` for all other types.
+ */
+ final def normalizedPrefix(implicit ctx: Context): Type = this match {
+ case tp: NamedType =>
+ if (tp.symbol.info.isAlias) tp.info.normalizedPrefix else tp.prefix
+ case tp: ClassInfo =>
+ tp.prefix
+ case tp: TypeProxy =>
+ tp.underlying.normalizedPrefix
+ case _ =>
+ NoType
+ }
+
+ /** For a ClassInfo type, its parents,
+ * Inherited by all type proxies. Empty for all other types.
+ * Overwritten in ClassInfo, where parents is cached.
+ */
+ def parents(implicit ctx: Context): List[TypeRef] = this match {
+ case tp: TypeProxy => tp.underlying.parents
+ case _ => List()
+ }
+
+ /** The full parent types, including all type arguments */
+ def parentsWithArgs(implicit ctx: Context): List[Type] = this match {
+ case tp: TypeProxy => tp.superType.parentsWithArgs
+ case _ => List()
+ }
+
+ /** The first parent of this type, AnyRef if list of parents is empty */
+ def firstParent(implicit ctx: Context): TypeRef = parents match {
+ case p :: _ => p
+ case _ => defn.AnyType
+ }
+
+ /** the self type of the underlying classtype */
+ def givenSelfType(implicit ctx: Context): Type = this match {
+ case tp: RefinedType => tp.wrapIfMember(tp.parent.givenSelfType)
+ case tp: ThisType => tp.tref.givenSelfType
+ case tp: TypeProxy => tp.superType.givenSelfType
+ case _ => NoType
+ }
+
+ /** The parameter types of a PolyType or MethodType, Empty list for others */
+ final def paramTypess(implicit ctx: Context): List[List[Type]] = this match {
+ case mt: MethodType => mt.paramTypes :: mt.resultType.paramTypess
+ case pt: PolyType => pt.resultType.paramTypess
+ case _ => Nil
+ }
+
+ /** The parameter names of a PolyType or MethodType, Empty list for others */
+ final def paramNamess(implicit ctx: Context): List[List[TermName]] = this match {
+ case mt: MethodType => mt.paramNames :: mt.resultType.paramNamess
+ case pt: PolyType => pt.resultType.paramNamess
+ case _ => Nil
+ }
+
+
+ /** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */
+ final def firstParamTypes(implicit ctx: Context): List[Type] = this match {
+ case mt: MethodType => mt.paramTypes
+ case pt: PolyType => pt.resultType.firstParamTypes
+ case _ => Nil
+ }
+
+ /** Is this either not a method at all, or a parameterless method? */
+ final def isParameterless(implicit ctx: Context): Boolean = this match {
+ case mt: MethodType => false
+ case pt: PolyType => pt.resultType.isParameterless
+ case _ => true
+ }
+
+ /** The resultType of a PolyType, MethodType, or ExprType, the type itself for others */
+ def resultType(implicit ctx: Context): Type = this
+
+ /** The final result type of a PolyType, MethodType, or ExprType, after skipping
+ * all parameter sections, the type itself for all others.
+ */
+ def finalResultType(implicit ctx: Context): Type = resultType match {
+ case mt: MethodType => mt.resultType.finalResultType
+ case pt: PolyType => pt.resultType.finalResultType
+ case _ => resultType
+ }
+
+ /** This type seen as a TypeBounds */
+ final def bounds(implicit ctx: Context): TypeBounds = this match {
+ case tp: TypeBounds => tp
+ case ci: ClassInfo => TypeAlias(ci.typeRef)
+ case wc: WildcardType =>
+ wc.optBounds match {
+ case bounds: TypeBounds => bounds
+ case NoType => TypeBounds.empty
+ }
+ case _ => TypeAlias(this)
+ }
+
+ /** The type parameter with given `name`. This tries first `decls`
+ * in order not to provoke a cycle by forcing the info. If that yields
+ * no symbol it tries `member` as an alternative.
+ */
+ def typeParamNamed(name: TypeName)(implicit ctx: Context): Symbol =
+ classSymbol.unforcedDecls.lookup(name) orElse member(name).symbol
+
+ /** If this is a prototype with some ignored component, reveal one more
+ * layer of it. Otherwise the type itself.
+ */
+ def deepenProto(implicit ctx: Context): Type = this
+
+// ----- Substitutions -----------------------------------------------------
+
+ /** Substitute all types that refer in their symbol attribute to
+ * one of the symbols in `from` by the corresponding types in `to`.
+ */
+ final def subst(from: List[Symbol], to: List[Type])(implicit ctx: Context): Type =
+ if (from.isEmpty) this
+ else {
+ val from1 = from.tail
+ if (from1.isEmpty) ctx.subst1(this, from.head, to.head, null)
+ else {
+ val from2 = from1.tail
+ if (from2.isEmpty) ctx.subst2(this, from.head, to.head, from1.head, to.tail.head, null)
+ else ctx.subst(this, from, to, null)
+ }
+ }
+
+ /** Same as `subst` but follows aliases as a fallback. When faced with a reference
+ * to an alias type, where normal substitution does not yield a new type, the
+ * substitution is instead applied to the alias. If that yields a new type,
+ * this type is returned, otherwise the original type (not the alias) is returned.
+ * A use case for this method is if one wants to substitute the type parameters
+ * of a class and also wants to substitute any parameter accessors that alias
+ * the type parameters.
+ */
+ final def substDealias(from: List[Symbol], to: List[Type])(implicit ctx: Context): Type =
+ ctx.substDealias(this, from, to, null)
+
+ /** Substitute all types of the form `PolyParam(from, N)` by
+ * `PolyParam(to, N)`.
+ */
+ final def subst(from: BindingType, to: BindingType)(implicit ctx: Context): Type =
+ ctx.subst(this, from, to, null)
+
+ /** Substitute all occurrences of `This(cls)` by `tp` */
+ final def substThis(cls: ClassSymbol, tp: Type)(implicit ctx: Context): Type =
+ ctx.substThis(this, cls, tp, null)
+
+ /** As substThis, but only is class is a static owner (i.e. a globally accessible object) */
+ final def substThisUnlessStatic(cls: ClassSymbol, tp: Type)(implicit ctx: Context): Type =
+ if (cls.isStaticOwner) this else ctx.substThis(this, cls, tp, null)
+
+ /** Substitute all occurrences of `RecThis(binder)` by `tp` */
+ final def substRecThis(binder: RecType, tp: Type)(implicit ctx: Context): Type =
+ ctx.substRecThis(this, binder, tp, null)
+
+ /** Substitute a bound type by some other type */
+ final def substParam(from: ParamType, to: Type)(implicit ctx: Context): Type =
+ ctx.substParam(this, from, to, null)
+
+ /** Substitute bound types by some other types */
+ final def substParams(from: BindingType, to: List[Type])(implicit ctx: Context): Type =
+ ctx.substParams(this, from, to, null)
+
+ /** Substitute all occurrences of symbols in `from` by references to corresponding symbols in `to`
+ */
+ final def substSym(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): Type =
+ ctx.substSym(this, from, to, null)
+
+// ----- misc -----------------------------------------------------------
+
+ /** Turn type into a function type.
+ * @pre this is a non-dependent method type.
+ * @param dropLast The number of trailing parameters that should be dropped
+ * when forming the function type.
+ */
+ def toFunctionType(dropLast: Int = 0)(implicit ctx: Context): Type = this match {
+ case mt @ MethodType(_, formals) if !mt.isDependent || ctx.mode.is(Mode.AllowDependentFunctions) =>
+ val formals1 = if (dropLast == 0) formals else formals dropRight dropLast
+ defn.FunctionOf(
+ formals1 mapConserve (_.underlyingIfRepeated(mt.isJava)), mt.resultType)
+ }
+
+ /** The signature of this type. This is by default NotAMethod,
+ * but is overridden for PolyTypes, MethodTypes, and TermRefWithSignature types.
+ * (the reason why we deviate from the "final-method-with-pattern-match-in-base-class"
+ * pattern is that method signatures use caching, so encapsulation
+ * is improved using an OO scheme).
+ */
+ def signature(implicit ctx: Context): Signature = Signature.NotAMethod
+
+ /** Convert to text */
+ def toText(printer: Printer): Text = printer.toText(this)
+
+ /** Utility method to show the underlying type of a TypeProxy chain together
+ * with the proxy type itself.
+ */
+ def showWithUnderlying(n: Int = 1)(implicit ctx: Context): String = this match {
+ case tp: TypeProxy if n > 0 => s"$show with underlying ${tp.underlying.showWithUnderlying(n - 1)}"
+ case _ => show
+ }
+
+ /** A simplified version of this type which is equivalent wrt =:= to this type.
+ * This applies a typemap to the type which (as all typemaps) follows type
+ * variable instances and reduces typerefs over refined types. It also
+ * re-evaluates all occurrences of And/OrType with &/| because
+ * what was a union or intersection of type variables might be a simpler type
+ * after the type variables are instantiated. Finally, it
+ * maps poly params in the current constraint set back to their type vars.
+ */
+ def simplified(implicit ctx: Context) = ctx.simplify(this, null)
+
+ /** customized hash code of this type.
+ * NotCached for uncached types. Cached types
+ * compute hash and use it as the type's hashCode.
+ */
+ def hash: Int
+ } // end Type
+
+// ----- Type categories ----------------------------------------------
+
+ /** A marker trait for cached types */
+ trait CachedType extends Type
+
+ /** A marker trait for type proxies.
+ * Each implementation is expected to redefine the `underlying` method.
+ */
+ abstract class TypeProxy extends Type {
+
+ /** The type to which this proxy forwards operations. */
+ def underlying(implicit ctx: Context): Type
+
+ /** The closest supertype of this type. This is the same as `underlying`,
+ * except for TypeRefs where the upper bound is returned, and HKApplys,
+ * where the upper bound of the constructor is re-applied to the arguments.
+ */
+ def superType(implicit ctx: Context): Type = underlying
+ }
+
+ // Every type has to inherit one of the following four abstract type classes.,
+ // which determine whether the type is cached, and whether
+ // it is a proxy of some other type. The duplication in their methods
+ // is for efficiency.
+
+ /** Instances of this class are cached and are not proxies. */
+ abstract class CachedGroundType extends Type with CachedType {
+ private[this] var myHash = HashUnknown
+ final def hash = {
+ if (myHash == HashUnknown) {
+ myHash = computeHash
+ assert(myHash != HashUnknown)
+ }
+ myHash
+ }
+ override final def hashCode =
+ if (hash == NotCached) System.identityHashCode(this) else hash
+ def computeHash: Int
+ }
+
+ /** Instances of this class are cached and are proxies. */
+ abstract class CachedProxyType extends TypeProxy with CachedType {
+ protected[this] var myHash = HashUnknown
+ final def hash = {
+ if (myHash == HashUnknown) {
+ myHash = computeHash
+ assert(myHash != HashUnknown)
+ }
+ myHash
+ }
+ override final def hashCode =
+ if (hash == NotCached) System.identityHashCode(this) else hash
+ def computeHash: Int
+ }
+
+ /** Instances of this class are uncached and are not proxies. */
+ abstract class UncachedGroundType extends Type {
+ final def hash = NotCached
+ if (monitored) {
+ record(s"uncachable")
+ record(s"uncachable: $getClass")
+ }
+ }
+
+ /** Instances of this class are uncached and are proxies. */
+ abstract class UncachedProxyType extends TypeProxy {
+ final def hash = NotCached
+ if (monitored) {
+ record(s"uncachable")
+ record(s"uncachable: $getClass")
+ }
+ }
+
+ /** A marker trait for types that apply only to type symbols */
+ trait TypeType extends Type
+
+ /** A marker trait for types that apply only to term symbols or that
+ * represent higher-kinded types.
+ */
+ trait TermType extends Type
+
+ /** A marker trait for types that can be types of values or prototypes of value types */
+ trait ValueTypeOrProto extends TermType
+
+ /** A marker trait for types that can be types of values or that are higher-kinded */
+ trait ValueType extends ValueTypeOrProto
+
+ /** A marker trait for types that are guaranteed to contain only a
+ * single non-null value (they might contain null in addition).
+ */
+ trait SingletonType extends TypeProxy with ValueType {
+ def isOverloaded(implicit ctx: Context) = false
+ }
+
+ /** A marker trait for types that bind other types that refer to them.
+ * Instances are: PolyType, MethodType, RefinedType.
+ */
+ trait BindingType extends Type
+
+ /** A trait for proto-types, used as expected types in typer */
+ trait ProtoType extends Type {
+ def isMatchedBy(tp: Type)(implicit ctx: Context): Boolean
+ def fold[T](x: T, ta: TypeAccumulator[T])(implicit ctx: Context): T
+ def map(tm: TypeMap)(implicit ctx: Context): ProtoType
+ }
+
+ /** Implementations of this trait cache the results of `narrow`. */
+ trait NarrowCached extends Type {
+ private var myNarrow: TermRef = null
+ override def narrow(implicit ctx: Context): TermRef = {
+ if (myNarrow eq null) myNarrow = super.narrow
+ myNarrow
+ }
+ }
+
+// --- NamedTypes ------------------------------------------------------------------
+
+ /** A NamedType of the form Prefix # name */
+ abstract class NamedType extends CachedProxyType with ValueType {
+
+ val prefix: Type
+ val name: Name
+
+ type ThisType >: this.type <: NamedType
+
+ assert(prefix.isValueType || (prefix eq NoPrefix), s"invalid prefix $prefix")
+
+ private[this] var lastDenotation: Denotation = _
+ private[this] var lastSymbol: Symbol = _
+ private[this] var checkedPeriod = Nowhere
+
+ // Invariants:
+ // (1) checkedPeriod != Nowhere => lastDenotation != null
+ // (2) lastDenotation != null => lastSymbol != null
+
+ /** There is a denotation computed which is valid (somewhere in) the
+ * current run.
+ */
+ def denotationIsCurrent(implicit ctx: Context) =
+ lastDenotation != null && lastDenotation.validFor.runId == ctx.runId
+
+ /** The denotation is current, its symbol, otherwise NoDenotation.
+ *
+ * Note: This operation does not force the denotation, and is therefore
+ * timing dependent. It should only be used if the outcome of the
+ * essential computation does not depend on the symbol being present or not.
+ * It's currently used to take an optimized path in substituters and
+ * type accumulators, as well as to be safe in diagnostic printing.
+ * Normally, it's better to use `symbol`, not `currentSymbol`.
+ */
+ def currentSymbol(implicit ctx: Context) =
+ if (denotationIsCurrent) symbol else NoSymbol
+
+ /** The denotation currently denoted by this type */
+ final def denot(implicit ctx: Context): Denotation = {
+ val now = ctx.period
+ if (checkedPeriod == now) lastDenotation else denotAt(now)
+ }
+
+ /** A first fall back to do a somewhat more expensive calculation in case the first
+ * attempt in `denot` does not yield a denotation.
+ */
+ private def denotAt(now: Period)(implicit ctx: Context): Denotation = {
+ val d = lastDenotation
+ if (d != null && (d.validFor contains now)) {
+ checkedPeriod = now
+ d
+ }
+ else computeDenot
+ }
+
+ /** Hook for adding debug check code when denotations are assigned */
+ final def checkDenot()(implicit ctx: Context) = {}
+
+ /** A second fallback to recompute the denotation if necessary */
+ private def computeDenot(implicit ctx: Context): Denotation = {
+ val savedEphemeral = ctx.typerState.ephemeral
+ ctx.typerState.ephemeral = false
+ try {
+ val d = lastDenotation match {
+ case null =>
+ val sym = lastSymbol
+ if (sym == null) loadDenot else denotOfSym(sym)
+ case d: SymDenotation =>
+ if (this.isInstanceOf[WithFixedSym]) d.current
+ else if (d.validFor.runId == ctx.runId || ctx.stillValid(d))
+ if (d.exists && prefix.isTightPrefix(d.owner) || d.isConstructor) d.current
+ else recomputeMember(d) // symbol could have been overridden, recompute membership
+ else {
+ val newd = loadDenot
+ if (newd.exists) newd else d.staleSymbolError
+ }
+ case d =>
+ if (d.validFor.runId != ctx.period.runId) loadDenot
+ else d.current
+ }
+ if (ctx.typerState.ephemeral) record("ephemeral cache miss: loadDenot")
+ else if (d.exists) {
+ // Avoid storing NoDenotations in the cache - we will not be able to recover from
+ // them. The situation might arise that a type has NoDenotation in some later
+ // phase but a defined denotation earlier (e.g. a TypeRef to an abstract type
+ // is undefined after erasure.) We need to be able to do time travel back and
+ // forth also in these cases.
+
+ // Don't use setDenot here; double binding checks can give spurious failures after erasure
+ lastDenotation = d
+ checkDenot()
+ lastSymbol = d.symbol
+ checkedPeriod = ctx.period
+ }
+ d
+ }
+ finally ctx.typerState.ephemeral |= savedEphemeral
+ }
+
+ /** A member of `prefix` (disambiguated by `d.signature`) or, if none was found, `d.current`. */
+ private def recomputeMember(d: SymDenotation)(implicit ctx: Context): Denotation =
+ asMemberOf(prefix) match {
+ case NoDenotation => d.current
+ case newd: SingleDenotation => newd
+ case newd =>
+ newd.atSignature(d.signature) match {
+ case newd1: SingleDenotation if newd1.exists => newd1
+ case _ => d.current
+ }
+ }
+
+ private def denotOfSym(sym: Symbol)(implicit ctx: Context): Denotation = {
+ val d = sym.denot
+ val owner = d.owner
+ if (owner.isTerm) d else d.asSeenFrom(prefix)
+ }
+
+ private def checkSymAssign(sym: Symbol)(implicit ctx: Context) = {
+ def selfTypeOf(sym: Symbol) = sym.owner.info match {
+ case info: ClassInfo => info.givenSelfType
+ case _ => NoType
+ }
+ assert(
+ (lastSymbol eq sym) ||
+ (lastSymbol eq null) || {
+ val lastDefRunId = lastDenotation match {
+ case d: SymDenotation => d.validFor.runId
+ case _ => lastSymbol.defRunId
+ }
+ (lastDefRunId != sym.defRunId) ||
+ (lastDefRunId == NoRunId)
+ } ||
+ (lastSymbol.infoOrCompleter == ErrorType ||
+ sym.owner != lastSymbol.owner &&
+ (sym.owner.derivesFrom(lastSymbol.owner) ||
+ selfTypeOf(sym).derivesFrom(lastSymbol.owner) ||
+ selfTypeOf(lastSymbol).derivesFrom(sym.owner))),
+ i"""data race? overwriting symbol of type $this,
+ |long form = $toString of class $getClass,
+ |last sym id = ${lastSymbol.id}, new sym id = ${sym.id},
+ |last owner = ${lastSymbol.owner}, new owner = ${sym.owner},
+ |period = ${ctx.phase} at run ${ctx.runId}""")
+ }
+
+ protected def sig: Signature = Signature.NotAMethod
+
+ private[dotc] def withDenot(denot: Denotation)(implicit ctx: Context): ThisType =
+ if (sig != denot.signature)
+ withSig(denot.signature).withDenot(denot).asInstanceOf[ThisType]
+ else {
+ setDenot(denot)
+ this
+ }
+
+ private[dotc] final def setDenot(denot: Denotation)(implicit ctx: Context): Unit = {
+ if (Config.checkNoDoubleBindings)
+ if (ctx.settings.YnoDoubleBindings.value)
+ checkSymAssign(denot.symbol)
+
+ // additional checks that intercept `denot` can be added here
+
+ lastDenotation = denot
+ checkDenot()
+ lastSymbol = denot.symbol
+ checkedPeriod = Nowhere
+ }
+
+ private[dotc] def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType =
+ if (sig != signature)
+ withSig(signature).withSym(sym, signature).asInstanceOf[ThisType]
+ else {
+ setSym(sym)
+ this
+ }
+
+ private[dotc] final def setSym(sym: Symbol)(implicit ctx: Context): Unit = {
+ if (Config.checkNoDoubleBindings)
+ if (ctx.settings.YnoDoubleBindings.value)
+ checkSymAssign(sym)
+ uncheckedSetSym(sym)
+ }
+
+ private[dotc] final def uncheckedSetSym(sym: Symbol): Unit = {
+ lastDenotation = null
+ lastSymbol = sym
+ checkedPeriod = Nowhere
+ }
+
+ private def withSig(sig: Signature)(implicit ctx: Context): NamedType =
+ TermRef.withSig(prefix, name.asTermName, sig)
+
+ protected def loadDenot(implicit ctx: Context): Denotation = {
+ val d = asMemberOf(prefix)
+ if (d.exists || ctx.phaseId == FirstPhaseId || !lastDenotation.isInstanceOf[SymDenotation])
+ d
+ else { // name has changed; try load in earlier phase and make current
+ val d = loadDenot(ctx.withPhase(ctx.phaseId - 1)).current
+ if (d.exists) d
+ else throw new Error(s"failure to reload $this of class $getClass")
+ }
+ }
+
+ protected def asMemberOf(prefix: Type)(implicit ctx: Context): Denotation =
+ if (name.isShadowedName) prefix.nonPrivateMember(name.revertShadowed)
+ else prefix.member(name)
+
+
+ /** (1) Reduce a type-ref `W # X` or `W { ... } # U`, where `W` is a wildcard type
+ * to an (unbounded) wildcard type.
+ *
+ * (2) Reduce a type-ref `T { X = U; ... } # X` to `U`
+ * provided `U` does not refer with a RecThis to the
+ * refinement type `T { X = U; ... }`
+ */
+ def reduceProjection(implicit ctx: Context): Type = {
+ val reduced = prefix.lookupRefined(name)
+ if (reduced.exists) reduced else this
+ }
+
+ def symbol(implicit ctx: Context): Symbol = {
+ val now = ctx.period
+ if (checkedPeriod == now ||
+ lastDenotation == null && lastSymbol != null) lastSymbol
+ else denot.symbol
+ }
+
+ /** Retrieves currently valid symbol without necessarily updating denotation.
+ * Assumes that symbols do not change between periods in the same run.
+ * Used to get the class underlying a ThisType.
+ */
+ private[Types] def stableInRunSymbol(implicit ctx: Context): Symbol =
+ if (checkedPeriod.runId == ctx.runId) lastSymbol
+ else symbol
+
+ def info(implicit ctx: Context): Type = denot.info
+
+ def isType = isInstanceOf[TypeRef]
+ def isTerm = isInstanceOf[TermRef]
+
+ /** Guard against cycles that can arise if given `op`
+ * follows info. The problematic cases are a type alias to itself or
+ * bounded by itself or a val typed as itself:
+ *
+ * type T <: T
+ * val x: x.type
+ *
+ * These are errors but we have to make sure that operations do
+ * not loop before the error is detected.
+ */
+ final def controlled[T](op: => T)(implicit ctx: Context): T = try {
+ ctx.underlyingRecursions += 1
+ if (ctx.underlyingRecursions < Config.LogPendingUnderlyingThreshold)
+ op
+ else if (ctx.pendingUnderlying contains this)
+ throw CyclicReference(symbol)
+ else
+ try {
+ ctx.pendingUnderlying += this
+ op
+ } finally {
+ ctx.pendingUnderlying -= this
+ }
+ } finally {
+ ctx.underlyingRecursions -= 1
+ }
+
+ /** A selection of the same kind, but with potentially a different prefix.
+ * The following normalizations are performed for type selections T#A:
+ *
+ * T#A --> B if A is bound to an alias `= B` in T
+ *
+ * If Config.splitProjections is set:
+ *
+ * (S & T)#A --> S#A if T does not have a member named A
+ * --> T#A if S does not have a member named A
+ * --> S#A & T#A otherwise
+ * (S | T)#A --> S#A | T#A
+ */
+ def derivedSelect(prefix: Type)(implicit ctx: Context): Type =
+ if (prefix eq this.prefix) this
+ else if (isType) {
+ val res = prefix.lookupRefined(name)
+ if (res.exists) res
+ else if (Config.splitProjections)
+ prefix match {
+ case prefix: AndType =>
+ def isMissing(tp: Type) = tp match {
+ case tp: TypeRef => !tp.info.exists
+ case _ => false
+ }
+ val derived1 = derivedSelect(prefix.tp1)
+ val derived2 = derivedSelect(prefix.tp2)
+ return (
+ if (isMissing(derived1)) derived2
+ else if (isMissing(derived2)) derived1
+ else prefix.derivedAndType(derived1, derived2))
+ case prefix: OrType =>
+ val derived1 = derivedSelect(prefix.tp1)
+ val derived2 = derivedSelect(prefix.tp2)
+ return prefix.derivedOrType(derived1, derived2)
+ case _ =>
+ newLikeThis(prefix)
+ }
+ else newLikeThis(prefix)
+ }
+ else newLikeThis(prefix)
+
+ /** Create a NamedType of the same kind as this type, but with a new prefix.
+ */
+ def newLikeThis(prefix: Type)(implicit ctx: Context): NamedType =
+ NamedType(prefix, name)
+
+ /** Create a NamedType of the same kind as this type, but with a "inherited name".
+ * This is necessary to in situations like the following:
+ *
+ * class B { def m: T1 }
+ * class C extends B { private def m: T2; ... C.m }
+ * object C extends C
+ * object X { ... C.m }
+ *
+ * The two references of C.m in class C and object X refer to different
+ * definitions: The one in C refers to C#m whereas the one in X refers to B#m.
+ * But the type C.m must have only one denotation, so it can't refer to two
+ * members depending on context.
+ *
+ * In situations like this, the reference in X would get the type
+ * `<C.m>.shadowed` to make clear that we mean the inherited member, not
+ * the private one.
+ *
+ * Note: An alternative, possibly more robust scheme would be to give
+ * private members special names. A private definition would have a special
+ * name (say m' in the example above), but would be entered in its enclosing
+ * under both private and public names, so it could still be found by looking up
+ * the public name.
+ */
+ final def shadowed(implicit ctx: Context): NamedType =
+ NamedType(prefix, name.shadowedName)
+
+ override def equals(that: Any) = that match {
+ case that: NamedType =>
+ this.name == that.name &&
+ this.prefix == that.prefix &&
+ !that.isInstanceOf[TermRefWithSignature] &&
+ !that.isInstanceOf[WithFixedSym]
+ case _ =>
+ false
+ }
+
+ /* A version of toString which also prints aliases. Can be used for debugging
+ override def toString =
+ if (isTerm) s"TermRef($prefix, $name)"
+ else s"TypeRef($prefix, $name)${
+ if (lastDenotation != null && lastDenotation.infoOrCompleter.isAlias)
+ s"@@@ ${lastDenotation.infoOrCompleter.asInstanceOf[TypeAlias].hi}"
+ else ""}"
+ */
+ }
+
+ abstract case class TermRef(override val prefix: Type, name: TermName) extends NamedType with SingletonType {
+
+ type ThisType = TermRef
+
+ //assert(name.toString != "<local Coder>")
+ override def underlying(implicit ctx: Context): Type = {
+ val d = denot
+ if (d.isOverloaded) NoType else d.info
+ }
+
+ override def signature(implicit ctx: Context): Signature = denot.signature
+
+ override def isOverloaded(implicit ctx: Context) = denot.isOverloaded
+
+ private def rewrap(sd: SingleDenotation)(implicit ctx: Context) =
+ TermRef.withSigAndDenot(prefix, name, sd.signature, sd)
+
+ def alternatives(implicit ctx: Context): List[TermRef] =
+ denot.alternatives map rewrap
+
+ def altsWith(p: Symbol => Boolean)(implicit ctx: Context): List[TermRef] =
+ denot.altsWith(p) map rewrap
+ }
+
+ abstract case class TypeRef(override val prefix: Type, name: TypeName) extends NamedType {
+
+ type ThisType = TypeRef
+
+ override def underlying(implicit ctx: Context): Type = info
+
+ override def superType(implicit ctx: Context): Type = info match {
+ case TypeBounds(_, hi) => hi
+ case _ => info
+ }
+ }
+
+ final class TermRefWithSignature(prefix: Type, name: TermName, override val sig: Signature) extends TermRef(prefix, name) {
+ assert(prefix ne NoPrefix)
+ override def signature(implicit ctx: Context) = sig
+ override def loadDenot(implicit ctx: Context): Denotation = {
+ val d = super.loadDenot
+ if (sig eq Signature.OverloadedSignature) d
+ else d.atSignature(sig).checkUnique
+ }
+
+ override def newLikeThis(prefix: Type)(implicit ctx: Context): TermRef = {
+ val candidate = TermRef.withSig(prefix, name, sig)
+ if (symbol.exists && !candidate.symbol.exists) { // recompute from previous symbol
+ val ownSym = symbol
+ val newd = asMemberOf(prefix)
+ candidate.withDenot(newd.suchThat(_.signature == ownSym.signature))
+ }
+ else candidate
+ }
+
+ override def equals(that: Any) = that match {
+ case that: TermRefWithSignature =>
+ this.prefix == that.prefix &&
+ this.name == that.name &&
+ this.sig == that.sig
+ case _ =>
+ false
+ }
+ override def computeHash = doHash((name, sig), prefix)
+ override def toString = super.toString ++ s"/withSig($sig)"
+ }
+
+ trait WithFixedSym extends NamedType {
+ def fixedSym: Symbol
+ assert(fixedSym ne NoSymbol)
+ uncheckedSetSym(fixedSym)
+
+ override def withDenot(denot: Denotation)(implicit ctx: Context): ThisType = {
+ assert(denot.symbol eq fixedSym)
+ setDenot(denot)
+ this
+ }
+
+ override def withSym(sym: Symbol, signature: Signature)(implicit ctx: Context): ThisType =
+ unsupported("withSym")
+
+ override def newLikeThis(prefix: Type)(implicit ctx: Context): NamedType =
+ NamedType.withFixedSym(prefix, fixedSym)
+
+ override def equals(that: Any) = that match {
+ case that: WithFixedSym => this.prefix == that.prefix && (this.fixedSym eq that.fixedSym)
+ case _ => false
+ }
+ override def computeHash = doHash(fixedSym, prefix)
+ }
+
+ final class CachedTermRef(prefix: Type, name: TermName, hc: Int) extends TermRef(prefix, name) {
+ assert(prefix ne NoPrefix)
+ myHash = hc
+ override def computeHash = unsupported("computeHash")
+ }
+
+ final class CachedTypeRef(prefix: Type, name: TypeName, hc: Int) extends TypeRef(prefix, name) {
+ assert(prefix ne NoPrefix)
+ myHash = hc
+ override def computeHash = unsupported("computeHash")
+ }
+
+ // Those classes are non final as Linker extends them.
+ class TermRefWithFixedSym(prefix: Type, name: TermName, val fixedSym: TermSymbol) extends TermRef(prefix, name) with WithFixedSym
+ class TypeRefWithFixedSym(prefix: Type, name: TypeName, val fixedSym: TypeSymbol) extends TypeRef(prefix, name) with WithFixedSym
+
+ /** Assert current phase does not have erasure semantics */
+ private def assertUnerased()(implicit ctx: Context) =
+ if (Config.checkUnerased) assert(!ctx.phase.erasedTypes)
+
+ object NamedType {
+ def apply(prefix: Type, name: Name)(implicit ctx: Context) =
+ if (name.isTermName) TermRef.all(prefix, name.asTermName)
+ else TypeRef(prefix, name.asTypeName)
+ def apply(prefix: Type, name: Name, denot: Denotation)(implicit ctx: Context) =
+ if (name.isTermName) TermRef(prefix, name.asTermName, denot)
+ else TypeRef(prefix, name.asTypeName, denot)
+ def withFixedSym(prefix: Type, sym: Symbol)(implicit ctx: Context) =
+ if (sym.isType) TypeRef.withFixedSym(prefix, sym.name.asTypeName, sym.asType)
+ else TermRef.withFixedSym(prefix, sym.name.asTermName, sym.asTerm)
+ def withSymAndName(prefix: Type, sym: Symbol, name: Name)(implicit ctx: Context): NamedType =
+ if (sym.isType) TypeRef.withSymAndName(prefix, sym.asType, name.asTypeName)
+ else TermRef.withSymAndName(prefix, sym.asTerm, name.asTermName)
+ }
+
+ object TermRef {
+
+ private def symbolicRefs(implicit ctx: Context) = ctx.phase.symbolicRefs
+
+ /** Create term ref with given name, without specifying a signature.
+ * Its meaning is the (potentially multi-) denotation of the member(s)
+ * of prefix with given name.
+ */
+ def all(prefix: Type, name: TermName)(implicit ctx: Context): TermRef = {
+ ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TermRef]
+ }
+
+ /** Create term ref referring to given symbol, taking the signature
+ * from the symbol if it is completed, or creating a term ref without
+ * signature, if symbol is not yet completed.
+ */
+ def apply(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef =
+ withSymAndName(prefix, sym, sym.name)
+
+ /** Create term ref to given initial denotation, taking the signature
+ * from the denotation if it is completed, or creating a term ref without
+ * signature, if denotation is not yet completed.
+ */
+ def apply(prefix: Type, name: TermName, denot: Denotation)(implicit ctx: Context): TermRef = {
+ if ((prefix eq NoPrefix) || denot.symbol.isFresh || symbolicRefs)
+ apply(prefix, denot.symbol.asTerm)
+ else denot match {
+ case denot: SymDenotation if denot.isCompleted => withSig(prefix, name, denot.signature)
+ case _ => all(prefix, name)
+ }
+ } withDenot denot
+
+ /** Create a non-member term ref (which cannot be reloaded using `member`),
+ * with given prefix, name, and signature
+ */
+ def withFixedSym(prefix: Type, name: TermName, sym: TermSymbol)(implicit ctx: Context): TermRef =
+ unique(new TermRefWithFixedSym(prefix, name, sym))
+
+ /** Create a term ref referring to given symbol with given name, taking the signature
+ * from the symbol if it is completed, or creating a term ref without
+ * signature, if symbol is not yet completed. This is very similar to TermRef(Type, Symbol),
+ * except for two differences:
+ * (1) The symbol might not yet have a denotation, so the name needs to be given explicitly.
+ * (2) The name in the term ref need not be the same as the name of the Symbol.
+ */
+ def withSymAndName(prefix: Type, sym: TermSymbol, name: TermName)(implicit ctx: Context): TermRef =
+ if ((prefix eq NoPrefix) || sym.isFresh || symbolicRefs)
+ withFixedSym(prefix, name, sym)
+ else if (sym.defRunId != NoRunId && sym.isCompleted)
+ withSig(prefix, name, sym.signature) withSym (sym, sym.signature)
+ // Linker note:
+ // this is problematic, as withSig method could return a hash-consed refference
+ // that could have symbol already set making withSym trigger a double-binding error
+ // ./tests/run/absoverride.scala demonstates this
+ else
+ all(prefix, name) withSym (sym, Signature.NotAMethod)
+
+ /** Create a term ref to given symbol, taking the signature from the symbol
+ * (which must be completed).
+ */
+ def withSig(prefix: Type, sym: TermSymbol)(implicit ctx: Context): TermRef =
+ if ((prefix eq NoPrefix) || sym.isFresh || symbolicRefs) withFixedSym(prefix, sym.name, sym)
+ else withSig(prefix, sym.name, sym.signature).withSym(sym, sym.signature)
+
+ /** Create a term ref with given prefix, name and signature */
+ def withSig(prefix: Type, name: TermName, sig: Signature)(implicit ctx: Context): TermRef =
+ unique(new TermRefWithSignature(prefix, name, sig))
+
+ /** Create a term ref with given prefix, name, signature, and initial denotation */
+ def withSigAndDenot(prefix: Type, name: TermName, sig: Signature, denot: Denotation)(implicit ctx: Context): TermRef = {
+ if ((prefix eq NoPrefix) || denot.symbol.isFresh || symbolicRefs)
+ withFixedSym(prefix, denot.symbol.asTerm.name, denot.symbol.asTerm)
+ else
+ withSig(prefix, name, sig)
+ } withDenot denot
+ }
+
+ object TypeRef {
+ /** Create type ref with given prefix and name */
+ def apply(prefix: Type, name: TypeName)(implicit ctx: Context): TypeRef =
+ ctx.uniqueNamedTypes.enterIfNew(prefix, name).asInstanceOf[TypeRef]
+
+ /** Create type ref to given symbol */
+ def apply(prefix: Type, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
+ withSymAndName(prefix, sym, sym.name)
+
+ /** Create a non-member type ref (which cannot be reloaded using `member`),
+ * with given prefix, name, and symbol.
+ */
+ def withFixedSym(prefix: Type, name: TypeName, sym: TypeSymbol)(implicit ctx: Context): TypeRef =
+ unique(new TypeRefWithFixedSym(prefix, name, sym))
+
+ /** Create a type ref referring to given symbol with given name.
+ * This is very similar to TypeRef(Type, Symbol),
+ * except for two differences:
+ * (1) The symbol might not yet have a denotation, so the name needs to be given explicitly.
+ * (2) The name in the type ref need not be the same as the name of the Symbol.
+ */
+ def withSymAndName(prefix: Type, sym: TypeSymbol, name: TypeName)(implicit ctx: Context): TypeRef =
+ if ((prefix eq NoPrefix) || sym.isFresh) withFixedSym(prefix, name, sym)
+ else apply(prefix, name).withSym(sym, Signature.NotAMethod)
+
+ /** Create a type ref with given name and initial denotation */
+ def apply(prefix: Type, name: TypeName, denot: Denotation)(implicit ctx: Context): TypeRef = {
+ if ((prefix eq NoPrefix) || denot.symbol.isFresh) apply(prefix, denot.symbol.asType)
+ else apply(prefix, name)
+ } withDenot denot
+ }
+
+ // --- Other SingletonTypes: ThisType/SuperType/ConstantType ---------------------------
+
+ /** The type cls.this
+ * @param tref A type ref which indicates the class `cls`.
+ * Note: we do not pass a class symbol directly, because symbols
+ * do not survive runs whereas typerefs do.
+ */
+ abstract case class ThisType(tref: TypeRef) extends CachedProxyType with SingletonType {
+ def cls(implicit ctx: Context): ClassSymbol = tref.stableInRunSymbol.asClass
+ override def underlying(implicit ctx: Context): Type =
+ if (ctx.erasedTypes) tref else cls.classInfo.selfType
+ override def computeHash = doHash(tref)
+ }
+
+ final class CachedThisType(tref: TypeRef) extends ThisType(tref)
+
+ object ThisType {
+ /** Normally one should use ClassSymbol#thisType instead */
+ def raw(tref: TypeRef)(implicit ctx: Context) =
+ unique(new CachedThisType(tref))
+ }
+
+ /** The type of a super reference cls.super where
+ * `thistpe` is cls.this and `supertpe` is the type of the value referenced
+ * by `super`.
+ */
+ abstract case class SuperType(thistpe: Type, supertpe: Type) extends CachedProxyType with SingletonType {
+ override def underlying(implicit ctx: Context) = supertpe
+ def derivedSuperType(thistpe: Type, supertpe: Type)(implicit ctx: Context) =
+ if ((thistpe eq this.thistpe) && (supertpe eq this.supertpe)) this
+ else SuperType(thistpe, supertpe)
+ override def computeHash = doHash(thistpe, supertpe)
+ }
+
+ final class CachedSuperType(thistpe: Type, supertpe: Type) extends SuperType(thistpe, supertpe)
+
+ object SuperType {
+ def apply(thistpe: Type, supertpe: Type)(implicit ctx: Context): Type = {
+ assert(thistpe != NoPrefix)
+ unique(new CachedSuperType(thistpe, supertpe))
+ }
+ }
+
+ /** A constant type with single `value`. */
+ abstract case class ConstantType(value: Constant) extends CachedProxyType with SingletonType {
+ override def underlying(implicit ctx: Context) = value.tpe
+ override def computeHash = doHash(value)
+ }
+
+ final class CachedConstantType(value: Constant) extends ConstantType(value)
+
+ object ConstantType {
+ def apply(value: Constant)(implicit ctx: Context) = {
+ assertUnerased()
+ unique(new CachedConstantType(value))
+ }
+ }
+
+ case class LazyRef(refFn: () => Type) extends UncachedProxyType with ValueType {
+ private var myRef: Type = null
+ private var computed = false
+ def ref = {
+ if (computed) assert(myRef != null)
+ else {
+ computed = true
+ myRef = refFn()
+ }
+ myRef
+ }
+ def evaluating = computed && myRef == null
+ override def underlying(implicit ctx: Context) = ref
+ override def toString = s"LazyRef($ref)"
+ override def equals(other: Any) = other match {
+ case other: LazyRef => this.ref.equals(other.ref)
+ case _ => false
+ }
+ override def hashCode = ref.hashCode + 37
+ }
+
+ // --- Refined Type and RecType ------------------------------------------------
+
+ abstract class RefinedOrRecType extends CachedProxyType with ValueType {
+ def parent: Type
+ }
+
+ /** A refined type parent { refinement }
+ * @param refinedName The name of the refinement declaration
+ * @param infoFn: A function that produces the info of the refinement declaration,
+ * given the refined type itself.
+ */
+ abstract case class RefinedType(parent: Type, refinedName: Name, refinedInfo: Type) extends RefinedOrRecType {
+
+ override def underlying(implicit ctx: Context) = parent
+
+ private def badInst =
+ throw new AssertionError(s"bad instantiation: $this")
+
+ def checkInst(implicit ctx: Context): this.type = this // debug hook
+
+ def derivedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type)(implicit ctx: Context): Type =
+ if ((parent eq this.parent) && (refinedName eq this.refinedName) && (refinedInfo eq this.refinedInfo)) this
+ else RefinedType(parent, refinedName, refinedInfo)
+
+ /** Add this refinement to `parent`, provided If `refinedName` is a member of `parent`. */
+ def wrapIfMember(parent: Type)(implicit ctx: Context): Type =
+ if (parent.member(refinedName).exists) derivedRefinedType(parent, refinedName, refinedInfo)
+ else parent
+
+ override def equals(that: Any) = that match {
+ case that: RefinedType =>
+ this.parent == that.parent &&
+ this.refinedName == that.refinedName &&
+ this.refinedInfo == that.refinedInfo
+ case _ =>
+ false
+ }
+ override def computeHash = doHash(refinedName, refinedInfo, parent)
+ override def toString = s"RefinedType($parent, $refinedName, $refinedInfo)"
+ }
+
+ class CachedRefinedType(parent: Type, refinedName: Name, refinedInfo: Type, hc: Int)
+ extends RefinedType(parent, refinedName, refinedInfo) {
+ myHash = hc
+ override def computeHash = unsupported("computeHash")
+ }
+
+ object RefinedType {
+ def make(parent: Type, names: List[Name], infos: List[Type])(implicit ctx: Context): Type =
+ if (names.isEmpty) parent
+ else make(RefinedType(parent, names.head, infos.head), names.tail, infos.tail)
+
+ def apply(parent: Type, name: Name, info: Type)(implicit ctx: Context): RefinedType = {
+ assert(!ctx.erasedTypes)
+ ctx.base.uniqueRefinedTypes.enterIfNew(parent, name, info).checkInst
+ }
+ }
+
+ class RecType(parentExp: RecType => Type) extends RefinedOrRecType with BindingType {
+
+ // See discussion in findMember#goRec why these vars are needed
+ private[Types] var opened: Boolean = false
+ private[Types] var openedTwice: Boolean = false
+
+ val parent = parentExp(this)
+
+ override def underlying(implicit ctx: Context): Type = parent
+
+ def derivedRecType(parent: Type)(implicit ctx: Context): RecType =
+ if (parent eq this.parent) this
+ else RecType(rt => parent.substRecThis(this, RecThis(rt)))
+
+ def rebind(parent: Type)(implicit ctx: Context): Type =
+ if (parent eq this.parent) this
+ else RecType.closeOver(rt => parent.substRecThis(this, RecThis(rt)))
+
+ override def equals(other: Any) = other match {
+ case other: RecType => other.parent == this.parent
+ case _ => false
+ }
+
+ def isReferredToBy(tp: Type)(implicit ctx: Context): Boolean = {
+ val refacc = new TypeAccumulator[Boolean] {
+ override def apply(x: Boolean, tp: Type) = x || {
+ tp match {
+ case tp: TypeRef => apply(x, tp.prefix)
+ case tp: RecThis => RecType.this eq tp.binder
+ case tp: LazyRef => true // To be safe, assume a reference exists
+ case _ => foldOver(x, tp)
+ }
+ }
+ }
+ refacc.apply(false, tp)
+ }
+
+ override def computeHash = doHash(parent)
+ override def toString = s"RecType($parent | $hashCode)"
+
+ private def checkInst(implicit ctx: Context): this.type = this // debug hook
+ }
+
+ object RecType {
+
+ /** Create a RecType, normalizing its contents. This means:
+ *
+ * 1. Nested Rec types on the type's spine are merged with the outer one.
+ * 2. Any refinement of the form `type T = z.T` on the spine of the type
+ * where `z` refers to the created rec-type is replaced by
+ * `type T`. This avoids infinite recursons later when we
+ * try to follow these references.
+ * TODO: Figure out how to guarantee absence of cycles
+ * of length > 1
+ */
+ def apply(parentExp: RecType => Type)(implicit ctx: Context): RecType = {
+ val rt = new RecType(parentExp)
+ def normalize(tp: Type): Type = tp.stripTypeVar match {
+ case tp: RecType =>
+ normalize(tp.parent.substRecThis(tp, RecThis(rt)))
+ case tp @ RefinedType(parent, rname, rinfo) =>
+ val rinfo1 = rinfo match {
+ case TypeAlias(TypeRef(RecThis(`rt`), `rname`)) => TypeBounds.empty
+ case _ => rinfo
+ }
+ tp.derivedRefinedType(normalize(parent), rname, rinfo1)
+ case tp =>
+ tp
+ }
+ unique(rt.derivedRecType(normalize(rt.parent))).checkInst
+ }
+ def closeOver(parentExp: RecType => Type)(implicit ctx: Context) = {
+ val rt = this(parentExp)
+ if (rt.isReferredToBy(rt.parent)) rt else rt.parent
+ }
+ }
+
+ // --- AndType/OrType ---------------------------------------------------------------
+
+ trait AndOrType extends ValueType { // todo: check where we can simplify using AndOrType
+ def tp1: Type
+ def tp2: Type
+ def isAnd: Boolean
+ def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type // needed?
+ }
+
+ abstract case class AndType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType {
+
+ def isAnd = true
+
+ def derivedAndType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
+ if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this
+ else AndType.make(tp1, tp2)
+
+ def derived_& (tp1: Type, tp2: Type)(implicit ctx: Context): Type =
+ if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this
+ else tp1 & tp2
+
+ def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
+ derivedAndType(tp1, tp2)
+
+ override def computeHash = doHash(tp1, tp2)
+ }
+
+ final class CachedAndType(tp1: Type, tp2: Type) extends AndType(tp1, tp2)
+
+ object AndType {
+ def apply(tp1: Type, tp2: Type)(implicit ctx: Context) = {
+ assert(tp1.isValueType && tp2.isValueType, i"$tp1 & $tp2 / " + s"$tp1 & $tp2")
+ unchecked(tp1, tp2)
+ }
+ def unchecked(tp1: Type, tp2: Type)(implicit ctx: Context) = {
+ assertUnerased()
+ unique(new CachedAndType(tp1, tp2))
+ }
+ def make(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
+ if ((tp1 eq tp2) || (tp2 eq defn.AnyType))
+ tp1
+ else if (tp1 eq defn.AnyType)
+ tp2
+ else
+ apply(tp1, tp2)
+ }
+
+ abstract case class OrType(tp1: Type, tp2: Type) extends CachedGroundType with AndOrType {
+
+ assert(tp1.isInstanceOf[ValueType] && tp2.isInstanceOf[ValueType])
+ def isAnd = false
+
+ private[this] var myJoin: Type = _
+ private[this] var myJoinPeriod: Period = Nowhere
+
+ /** Replace or type by the closest non-or type above it */
+ def join(implicit ctx: Context): Type = {
+ if (myJoinPeriod != ctx.period) {
+ myJoin = ctx.orDominator(this)
+ core.println(i"join of $this == $myJoin")
+ assert(myJoin != this)
+ myJoinPeriod = ctx.period
+ }
+ myJoin
+ }
+
+ def derivedOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
+ if ((tp1 eq this.tp1) && (tp2 eq this.tp2)) this
+ else OrType.make(tp1, tp2)
+
+ def derivedAndOrType(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
+ derivedOrType(tp1, tp2)
+
+ override def computeHash = doHash(tp1, tp2)
+ }
+
+ final class CachedOrType(tp1: Type, tp2: Type) extends OrType(tp1, tp2)
+
+ object OrType {
+ def apply(tp1: Type, tp2: Type)(implicit ctx: Context) = {
+ assertUnerased()
+ unique(new CachedOrType(tp1, tp2))
+ }
+ def make(tp1: Type, tp2: Type)(implicit ctx: Context): Type =
+ if (tp1 eq tp2) tp1 else apply(tp1, tp2)
+ }
+
+ // ----- Method types: MethodType/ExprType/PolyType -------------------------------
+
+ // Note: method types are cached whereas poly types are not. The reason
+ // is that most poly types are cyclic via poly params,
+ // and therefore two different poly types would never be equal.
+
+ /** A trait that mixes in functionality for signature caching */
+ trait MethodicType extends TermType {
+
+ private[this] var mySignature: Signature = _
+ private[this] var mySignatureRunId: Int = NoRunId
+
+ protected def computeSignature(implicit ctx: Context): Signature
+
+ protected def resultSignature(implicit ctx: Context) = try resultType match {
+ case rtp: MethodicType => rtp.signature
+ case tp => Signature(tp, isJava = false)
+ }
+ catch {
+ case ex: AssertionError =>
+ println(i"failure while taking result signture of $this: $resultType")
+ throw ex
+ }
+
+ final override def signature(implicit ctx: Context): Signature = {
+ if (ctx.runId != mySignatureRunId) {
+ mySignature = computeSignature
+ if (!mySignature.isUnderDefined) mySignatureRunId = ctx.runId
+ }
+ mySignature
+ }
+ }
+
+ trait MethodOrPoly extends MethodicType
+
+ abstract case class MethodType(paramNames: List[TermName], paramTypes: List[Type])
+ (resultTypeExp: MethodType => Type)
+ extends CachedGroundType with BindingType with TermType with MethodOrPoly with NarrowCached { thisMethodType =>
+ import MethodType._
+
+ def isJava = false
+ def isImplicit = false
+
+ private[core] val resType = resultTypeExp(this)
+ assert(resType.exists)
+
+ override def resultType(implicit ctx: Context): Type =
+ if (dependencyStatus == FalseDeps) { // dealias all false dependencies
+ val dealiasMap = new TypeMap {
+ def apply(tp: Type) = tp match {
+ case tp @ TypeRef(pre, name) =>
+ tp.info match {
+ case TypeAlias(alias) if depStatus(pre) == TrueDeps => apply(alias)
+ case _ => mapOver(tp)
+ }
+ case _ =>
+ mapOver(tp)
+ }
+ }
+ dealiasMap(resType)
+ }
+ else resType
+
+ var myDependencyStatus: DependencyStatus = Unknown
+
+ private def depStatus(tp: Type)(implicit ctx: Context): DependencyStatus = {
+ def combine(x: DependencyStatus, y: DependencyStatus) = {
+ val status = (x & StatusMask) max (y & StatusMask)
+ val provisional = (x | y) & Provisional
+ (if (status == TrueDeps) status else status | provisional).toByte
+ }
+ val depStatusAcc = new TypeAccumulator[DependencyStatus] {
+ def apply(status: DependencyStatus, tp: Type) =
+ if (status == TrueDeps) status
+ else
+ tp match {
+ case MethodParam(`thisMethodType`, _) => TrueDeps
+ case tp: TypeRef =>
+ val status1 = foldOver(status, tp)
+ tp.info match { // follow type alias to avoid dependency
+ case TypeAlias(alias) if status1 == TrueDeps && status != TrueDeps =>
+ combine(apply(status, alias), FalseDeps)
+ case _ =>
+ status1
+ }
+ case tp: TypeVar if !tp.isInstantiated => combine(status, Provisional)
+ case _ => foldOver(status, tp)
+ }
+ }
+ depStatusAcc(NoDeps, tp)
+ }
+
+ /** The dependency status of this method. Some examples:
+ *
+ * class C extends { type S; type T = String }
+ * def f(x: C)(y: Boolean) // dependencyStatus = NoDeps
+ * def f(x: C)(y: x.S) // dependencyStatus = TrueDeps
+ * def f(x: C)(y: x.T) // dependencyStatus = FalseDeps, i.e.
+ * // dependency can be eliminated by dealiasing.
+ */
+ private def dependencyStatus(implicit ctx: Context): DependencyStatus = {
+ if (myDependencyStatus != Unknown) myDependencyStatus
+ else {
+ val result = depStatus(resType)
+ if ((result & Provisional) == 0) myDependencyStatus = result
+ (result & StatusMask).toByte
+ }
+ }
+
+ /** Does result type contain references to parameters of this method type,
+ * which cannot be eliminated by de-aliasing?
+ */
+ def isDependent(implicit ctx: Context): Boolean = dependencyStatus == TrueDeps
+
+ protected def computeSignature(implicit ctx: Context): Signature =
+ resultSignature.prepend(paramTypes, isJava)
+
+ def derivedMethodType(paramNames: List[TermName], paramTypes: List[Type], resType: Type)(implicit ctx: Context) =
+ if ((paramNames eq this.paramNames) && (paramTypes eq this.paramTypes) && (resType eq this.resType)) this
+ else {
+ val resTypeFn = (x: MethodType) => resType.subst(this, x)
+ if (isJava) JavaMethodType(paramNames, paramTypes)(resTypeFn)
+ else if (isImplicit) ImplicitMethodType(paramNames, paramTypes)(resTypeFn)
+ else MethodType(paramNames, paramTypes)(resTypeFn)
+ }
+
+ def instantiate(argTypes: => List[Type])(implicit ctx: Context): Type =
+ if (isDependent) resultType.substParams(this, argTypes)
+ else resultType
+
+ override def equals(that: Any) = that match {
+ case that: MethodType =>
+ this.paramNames == that.paramNames &&
+ this.paramTypes == that.paramTypes &&
+ this.resType == that.resType
+ case _ =>
+ false
+ }
+
+ override def computeHash = doHash(paramNames, resType, paramTypes)
+
+ protected def prefixString = "MethodType"
+ override def toString = s"$prefixString($paramNames, $paramTypes, $resType)"
+ }
+
+ final class CachedMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)
+ extends MethodType(paramNames, paramTypes)(resultTypeExp) {
+ override def equals(that: Any) = super.equals(that) && that.isInstanceOf[CachedMethodType]
+ }
+
+ final class JavaMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)
+ extends MethodType(paramNames, paramTypes)(resultTypeExp) {
+ override def isJava = true
+ override def equals(that: Any) = super.equals(that) && that.isInstanceOf[JavaMethodType]
+ override def computeHash = addDelta(super.computeHash, 1)
+ override protected def prefixString = "JavaMethodType"
+ }
+
+ final class ImplicitMethodType(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)
+ extends MethodType(paramNames, paramTypes)(resultTypeExp) {
+ override def isImplicit = true
+ override def equals(that: Any) = super.equals(that) && that.isInstanceOf[ImplicitMethodType]
+ override def computeHash = addDelta(super.computeHash, 2)
+ override protected def prefixString = "ImplicitMethodType"
+ }
+
+ abstract class MethodTypeCompanion {
+ def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType
+ def apply(paramNames: List[TermName], paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType =
+ apply(paramNames, paramTypes)(_ => resultType)
+ def apply(paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context): MethodType =
+ apply(nme.syntheticParamNames(paramTypes.length), paramTypes)(resultTypeExp)
+ def apply(paramTypes: List[Type], resultType: Type)(implicit ctx: Context): MethodType =
+ apply(nme.syntheticParamNames(paramTypes.length), paramTypes, resultType)
+
+ /** Produce method type from parameter symbols, with special mappings for repeated
+ * and inline parameters.
+ */
+ def fromSymbols(params: List[Symbol], resultType: Type)(implicit ctx: Context) = {
+ /** Replace @repeated annotations on Seq or Array types by <repeated> types */
+ def translateRepeated(tp: Type): Type = tp match {
+ case tp @ ExprType(tp1) => tp.derivedExprType(translateRepeated(tp1))
+ case AnnotatedType(tp, annot) if annot matches defn.RepeatedAnnot =>
+ val typeSym = tp.typeSymbol.asClass
+ assert(typeSym == defn.SeqClass || typeSym == defn.ArrayClass)
+ tp.translateParameterized(typeSym, defn.RepeatedParamClass)
+ case tp =>
+ tp
+ }
+ /** Add @inlineParam to inline call-by-value parameters */
+ def translateInline(tp: Type): Type = tp match {
+ case _: ExprType => tp
+ case _ => AnnotatedType(tp, Annotation(defn.InlineParamAnnot))
+ }
+ def paramInfo(param: Symbol): Type = {
+ val paramType = translateRepeated(param.info)
+ if (param.is(Inline)) translateInline(paramType) else paramType
+ }
+ def transformResult(mt: MethodType) =
+ resultType.subst(params, (0 until params.length).toList map (MethodParam(mt, _)))
+ apply(params map (_.name.asTermName), params map paramInfo)(transformResult _)
+ }
+ }
+
+ object MethodType extends MethodTypeCompanion {
+ def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) =
+ unique(new CachedMethodType(paramNames, paramTypes)(resultTypeExp))
+
+ private type DependencyStatus = Byte
+ private final val Unknown: DependencyStatus = 0 // not yet computed
+ private final val NoDeps: DependencyStatus = 1 // no dependent parameters found
+ private final val FalseDeps: DependencyStatus = 2 // all dependent parameters are prefixes of non-depended alias types
+ private final val TrueDeps: DependencyStatus = 3 // some truly dependent parameters exist
+ private final val StatusMask: DependencyStatus = 3 // the bits indicating actual dependency status
+ private final val Provisional: DependencyStatus = 4 // set if dependency status can still change due to type variable instantiations
+ }
+
+ object JavaMethodType extends MethodTypeCompanion {
+ def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) =
+ unique(new JavaMethodType(paramNames, paramTypes)(resultTypeExp))
+ }
+
+ object ImplicitMethodType extends MethodTypeCompanion {
+ def apply(paramNames: List[TermName], paramTypes: List[Type])(resultTypeExp: MethodType => Type)(implicit ctx: Context) =
+ unique(new ImplicitMethodType(paramNames, paramTypes)(resultTypeExp))
+ }
+
+ /** A by-name parameter type of the form `=> T`, or the type of a method with no parameter list. */
+ abstract case class ExprType(resType: Type)
+ extends CachedProxyType with TermType with MethodicType {
+ override def resultType(implicit ctx: Context): Type = resType
+ override def underlying(implicit ctx: Context): Type = resType
+ protected def computeSignature(implicit ctx: Context): Signature = resultSignature
+ def derivedExprType(resType: Type)(implicit ctx: Context) =
+ if (resType eq this.resType) this else ExprType(resType)
+ override def computeHash = doHash(resType)
+ }
+
+ final class CachedExprType(resultType: Type) extends ExprType(resultType)
+
+ object ExprType {
+ def apply(resultType: Type)(implicit ctx: Context) = {
+ assertUnerased()
+ unique(new CachedExprType(resultType))
+ }
+ }
+
+ /** A type lambda of the form `[v_0 X_0, ..., v_n X_n] => T` */
+ class PolyType(val paramNames: List[TypeName], val variances: List[Int])(
+ paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type)
+ extends CachedProxyType with BindingType with MethodOrPoly {
+
+ /** The bounds of the type parameters */
+ val paramBounds: List[TypeBounds] = paramBoundsExp(this)
+
+ /** The result type of a PolyType / body of a type lambda */
+ val resType: Type = resultTypeExp(this)
+
+ assert(resType.isInstanceOf[TermType], this)
+ assert(paramNames.nonEmpty)
+
+ protected def computeSignature(implicit ctx: Context) = resultSignature
+
+ def isPolymorphicMethodType: Boolean = resType match {
+ case _: MethodType => true
+ case _ => false
+ }
+
+ /** Is this polytype a higher-kinded type lambda as opposed to a polymorphic?
+ * method type? Only type lambdas get created with variances, that's how we can tell.
+ */
+ def isTypeLambda: Boolean = variances.nonEmpty
+
+ /** PolyParam references to all type parameters of this type */
+ lazy val paramRefs: List[PolyParam] = paramNames.indices.toList.map(PolyParam(this, _))
+
+ lazy val typeParams: List[LambdaParam] =
+ paramNames.indices.toList.map(new LambdaParam(this, _))
+
+ override def resultType(implicit ctx: Context) = resType
+ override def underlying(implicit ctx: Context) = resType
+
+ /** Instantiate result type by substituting parameters with given arguments */
+ final def instantiate(argTypes: List[Type])(implicit ctx: Context): Type =
+ resultType.substParams(this, argTypes)
+
+ /** Instantiate parameter bounds by substituting parameters with given arguments */
+ final def instantiateBounds(argTypes: List[Type])(implicit ctx: Context): List[TypeBounds] =
+ paramBounds.mapConserve(_.substParams(this, argTypes).bounds)
+
+ def newLikeThis(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): PolyType =
+ PolyType.apply(paramNames, variances)(
+ x => paramBounds mapConserve (_.subst(this, x).bounds),
+ x => resType.subst(this, x))
+
+ def derivedPolyType(paramNames: List[TypeName] = this.paramNames,
+ paramBounds: List[TypeBounds] = this.paramBounds,
+ resType: Type = this.resType)(implicit ctx: Context) =
+ if ((paramNames eq this.paramNames) && (paramBounds eq this.paramBounds) && (resType eq this.resType)) this
+ else newLikeThis(paramNames, paramBounds, resType)
+
+ def derivedLambdaAbstraction(paramNames: List[TypeName], paramBounds: List[TypeBounds], resType: Type)(implicit ctx: Context): Type =
+ resType match {
+ case resType @ TypeAlias(alias) =>
+ resType.derivedTypeAlias(newLikeThis(paramNames, paramBounds, alias))
+ case resType @ TypeBounds(lo, hi) =>
+ resType.derivedTypeBounds(
+ if (lo.isRef(defn.NothingClass)) lo else newLikeThis(paramNames, paramBounds, lo),
+ newLikeThis(paramNames, paramBounds, hi))
+ case _ =>
+ derivedPolyType(paramNames, paramBounds, resType)
+ }
+
+ /** Merge nested polytypes into one polytype. nested polytypes are normally not supported
+ * but can arise as temporary data structures.
+ */
+ def flatten(implicit ctx: Context): PolyType = resType match {
+ case that: PolyType =>
+ val shift = new TypeMap {
+ def apply(t: Type) = t match {
+ case PolyParam(`that`, n) => PolyParam(that, n + paramNames.length)
+ case t => mapOver(t)
+ }
+ }
+ PolyType(paramNames ++ that.paramNames)(
+ x => this.paramBounds.mapConserve(_.subst(this, x).bounds) ++
+ that.paramBounds.mapConserve(shift(_).subst(that, x).bounds),
+ x => shift(that.resultType).subst(that, x).subst(this, x))
+ case _ => this
+ }
+
+ /** The type `[tparams := paramRefs] tp`, where `tparams` can be
+ * either a list of type parameter symbols or a list of lambda parameters
+ */
+ def lifted(tparams: List[TypeParamInfo], tp: Type)(implicit ctx: Context): Type =
+ tparams match {
+ case LambdaParam(poly, _) :: _ => tp.subst(poly, this)
+ case tparams: List[Symbol @unchecked] => tp.subst(tparams, paramRefs)
+ }
+
+ override def equals(other: Any) = other match {
+ case other: PolyType =>
+ other.paramNames == this.paramNames &&
+ other.paramBounds == this.paramBounds &&
+ other.resType == this.resType &&
+ other.variances == this.variances
+ case _ => false
+ }
+
+ override def toString = s"PolyType($variances, $paramNames, $paramBounds, $resType)"
+
+ override def computeHash = doHash(variances ::: paramNames, resType, paramBounds)
+ }
+
+ object PolyType {
+ def apply(paramNames: List[TypeName], variances: List[Int] = Nil)(
+ paramBoundsExp: PolyType => List[TypeBounds],
+ resultTypeExp: PolyType => Type)(implicit ctx: Context): PolyType = {
+ val vs = if (variances.isEmpty) paramNames.map(alwaysZero) else variances
+ unique(new PolyType(paramNames, vs)(paramBoundsExp, resultTypeExp))
+ }
+
+ def unapply(tl: PolyType): Some[(List[LambdaParam], Type)] =
+ Some((tl.typeParams, tl.resType))
+
+ def any(n: Int)(implicit ctx: Context) =
+ apply(tpnme.syntheticTypeParamNames(n), List.fill(n)(0))(
+ pt => List.fill(n)(TypeBounds.empty), pt => defn.AnyType)
+ }
+
+ // ----- HK types: LambdaParam, HKApply ---------------------
+
+ /** The parameter of a type lambda */
+ case class LambdaParam(tl: PolyType, n: Int) extends TypeParamInfo {
+ def isTypeParam(implicit ctx: Context) = true
+ def paramName(implicit ctx: Context): TypeName = tl.paramNames(n)
+ def paramBounds(implicit ctx: Context): TypeBounds = tl.paramBounds(n)
+ def paramBoundsAsSeenFrom(pre: Type)(implicit ctx: Context): TypeBounds = paramBounds
+ def paramBoundsOrCompleter(implicit ctx: Context): Type = paramBounds
+ def paramVariance(implicit ctx: Context): Int = tl.variances(n)
+ def toArg: Type = PolyParam(tl, n)
+ def paramRef(implicit ctx: Context): Type = PolyParam(tl, n)
+ }
+
+ /** A higher kinded type application `C[T_1, ..., T_n]` */
+ abstract case class HKApply(tycon: Type, args: List[Type])
+ extends CachedProxyType with ValueType {
+
+ private var validSuper: Period = Nowhere
+ private var cachedSuper: Type = _
+
+ override def underlying(implicit ctx: Context): Type = tycon
+
+ override def superType(implicit ctx: Context): Type = {
+ if (ctx.period != validSuper) {
+ cachedSuper = tycon match {
+ case tp: PolyType => defn.AnyType
+ case tp: TypeVar if !tp.inst.exists =>
+ // supertype not stable, since underlying might change
+ return tp.underlying.applyIfParameterized(args)
+ case tp: TypeProxy => tp.superType.applyIfParameterized(args)
+ case _ => defn.AnyType
+ }
+ validSuper = ctx.period
+ }
+ cachedSuper
+ }
+
+ def lowerBound(implicit ctx: Context) = tycon.stripTypeVar match {
+ case tycon: TypeRef =>
+ tycon.info match {
+ case TypeBounds(lo, hi) =>
+ if (lo eq hi) superType // optimization, can profit from caching in this case
+ else lo.applyIfParameterized(args)
+ case _ => NoType
+ }
+ case _ =>
+ NoType
+ }
+
+ def typeParams(implicit ctx: Context): List[TypeParamInfo] = {
+ val tparams = tycon.typeParams
+ if (tparams.isEmpty) PolyType.any(args.length).typeParams else tparams
+ }
+
+ def derivedAppliedType(tycon: Type, args: List[Type])(implicit ctx: Context): Type =
+ if ((tycon eq this.tycon) && (args eq this.args)) this
+ else tycon.appliedTo(args)
+
+ override def computeHash = doHash(tycon, args)
+
+ protected def checkInst(implicit ctx: Context): this.type = {
+ def check(tycon: Type): Unit = tycon.stripTypeVar match {
+ case tycon: TypeRef if !tycon.symbol.isClass =>
+ case _: PolyParam | ErrorType | _: WildcardType =>
+ case _: PolyType =>
+ assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this")
+ case tycon: AnnotatedType =>
+ check(tycon.underlying)
+ case _ =>
+ assert(false, s"illegal type constructor in $this")
+ }
+ if (Config.checkHKApplications) check(tycon)
+ this
+ }
+ }
+
+ final class CachedHKApply(tycon: Type, args: List[Type]) extends HKApply(tycon, args)
+
+ object HKApply {
+ def apply(tycon: Type, args: List[Type])(implicit ctx: Context) =
+ unique(new CachedHKApply(tycon, args)).checkInst
+ }
+
+ // ----- Bound types: MethodParam, PolyParam --------------------------
+
+ abstract class BoundType extends CachedProxyType with ValueType {
+ type BT <: Type
+ def binder: BT
+ // Dotty deviation: copyBoundType was copy, but
+ // dotty generates copy methods always automatically, and therefore
+ // does not accept same-named method definitions in subclasses.
+ // Scala2x, on the other hand, requires them (not sure why!)
+ def copyBoundType(bt: BT): Type
+ }
+
+ abstract class ParamType extends BoundType {
+ def paramNum: Int
+ def paramName: Name
+ }
+
+ abstract case class MethodParam(binder: MethodType, paramNum: Int) extends ParamType with SingletonType {
+ type BT = MethodType
+
+ def paramName = binder.paramNames(paramNum)
+
+ override def underlying(implicit ctx: Context): Type = binder.paramTypes(paramNum)
+ def copyBoundType(bt: BT) = new MethodParamImpl(bt, paramNum)
+
+ // need to customize hashCode and equals to prevent infinite recursion for dep meth types.
+ override def computeHash = addDelta(binder.identityHash, paramNum)
+ override def equals(that: Any) = that match {
+ case that: MethodParam =>
+ (this.binder eq that.binder) && this.paramNum == that.paramNum
+ case _ =>
+ false
+ }
+
+ override def toString = s"MethodParam($paramName)"
+ }
+
+ class MethodParamImpl(binder: MethodType, paramNum: Int) extends MethodParam(binder, paramNum)
+
+ object MethodParam {
+ def apply(binder: MethodType, paramNum: Int)(implicit ctx: Context): MethodParam = {
+ assertUnerased()
+ new MethodParamImpl(binder, paramNum)
+ }
+ }
+
+ /** TODO Some docs would be nice here! */
+ case class PolyParam(binder: PolyType, paramNum: Int) extends ParamType {
+ type BT = PolyType
+ def copyBoundType(bt: BT) = PolyParam(bt, paramNum)
+
+ /** Looking only at the structure of `bound`, is one of the following true?
+ * - fromBelow and param <:< bound
+ * - !fromBelow and param >:> bound
+ */
+ def occursIn(bound: Type, fromBelow: Boolean)(implicit ctx: Context): Boolean = bound.stripTypeVar match {
+ case bound: PolyParam => bound == this
+ case bound: AndOrType =>
+ def occ1 = occursIn(bound.tp1, fromBelow)
+ def occ2 = occursIn(bound.tp2, fromBelow)
+ if (fromBelow == bound.isAnd) occ1 && occ2 else occ1 || occ2
+ case _ => false
+ }
+
+ def paramName = binder.paramNames(paramNum)
+
+ override def underlying(implicit ctx: Context): Type = {
+ val bounds = binder.paramBounds
+ if (bounds == null) NoType // this can happen if the referenced generic type is not initialized yet
+ else bounds(paramNum)
+ }
+ // no customized hashCode/equals needed because cycle is broken in PolyType
+ override def toString =
+ try s"PolyParam($paramName)"
+ catch {
+ case ex: IndexOutOfBoundsException => s"PolyParam(<bad index: $paramNum>)"
+ }
+
+ override def computeHash = doHash(paramNum, binder.identityHash)
+
+ override def equals(that: Any) = that match {
+ case that: PolyParam =>
+ (this.binder eq that.binder) && this.paramNum == that.paramNum
+ case _ =>
+ false
+ }
+ }
+
+ /** a self-reference to an enclosing recursive type. */
+ case class RecThis(binder: RecType) extends BoundType with SingletonType {
+ type BT = RecType
+ override def underlying(implicit ctx: Context) = binder
+ def copyBoundType(bt: BT) = RecThis(bt)
+
+ // need to customize hashCode and equals to prevent infinite recursion
+ // between RecTypes and RecRefs.
+ override def computeHash = addDelta(binder.identityHash, 41)
+ override def equals(that: Any) = that match {
+ case that: RecThis => this.binder eq that.binder
+ case _ => false
+ }
+ override def toString =
+ try s"RecThis(${binder.hashCode})"
+ catch {
+ case ex: NullPointerException => s"RecThis(<under construction>)"
+ }
+ }
+
+ // ----- Skolem types -----------------------------------------------
+
+ /** A skolem type reference with underlying type `binder`. */
+ abstract case class SkolemType(info: Type) extends UncachedProxyType with ValueType with SingletonType {
+ override def underlying(implicit ctx: Context) = info
+ def derivedSkolemType(info: Type)(implicit ctx: Context) =
+ if (info eq this.info) this else SkolemType(info)
+ override def hashCode: Int = identityHash
+ override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
+
+ private var myRepr: String = null
+ def repr(implicit ctx: Context) = {
+ if (myRepr == null) myRepr = ctx.freshName("?")
+ myRepr
+ }
+
+ override def toString = s"Skolem($hashCode)"
+ }
+
+ final class CachedSkolemType(info: Type) extends SkolemType(info)
+
+ object SkolemType {
+ def apply(info: Type)(implicit ctx: Context) =
+ unique(new CachedSkolemType(info))
+ }
+
+ // ------------ Type variables ----------------------------------------
+
+ /** In a TypeApply tree, a TypeVar is created for each argument type to be inferred.
+ * Every type variable is referred to by exactly one inferred type parameter of some
+ * TypeApply tree.
+ *
+ * A type variable is essentially a switch that models some part of a substitution.
+ * It is first linked to `origin`, a poly param that's in the current constraint set.
+ * It can then be (once) instantiated to some other type. The instantiation is
+ * recorded in the type variable itself, or else, if the current type state
+ * is different from the variable's creation state (meaning unrolls are possible)
+ * in the current typer state.
+ *
+ * @param origin The parameter that's tracked by the type variable.
+ * @param creatorState The typer state in which the variable was created.
+ * @param owningTree The function part of the TypeApply tree tree that introduces
+ * the type variable.
+ * @paran owner The current owner if the context where the variable was created.
+ *
+ * `owningTree` and `owner` are used to determine whether a type-variable can be instantiated
+ * at some given point. See `Inferencing#interpolateUndetVars`.
+ */
+ final class TypeVar(val origin: PolyParam, creatorState: TyperState, val owningTree: untpd.Tree, val owner: Symbol) extends CachedProxyType with ValueType {
+
+ /** The permanent instance type of the variable, or NoType is none is given yet */
+ private[core] var inst: Type = NoType
+
+ /** The state owning the variable. This is at first `creatorState`, but it can
+ * be changed to an enclosing state on a commit.
+ */
+ private[core] var owningState = creatorState
+
+ /** The instance type of this variable, or NoType if the variable is currently
+ * uninstantiated
+ */
+ def instanceOpt(implicit ctx: Context): Type =
+ if (inst.exists) inst else {
+ ctx.typerState.ephemeral = true
+ ctx.typerState.instType(this)
+ }
+
+ /** Is the variable already instantiated? */
+ def isInstantiated(implicit ctx: Context) = instanceOpt.exists
+
+ /** Instantiate variable with given type */
+ private def instantiateWith(tp: Type)(implicit ctx: Context): Type = {
+ assert(tp ne this, s"self instantiation of ${tp.show}, constraint = ${ctx.typerState.constraint.show}")
+ typr.println(s"instantiating ${this.show} with ${tp.show}")
+ assert(ctx.typerState.constraint contains this) // !!! DEBUG
+ if ((ctx.typerState eq owningState) && !ctx.typeComparer.subtypeCheckInProgress)
+ inst = tp
+ ctx.typerState.constraint = ctx.typerState.constraint.replace(origin, tp)
+ tp
+ }
+
+ /** Instantiate variable from the constraints over its `origin`.
+ * If `fromBelow` is true, the variable is instantiated to the lub
+ * of its lower bounds in the current constraint; otherwise it is
+ * instantiated to the glb of its upper bounds. However, a lower bound
+ * instantiation can be a singleton type only if the upper bound
+ * is also a singleton type.
+ */
+ def instantiate(fromBelow: Boolean)(implicit ctx: Context): Type = {
+ val inst = ctx.typeComparer.instanceType(origin, fromBelow)
+ if (ctx.typerState.isGlobalCommittable)
+ inst match {
+ case inst: PolyParam =>
+ assert(inst.binder.isTypeLambda, i"bad inst $this := $inst, constr = ${ctx.typerState.constraint}")
+ // If this fails, you might want to turn on Config.debugCheckConstraintsClosed
+ // to help find the root of the problem.
+ // Note: Parameters of type lambdas are excluded from the assertion because
+ // they might arise from ill-kinded code. See #1652
+ case _ =>
+ }
+ instantiateWith(inst)
+ }
+
+ /** Unwrap to instance (if instantiated) or origin (if not), until result
+ * is no longer a TypeVar
+ */
+ override def stripTypeVar(implicit ctx: Context): Type = {
+ val inst = instanceOpt
+ if (inst.exists) inst.stripTypeVar else origin
+ }
+
+ /** If the variable is instantiated, its instance, otherwise its origin */
+ override def underlying(implicit ctx: Context): Type = {
+ val inst = instanceOpt
+ if (inst.exists) inst
+ else {
+ ctx.typerState.ephemeral = true
+ origin
+ }
+ }
+
+ override def computeHash: Int = identityHash
+ override def equals(that: Any) = this eq that.asInstanceOf[AnyRef]
+
+ override def toString = {
+ def instStr = if (inst.exists) s" -> $inst" else ""
+ s"TypeVar($origin$instStr)"
+ }
+ }
+
+ // ------ ClassInfo, Type Bounds ------------------------------------------------------------
+
+ /** Roughly: the info of a class during a period.
+ * @param prefix The prefix on which parents, decls, and selfType need to be rebased.
+ * @param cls The class symbol.
+ * @param classParents The parent types of this class.
+ * These are all normalized to be TypeRefs by moving any refinements
+ * to be member definitions of the class itself.
+ * @param decls The symbols defined directly in this class.
+ * @param selfInfo The type of `this` in this class, if explicitly given,
+ * NoType otherwise. If class is compiled from source, can also
+ * be a reference to the self symbol containing the type.
+ */
+ abstract case class ClassInfo(
+ prefix: Type,
+ cls: ClassSymbol,
+ classParents: List[TypeRef],
+ decls: Scope,
+ selfInfo: DotClass /* should be: Type | Symbol */) extends CachedGroundType with TypeType {
+
+ /** The self type of a class is the conjunction of
+ * - the explicit self type if given (or the info of a given self symbol), and
+ * - the fully applied reference to the class itself.
+ */
+ def selfType(implicit ctx: Context): Type = {
+ if (selfTypeCache == null)
+ selfTypeCache = {
+ def fullRef = fullyAppliedRef
+ val given = givenSelfType
+ val raw =
+ if (!given.exists) fullRef
+ else if (cls is Module) given
+ else if (ctx.erasedTypes) fullRef
+ else AndType(given, fullRef)
+ raw//.asSeenFrom(prefix, cls.owner)
+ }
+ selfTypeCache
+ }
+
+ /** The explicitly given self type (self types of modules are assumed to be
+ * explcitly given here).
+ */
+ override def givenSelfType(implicit ctx: Context): Type = selfInfo match {
+ case tp: Type => tp
+ case self: Symbol => self.info
+ }
+
+ private var selfTypeCache: Type = null
+
+ private def fullyAppliedRef(base: Type, tparams: List[TypeSymbol])(implicit ctx: Context): Type = tparams match {
+ case tparam :: tparams1 =>
+ fullyAppliedRef(
+ RefinedType(base, tparam.name, TypeRef(cls.thisType, tparam).toBounds(tparam)),
+ tparams1)
+ case nil =>
+ base
+ }
+
+ /** The class type with all type parameters */
+ def fullyAppliedRef(implicit ctx: Context): Type = fullyAppliedRef(cls.typeRef, cls.typeParams)
+
+ private var typeRefCache: TypeRef = null
+
+ def typeRef(implicit ctx: Context): TypeRef = {
+ def clsDenot = if (prefix eq cls.owner.thisType) cls.denot else cls.denot.copySymDenotation(info = this)
+ if (typeRefCache == null)
+ typeRefCache =
+ if ((cls is PackageClass) || cls.owner.isTerm) symbolicTypeRef
+ else TypeRef(prefix, cls.name, clsDenot)
+ typeRefCache
+ }
+
+ def symbolicTypeRef(implicit ctx: Context): TypeRef = TypeRef(prefix, cls)
+
+ // cached because baseType needs parents
+ private var parentsCache: List[TypeRef] = null
+
+ /** The parent type refs as seen from the given prefix */
+ override def parents(implicit ctx: Context): List[TypeRef] = {
+ if (parentsCache == null)
+ parentsCache = cls.classParents.mapConserve(_.asSeenFrom(prefix, cls.owner).asInstanceOf[TypeRef])
+ parentsCache
+ }
+
+ /** The parent types with all type arguments */
+ override def parentsWithArgs(implicit ctx: Context): List[Type] =
+ parents mapConserve { pref =>
+ ((pref: Type) /: pref.classSymbol.typeParams) { (parent, tparam) =>
+ val targSym = decls.lookup(tparam.name)
+ if (targSym.exists) RefinedType(parent, targSym.name, targSym.info)
+ else parent
+ }
+ }
+
+ def derivedClassInfo(prefix: Type)(implicit ctx: Context) =
+ if (prefix eq this.prefix) this
+ else ClassInfo(prefix, cls, classParents, decls, selfInfo)
+
+ def derivedClassInfo(prefix: Type = this.prefix, classParents: List[TypeRef] = classParents, decls: Scope = this.decls, selfInfo: DotClass = this.selfInfo)(implicit ctx: Context) =
+ if ((prefix eq this.prefix) && (classParents eq this.classParents) && (decls eq this.decls) && (selfInfo eq this.selfInfo)) this
+ else ClassInfo(prefix, cls, classParents, decls, selfInfo)
+
+ override def computeHash = doHash(cls, prefix)
+
+ override def toString = s"ClassInfo($prefix, $cls)"
+ }
+
+ class CachedClassInfo(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass)
+ extends ClassInfo(prefix, cls, classParents, decls, selfInfo)
+
+ /** A class for temporary class infos where `parents` are not yet known. */
+ final class TempClassInfo(prefix: Type, cls: ClassSymbol, decls: Scope, selfInfo: DotClass)
+ extends CachedClassInfo(prefix, cls, Nil, decls, selfInfo) {
+
+ /** A list of actions that were because they rely on the class info of `cls` to
+ * be no longer temporary. These actions will be performed once `cls` gets a real
+ * ClassInfo.
+ */
+ private var suspensions: List[() => Unit] = Nil
+
+ def addSuspension(suspension: () => Unit): Unit = suspensions ::= suspension
+
+ /** Install classinfo with known parents in `denot` and resume all suspensions */
+ def finalize(denot: SymDenotation, parents: List[TypeRef])(implicit ctx: Context) = {
+ denot.info = derivedClassInfo(classParents = parents)
+ suspensions.foreach(_())
+ }
+ }
+
+ object ClassInfo {
+ def apply(prefix: Type, cls: ClassSymbol, classParents: List[TypeRef], decls: Scope, selfInfo: DotClass = NoType)(implicit ctx: Context) =
+ unique(new CachedClassInfo(prefix, cls, classParents, decls, selfInfo))
+ }
+
+ /** Type bounds >: lo <: hi */
+ abstract case class TypeBounds(lo: Type, hi: Type) extends CachedProxyType with TypeType {
+
+ assert(lo.isInstanceOf[TermType])
+ assert(hi.isInstanceOf[TermType])
+
+ def variance: Int = 0
+
+ override def underlying(implicit ctx: Context): Type = hi
+
+ /** The non-alias type bounds type with given bounds */
+ def derivedTypeBounds(lo: Type, hi: Type)(implicit ctx: Context) =
+ if ((lo eq this.lo) && (hi eq this.hi) && (variance == 0)) this
+ else TypeBounds(lo, hi)
+
+ /** If this is an alias, a derived alias with the new variance,
+ * Otherwise the type itself.
+ */
+ def withVariance(variance: Int)(implicit ctx: Context) = this match {
+ case tp: TypeAlias => tp.derivedTypeAlias(tp.alias, variance)
+ case _ => this
+ }
+
+ def contains(tp: Type)(implicit ctx: Context): Boolean = tp match {
+ case tp: TypeBounds => lo <:< tp.lo && tp.hi <:< hi
+ case tp: ClassInfo =>
+ // Note: Taking a normal typeRef does not work here. A normal ref might contain
+ // also other information about the named type (e.g. bounds).
+ contains(tp.symbolicTypeRef)
+ case _ => lo <:< tp && tp <:< hi
+ }
+
+ def & (that: TypeBounds)(implicit ctx: Context): TypeBounds =
+ if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) that
+ else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) this
+ else TypeBounds(this.lo | that.lo, this.hi & that.hi)
+
+ def | (that: TypeBounds)(implicit ctx: Context): TypeBounds =
+ if ((this.lo frozen_<:< that.lo) && (that.hi frozen_<:< this.hi)) this
+ else if ((that.lo frozen_<:< this.lo) && (this.hi frozen_<:< that.hi)) that
+ else TypeBounds(this.lo & that.lo, this.hi | that.hi)
+
+ override def & (that: Type)(implicit ctx: Context) = that match {
+ case that: TypeBounds => this & that
+ case _ => super.& (that)
+ }
+
+ override def | (that: Type)(implicit ctx: Context) = that match {
+ case that: TypeBounds => this | that
+ case _ => super.| (that)
+ }
+
+ /** The implied bounds, where aliases are mapped to intervals from
+ * Nothing/Any
+ */
+ def boundsInterval(implicit ctx: Context): TypeBounds = this
+
+ /** If this type and that type have the same variance, this variance, otherwise 0 */
+ final def commonVariance(that: TypeBounds): Int = (this.variance + that.variance) / 2
+
+ override def computeHash = doHash(variance, lo, hi)
+ override def equals(that: Any): Boolean = that match {
+ case that: TypeBounds =>
+ (this.lo eq that.lo) && (this.hi eq that.hi) && (this.variance == that.variance)
+ case _ =>
+ false
+ }
+
+ override def toString =
+ if (lo eq hi) s"TypeAlias($lo, $variance)" else s"TypeBounds($lo, $hi)"
+ }
+
+ class RealTypeBounds(lo: Type, hi: Type) extends TypeBounds(lo, hi)
+
+ abstract class TypeAlias(val alias: Type, override val variance: Int) extends TypeBounds(alias, alias) {
+ /** pre: this is a type alias */
+ def derivedTypeAlias(alias: Type, variance: Int = this.variance)(implicit ctx: Context) =
+ if ((alias eq this.alias) && (variance == this.variance)) this
+ else TypeAlias(alias, variance)
+
+ override def & (that: TypeBounds)(implicit ctx: Context): TypeBounds = {
+ val v = this commonVariance that
+ if (v > 0) derivedTypeAlias(this.hi & that.hi, v)
+ else if (v < 0) derivedTypeAlias(this.lo | that.lo, v)
+ else super.& (that)
+ }
+
+ override def | (that: TypeBounds)(implicit ctx: Context): TypeBounds = {
+ val v = this commonVariance that
+ if (v > 0) derivedTypeAlias(this.hi | that.hi, v)
+ else if (v < 0) derivedTypeAlias(this.lo & that.lo, v)
+ else super.| (that)
+ }
+
+ override def boundsInterval(implicit ctx: Context): TypeBounds =
+ if (variance == 0) this
+ else if (variance < 0) TypeBounds.lower(alias)
+ else TypeBounds.upper(alias)
+ }
+
+ class CachedTypeAlias(alias: Type, variance: Int, hc: Int) extends TypeAlias(alias, variance) {
+ myHash = hc
+ }
+
+ object TypeBounds {
+ def apply(lo: Type, hi: Type)(implicit ctx: Context): TypeBounds =
+ unique(new RealTypeBounds(lo, hi))
+ def empty(implicit ctx: Context) = apply(defn.NothingType, defn.AnyType)
+ def upper(hi: Type)(implicit ctx: Context) = apply(defn.NothingType, hi)
+ def lower(lo: Type)(implicit ctx: Context) = apply(lo, defn.AnyType)
+ }
+
+ object TypeAlias {
+ def apply(alias: Type, variance: Int = 0)(implicit ctx: Context) =
+ ctx.uniqueTypeAliases.enterIfNew(alias, variance)
+ def unapply(tp: TypeAlias): Option[Type] = Some(tp.alias)
+ }
+
+ // ----- Annotated and Import types -----------------------------------------------
+
+ /** An annotated type tpe @ annot */
+ case class AnnotatedType(tpe: Type, annot: Annotation)
+ extends UncachedProxyType with ValueType {
+ // todo: cache them? but this makes only sense if annotations and trees are also cached.
+ override def underlying(implicit ctx: Context): Type = tpe
+ def derivedAnnotatedType(tpe: Type, annot: Annotation) =
+ if ((tpe eq this.tpe) && (annot eq this.annot)) this
+ else AnnotatedType(tpe, annot)
+
+ override def stripTypeVar(implicit ctx: Context): Type =
+ derivedAnnotatedType(tpe.stripTypeVar, annot)
+ override def stripAnnots(implicit ctx: Context): Type = tpe.stripAnnots
+ }
+
+ object AnnotatedType {
+ def make(underlying: Type, annots: List[Annotation]) =
+ (underlying /: annots)(AnnotatedType(_, _))
+ }
+
+ // Special type objects and classes -----------------------------------------------------
+
+ /** The type of an erased array */
+ abstract case class JavaArrayType(elemType: Type) extends CachedGroundType with ValueType {
+ override def computeHash = doHash(elemType)
+ def derivedJavaArrayType(elemtp: Type)(implicit ctx: Context) =
+ if (elemtp eq this.elemType) this else JavaArrayType(elemtp)
+ }
+ final class CachedJavaArrayType(elemType: Type) extends JavaArrayType(elemType)
+ object JavaArrayType {
+ def apply(elemType: Type)(implicit ctx: Context) = unique(new CachedJavaArrayType(elemType))
+ }
+
+ /** The type of an import clause tree */
+ case class ImportType(expr: Tree) extends UncachedGroundType
+
+ /** Sentinel for "missing type" */
+ @sharable case object NoType extends CachedGroundType {
+ override def exists = false
+ override def computeHash = hashSeed
+ }
+
+ /** Missing prefix */
+ @sharable case object NoPrefix extends CachedGroundType {
+ override def computeHash = hashSeed
+ }
+
+ abstract class ErrorType extends UncachedGroundType with ValueType
+
+ object ErrorType extends ErrorType
+
+ /* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */
+ object TryDynamicCallType extends ErrorType
+
+ /** Wildcard type, possibly with bounds */
+ abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType {
+ def derivedWildcardType(optBounds: Type)(implicit ctx: Context) =
+ if (optBounds eq this.optBounds) this
+ else if (!optBounds.exists) WildcardType
+ else WildcardType(optBounds.asInstanceOf[TypeBounds])
+ override def computeHash = doHash(optBounds)
+ }
+
+ final class CachedWildcardType(optBounds: Type) extends WildcardType(optBounds)
+
+ @sharable object WildcardType extends WildcardType(NoType) {
+ def apply(bounds: TypeBounds)(implicit ctx: Context) = unique(new CachedWildcardType(bounds))
+ }
+
+ /** An extractor for single abstract method types.
+ * A type is a SAM type if it is a reference to a class or trait, which
+ *
+ * - has a single abstract method with a method type (ExprType
+ * and PolyType not allowed!)
+ * - can be instantiated without arguments or with just () as argument.
+ *
+ * The pattern `SAMType(denot)` matches a SAM type, where `denot` is the
+ * denotation of the single abstract method as a member of the type.
+ */
+ object SAMType {
+ def zeroParamClass(tp: Type)(implicit ctx: Context): Type = tp match {
+ case tp: ClassInfo =>
+ def zeroParams(tp: Type): Boolean = tp match {
+ case pt: PolyType => zeroParams(pt.resultType)
+ case mt: MethodType => mt.paramTypes.isEmpty && !mt.resultType.isInstanceOf[MethodType]
+ case et: ExprType => true
+ case _ => false
+ }
+ if ((tp.cls is Trait) || zeroParams(tp.cls.primaryConstructor.info)) tp // !!! needs to be adapted once traits have parameters
+ else NoType
+ case tp: TypeRef =>
+ zeroParamClass(tp.underlying)
+ case tp: RefinedType =>
+ zeroParamClass(tp.underlying)
+ case tp: TypeBounds =>
+ zeroParamClass(tp.underlying)
+ case tp: TypeVar =>
+ zeroParamClass(tp.underlying)
+ case _ =>
+ NoType
+ }
+ def isInstantiatable(tp: Type)(implicit ctx: Context): Boolean = zeroParamClass(tp) match {
+ case cinfo: ClassInfo =>
+ val tref = tp.narrow
+ val selfType = cinfo.selfType.asSeenFrom(tref, cinfo.cls)
+ tref <:< selfType
+ case _ =>
+ false
+ }
+ def unapply(tp: Type)(implicit ctx: Context): Option[SingleDenotation] =
+ if (isInstantiatable(tp)) {
+ val absMems = tp.abstractTermMembers
+ // println(s"absMems: ${absMems map (_.show) mkString ", "}")
+ if (absMems.size == 1)
+ absMems.head.info match {
+ case mt: MethodType if !mt.isDependent => Some(absMems.head)
+ case _ => None
+ }
+ else if (tp isRef defn.PartialFunctionClass)
+ // To maintain compatibility with 2.x, we treat PartialFunction specially,
+ // pretending it is a SAM type. In the future it would be better to merge
+ // Function and PartialFunction, have Function1 contain a isDefinedAt method
+ // def isDefinedAt(x: T) = true
+ // and overwrite that method whenever the function body is a sequence of
+ // case clauses.
+ absMems.find(_.symbol.name == nme.apply)
+ else None
+ }
+ else None
+ }
+
+ // ----- TypeMaps --------------------------------------------------------------------
+
+ abstract class TypeMap(implicit protected val ctx: Context) extends (Type => Type) { thisMap =>
+
+ protected def stopAtStatic = true
+
+ def apply(tp: Type): Type
+
+ protected var variance = 1
+
+ protected def derivedSelect(tp: NamedType, pre: Type): Type =
+ tp.derivedSelect(pre)
+ protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type): Type =
+ tp.derivedRefinedType(parent, tp.refinedName, info)
+ protected def derivedRecType(tp: RecType, parent: Type): Type =
+ tp.rebind(parent)
+ protected def derivedTypeAlias(tp: TypeAlias, alias: Type): Type =
+ tp.derivedTypeAlias(alias)
+ protected def derivedTypeBounds(tp: TypeBounds, lo: Type, hi: Type): Type =
+ tp.derivedTypeBounds(lo, hi)
+ protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type): Type =
+ tp.derivedSuperType(thistp, supertp)
+ protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type =
+ tp.derivedAppliedType(tycon, args)
+ protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type): Type =
+ tp.derivedAndOrType(tp1, tp2)
+ protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation): Type =
+ tp.derivedAnnotatedType(underlying, annot)
+ protected def derivedWildcardType(tp: WildcardType, bounds: Type): Type =
+ tp.derivedWildcardType(bounds)
+ protected def derivedClassInfo(tp: ClassInfo, pre: Type): Type =
+ tp.derivedClassInfo(pre)
+ protected def derivedJavaArrayType(tp: JavaArrayType, elemtp: Type): Type =
+ tp.derivedJavaArrayType(elemtp)
+ protected def derivedMethodType(tp: MethodType, formals: List[Type], restpe: Type): Type =
+ tp.derivedMethodType(tp.paramNames, formals, restpe)
+ protected def derivedExprType(tp: ExprType, restpe: Type): Type =
+ tp.derivedExprType(restpe)
+ protected def derivedPolyType(tp: PolyType, pbounds: List[TypeBounds], restpe: Type): Type =
+ tp.derivedPolyType(tp.paramNames, pbounds, restpe)
+
+ /** Map this function over given type */
+ def mapOver(tp: Type): Type = {
+ implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type
+ tp match {
+ case tp: NamedType =>
+ if (stopAtStatic && tp.symbol.isStatic) tp
+ else derivedSelect(tp, this(tp.prefix))
+
+ case _: ThisType
+ | _: BoundType
+ | NoPrefix => tp
+
+ case tp: RefinedType =>
+ derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo))
+
+ case tp: TypeAlias =>
+ val saved = variance
+ variance = variance * tp.variance
+ val alias1 = this(tp.alias)
+ variance = saved
+ derivedTypeAlias(tp, alias1)
+
+ case tp: TypeBounds =>
+ variance = -variance
+ val lo1 = this(tp.lo)
+ variance = -variance
+ derivedTypeBounds(tp, lo1, this(tp.hi))
+
+ case tp: MethodType =>
+ def mapOverMethod = {
+ variance = -variance
+ val ptypes1 = tp.paramTypes mapConserve this
+ variance = -variance
+ derivedMethodType(tp, ptypes1, this(tp.resultType))
+ }
+ mapOverMethod
+
+ case tp: ExprType =>
+ derivedExprType(tp, this(tp.resultType))
+
+ case tp: PolyType =>
+ def mapOverPoly = {
+ variance = -variance
+ val bounds1 = tp.paramBounds.mapConserve(this).asInstanceOf[List[TypeBounds]]
+ variance = -variance
+ derivedPolyType(tp, bounds1, this(tp.resultType))
+ }
+ mapOverPoly
+
+ case tp: RecType =>
+ derivedRecType(tp, this(tp.parent))
+
+ case tp @ SuperType(thistp, supertp) =>
+ derivedSuperType(tp, this(thistp), this(supertp))
+
+ case tp: LazyRef =>
+ LazyRef(() => this(tp.ref))
+
+ case tp: ClassInfo =>
+ mapClassInfo(tp)
+
+ case tp: TypeVar =>
+ val inst = tp.instanceOpt
+ if (inst.exists) apply(inst) else tp
+
+ case tp: HKApply =>
+ def mapArg(arg: Type, tparam: TypeParamInfo): Type = {
+ val saved = variance
+ variance *= tparam.paramVariance
+ try this(arg)
+ finally variance = saved
+ }
+ derivedAppliedType(tp, this(tp.tycon),
+ tp.args.zipWithConserve(tp.typeParams)(mapArg))
+
+ case tp: AndOrType =>
+ derivedAndOrType(tp, this(tp.tp1), this(tp.tp2))
+
+ case tp: SkolemType =>
+ tp
+
+ case tp @ AnnotatedType(underlying, annot) =>
+ val underlying1 = this(underlying)
+ if (underlying1 eq underlying) tp
+ else derivedAnnotatedType(tp, underlying1, mapOver(annot))
+
+ case tp @ WildcardType =>
+ derivedWildcardType(tp, mapOver(tp.optBounds))
+
+ case tp: JavaArrayType =>
+ derivedJavaArrayType(tp, this(tp.elemType))
+
+ case tp: ProtoType =>
+ tp.map(this)
+
+ case _ =>
+ tp
+ }
+ }
+
+ private def treeTypeMap = new TreeTypeMap(typeMap = this)
+
+ def mapOver(syms: List[Symbol]): List[Symbol] = ctx.mapSymbols(syms, treeTypeMap)
+
+ def mapOver(scope: Scope): Scope = {
+ val elems = scope.toList
+ val elems1 = mapOver(elems)
+ if (elems1 eq elems) scope
+ else newScopeWith(elems1: _*)
+ }
+
+ def mapOver(annot: Annotation): Annotation =
+ annot.derivedAnnotation(mapOver(annot.tree))
+
+ def mapOver(tree: Tree): Tree = treeTypeMap(tree)
+
+ /** Can be overridden. By default, only the prefix is mapped. */
+ protected def mapClassInfo(tp: ClassInfo): Type =
+ derivedClassInfo(tp, this(tp.prefix))
+
+ def andThen(f: Type => Type): TypeMap = new TypeMap {
+ override def stopAtStatic = thisMap.stopAtStatic
+ def apply(tp: Type) = f(thisMap(tp))
+ }
+ }
+
+ /** A type map that maps also parents and self type of a ClassInfo */
+ abstract class DeepTypeMap(implicit ctx: Context) extends TypeMap {
+ override def mapClassInfo(tp: ClassInfo) = {
+ val prefix1 = this(tp.prefix)
+ val parents1 = (tp.parents mapConserve this).asInstanceOf[List[TypeRef]]
+ val selfInfo1 = tp.selfInfo match {
+ case selfInfo: Type => this(selfInfo)
+ case selfInfo => selfInfo
+ }
+ tp.derivedClassInfo(prefix1, parents1, tp.decls, selfInfo1)
+ }
+ }
+
+ @sharable object IdentityTypeMap extends TypeMap()(NoContext) {
+ override def stopAtStatic = true
+ def apply(tp: Type) = tp
+ }
+
+ abstract class ApproximatingTypeMap(implicit ctx: Context) extends TypeMap { thisMap =>
+ def approx(lo: Type = defn.NothingType, hi: Type = defn.AnyType) =
+ if (variance == 0) NoType
+ else apply(if (variance < 0) lo else hi)
+
+ override protected def derivedSelect(tp: NamedType, pre: Type) =
+ if (pre eq tp.prefix) tp
+ else tp.info match {
+ case TypeAlias(alias) => apply(alias) // try to heal by following aliases
+ case _ =>
+ if (pre.exists && !pre.isRef(defn.NothingClass) && variance > 0) tp.derivedSelect(pre)
+ else tp.info match {
+ case TypeBounds(lo, hi) => approx(lo, hi)
+ case _ => approx()
+ }
+ }
+ override protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type) =
+ if (parent.exists && info.exists) tp.derivedRefinedType(parent, tp.refinedName, info)
+ else approx(hi = parent)
+ override protected def derivedRecType(tp: RecType, parent: Type) =
+ if (parent.exists) tp.rebind(parent)
+ else approx()
+ override protected def derivedTypeAlias(tp: TypeAlias, alias: Type) =
+ if (alias.exists) tp.derivedTypeAlias(alias)
+ else approx(NoType, TypeBounds.empty)
+ override protected def derivedTypeBounds(tp: TypeBounds, lo: Type, hi: Type) =
+ if (lo.exists && hi.exists) tp.derivedTypeBounds(lo, hi)
+ else approx(NoType,
+ if (lo.exists) TypeBounds.lower(lo)
+ else if (hi.exists) TypeBounds.upper(hi)
+ else TypeBounds.empty)
+ override protected def derivedSuperType(tp: SuperType, thistp: Type, supertp: Type) =
+ if (thistp.exists && supertp.exists) tp.derivedSuperType(thistp, supertp)
+ else NoType
+ override protected def derivedAppliedType(tp: HKApply, tycon: Type, args: List[Type]): Type =
+ if (tycon.exists && args.forall(_.exists)) tp.derivedAppliedType(tycon, args)
+ else approx() // This is rather coarse, but to do better is a bit complicated
+ override protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type) =
+ if (tp1.exists && tp2.exists) tp.derivedAndOrType(tp1, tp2)
+ else if (tp.isAnd) approx(hi = tp1 & tp2) // if one of tp1d, tp2d exists, it is the result of tp1d & tp2d
+ else approx(lo = tp1 & tp2)
+ override protected def derivedAnnotatedType(tp: AnnotatedType, underlying: Type, annot: Annotation) =
+ if (underlying.exists) tp.derivedAnnotatedType(underlying, annot)
+ else NoType
+ override protected def derivedWildcardType(tp: WildcardType, bounds: Type) =
+ if (bounds.exists) tp.derivedWildcardType(bounds)
+ else WildcardType
+ override protected def derivedClassInfo(tp: ClassInfo, pre: Type): Type =
+ if (pre.exists) tp.derivedClassInfo(pre)
+ else NoType
+ }
+
+ // ----- TypeAccumulators ----------------------------------------------------
+
+ abstract class TypeAccumulator[T](implicit protected val ctx: Context) extends ((T, Type) => T) {
+
+ protected def stopAtStatic = true
+
+ def apply(x: T, tp: Type): T
+
+ protected def applyToAnnot(x: T, annot: Annotation): T = x // don't go into annotations
+
+ protected var variance = 1
+
+ protected def applyToPrefix(x: T, tp: NamedType) = {
+ val saved = variance
+ variance = 0
+ val result = this(x, tp.prefix)
+ variance = saved
+ result
+ }
+
+ def foldOver(x: T, tp: Type): T = tp match {
+ case tp: TypeRef =>
+ if (stopAtStatic && tp.symbol.isStatic) x
+ else {
+ val tp1 = tp.prefix.lookupRefined(tp.name)
+ if (tp1.exists) this(x, tp1) else applyToPrefix(x, tp)
+ }
+ case tp: TermRef =>
+ if (stopAtStatic && tp.currentSymbol.isStatic) x
+ else applyToPrefix(x, tp)
+
+ case _: ThisType
+ | _: BoundType
+ | NoPrefix => x
+
+ case tp: RefinedType =>
+ this(this(x, tp.parent), tp.refinedInfo)
+
+ case bounds @ TypeBounds(lo, hi) =>
+ if (lo eq hi) {
+ val saved = variance
+ variance = variance * bounds.variance
+ val result = this(x, lo)
+ variance = saved
+ result
+ }
+ else {
+ variance = -variance
+ val y = this(x, lo)
+ variance = -variance
+ this(y, hi)
+ }
+
+ case tp @ MethodType(pnames, ptypes) =>
+ variance = -variance
+ val y = foldOver(x, ptypes)
+ variance = -variance
+ this(y, tp.resultType)
+
+ case ExprType(restpe) =>
+ this(x, restpe)
+
+ case tp: PolyType =>
+ variance = -variance
+ val y = foldOver(x, tp.paramBounds)
+ variance = -variance
+ this(y, tp.resultType)
+
+ case tp: RecType =>
+ this(x, tp.parent)
+
+ case SuperType(thistp, supertp) =>
+ this(this(x, thistp), supertp)
+
+ case tp @ ClassInfo(prefix, _, _, _, _) =>
+ this(x, prefix)
+
+ case tp @ HKApply(tycon, args) =>
+ def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T =
+ if (args.isEmpty) {
+ assert(tparams.isEmpty)
+ x
+ }
+ else {
+ val tparam = tparams.head
+ val saved = variance
+ variance *= tparam.paramVariance
+ val acc =
+ try this(x, args.head)
+ finally variance = saved
+ foldArgs(acc, tparams.tail, args.tail)
+ }
+ foldArgs(this(x, tycon), tp.typeParams, args)
+
+ case tp: AndOrType =>
+ this(this(x, tp.tp1), tp.tp2)
+
+ case tp: SkolemType =>
+ this(x, tp.info)
+
+ case AnnotatedType(underlying, annot) =>
+ this(applyToAnnot(x, annot), underlying)
+
+ case tp: TypeVar =>
+ this(x, tp.underlying)
+
+ case tp: WildcardType =>
+ this(x, tp.optBounds)
+
+ case tp: JavaArrayType =>
+ this(x, tp.elemType)
+
+ case tp: LazyRef =>
+ this(x, tp.ref)
+
+ case tp: ProtoType =>
+ tp.fold(x, this)
+
+ case _ => x
+ }
+
+ final def foldOver(x: T, ts: List[Type]): T = ts match {
+ case t :: ts1 => foldOver(apply(x, t), ts1)
+ case nil => x
+ }
+ }
+
+ abstract class TypeTraverser(implicit ctx: Context) extends TypeAccumulator[Unit] {
+ def traverse(tp: Type): Unit
+ def apply(x: Unit, tp: Type): Unit = traverse(tp)
+ protected def traverseChildren(tp: Type) = foldOver((), tp)
+ }
+
+ class ExistsAccumulator(p: Type => Boolean, forceLazy: Boolean = true)(implicit ctx: Context) extends TypeAccumulator[Boolean] {
+ override def stopAtStatic = false
+ def apply(x: Boolean, tp: Type) =
+ x || p(tp) || (forceLazy || !tp.isInstanceOf[LazyRef]) && foldOver(x, tp)
+ }
+
+ class ForeachAccumulator(p: Type => Unit, override val stopAtStatic: Boolean)(implicit ctx: Context) extends TypeAccumulator[Unit] {
+ def apply(x: Unit, tp: Type): Unit = foldOver(p(tp), tp)
+ }
+
+ class HasUnsafeNonAccumulator(implicit ctx: Context) extends TypeAccumulator[Boolean] {
+ def apply(x: Boolean, tp: Type) = x || tp.isUnsafeNonvariant || foldOver(x, tp)
+ }
+
+ class NamedPartsAccumulator(p: NamedType => Boolean, excludeLowerBounds: Boolean = false)
+ (implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
+ override def stopAtStatic = false
+ def maybeAdd(x: mutable.Set[NamedType], tp: NamedType) = if (p(tp)) x += tp else x
+ val seen: mutable.Set[Type] = mutable.Set()
+ def apply(x: mutable.Set[NamedType], tp: Type): mutable.Set[NamedType] =
+ if (seen contains tp) x
+ else {
+ seen += tp
+ tp match {
+ case tp: TermRef =>
+ apply(foldOver(maybeAdd(x, tp), tp), tp.underlying)
+ case tp: TypeRef =>
+ foldOver(maybeAdd(x, tp), tp)
+ case TypeBounds(lo, hi) =>
+ if (!excludeLowerBounds) apply(x, lo)
+ apply(x, hi)
+ case tp: ThisType =>
+ apply(x, tp.tref)
+ case tp: ConstantType =>
+ apply(x, tp.underlying)
+ case tp: MethodParam =>
+ apply(x, tp.underlying)
+ case tp: PolyParam =>
+ apply(x, tp.underlying)
+ case _ =>
+ foldOver(x, tp)
+ }
+ }
+ }
+
+ // ----- Name Filters --------------------------------------------------
+
+ /** A name filter selects or discards a member name of a type `pre`.
+ * To enable efficient caching, name filters have to satisfy the
+ * following invariant: If `keep` is a name filter, and `pre` has
+ * class `C` as a base class, then
+ *
+ * keep(pre, name) implies keep(C.this, name)
+ */
+ abstract class NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean
+ }
+
+ /** A filter for names of abstract types of a given type */
+ object abstractTypeNameFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
+ name.isTypeName && {
+ val mbr = pre.nonPrivateMember(name)
+ (mbr.symbol is Deferred) && mbr.info.isInstanceOf[RealTypeBounds]
+ }
+ }
+
+ /** A filter for names of abstract types of a given type */
+ object nonClassTypeNameFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
+ name.isTypeName && {
+ val mbr = pre.member(name)
+ mbr.symbol.isType && !mbr.symbol.isClass
+ }
+ }
+
+ /** A filter for names of deferred term definitions of a given type */
+ object abstractTermNameFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
+ name.isTermName && pre.nonPrivateMember(name).hasAltWith(_.symbol is Deferred)
+ }
+
+ object typeNameFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = name.isTypeName
+ }
+
+ object fieldFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean =
+ name.isTermName && (pre member name).hasAltWith(!_.symbol.is(Method))
+ }
+
+ object takeAllFilter extends NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true
+ }
+
+ object implicitFilter extends NameFilter {
+ /** A dummy filter method.
+ * Implicit filtering is handled specially in computeMemberNames, so
+ * no post-filtering is needed.
+ */
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true
+ }
+
+ // ----- Exceptions -------------------------------------------------------------
+
+ class TypeError(msg: String) extends Exception(msg)
+
+ class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])
+ extends TypeError(
+ s"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}")
+
+ class MissingType(pre: Type, name: Name)(implicit ctx: Context) extends TypeError(
+ i"""cannot resolve reference to type $pre.$name
+ |the classfile defining the type might be missing from the classpath${otherReason(pre)}""") {
+ if (ctx.debug) printStackTrace()
+ }
+
+ private def otherReason(pre: Type)(implicit ctx: Context): String = pre match {
+ case pre: ThisType if pre.givenSelfType.exists =>
+ i"\nor the self type of $pre might not contain all transitive dependencies"
+ case _ => ""
+ }
+
+ class CyclicReference private (val denot: SymDenotation)
+ extends TypeError(s"cyclic reference involving $denot") {
+ def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}"
+ }
+
+ object CyclicReference {
+ def apply(denot: SymDenotation)(implicit ctx: Context): CyclicReference = {
+ val ex = new CyclicReference(denot)
+ if (!(ctx.mode is Mode.CheckCyclic)) {
+ cyclicErrors.println(ex.getMessage)
+ for (elem <- ex.getStackTrace take 200)
+ cyclicErrors.println(elem.toString)
+ }
+ ex
+ }
+ }
+
+ class MergeError(msg: String, val tp1: Type, val tp2: Type) extends TypeError(msg)
+
+ // ----- Debug ---------------------------------------------------------
+
+ @sharable var debugTrace = false
+
+ val watchList = List[String](
+ ) map (_.toTypeName)
+
+ def isWatched(tp: Type) = tp match {
+ case TypeRef(_, name) => watchList contains name
+ case _ => false
+ }
+
+ // ----- Decorator implicits --------------------------------------------
+
+ implicit def decorateTypeApplications(tpe: Type): TypeApplications = new TypeApplications(tpe)
+}