diff options
Diffstat (limited to 'src/dotty/tools/dotc/core/Denotations.scala')
-rw-r--r-- | src/dotty/tools/dotc/core/Denotations.scala | 1217 |
1 files changed, 0 insertions, 1217 deletions
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala deleted file mode 100644 index 6a39c5787..000000000 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ /dev/null @@ -1,1217 +0,0 @@ -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 |