package dotty.tools package dotc package core import SymDenotations.{ SymDenotation, ClassDenotation, NoDenotation } import Contexts.{Context, ContextBase} import Names._ import NameOps._ import NameKinds._ 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.paramInfoss == 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 parameter names of lambda types. If names in corresponding positions match, keep them, * otherwise generate new synthetic names. */ private def mergeParamNames(tp1: LambdaType, tp2: LambdaType): List[tp1.ThisName] = (for ((name1, name2, idx) <- (tp1.paramNames, tp2.paramNames, tp1.paramNames.indices).zipped) yield if (name1 == name2) name1 else tp1.companion.syntheticParamName(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: MethodOrPoly => tp2 match { case tp2: MethodOrPoly if ctx.typeComparer.matchingParams(tp1, tp2) && tp1.isImplicit == tp2.isImplicit => tp1.derivedLambdaType( mergeParamNames(tp1, tp2), tp1.paramInfos, infoMeet(tp1.resultType, tp2.resultType.subst(tp2, tp1))) case _ => 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: MethodOrPoly => tp2 match { case tp2: MethodOrPoly if ctx.typeComparer.matchingParams(tp1, tp2) && tp1.isImplicit == tp2.isImplicit => tp1.derivedLambdaType( mergeParamNames(tp1, tp2), tp1.paramInfos, 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(" ") 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 } /** The next defined denotation (following `nextInRun`) or an arbitrary * undefined denotation, if all denotations in a `nextinRun` cycle are * undefined. */ private def nextDefined: SingleDenotation = { var p1 = this var p2 = nextInRun while (p1.validFor == Nowhere && (p1 ne p2)) { p1 = p1.nextInRun p2 = p2.nextInRun.nextInRun } p1 } /** 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. val nxt = nextDefined if (nxt.validFor != Nowhere) return nxt assert(false, this) } 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 old.nextInRun = this } 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"" 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 select(prefix: Denotation, selector: Name): Denotation = { val owner = prefix.disambiguate(_.info.isParameterless) if (owner.exists) { val result = owner.info.member(selector) if (result.exists) result else { val alt = if (generateStubs) missingHook(owner.symbol.moduleClass, selector) else NoSymbol if (alt.exists) alt.denot else MissingRef(owner, selector) } } else owner } def recur(path: Name, wrap: TermName => Name = identity): Denotation = path match { case path: TypeName => recur(path.toTermName, n => n.toTypeName) case ModuleClassName(underlying) => recur(underlying, n => wrap(ModuleClassName(n))) case QualifiedName(prefix, selector) => select(recur(prefix), wrap(selector)) case qn @ AnyQualifiedName(prefix, _) => recur(prefix, n => wrap(qn.info.mkString(n).toTermName)) case path: SimpleTermName => def recurSimple(len: Int, wrap: TermName => Name): Denotation = { val point = path.lastIndexOf('.', len - 1) val selector = wrap(path.slice(point + 1, len).asTermName) val prefix = if (point > 0) recurSimple(point, identity) else if (selector.isTermName) defn.RootClass.denot else defn.EmptyPackageClass.denot select(prefix, selector) } recurSimple(path.length, wrap) } recur(path) } /** 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 } }