aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/core/Denotations.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/core/Denotations.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/core/Denotations.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/core/Denotations.scala1217
1 files changed, 1217 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala
new file mode 100644
index 000000000..6a39c5787
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala
@@ -0,0 +1,1217 @@
+package dotty.tools
+package dotc
+package core
+
+import SymDenotations.{ SymDenotation, ClassDenotation, NoDenotation }
+import Contexts.{Context, ContextBase}
+import Names.{Name, PreName}
+import Names.TypeName
+import StdNames._
+import Symbols.NoSymbol
+import Symbols._
+import Types._
+import Periods._
+import Flags._
+import DenotTransformers._
+import Decorators._
+import dotc.transform.Erasure
+import printing.Texts._
+import printing.Printer
+import io.AbstractFile
+import config.Config
+import util.common._
+import collection.mutable.ListBuffer
+import Decorators.SymbolIteratorDecorator
+
+/** Denotations represent the meaning of symbols and named types.
+ * The following diagram shows how the principal types of denotations
+ * and their denoting entities relate to each other. Lines ending in
+ * a down-arrow `v` are member methods. The two methods shown in the diagram are
+ * "symbol" and "deref". Both methods are parameterized by the current context,
+ * and are effectively indexed by current period.
+ *
+ * Lines ending in a horizontal line mean subtying (right is a subtype of left).
+ *
+ * NamedType------TermRefWithSignature
+ * | | Symbol---------ClassSymbol
+ * | | | |
+ * | denot | denot | denot | denot
+ * v v v v
+ * Denotation-+-----SingleDenotation-+------SymDenotation-+----ClassDenotation
+ * | |
+ * +-----MultiDenotation |
+ * |
+ * +--UniqueRefDenotation
+ * +--JointRefDenotation
+ *
+ * Here's a short summary of the classes in this diagram.
+ *
+ * NamedType A type consisting of a prefix type and a name, with fields
+ * prefix: Type
+ * name: Name
+ * It has two subtypes: TermRef and TypeRef
+ * TermRefWithSignature A TermRef that has in addition a signature to select an overloaded variant, with new field
+ * sig: Signature
+ * Symbol A label for a definition or declaration in one compiler run
+ * ClassSymbol A symbol representing a class
+ * Denotation The meaning of a named type or symbol during a period
+ * MultiDenotation A denotation representing several overloaded members
+ * SingleDenotation A denotation representing a non-overloaded member or definition, with main fields
+ * symbol: Symbol
+ * info: Type
+ * UniqueRefDenotation A denotation referring to a single definition with some member type
+ * JointRefDenotation A denotation referring to a member that could resolve to several definitions
+ * SymDenotation A denotation representing a single definition with its original type, with main fields
+ * name: Name
+ * owner: Symbol
+ * flags: Flags
+ * privateWithin: Symbol
+ * annotations: List[Annotation]
+ * ClassDenotation A denotation representing a single class definition.
+ */
+object Denotations {
+
+ implicit def eqDenotation: Eq[Denotation, Denotation] = Eq
+
+ /** A denotation is the result of resolving
+ * a name (either simple identifier or select) during a given period.
+ *
+ * Denotations can be combined with `&` and `|`.
+ * & is conjunction, | is disjunction.
+ *
+ * `&` will create an overloaded denotation from two
+ * non-overloaded denotations if their signatures differ.
+ * Analogously `|` of two denotations with different signatures will give
+ * an empty denotation `NoDenotation`.
+ *
+ * A denotation might refer to `NoSymbol`. This is the case if the denotation
+ * was produced from a disjunction of two denotations with different symbols
+ * and there was no common symbol in a superclass that could substitute for
+ * both symbols. Here is an example:
+ *
+ * Say, we have:
+ *
+ * class A { def f: A }
+ * class B { def f: B }
+ * val x: A | B = if (test) new A else new B
+ * val y = x.f
+ *
+ * Then the denotation of `y` is `SingleDenotation(NoSymbol, A | B)`.
+ *
+ * @param symbol The referencing symbol, or NoSymbol is none exists
+ */
+ abstract class Denotation(val symbol: Symbol) extends util.DotClass with printing.Showable {
+
+ /** The type info of the denotation, exists only for non-overloaded denotations */
+ def info(implicit ctx: Context): Type
+
+ /** The type info, or, if this is a SymDenotation where the symbol
+ * is not yet completed, the completer
+ */
+ def infoOrCompleter: Type
+
+ /** The period during which this denotation is valid. */
+ def validFor: Period
+
+ /** Is this a reference to a type symbol? */
+ def isType: Boolean
+
+ /** Is this a reference to a term symbol? */
+ def isTerm: Boolean = !isType
+
+ /** Is this denotation overloaded? */
+ final def isOverloaded = isInstanceOf[MultiDenotation]
+
+ /** The signature of the denotation. */
+ def signature(implicit ctx: Context): Signature
+
+ /** Resolve overloaded denotation to pick the ones with the given signature
+ * when seen from prefix `site`.
+ * @param relaxed When true, consider only parameter signatures for a match.
+ */
+ def atSignature(sig: Signature, site: Type = NoPrefix, relaxed: Boolean = false)(implicit ctx: Context): Denotation
+
+ /** The variant of this denotation that's current in the given context.
+ * If no such denotation exists, returns the denotation with each alternative
+ * at its first point of definition.
+ */
+ def current(implicit ctx: Context): Denotation
+
+ /** Is this denotation different from NoDenotation or an ErrorDenotation? */
+ def exists: Boolean = true
+
+ /** A denotation with the info of this denotation transformed using `f` */
+ def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation
+
+ /** If this denotation does not exist, fallback to alternative */
+ final def orElse(that: => Denotation) = if (this.exists) this else that
+
+ /** The set of alternative single-denotations making up this denotation */
+ final def alternatives: List[SingleDenotation] = altsWith(alwaysTrue)
+
+ /** The alternatives of this denotation that satisfy the predicate `p`. */
+ def altsWith(p: Symbol => Boolean): List[SingleDenotation]
+
+ /** The unique alternative of this denotation that satisfies the predicate `p`,
+ * or NoDenotation if no satisfying alternative exists.
+ * @throws TypeError if there is at more than one alternative that satisfies `p`.
+ */
+ def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation
+
+ /** If this is a SingleDenotation, return it, otherwise throw a TypeError */
+ def checkUnique(implicit ctx: Context): SingleDenotation = suchThat(alwaysTrue)
+
+ /** Does this denotation have an alternative that satisfies the predicate `p`? */
+ def hasAltWith(p: SingleDenotation => Boolean): Boolean
+
+ /** The denotation made up from the alternatives of this denotation that
+ * are accessible from prefix `pre`, or NoDenotation if no accessible alternative exists.
+ */
+ def accessibleFrom(pre: Type, superAccess: Boolean = false)(implicit ctx: Context): Denotation
+
+ /** Find member of this denotation 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.
+ */
+ def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation =
+ info.findMember(name, pre, excluded)
+
+ /** If this denotation is overloaded, filter with given predicate.
+ * If result is still overloaded throw a TypeError.
+ * Note: disambiguate is slightly different from suchThat in that
+ * single-denotations that do not satisfy the predicate are left alone
+ * (whereas suchThat would map them to NoDenotation).
+ */
+ def disambiguate(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation = this match {
+ case sdenot: SingleDenotation => sdenot
+ case mdenot => suchThat(p) orElse NoQualifyingRef(alternatives)
+ }
+
+ /** Return symbol in this denotation that satisfies the given predicate.
+ * if generateStubs is specified, return a stubsymbol if denotation is a missing ref.
+ * Throw a `TypeError` if predicate fails to disambiguate symbol or no alternative matches.
+ */
+ def requiredSymbol(p: Symbol => Boolean, source: AbstractFile = null, generateStubs: Boolean = true)(implicit ctx: Context): Symbol =
+ disambiguate(p) match {
+ case m @ MissingRef(ownerd, name) =>
+ if (generateStubs) {
+ m.ex.printStackTrace()
+ ctx.newStubSymbol(ownerd.symbol, name, source)
+ }
+ else NoSymbol
+ case NoDenotation | _: NoQualifyingRef =>
+ throw new TypeError(s"None of the alternatives of $this satisfies required predicate")
+ case denot =>
+ denot.symbol
+ }
+
+ def requiredMethod(name: PreName)(implicit ctx: Context): TermSymbol =
+ info.member(name.toTermName).requiredSymbol(_ is Method).asTerm
+ def requiredMethodRef(name: PreName)(implicit ctx: Context): TermRef =
+ requiredMethod(name).termRef
+
+ def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol =
+ info.member(name.toTermName).requiredSymbol(x=>
+ (x is Method) && x.info.paramTypess == List(argTypes)
+ ).asTerm
+ def requiredMethodRef(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermRef =
+ requiredMethod(name, argTypes).termRef
+
+ def requiredValue(name: PreName)(implicit ctx: Context): TermSymbol =
+ info.member(name.toTermName).requiredSymbol(_.info.isParameterless).asTerm
+ def requiredValueRef(name: PreName)(implicit ctx: Context): TermRef =
+ requiredValue(name).termRef
+
+ def requiredClass(name: PreName)(implicit ctx: Context): ClassSymbol =
+ info.member(name.toTypeName).requiredSymbol(_.isClass).asClass
+
+ /** The alternative of this denotation that has a type matching `targetType` when seen
+ * as a member of type `site`, `NoDenotation` if none exists.
+ */
+ def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = {
+ def qualifies(sym: Symbol) = site.memberInfo(sym).matchesLoosely(targetType)
+ if (isOverloaded) {
+ atSignature(targetType.signature, site, relaxed = true) match {
+ case sd: SingleDenotation => sd.matchingDenotation(site, targetType)
+ case md => md.suchThat(qualifies(_))
+ }
+ }
+ else if (exists && !qualifies(symbol)) NoDenotation
+ else asSingleDenotation
+ }
+
+ /** Handle merge conflict by throwing a `MergeError` exception */
+ private def mergeConflict(tp1: Type, tp2: Type)(implicit ctx: Context): Type = {
+ def showType(tp: Type) = tp match {
+ case ClassInfo(_, cls, _, _, _) => cls.showLocated
+ case bounds: TypeBounds => i"type bounds $bounds"
+ case _ => tp.show
+ }
+ if (true) throw new MergeError(s"cannot merge ${showType(tp1)} with ${showType(tp2)}", tp1, tp2)
+ else throw new Error(s"cannot merge ${showType(tp1)} with ${showType(tp2)}") // flip condition for debugging
+ }
+
+ /** Merge two lists of names. If names in corresponding positions match, keep them,
+ * otherwise generate new synthetic names.
+ */
+ def mergeNames[N <: Name](names1: List[N], names2: List[N], syntheticName: Int => N): List[N] = {
+ for ((name1, name2, idx) <- (names1, names2, 0 until names1.length).zipped)
+ yield if (name1 == name2) name1 else syntheticName(idx)
+ }.toList
+
+ /** Form a denotation by conjoining with denotation `that`.
+ *
+ * NoDenotations are dropped. MultiDenotations are handled by merging
+ * parts with same signatures. SingleDenotations with equal signatures
+ * are joined as follows:
+ *
+ * In a first step, consider only those denotations which have symbols
+ * that are accessible from prefix `pre`.
+ *
+ * If there are several such denotations, try to pick one by applying the following
+ * three precedence rules in decreasing order of priority:
+ *
+ * 1. Prefer denotations with more specific infos.
+ * 2. If infos are equally specific, prefer denotations with concrete symbols over denotations
+ * with abstract symbols.
+ * 3. If infos are equally specific and symbols are equally concrete,
+ * prefer denotations with symbols defined in subclasses
+ * over denotations with symbols defined in proper superclasses.
+ *
+ * If there is exactly one (preferred) accessible denotation, return it.
+ *
+ * If there is no preferred accessible denotation, return a JointRefDenotation
+ * with one of the operand symbols (unspecified which one), and an info which
+ * is the intersection (using `&` or `safe_&` if `safeIntersection` is true)
+ * of the infos of the operand denotations.
+ *
+ * If SingleDenotations with different signatures are joined, return NoDenotation.
+ */
+ def & (that: Denotation, pre: Type, safeIntersection: Boolean = false)(implicit ctx: Context): Denotation = {
+
+ /** Normally, `tp1 & tp2`. Special cases for matching methods and classes, with
+ * the possibility of raising a merge error.
+ */
+ def infoMeet(tp1: Type, tp2: Type): Type = {
+ if (tp1 eq tp2) tp1
+ else tp1 match {
+ case tp1: TypeBounds =>
+ tp2 match {
+ case tp2: TypeBounds => if (safeIntersection) tp1 safe_& tp2 else tp1 & tp2
+ case tp2: ClassInfo if tp1 contains tp2 => tp2
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1: ClassInfo =>
+ tp2 match {
+ case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix)
+ case tp2: TypeBounds if tp2 contains tp1 => tp1
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1 @ MethodType(names1, formals1) if isTerm =>
+ tp2 match {
+ case tp2 @ MethodType(names2, formals2) if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
+ tp1.isImplicit == tp2.isImplicit =>
+ tp1.derivedMethodType(
+ mergeNames(names1, names2, nme.syntheticParamName),
+ formals1,
+ infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
+ case _ =>
+ mergeConflict(tp1, tp2)
+ }
+ case tp1: PolyType if isTerm =>
+ tp2 match {
+ case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
+ tp1.derivedPolyType(
+ mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
+ tp1.paramBounds,
+ infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1)))
+ case _: MethodicType =>
+ mergeConflict(tp1, tp2)
+ }
+ case _ =>
+ tp1 & tp2
+ }
+ }
+
+ /** Try to merge denot1 and denot2 without adding a new signature. */
+ def mergeDenot(denot1: Denotation, denot2: SingleDenotation): Denotation = denot1 match {
+ case denot1 @ MultiDenotation(denot11, denot12) =>
+ val d1 = mergeDenot(denot11, denot2)
+ if (d1.exists) denot1.derivedMultiDenotation(d1, denot12)
+ else {
+ val d2 = mergeDenot(denot12, denot2)
+ if (d2.exists) denot1.derivedMultiDenotation(denot11, d2)
+ else NoDenotation
+ }
+ case denot1: SingleDenotation =>
+ if (denot1 eq denot2) denot1
+ else if (denot1.matches(denot2)) mergeSingleDenot(denot1, denot2)
+ else NoDenotation
+ }
+
+ /** Try to merge single-denotations. */
+ def mergeSingleDenot(denot1: SingleDenotation, denot2: SingleDenotation): SingleDenotation = {
+ val info1 = denot1.info
+ val info2 = denot2.info
+ val sym1 = denot1.symbol
+ val sym2 = denot2.symbol
+
+ val sym2Accessible = sym2.isAccessibleFrom(pre)
+
+ /** Does `sym1` come before `sym2` in the linearization of `pre`? */
+ def precedes(sym1: Symbol, sym2: Symbol) = {
+ def precedesIn(bcs: List[ClassSymbol]): Boolean = bcs match {
+ case bc :: bcs1 => (sym1 eq bc) || !(sym2 eq bc) && precedesIn(bcs1)
+ case Nil => true
+ }
+ (sym1 ne sym2) &&
+ (sym1.derivesFrom(sym2) ||
+ !sym2.derivesFrom(sym1) && precedesIn(pre.baseClasses))
+ }
+
+ /** Similar to SymDenotation#accessBoundary, but without the special cases. */
+ def accessBoundary(sym: Symbol) =
+ if (sym.is(Private)) sym.owner
+ else sym.privateWithin.orElse(
+ if (sym.is(Protected)) sym.owner.enclosingPackageClass
+ else defn.RootClass)
+
+ /** Establish a partial order "preference" order between symbols.
+ * Give preference to `sym1` over `sym2` if one of the following
+ * conditions holds, in decreasing order of weight:
+ * 1. sym1 is concrete and sym2 is abstract
+ * 2. The owner of sym1 comes before the owner of sym2 in the linearization
+ * of the type of the prefix `pre`.
+ * 3. The access boundary of sym2 is properly contained in the access
+ * boundary of sym1. For protected access, we count the enclosing
+ * package as access boundary.
+ * 4. sym1 a method but sym2 is not.
+ * The aim of these criteria is to give some disambiguation on access which
+ * - does not depend on textual order or other arbitrary choices
+ * - minimizes raising of doubleDef errors
+ */
+ def preferSym(sym1: Symbol, sym2: Symbol) =
+ sym1.eq(sym2) ||
+ sym1.isAsConcrete(sym2) &&
+ (!sym2.isAsConcrete(sym1) ||
+ precedes(sym1.owner, sym2.owner) ||
+ accessBoundary(sym2).isProperlyContainedIn(accessBoundary(sym1)) ||
+ sym1.is(Method) && !sym2.is(Method)) ||
+ sym1.info.isErroneous
+
+ /** Sym preference provided types also override */
+ def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) =
+ preferSym(sym1, sym2) && info1.overrides(info2)
+
+ def handleDoubleDef =
+ if (preferSym(sym1, sym2)) denot1
+ else if (preferSym(sym2, sym1)) denot2
+ else doubleDefError(denot1, denot2, pre)
+
+ if (sym2Accessible && prefer(sym2, sym1, info2, info1)) denot2
+ else {
+ val sym1Accessible = sym1.isAccessibleFrom(pre)
+ if (sym1Accessible && prefer(sym1, sym2, info1, info2)) denot1
+ else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
+ else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
+ else if (isDoubleDef(sym1, sym2)) handleDoubleDef
+ else {
+ val sym =
+ if (!sym1.exists) sym2
+ else if (!sym2.exists) sym1
+ else if (preferSym(sym2, sym1)) sym2
+ else sym1
+ val jointInfo =
+ try infoMeet(info1, info2)
+ catch {
+ case ex: MergeError =>
+ if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
+ info1 // follow Scala2 linearization -
+ // compare with way merge is performed in SymDenotation#computeMembersNamed
+ else
+ throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
+ }
+ new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
+ }
+ }
+ }
+
+ if (this eq that) this
+ else if (!this.exists) that
+ else if (!that.exists) this
+ else that match {
+ case that: SingleDenotation =>
+ val r = mergeDenot(this, that)
+ if (r.exists) r else MultiDenotation(this, that)
+ case that @ MultiDenotation(denot1, denot2) =>
+ this & (denot1, pre) & (denot2, pre)
+ }
+ }
+
+ /** Form a choice between this denotation and that one.
+ * @param pre The prefix type of the members of the denotation, used
+ * to determine an accessible symbol if it exists.
+ */
+ def | (that: Denotation, pre: Type)(implicit ctx: Context): Denotation = {
+
+ /** Normally, `tp1 | tp2`. Special cases for matching methods and classes, with
+ * the possibility of raising a merge error.
+ */
+ def infoJoin(tp1: Type, tp2: Type): Type = tp1 match {
+ case tp1: TypeBounds =>
+ tp2 match {
+ case tp2: TypeBounds => tp1 | tp2
+ case tp2: ClassInfo if tp1 contains tp2 => tp1
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1: ClassInfo =>
+ tp2 match {
+ case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix | tp2.prefix)
+ case tp2: TypeBounds if tp2 contains tp1 => tp2
+ case _ => mergeConflict(tp1, tp2)
+ }
+ case tp1 @ MethodType(names1, formals1) =>
+ tp2 match {
+ case tp2 @ MethodType(names2, formals2)
+ if ctx.typeComparer.matchingParams(formals1, formals2, tp1.isJava, tp2.isJava) &&
+ tp1.isImplicit == tp2.isImplicit =>
+ tp1.derivedMethodType(
+ mergeNames(names1, names2, nme.syntheticParamName),
+ formals1, tp1.resultType | tp2.resultType.subst(tp2, tp1))
+ case _ =>
+ mergeConflict(tp1, tp2)
+ }
+ case tp1: PolyType =>
+ tp2 match {
+ case tp2: PolyType if ctx.typeComparer.matchingTypeParams(tp1, tp2) =>
+ tp1.derivedPolyType(
+ mergeNames(tp1.paramNames, tp2.paramNames, tpnme.syntheticTypeParamName),
+ tp1.paramBounds, tp1.resultType | tp2.resultType.subst(tp2, tp1))
+ case _ =>
+ mergeConflict(tp1, tp2)
+ }
+ case _ =>
+ tp1 | tp2
+ }
+
+ def unionDenot(denot1: SingleDenotation, denot2: SingleDenotation): Denotation =
+ if (denot1.matches(denot2)) {
+ val sym1 = denot1.symbol
+ val sym2 = denot2.symbol
+ val info1 = denot1.info
+ val info2 = denot2.info
+ val sameSym = sym1 eq sym2
+ if (sameSym && (info1 frozen_<:< info2)) denot2
+ else if (sameSym && (info2 frozen_<:< info1)) denot1
+ else {
+ val jointSym =
+ if (sameSym) sym1
+ else {
+ val owner2 = if (sym2 ne NoSymbol) sym2.owner else NoSymbol
+ /** Determine a symbol which is overridden by both sym1 and sym2.
+ * Preference is given to accessible symbols.
+ */
+ def lubSym(overrides: Iterator[Symbol], previous: Symbol): Symbol =
+ if (!overrides.hasNext) previous
+ else {
+ val candidate = overrides.next
+ if (owner2 derivesFrom candidate.owner)
+ if (candidate isAccessibleFrom pre) candidate
+ else lubSym(overrides, previous orElse candidate)
+ else
+ lubSym(overrides, previous)
+ }
+ lubSym(sym1.allOverriddenSymbols, NoSymbol)
+ }
+ new JointRefDenotation(
+ jointSym, infoJoin(info1, info2), denot1.validFor & denot2.validFor)
+ }
+ }
+ else NoDenotation
+
+ if (this eq that) this
+ else if (!this.exists) this
+ else if (!that.exists) that
+ else this match {
+ case denot1 @ MultiDenotation(denot11, denot12) =>
+ denot1.derivedMultiDenotation(denot11 | (that, pre), denot12 | (that, pre))
+ case denot1: SingleDenotation =>
+ that match {
+ case denot2 @ MultiDenotation(denot21, denot22) =>
+ denot2.derivedMultiDenotation(this | (denot21, pre), this | (denot22, pre))
+ case denot2: SingleDenotation =>
+ unionDenot(denot1, denot2)
+ }
+ }
+ }
+
+ final def asSingleDenotation = asInstanceOf[SingleDenotation]
+ final def asSymDenotation = asInstanceOf[SymDenotation]
+
+ def toText(printer: Printer): Text = printer.toText(this)
+ }
+
+ /** An overloaded denotation consisting of the alternatives of both given denotations.
+ */
+ case class MultiDenotation(denot1: Denotation, denot2: Denotation) extends Denotation(NoSymbol) {
+ final def infoOrCompleter = multiHasNot("info")
+ final def info(implicit ctx: Context) = infoOrCompleter
+ final def validFor = denot1.validFor & denot2.validFor
+ final def isType = false
+ final def signature(implicit ctx: Context) = Signature.OverloadedSignature
+ def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): Denotation =
+ derivedMultiDenotation(denot1.atSignature(sig, site, relaxed), denot2.atSignature(sig, site, relaxed))
+ def current(implicit ctx: Context): Denotation =
+ derivedMultiDenotation(denot1.current, denot2.current)
+ def altsWith(p: Symbol => Boolean): List[SingleDenotation] =
+ denot1.altsWith(p) ++ denot2.altsWith(p)
+ def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation = {
+ val sd1 = denot1.suchThat(p)
+ val sd2 = denot2.suchThat(p)
+ if (sd1.exists)
+ if (sd2.exists)
+ if (isDoubleDef(denot1.symbol, denot2.symbol)) doubleDefError(denot1, denot2)
+ else throw new TypeError(s"failure to disambiguate overloaded reference $this")
+ else sd1
+ else sd2
+ }
+ def hasAltWith(p: SingleDenotation => Boolean): Boolean =
+ denot1.hasAltWith(p) || denot2.hasAltWith(p)
+ def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = {
+ val d1 = denot1 accessibleFrom (pre, superAccess)
+ val d2 = denot2 accessibleFrom (pre, superAccess)
+ if (!d1.exists) d2
+ else if (!d2.exists) d1
+ else derivedMultiDenotation(d1, d2)
+ }
+ def mapInfo(f: Type => Type)(implicit ctx: Context): Denotation =
+ derivedMultiDenotation(denot1.mapInfo(f), denot2.mapInfo(f))
+ def derivedMultiDenotation(d1: Denotation, d2: Denotation) =
+ if ((d1 eq denot1) && (d2 eq denot2)) this else MultiDenotation(d1, d2)
+ override def toString = alternatives.mkString(" <and> ")
+
+ private def multiHasNot(op: String): Nothing =
+ throw new UnsupportedOperationException(
+ s"multi-denotation with alternatives $alternatives does not implement operation $op")
+ }
+
+ /** A non-overloaded denotation */
+ abstract class SingleDenotation(symbol: Symbol) extends Denotation(symbol) with PreDenotation {
+ def hasUniqueSym: Boolean
+ protected def newLikeThis(symbol: Symbol, info: Type): SingleDenotation
+
+ final def signature(implicit ctx: Context): Signature = {
+ if (isType) Signature.NotAMethod // don't force info if this is a type SymDenotation
+ else info match {
+ case info: MethodicType =>
+ try info.signature
+ catch { // !!! DEBUG
+ case scala.util.control.NonFatal(ex) =>
+ ctx.echo(s"cannot take signature of ${info.show}")
+ throw ex
+ }
+ case _ => Signature.NotAMethod
+ }
+ }
+
+ def derivedSingleDenotation(symbol: Symbol, info: Type)(implicit ctx: Context): SingleDenotation =
+ if ((symbol eq this.symbol) && (info eq this.info)) this
+ else newLikeThis(symbol, info)
+
+ def mapInfo(f: Type => Type)(implicit ctx: Context): SingleDenotation =
+ derivedSingleDenotation(symbol, f(info))
+
+ def orElse(that: => SingleDenotation) = if (this.exists) this else that
+
+ def altsWith(p: Symbol => Boolean): List[SingleDenotation] =
+ if (exists && p(symbol)) this :: Nil else Nil
+
+ def suchThat(p: Symbol => Boolean)(implicit ctx: Context): SingleDenotation =
+ if (exists && p(symbol)) this else NoDenotation
+
+ def hasAltWith(p: SingleDenotation => Boolean): Boolean =
+ exists && p(this)
+
+ def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation =
+ if (!symbol.exists || symbol.isAccessibleFrom(pre, superAccess)) this else NoDenotation
+
+ def atSignature(sig: Signature, site: Type, relaxed: Boolean)(implicit ctx: Context): SingleDenotation = {
+ val situated = if (site == NoPrefix) this else asSeenFrom(site)
+ val matches = sig.matchDegree(situated.signature) >=
+ (if (relaxed) Signature.ParamMatch else Signature.FullMatch)
+ if (matches) this else NoDenotation
+ }
+
+ // ------ Forming types -------------------------------------------
+
+ /** The TypeRef representing this type denotation at its original location. */
+ def typeRef(implicit ctx: Context): TypeRef =
+ TypeRef(symbol.owner.thisType, symbol.name.asTypeName, this)
+
+ /** The TermRef representing this term denotation at its original location. */
+ def termRef(implicit ctx: Context): TermRef =
+ TermRef(symbol.owner.thisType, symbol.name.asTermName, this)
+
+ /** The TermRef representing this term denotation at its original location
+ * and at signature `NotAMethod`.
+ */
+ def valRef(implicit ctx: Context): TermRef =
+ TermRef.withSigAndDenot(symbol.owner.thisType, symbol.name.asTermName, Signature.NotAMethod, this)
+
+ /** The TermRef representing this term denotation at its original location
+ * at the denotation's signature.
+ * @note Unlike `valRef` and `termRef`, this will force the completion of the
+ * denotation via a call to `info`.
+ */
+ def termRefWithSig(implicit ctx: Context): TermRef =
+ TermRef.withSigAndDenot(symbol.owner.thisType, symbol.name.asTermName, signature, this)
+
+ /** The NamedType representing this denotation at its original location.
+ * Same as either `typeRef` or `termRefWithSig` depending whether this denotes a type or not.
+ */
+ def namedType(implicit ctx: Context): NamedType =
+ if (isType) typeRef else termRefWithSig
+
+ // ------ Transformations -----------------------------------------
+
+ private[this] var myValidFor: Period = Nowhere
+
+ def validFor = myValidFor
+ def validFor_=(p: Period) =
+ myValidFor = p
+
+ /** The next SingleDenotation in this run, with wrap-around from last to first.
+ *
+ * There may be several `SingleDenotation`s with different validity
+ * representing the same underlying definition at different phases.
+ * These are called a "flock". Flock members are generated by
+ * @See current. Flock members are connected in a ring
+ * with their `nextInRun` fields.
+ *
+ * There are the following invariants concerning flock members
+ *
+ * 1) validity periods are non-overlapping
+ * 2) the union of all validity periods is a contiguous
+ * interval.
+ */
+ protected var nextInRun: SingleDenotation = this
+
+ /** The version of this SingleDenotation that was valid in the first phase
+ * of this run.
+ */
+ def initial: SingleDenotation =
+ if (validFor == Nowhere) this
+ else {
+ var current = nextInRun
+ while (current.validFor.code > this.myValidFor.code) current = current.nextInRun
+ current
+ }
+
+ def history: List[SingleDenotation] = {
+ val b = new ListBuffer[SingleDenotation]
+ var current = initial
+ do {
+ b += (current)
+ current = current.nextInRun
+ }
+ while (current ne initial)
+ b.toList
+ }
+
+ /** Invalidate all caches and fields that depend on base classes and their contents */
+ def invalidateInheritedInfo(): Unit = ()
+
+ /** Move validity period of this denotation to a new run. Throw a StaleSymbol error
+ * if denotation is no longer valid.
+ */
+ private def bringForward()(implicit ctx: Context): SingleDenotation = this match {
+ case denot: SymDenotation if ctx.stillValid(denot) =>
+ assert(ctx.runId > validFor.runId || ctx.settings.YtestPickler.value, // mixing test pickler with debug printing can travel back in time
+ s"denotation $denot invalid in run ${ctx.runId}. ValidFor: $validFor")
+ var d: SingleDenotation = denot
+ do {
+ d.validFor = Period(ctx.period.runId, d.validFor.firstPhaseId, d.validFor.lastPhaseId)
+ d.invalidateInheritedInfo()
+ d = d.nextInRun
+ } while (d ne denot)
+ this
+ case _ =>
+ if (coveredInterval.containsPhaseId(ctx.phaseId)) {
+ if (ctx.debug) ctx.traceInvalid(this)
+ staleSymbolError
+ }
+ else NoDenotation
+ }
+
+ /** Produce a denotation that is valid for the given context.
+ * Usually called when !(validFor contains ctx.period)
+ * (even though this is not a precondition).
+ * If the runId of the context is the same as runId of this denotation,
+ * the right flock member is located, or, if it does not exist yet,
+ * created by invoking a transformer (@See Transformers).
+ * If the runId's differ, but this denotation is a SymDenotation
+ * and its toplevel owner class or module
+ * is still a member of its enclosing package, then the whole flock
+ * is brought forward to be valid in the new runId. Otherwise
+ * the symbol is stale, which constitutes an internal error.
+ */
+ def current(implicit ctx: Context): SingleDenotation = {
+ val currentPeriod = ctx.period
+ val valid = myValidFor
+ if (valid.code <= 0) {
+ // can happen if we sit on a stale denotation which has been replaced
+ // wholesale by an installAfter; in this case, proceed to the next
+ // denotation and try again.
+ if (validFor == Nowhere && nextInRun.validFor != Nowhere) return nextInRun.current
+ assert(false)
+ }
+
+ if (valid.runId != currentPeriod.runId)
+ if (exists) initial.bringForward.current
+ else this
+ else {
+ var cur = this
+ if (currentPeriod.code > valid.code) {
+ // search for containing period as long as nextInRun increases.
+ var next = nextInRun
+ while (next.validFor.code > valid.code && !(next.validFor contains currentPeriod)) {
+ cur = next
+ next = next.nextInRun
+ }
+ if (next.validFor.code > valid.code) {
+ // in this case, next.validFor contains currentPeriod
+ cur = next
+ cur
+ } else {
+ //println(s"might need new denot for $cur, valid for ${cur.validFor} at $currentPeriod")
+ // not found, cur points to highest existing variant
+ val nextTransformerId = ctx.nextDenotTransformerId(cur.validFor.lastPhaseId)
+ if (currentPeriod.lastPhaseId <= nextTransformerId)
+ cur.validFor = Period(currentPeriod.runId, cur.validFor.firstPhaseId, nextTransformerId)
+ else {
+ var startPid = nextTransformerId + 1
+ val transformer = ctx.denotTransformers(nextTransformerId)
+ //println(s"transforming $this with $transformer")
+ try {
+ next = transformer.transform(cur)(ctx.withPhase(transformer)).syncWithParents
+ } catch {
+ case ex: CyclicReference =>
+ println(s"error while transforming $this") // DEBUG
+ throw ex
+ }
+ if (next eq cur)
+ startPid = cur.validFor.firstPhaseId
+ else {
+ next match {
+ case next: ClassDenotation =>
+ assert(!next.is(Package), s"illegal transformation of package denotation by transformer ${ctx.withPhase(transformer).phase}")
+ next.resetFlag(Frozen)
+ case _ =>
+ }
+ next.insertAfter(cur)
+ cur = next
+ }
+ cur.validFor = Period(currentPeriod.runId, startPid, transformer.lastPhaseId)
+ //printPeriods(cur)
+ //println(s"new denot: $cur, valid for ${cur.validFor}")
+ }
+ cur.current // multiple transformations could be required
+ }
+ } else {
+ // currentPeriod < end of valid; in this case a version must exist
+ // but to be defensive we check for infinite loop anyway
+ var cnt = 0
+ while (!(cur.validFor contains currentPeriod)) {
+ //println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}")
+ cur = cur.nextInRun
+ // Note: One might be tempted to add a `prev` field to get to the new denotation
+ // more directly here. I tried that, but it degrades rather than improves
+ // performance: Test setup: Compile everything in dotc and immediate subdirectories
+ // 10 times. Best out of 10: 18154ms with `prev` field, 17777ms without.
+ cnt += 1
+ if (cnt > MaxPossiblePhaseId)
+ return current(ctx.withPhase(coveredInterval.firstPhaseId))
+ }
+ cur
+ }
+ }
+ }
+
+ private def demandOutsideDefinedMsg(implicit ctx: Context): String =
+ s"demanding denotation of $this at phase ${ctx.phase}(${ctx.phaseId}) outside defined interval: defined periods are${definedPeriodsString}"
+
+ /** Install this denotation to be the result of the given denotation transformer.
+ * This is the implementation of the same-named method in SymDenotations.
+ * It's placed here because it needs access to private fields of SingleDenotation.
+ * @pre Can only be called in `phase.next`.
+ */
+ protected def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit = {
+ val targetId = phase.next.id
+ if (ctx.phaseId != targetId) installAfter(phase)(ctx.withPhase(phase.next))
+ else {
+ val current = symbol.current
+ // println(s"installing $this after $phase/${phase.id}, valid = ${current.validFor}")
+ // printPeriods(current)
+ this.validFor = Period(ctx.runId, targetId, current.validFor.lastPhaseId)
+ if (current.validFor.firstPhaseId >= targetId)
+ insertInsteadOf(current)
+ else {
+ current.validFor = Period(ctx.runId, current.validFor.firstPhaseId, targetId - 1)
+ insertAfter(current)
+ }
+ // printPeriods(this)
+ }
+ }
+
+ /** Apply a transformation `f` to all denotations in this group that start at or after
+ * given phase. Denotations are replaced while keeping the same validity periods.
+ */
+ protected def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit = {
+ var current = symbol.current
+ while (current.validFor.firstPhaseId < phase.id && (current.nextInRun.validFor.code > current.validFor.code))
+ current = current.nextInRun
+ var hasNext = true
+ while ((current.validFor.firstPhaseId >= phase.id) && hasNext) {
+ val current1: SingleDenotation = f(current.asSymDenotation)
+ if (current1 ne current) {
+ current1.validFor = current.validFor
+ current1.insertInsteadOf(current)
+ }
+ hasNext = current1.nextInRun.validFor.code > current1.validFor.code
+ current = current1.nextInRun
+ }
+ }
+
+ /** Insert this denotation so that it follows `prev`. */
+ private def insertAfter(prev: SingleDenotation) = {
+ this.nextInRun = prev.nextInRun
+ prev.nextInRun = this
+ }
+
+ /** Insert this denotation instead of `old`.
+ * Also ensure that `old` refers with `nextInRun` to this denotation
+ * and set its `validFor` field to `NoWhere`. This is necessary so that
+ * references to the old denotation can be brought forward via `current`
+ * to a valid denotation.
+ *
+ * The code to achieve this is subtle in that it works correctly
+ * whether the replaced denotation is the only one in its cycle or not.
+ */
+ private def insertInsteadOf(old: SingleDenotation): Unit = {
+ var prev = old
+ while (prev.nextInRun ne old) prev = prev.nextInRun
+ // order of next two assignments is important!
+ prev.nextInRun = this
+ this.nextInRun = old.nextInRun
+ old.validFor = Nowhere
+ }
+
+ def staleSymbolError(implicit ctx: Context) = {
+ def ownerMsg = this match {
+ case denot: SymDenotation => s"in ${denot.owner}"
+ case _ => ""
+ }
+ def msg = s"stale symbol; $this#${symbol.id} $ownerMsg, defined in ${myValidFor}, is referred to in run ${ctx.period}"
+ throw new StaleSymbol(msg)
+ }
+
+ /** The period (interval of phases) for which there exists
+ * a valid denotation in this flock.
+ */
+ def coveredInterval(implicit ctx: Context): Period = {
+ var cur = this
+ var cnt = 0
+ var interval = validFor
+ do {
+ cur = cur.nextInRun
+ cnt += 1
+ assert(cnt <= MaxPossiblePhaseId, demandOutsideDefinedMsg)
+ interval |= cur.validFor
+ } while (cur ne this)
+ interval
+ }
+
+ /** For ClassDenotations only:
+ * If caches influenced by parent classes are still valid, the denotation
+ * itself, otherwise a freshly initialized copy.
+ */
+ def syncWithParents(implicit ctx: Context): SingleDenotation = this
+
+ /** Show declaration string; useful for showing declarations
+ * as seen from subclasses.
+ */
+ def showDcl(implicit ctx: Context): String = ctx.dclText(this).show
+
+ override def toString =
+ if (symbol == NoSymbol) symbol.toString
+ else s"<SingleDenotation of type $infoOrCompleter>"
+
+ def definedPeriodsString: String = {
+ var sb = new StringBuilder()
+ var cur = this
+ var cnt = 0
+ do {
+ sb.append(" " + cur.validFor)
+ cur = cur.nextInRun
+ cnt += 1
+ if (cnt > MaxPossiblePhaseId) { sb.append(" ..."); cur = this }
+ } while (cur ne this)
+ sb.toString
+ }
+
+ // ------ PreDenotation ops ----------------------------------------------
+
+ final def first = this
+ final def last = this
+ final def toDenot(pre: Type)(implicit ctx: Context): Denotation = this
+ final def containsSym(sym: Symbol): Boolean = hasUniqueSym && (symbol eq sym)
+ final def matches(other: SingleDenotation)(implicit ctx: Context): Boolean = {
+ val d = signature.matchDegree(other.signature)
+ d == Signature.FullMatch ||
+ d >= Signature.ParamMatch && info.matches(other.info)
+ }
+ final def filterWithPredicate(p: SingleDenotation => Boolean): SingleDenotation =
+ if (p(this)) this else NoDenotation
+ final def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): SingleDenotation =
+ if (denots.exists && denots.matches(this)) NoDenotation else this
+ def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): SingleDenotation =
+ if (hasUniqueSym && prevDenots.containsSym(symbol)) NoDenotation
+ else if (isType) filterDisjoint(ownDenots).asSeenFrom(pre)
+ else asSeenFrom(pre).filterDisjoint(ownDenots)
+ final def filterExcluded(excluded: FlagSet)(implicit ctx: Context): SingleDenotation =
+ if (excluded.isEmpty || !(this overlaps excluded)) this else NoDenotation
+
+ type AsSeenFromResult = SingleDenotation
+ protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = {
+ val symbol = this.symbol
+ val owner = this match {
+ case thisd: SymDenotation => thisd.owner
+ case _ => if (symbol.exists) symbol.owner else NoSymbol
+ }
+ if (!owner.membersNeedAsSeenFrom(pre)) this
+ else derivedSingleDenotation(symbol, info.asSeenFrom(pre, owner))
+ }
+
+ private def overlaps(fs: FlagSet)(implicit ctx: Context): Boolean = this match {
+ case sd: SymDenotation => sd is fs
+ case _ => symbol is fs
+ }
+ }
+
+ abstract class NonSymSingleDenotation(symbol: Symbol) extends SingleDenotation(symbol) {
+ def infoOrCompleter: Type
+ def info(implicit ctx: Context) = infoOrCompleter
+ def isType = infoOrCompleter.isInstanceOf[TypeType]
+ }
+
+ class UniqueRefDenotation(
+ symbol: Symbol,
+ val infoOrCompleter: Type,
+ initValidFor: Period) extends NonSymSingleDenotation(symbol) {
+ validFor = initValidFor
+ override def hasUniqueSym: Boolean = true
+ protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = new UniqueRefDenotation(s, i, validFor)
+ }
+
+ class JointRefDenotation(
+ symbol: Symbol,
+ val infoOrCompleter: Type,
+ initValidFor: Period) extends NonSymSingleDenotation(symbol) {
+ validFor = initValidFor
+ override def hasUniqueSym = false
+ protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = new JointRefDenotation(s, i, validFor)
+ }
+
+ class ErrorDenotation(implicit ctx: Context) extends NonSymSingleDenotation(NoSymbol) {
+ override def exists = false
+ override def hasUniqueSym = false
+ def infoOrCompleter = NoType
+ validFor = Period.allInRun(ctx.runId)
+ protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = this
+ }
+
+ /** An error denotation that provides more info about the missing reference.
+ * Produced by staticRef, consumed by requiredSymbol.
+ */
+ case class MissingRef(val owner: SingleDenotation, name: Name)(implicit ctx: Context) extends ErrorDenotation {
+ val ex: Exception = new Exception
+ }
+
+ /** An error denotation that provides more info about alternatives
+ * that were found but that do not qualify.
+ * Produced by staticRef, consumed by requiredSymbol.
+ */
+ case class NoQualifyingRef(alts: List[SingleDenotation])(implicit ctx: Context) extends ErrorDenotation
+
+ /** A double definition
+ */
+ def isDoubleDef(sym1: Symbol, sym2: Symbol)(implicit ctx: Context): Boolean =
+ (sym1.exists && sym2.exists &&
+ (sym1 ne sym2) && (sym1.owner eq sym2.owner) &&
+ !sym1.is(Bridge) && !sym2.is(Bridge))
+
+ def doubleDefError(denot1: Denotation, denot2: Denotation, pre: Type = NoPrefix)(implicit ctx: Context): Nothing = {
+ val sym1 = denot1.symbol
+ val sym2 = denot2.symbol
+ def fromWhere = if (pre == NoPrefix) "" else i"\nwhen seen as members of $pre"
+ throw new MergeError(
+ i"""cannot merge
+ | $sym1: ${sym1.info} and
+ | $sym2: ${sym2.info};
+ |they are both defined in ${sym1.owner} but have matching signatures
+ | ${denot1.info} and
+ | ${denot2.info}$fromWhere""",
+ denot2.info, denot2.info)
+ }
+
+ // --------------- PreDenotations -------------------------------------------------
+
+ /** A PreDenotation represents a group of single denotations
+ * It is used as an optimization to avoid forming MultiDenotations too eagerly.
+ */
+ trait PreDenotation {
+
+ /** A denotation in the group exists */
+ def exists: Boolean
+
+ /** First/last denotation in the group */
+ def first: Denotation
+ def last: Denotation
+
+ /** Convert to full denotation by &-ing all elements */
+ def toDenot(pre: Type)(implicit ctx: Context): Denotation
+
+ /** Group contains a denotation that refers to given symbol */
+ def containsSym(sym: Symbol): Boolean
+
+ /** Group contains a denotation with given signature */
+ def matches(other: SingleDenotation)(implicit ctx: Context): Boolean
+
+ /** Keep only those denotations in this group which satisfy predicate `p`. */
+ def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation
+
+ /** Keep only those denotations in this group which have a signature
+ * that's not already defined by `denots`.
+ */
+ def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation
+
+ /** Keep only those inherited members M of this predenotation for which the following is true
+ * - M is not marked Private
+ * - If M has a unique symbol, it does not appear in `prevDenots`.
+ * - M's signature as seen from prefix `pre` does not appear in `ownDenots`
+ * Return the denotation as seen from `pre`.
+ * Called from SymDenotations.computeMember. There, `ownDenots` are the denotations found in
+ * the base class, which shadow any inherited denotations with the same signature.
+ * `prevDenots` are the denotations that are defined in the class or inherited from
+ * a base type which comes earlier in the linearization.
+ */
+ def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation
+
+ /** Keep only those denotations in this group whose flags do not intersect
+ * with `excluded`.
+ */
+ def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation
+
+ private var cachedPrefix: Type = _
+ private var cachedAsSeenFrom: AsSeenFromResult = _
+ private var validAsSeenFrom: Period = Nowhere
+ type AsSeenFromResult <: PreDenotation
+
+ /** The denotation with info(s) as seen from prefix type */
+ final def asSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult =
+ if (Config.cacheAsSeenFrom) {
+ if ((cachedPrefix ne pre) || ctx.period != validAsSeenFrom) {
+ cachedAsSeenFrom = computeAsSeenFrom(pre)
+ cachedPrefix = pre
+ validAsSeenFrom = ctx.period
+ }
+ cachedAsSeenFrom
+ } else computeAsSeenFrom(pre)
+
+ protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): AsSeenFromResult
+
+ /** The union of two groups. */
+ def union(that: PreDenotation) =
+ if (!this.exists) that
+ else if (!that.exists) this
+ else DenotUnion(this, that)
+ }
+
+ final case class DenotUnion(denots1: PreDenotation, denots2: PreDenotation) extends PreDenotation {
+ assert(denots1.exists && denots2.exists, s"Union of non-existing denotations ($denots1) and ($denots2)")
+ def exists = true
+ def first = denots1.first
+ def last = denots2.last
+ def toDenot(pre: Type)(implicit ctx: Context) =
+ (denots1 toDenot pre) & (denots2 toDenot pre, pre)
+ def containsSym(sym: Symbol) =
+ (denots1 containsSym sym) || (denots2 containsSym sym)
+ def matches(other: SingleDenotation)(implicit ctx: Context): Boolean =
+ denots1.matches(other) || denots2.matches(other)
+ def filterWithPredicate(p: SingleDenotation => Boolean): PreDenotation =
+ derivedUnion(denots1 filterWithPredicate p, denots2 filterWithPredicate p)
+ def filterDisjoint(denots: PreDenotation)(implicit ctx: Context): PreDenotation =
+ derivedUnion(denots1 filterDisjoint denots, denots2 filterDisjoint denots)
+ def mapInherited(ownDenots: PreDenotation, prevDenots: PreDenotation, pre: Type)(implicit ctx: Context): PreDenotation =
+ derivedUnion(denots1.mapInherited(ownDenots, prevDenots, pre), denots2.mapInherited(ownDenots, prevDenots, pre))
+ def filterExcluded(excluded: FlagSet)(implicit ctx: Context): PreDenotation =
+ derivedUnion(denots1.filterExcluded(excluded), denots2.filterExcluded(excluded))
+
+ type AsSeenFromResult = PreDenotation
+ protected def computeAsSeenFrom(pre: Type)(implicit ctx: Context): PreDenotation =
+ derivedUnion(denots1.asSeenFrom(pre), denots2.asSeenFrom(pre))
+ private def derivedUnion(denots1: PreDenotation, denots2: PreDenotation) =
+ if ((denots1 eq this.denots1) && (denots2 eq this.denots2)) this
+ else denots1 union denots2
+ }
+
+ // --------------- Context Base Trait -------------------------------
+
+ trait DenotationsBase { this: ContextBase =>
+
+ /** The current denotation of the static reference given by path,
+ * or a MissingRef or NoQualifyingRef instance, if it does not exist.
+ * if generateStubs is set, generates stubs for missing top-level symbols
+ */
+ def staticRef(path: Name, generateStubs: Boolean = true)(implicit ctx: Context): Denotation = {
+ def recur(path: Name, len: Int): Denotation = {
+ val point = path.lastIndexOf('.', len - 1)
+ val owner =
+ if (point > 0) recur(path.toTermName, point).disambiguate(_.info.isParameterless)
+ else if (path.isTermName) defn.RootClass.denot
+ else defn.EmptyPackageClass.denot
+ if (owner.exists) {
+ val name = path slice (point + 1, len)
+ val result = owner.info.member(name)
+ if (result ne NoDenotation) result
+ else {
+ val alt =
+ if (generateStubs) missingHook(owner.symbol.moduleClass, name)
+ else NoSymbol
+ if (alt.exists) alt.denot
+ else MissingRef(owner, name)
+ }
+ }
+ else owner
+ }
+ recur(path, path.length)
+ }
+
+ /** If we are looking for a non-existing term name in a package,
+ * assume it is a package for which we do not have a directory and
+ * enter it.
+ */
+ def missingHook(owner: Symbol, name: Name)(implicit ctx: Context): Symbol =
+ if ((owner is Package) && name.isTermName)
+ ctx.newCompletePackageSymbol(owner, name.asTermName).entered
+ else
+ NoSymbol
+ }
+
+ /** An exception for accessing symbols that are no longer valid in current run */
+ class StaleSymbol(msg: => String) extends Exception {
+ util.Stats.record("stale symbol")
+ override def getMessage() = msg
+ }
+} \ No newline at end of file