All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.tools.dotc.core.Symbols.scala Maven / Gradle / Ivy

The newest version!
package dotty.tools
package dotc
package core

import Periods._
import Names._
import Scopes._
import Flags._
import Decorators._
import Symbols._
import Contexts._
import SymDenotations._
import printing.Texts._
import printing.Printer
import Types._
import util.Spans._
import DenotTransformers._
import StdNames._
import NameOps._
import NameKinds.LazyImplicitName
import ast.tpd
import tpd.{Tree, TreeProvider, TreeOps}
import ast.TreeTypeMap
import Constants.Constant
import reporting.diagnostic.Message
import collection.mutable
import io.AbstractFile
import language.implicitConversions
import util.{SourceFile, NoSource, Property, SourcePosition}
import scala.collection.JavaConverters._
import scala.annotation.internal.sharable
import config.Printers.typr

/** Creation methods for symbols */
trait Symbols { this: Context =>

// ---- Factory methods for symbol creation ----------------------
//
// All symbol creations should be done via the next two methods.

  /** Create a symbol without a denotation.
   *  Note this uses a cast instead of a direct type refinement because
   *  it's debug-friendlier not to create an anonymous class here.
   */
  def newNakedSymbol[N <: Name](coord: Coord = NoCoord)(implicit ctx: Context): Symbol { type ThisName = N } =
    new Symbol(coord, ctx.nextSymId).asInstanceOf[Symbol { type ThisName = N }]

  /** Create a class symbol without a denotation. */
  def newNakedClassSymbol(coord: Coord = NoCoord, assocFile: AbstractFile = null)(implicit ctx: Context): ClassSymbol =
    new ClassSymbol(coord, assocFile, ctx.nextSymId)

// ---- Symbol creation methods ----------------------------------

  /** Create a symbol from its fields (info may be lazy) */
  def newSymbol[N <: Name](
      owner: Symbol,
      name: N,
      flags: FlagSet,
      info: Type,
      privateWithin: Symbol = NoSymbol,
      coord: Coord = NoCoord): Symbol { type ThisName = N } = {
    val sym = newNakedSymbol[N](coord)
    val denot = SymDenotation(sym, owner, name, flags, info, privateWithin)
    sym.denot = denot
    sym
  }

  /** Create a class symbol from a function producing its denotation */
  def newClassSymbolDenoting(denotFn: ClassSymbol => SymDenotation, coord: Coord = NoCoord, assocFile: AbstractFile = null): ClassSymbol = {
    val cls = newNakedClassSymbol(coord, assocFile)
    cls.denot = denotFn(cls)
    cls
  }

  /** Create a class symbol from its non-info fields and a function
   *  producing its info (the produced info may be lazy).
   */
  def newClassSymbol(
      owner: Symbol,
      name: TypeName,
      flags: FlagSet,
      infoFn: ClassSymbol => Type,
      privateWithin: Symbol = NoSymbol,
      coord: Coord = NoCoord,
      assocFile: AbstractFile = null): ClassSymbol
  = {
    val cls = newNakedClassSymbol(coord, assocFile)
    val denot = SymDenotation(cls, owner, name, flags, infoFn(cls), privateWithin)
    cls.denot = denot
    cls
  }

  /** Create a class symbol from its non-info fields and the fields of its info. */
  def newCompleteClassSymbol(
      owner: Symbol,
      name: TypeName,
      flags: FlagSet,
      parents: List[TypeRef],
      decls: Scope = newScope,
      selfInfo: Type = NoType,
      privateWithin: Symbol = NoSymbol,
      coord: Coord = NoCoord,
      assocFile: AbstractFile = null): ClassSymbol =
    newClassSymbol(
        owner, name, flags,
        ClassInfo(owner.thisType, _, parents, decls, selfInfo),
        privateWithin, coord, assocFile)

  /** Same as `newCompleteClassSymbol` except that `parents` can be a list of arbitrary
   *  types which get normalized into type refs and parameter bindings.
   */
  def newNormalizedClassSymbol(
      owner: Symbol,
      name: TypeName,
      flags: FlagSet,
      parentTypes: List[Type],
      decls: Scope = newScope,
      selfInfo: Type = NoType,
      privateWithin: Symbol = NoSymbol,
      coord: Coord = NoCoord,
      assocFile: AbstractFile = null): ClassSymbol = {
    def completer = new LazyType {
      def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
        val cls = denot.asClass.classSymbol
        val decls = newScope
        denot.info = ClassInfo(owner.thisType, cls, parentTypes.map(_.dealias), decls)
      }
    }
    newClassSymbol(owner, name, flags, completer, privateWithin, coord, assocFile)
  }

  def newRefinedClassSymbol(coord: Coord = NoCoord): ClassSymbol =
    newCompleteClassSymbol(ctx.owner, tpnme.REFINE_CLASS, NonMember, parents = Nil, coord = coord)

  /** Create a module symbol with associated module class
   *  from its non-info fields and a function producing the info
   *  of the module class (this info may be lazy).
   */
  def newModuleSymbol(
      owner: Symbol,
      name: TermName,
      modFlags: FlagSet,
      clsFlags: FlagSet,
      infoFn: (TermSymbol, ClassSymbol) => Type, // typically a ModuleClassCompleterWithDecls
      privateWithin: Symbol = NoSymbol,
      coord: Coord = NoCoord,
      assocFile: AbstractFile = null): TermSymbol
  = {
    val base = owner.thisType
    val module = newNakedSymbol[TermName](coord)
    val modcls = newNakedClassSymbol(coord, assocFile)
    val modclsFlags = clsFlags | ModuleClassCreationFlags
    val modclsName = name.toTypeName.adjustIfModuleClass(modclsFlags)
    val cdenot = SymDenotation(
        modcls, owner, modclsName, modclsFlags,
        infoFn(module, modcls), privateWithin)
    val mdenot = SymDenotation(
        module, owner, name, modFlags | ModuleValCreationFlags,
        if (cdenot.isCompleted) TypeRef(owner.thisType, modcls)
        else new ModuleCompleter(modcls))
    module.denot = mdenot
    modcls.denot = cdenot
    module
  }

  /** Create a module symbol with associated module class
   *  from its non-info fields and the fields of the module class info.
   *  @param flags  The combined flags of the module and the module class
   *                These are masked with RetainedModuleValFlags/RetainedModuleClassFlags.
   */
  def newCompleteModuleSymbol(
      owner: Symbol,
      name: TermName,
      modFlags: FlagSet,
      clsFlags: FlagSet,
      parents: List[TypeRef],
      decls: Scope,
      privateWithin: Symbol = NoSymbol,
      coord: Coord = NoCoord,
      assocFile: AbstractFile = null): TermSymbol =
    newModuleSymbol(
        owner, name, modFlags, clsFlags,
        (module, modcls) => ClassInfo(
          owner.thisType, modcls, parents, decls, TermRef(owner.thisType, module)),
        privateWithin, coord, assocFile)

  val companionMethodFlags: FlagSet = Flags.Synthetic | Flags.Private | Flags.Method

  /** Create a package symbol with associated package class
   *  from its non-info fields and a lazy type for loading the package's members.
   */
  def newPackageSymbol(
      owner: Symbol,
      name: TermName,
      infoFn: (TermSymbol, ClassSymbol) => LazyType): TermSymbol =
    newModuleSymbol(owner, name, PackageCreationFlags, PackageCreationFlags, infoFn)

  /** Create a package symbol with associated package class
   *  from its non-info fields its member scope.
   */
  def newCompletePackageSymbol(
      owner: Symbol,
      name: TermName,
      modFlags: FlagSet = EmptyFlags,
      clsFlags: FlagSet = EmptyFlags,
      decls: Scope = newScope): TermSymbol =
    newCompleteModuleSymbol(
      owner, name,
      modFlags | PackageCreationFlags, clsFlags | PackageCreationFlags,
      Nil, decls)

  /** Define a new symbol associated with a Bind or pattern wildcard and, by default, make it gadt narrowable. */
  def newPatternBoundSymbol(name: Name, info: Type, span: Span, addToGadt: Boolean = true, flags: FlagSet = EmptyFlags): Symbol = {
    val sym = newSymbol(owner, name, Case | flags, info, coord = span)
    if (addToGadt && name.isTypeName) gadt.addToConstraint(sym)
    sym
  }

  /** Create a stub symbol that will issue a missing reference error
   *  when attempted to be completed.
   */
  def newStubSymbol(owner: Symbol, name: Name, file: AbstractFile = null): Symbol = {
    def stubCompleter = new StubInfo()
    val normalizedOwner = if (owner.is(ModuleVal)) owner.moduleClass else owner
    typr.println(s"creating stub for ${name.show}, owner = ${normalizedOwner.denot.debugString}, file = $file")
    typr.println(s"decls = ${normalizedOwner.unforcedDecls.toList.map(_.debugString).mkString("\n  ")}") // !!! DEBUG
    //if (base.settings.debug.value) throw new Error()
    val stub = name match {
      case name: TermName =>
        newModuleSymbol(normalizedOwner, name, EmptyFlags, EmptyFlags, stubCompleter, assocFile = file)
      case name: TypeName =>
        newClassSymbol(normalizedOwner, name, EmptyFlags, stubCompleter, assocFile = file)
    }
    stub
  }

  /** Create the local template dummy of given class `cls`.
   *  In a template
   *
   *     trait T { val fld: Int; { val x: int = 2 }; val fld2 = { val y = 2; y }}
   *
   *  the owner of `x` is the local dummy of the template. The owner of the local
   *  dummy is then the class of the template itself. By contrast, the owner of `y`
   *  would be `fld2`. There is a single local dummy per template.
   */
  def newLocalDummy(cls: Symbol, coord: Coord = NoCoord): TermSymbol =
    newSymbol(cls, nme.localDummyName(cls), NonMember, NoType)

  /** Create an import symbol pointing back to given qualifier `expr`. */
  def newImportSymbol(owner: Symbol, expr: Tree, coord: Coord = NoCoord): TermSymbol =
    newImportSymbol(owner, ImportType(expr), coord = coord)

  /** Create an import symbol with given `info`. */
  def newImportSymbol(owner: Symbol, info: Type, coord: Coord): TermSymbol =
    newSymbol(owner, nme.IMPORT, Synthetic | NonMember, info, coord = coord)

  /** Create a class constructor symbol for given class `cls`. */
  def newConstructor(cls: ClassSymbol, flags: FlagSet, paramNames: List[TermName], paramTypes: List[Type], privateWithin: Symbol = NoSymbol, coord: Coord = NoCoord): TermSymbol =
    newSymbol(cls, nme.CONSTRUCTOR, flags | Method, MethodType(paramNames, paramTypes, cls.typeRef), privateWithin, coord)

  /** Create an empty default constructor symbol for given class `cls`. */
  def newDefaultConstructor(cls: ClassSymbol): TermSymbol =
    newConstructor(cls, EmptyFlags, Nil, Nil)

  def newLazyImplicit(info: Type, coord: Coord = NoCoord): TermSymbol =
    newSymbol(owner, LazyImplicitName.fresh(), EmptyFlags, info, coord = coord)

  /** Create a symbol representing a selftype declaration for class `cls`. */
  def newSelfSym(cls: ClassSymbol, name: TermName = nme.WILDCARD, selfInfo: Type = NoType): TermSymbol =
    ctx.newSymbol(cls, name, SelfSymFlags, selfInfo orElse cls.classInfo.selfType, coord = cls.coord)

  /** Create new type parameters with given owner, names, and flags.
   *  @param boundsFn  A function that, given type refs to the newly created
   *                   parameters returns a list of their bounds.
   */
  def newTypeParams(
    owner: Symbol,
    names: List[TypeName],
    flags: FlagSet,
    boundsFn: List[TypeRef] => List[Type]): List[TypeSymbol] = {

    val tparamBuf = new mutable.ListBuffer[TypeSymbol]
    val trefBuf = new mutable.ListBuffer[TypeRef]
    for (name <- names) {
      val tparam = newNakedSymbol[TypeName](owner.coord)
      tparamBuf += tparam
      trefBuf += TypeRef(owner.thisType, tparam)
    }
    val tparams = tparamBuf.toList
    val bounds = boundsFn(trefBuf.toList)
    for ((name, tparam, bound) <- (names, tparams, bounds).zipped)
      tparam.denot = SymDenotation(tparam, owner, name, flags | owner.typeParamCreationFlags, bound)
    tparams
  }

  /** Create a new skolem symbol. This is not the same as SkolemType, even though the
   *  motivation (create a singleton referencing to a type) is similar.
   */
  def newSkolem(tp: Type): TermSymbol = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | NonMember | Permanent, tp)

  def newErrorSymbol(owner: Symbol, name: Name, msg: => Message): Symbol = {
    val errType = ErrorType(msg)
    newSymbol(owner, name, SyntheticArtifact,
        if (name.isTypeName) TypeAlias(errType) else errType)
  }

  /** Map given symbols, subjecting their attributes to the mappings
   *  defined in the given TreeTypeMap `ttmap`.
   *  Cross symbol references are brought over from originals to copies.
   *  Do not copy any symbols if all attributes of all symbols stay the same.
   */
  def mapSymbols(originals: List[Symbol], ttmap: TreeTypeMap, mapAlways: Boolean = false): List[Symbol] =
    if (originals.forall(sym =>
        (ttmap.mapType(sym.info) eq sym.info) &&
        !(ttmap.oldOwners contains sym.owner)) && !mapAlways)
      originals
    else {
      val copies: List[Symbol] = for (original <- originals) yield
        original match {
          case original: ClassSymbol =>
            newNakedClassSymbol(original.coord, original.assocFile)
          case _ =>
            newNakedSymbol[original.ThisName](original.coord)
        }
      val ttmap1 = ttmap.withSubstitution(originals, copies)
      (originals, copies).zipped foreach { (original, copy) =>
        val odenot = original.denot
        val oinfo = original.info match {
          case ClassInfo(pre, _, parents, decls, selfInfo) =>
            assert(original.isClass)
            ClassInfo(pre, copy.asClass, parents, decls.cloneScope, selfInfo)
          case oinfo => oinfo
        }

        val completer = new LazyType {
          def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
            denot.info = oinfo // needed as otherwise we won't be able to go from Sym -> parents & etc
                               // Note that this is a hack, but hack commonly used in Dotty
                               // The same thing is done by other completers all the time
            denot.info = ttmap1.mapType(oinfo)
            denot.annotations = odenot.annotations.mapConserve(ttmap1.apply)
          }
        }

        copy.denot = odenot.copySymDenotation(
          symbol = copy,
          owner = ttmap1.mapOwner(odenot.owner),
          initFlags = odenot.flags &~ Touched,
          info = completer,
          privateWithin = ttmap1.mapOwner(odenot.privateWithin), // since this refers to outer symbols, need not include copies (from->to) in ownermap here.
          annotations = odenot.annotations)
      }

      copies.foreach(_.ensureCompleted()) // avoid memory leak
      copies
    }

// ----- Locating predefined symbols ----------------------------------------

  def requiredPackage(path: PreName): TermSymbol = {
    val name = path.toTermName
    base.staticRef(name, isPackage = true).requiredSymbol("package", name)(_.is(Package)).asTerm
  }

  def requiredPackageRef(path: PreName): TermRef = requiredPackage(path).termRef

  def requiredClass(path: PreName): ClassSymbol = {
    val name = path.toTypeName
    base.staticRef(name).requiredSymbol("class", name)(_.isClass) match {
      case cls: ClassSymbol => cls
      case sym => defn.AnyClass
    }
  }

  def requiredClassRef(path: PreName): TypeRef = requiredClass(path).typeRef

  /** Get ClassSymbol if class is either defined in current compilation run
   *  or present on classpath.
   *  Returns NoSymbol otherwise. */
  def getClassIfDefined(path: PreName): Symbol = {
    val name = path.toTypeName
    base.staticRef(name, generateStubs = false)
      .requiredSymbol("class", name, generateStubs = false)(_.isClass)
  }

  /** Get ClassSymbol if package is either defined in current compilation run
   *  or present on classpath.
   *  Returns NoSymbol otherwise. */
  def getPackageClassIfDefined(path: PreName): Symbol = {
    val name = path.toTypeName
    base.staticRef(name, isPackage = true, generateStubs = false)
      .requiredSymbol("package", name, generateStubs = false)(_ is PackageClass)
  }

  def requiredModule(path: PreName): TermSymbol = {
    val name = path.toTermName
    base.staticRef(name).requiredSymbol("object", name)(_.is(Module)).asTerm
  }

  def requiredModuleRef(path: PreName): TermRef = requiredModule(path).termRef

  def requiredMethod(path: PreName): TermSymbol = {
    val name = path.toTermName
    base.staticRef(name).requiredSymbol("method", name)(_.is(Method)).asTerm
  }

  def requiredMethodRef(path: PreName): TermRef = requiredMethod(path).termRef
}

object Symbols {

  implicit def eqSymbol: Eql[Symbol, Symbol] = Eql.derived

  /** Tree attachment containing the identifiers in a tree as a sorted array */
  val Ids: Property.Key[Array[String]] = new Property.Key

  /** A Symbol represents a Scala definition/declaration or a package.
   *  @param coord  The coordinates of the symbol (a position or an index)
   *  @param id     A unique identifier of the symbol (unique per ContextBase)
   */
  class Symbol private[Symbols] (private[this] var myCoord: Coord, val id: Int)
    extends Designator with ParamInfo with printing.Showable {

    type ThisName <: Name

    //assert(id != 723)

    def coord: Coord = myCoord
    /** Set the coordinate of this class, this is only useful when the coordinate is
     *  not known at symbol creation. This is the case for root symbols
     *  unpickled from TASTY.
     *
     *  @pre coord == NoCoord
     */
    private[core] def coord_=(c: Coord): Unit = {
      assert(myCoord == NoCoord)
      myCoord = c
    }

    private[this] var myDefTree: Tree = null

    /** The tree defining the symbol at pickler time, EmptyTree if none was retained */
    def defTree: Tree =
      if (myDefTree == null) tpd.EmptyTree else myDefTree

    /** Set defining tree if this symbol retains its definition tree */
    def defTree_=(tree: Tree)(implicit ctx: Context): Unit =
      if (retainsDefTree) myDefTree = tree

    /** Does this symbol retain its definition tree?
     *  A good policy for this needs to balance costs and benefits, where
     *  costs are mainly memoty leaks, in particular across runs.
     */
    def retainsDefTree(implicit ctx: Context): Boolean =
      ctx.settings.YretainTrees.value ||
      denot.owner.isTerm ||        // no risk of leaking memory after a run for these
      denot.isOneOf(InlineOrProxy)      // need to keep inline info

    /** The last denotation of this symbol */
    private[this] var lastDenot: SymDenotation = _
    private[this] var checkedPeriod: Period = Nowhere

    private[core] def invalidateDenotCache(): Unit = { checkedPeriod = Nowhere }

    /** Set the denotation of this symbol */
    private[core] def denot_=(d: SymDenotation): Unit = {
      lastDenot = d
      checkedPeriod = Nowhere
    }

    /** The current denotation of this symbol */
    final def denot(implicit ctx: Context): SymDenotation = {
      val lastd = lastDenot
      if (checkedPeriod == ctx.period) lastd
      else computeDenot(lastd)
    }

    private def computeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = {
      val now = ctx.period
      checkedPeriod = now
      if (lastd.validFor contains now) lastd else recomputeDenot(lastd)
    }

    /** Overridden in NoSymbol */
    protected def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = {
      val newd = lastd.current.asInstanceOf[SymDenotation]
      lastDenot = newd
      newd
    }

    /** The original denotation of this symbol, without forcing anything */
    final def originDenotation: SymDenotation =
      lastDenot.initial

    /** The last known denotation of this symbol, without going through `current` */
    final def lastKnownDenotation: SymDenotation =
      lastDenot

    private[core] def defRunId: RunId =
      if (lastDenot == null) NoRunId else lastDenot.validFor.runId

    /** Does this symbol come from a currently compiled source file? */
    final def isDefinedInCurrentRun(implicit ctx: Context): Boolean =
      span.exists && defRunId == ctx.runId && {
        val file = associatedFile
        file != null && ctx.run.files.contains(file)
      }

    /** Is symbol valid in current run? */
    final def isValidInCurrentRun(implicit ctx: Context): Boolean =
      (lastDenot.validFor.runId == ctx.runId || ctx.stillValid(lastDenot)) &&
      (lastDenot.symbol eq this)
        // the last condition is needed because under ctx.staleOK overwritten
        // members keep denotations pointing to the new symbol, so the validity
        // periods check out OK. But once a package member is overridden it is not longer
        // valid. If the option would be removed, the check would be no longer needed.

    final def isTerm(implicit ctx: Context): Boolean =
      (if (defRunId == ctx.runId) lastDenot else denot).isTerm
    final def isType(implicit ctx: Context): Boolean =
      (if (defRunId == ctx.runId) lastDenot else denot).isType
    final def asTerm(implicit ctx: Context): TermSymbol = {
      assert(isTerm, s"asTerm called on not-a-Term $this" );
      asInstanceOf[TermSymbol]
    }
    final def asType(implicit ctx: Context): TypeSymbol = {
      assert(isType, s"isType called on not-a-Type $this");
      asInstanceOf[TypeSymbol]
    }

    final def isClass: Boolean = isInstanceOf[ClassSymbol]
    final def asClass: ClassSymbol = asInstanceOf[ClassSymbol]

    /** Test whether symbol is private. This
     *  conservatively returns `false` if symbol does not yet have a denotation, or denotation
     *  is a class that is not yet read.
     */
    final def isPrivate(implicit ctx: Context): Boolean = {
      val d = lastDenot
      d != null && d.flagsUNSAFE.is(Private)
    }

    /** The symbol's signature if it is completed or a method, NotAMethod otherwise. */
    final def signature(implicit ctx: Context): Signature =
      if (lastDenot != null && (lastDenot.isCompleted || lastDenot.is(Method)))
        denot.signature
      else
        Signature.NotAMethod

    /** Special cased here, because it may be used on naked symbols in substituters */
    final def isStatic(implicit ctx: Context): Boolean =
      lastDenot != null && lastDenot.initial.isStatic

    /** This symbol entered into owner's scope (owner must be a class). */
    final def entered(implicit ctx: Context): this.type = {
      assert(this.owner.isClass, s"symbol ($this) entered the scope of non-class owner ${this.owner}") // !!! DEBUG
      this.owner.asClass.enter(this)
      if (this.is(Module)) this.owner.asClass.enter(this.moduleClass)
      this
    }

    /** Enter this symbol in its class owner after given `phase`. Create a fresh
     *  denotation for its owner class if the class has not yet already one
     *  that starts being valid after `phase`.
     *  @pre  Symbol is a class member
     */
    def enteredAfter(phase: DenotTransformer)(implicit ctx: Context): this.type =
      if (ctx.phaseId != phase.next.id) enteredAfter(phase)(ctx.withPhase(phase.next))
      else {
        if (this.owner.is(Package)) {
          denot.validFor |= InitialPeriod
          if (this.is(Module)) this.moduleClass.validFor |= InitialPeriod
        }
        else this.owner.asClass.ensureFreshScopeAfter(phase)
        assert(isPrivate || phase.changesMembers, i"$this entered in ${this.owner} at undeclared phase $phase")
        entered
      }

    /** Remove symbol from scope of owning class */
    final def drop()(implicit ctx: Context): Unit = {
      this.owner.asClass.delete(this)
      if (this.is(Module)) this.owner.asClass.delete(this.moduleClass)
    }

    /** Remove symbol from scope of owning class after given `phase`. Create a fresh
     *  denotation for its owner class if the class has not yet already one that starts being valid after `phase`.
     *  @pre  Symbol is a class member
     */
    def dropAfter(phase: DenotTransformer)(implicit ctx: Context): Unit =
      if (ctx.phaseId != phase.next.id) dropAfter(phase)(ctx.withPhase(phase.next))
      else {
        assert (!this.owner.is(Package))
        this.owner.asClass.ensureFreshScopeAfter(phase)
        assert(isPrivate || phase.changesMembers, i"$this deleted in ${this.owner} at undeclared phase $phase")
        drop()
      }

    /** This symbol, if it exists, otherwise the result of evaluating `that` */
    def orElse(that: => Symbol)(implicit ctx: Context): Symbol =
      if (this.exists) this else that

    /** If this symbol satisfies predicate `p` this symbol, otherwise `NoSymbol` */
    def filter(p: Symbol => Boolean): Symbol = if (p(this)) this else NoSymbol

    /** The current name of this symbol */
    final def name(implicit ctx: Context): ThisName = denot.name.asInstanceOf[ThisName]

    /** The source or class file from which this class or
     *  the class containing this symbol was generated, null if not applicable.
     *  Overridden in ClassSymbol
     */
    def associatedFile(implicit ctx: Context): AbstractFile =
      if (lastDenot == null) null else lastDenot.topLevelClass.associatedFile

    /** The class file from which this class was generated, null if not applicable. */
    final def binaryFile(implicit ctx: Context): AbstractFile = {
      val file = associatedFile
      if (file != null && file.extension == "class") file else null
    }

    /** A trap to avoid calling x.symbol on something that is already a symbol.
     *  This would be expanded to `toDenot(x).symbol` which is guaraneteed to be
     *  the same as `x`.
     *  With the given setup, all such calls will give implicit-not found errors
     */
    final def symbol(implicit ev: DontUseSymbolOnSymbol): Nothing = unsupported("symbol")
    type DontUseSymbolOnSymbol

    final def source(implicit ctx: Context): SourceFile = {
      def valid(src: SourceFile): SourceFile =
        if (src.exists && src.file.extension != "class") src
        else NoSource

      if (!denot.exists) NoSource
      else
        valid(defTree.source) match {
          case NoSource =>
            valid(denot.owner.source) match {
              case NoSource =>
                this match {
                  case cls: ClassSymbol      => valid(cls.sourceOfClass)
                  case _ if denot.is(Module) => valid(denot.moduleClass.source)
                  case _ => NoSource
                }
              case src => src
            }
          case src => src
        }
    }

    /** A symbol related to `sym` that is defined in source code.
     *
     *  @see enclosingSourceSymbols
     */
    @annotation.tailrec final def sourceSymbol(implicit ctx: Context): Symbol =
      if (!denot.exists)
        this
      else if (denot.is(ModuleVal))
        this.moduleClass.sourceSymbol // The module val always has a zero-extent position
      else if (denot.is(Synthetic)) {
        val linked = denot.linkedClass
        if (linked.exists && !linked.is(Synthetic))
          linked
        else
          denot.owner.sourceSymbol
      }
      else if (denot.isPrimaryConstructor)
        denot.owner.sourceSymbol
      else this

    /** The position of this symbol, or NoSpan if the symbol was not loaded
     *  from source or from TASTY. This is always a zero-extent position.
     */
    final def span: Span = if (coord.isSpan) coord.toSpan else NoSpan

    final def sourcePos(implicit ctx: Context): SourcePosition = {
      val src = source
      (if (src.exists) src else ctx.source).atSpan(span)
    }

    // ParamInfo types and methods
    def isTypeParam(implicit ctx: Context): Boolean = denot.is(TypeParam)
    def paramName(implicit ctx: Context): ThisName = name.asInstanceOf[ThisName]
    def paramInfo(implicit ctx: Context): Type = denot.info
    def paramInfoAsSeenFrom(pre: Type)(implicit ctx: Context): Type = pre.memberInfo(this)
    def paramInfoOrCompleter(implicit ctx: Context): Type = denot.infoOrCompleter
    def paramVariance(implicit ctx: Context): Int = denot.variance
    def paramRef(implicit ctx: Context): TypeRef = denot.typeRef

// -------- Printing --------------------------------------------------------

    /** The prefix string to be used when displaying this symbol without denotation */
    protected def prefixString: String = "Symbol"

    override def toString: String =
      if (lastDenot == null) s"Naked$prefixString#$id"
      else lastDenot.toString// + "#" + id // !!! DEBUG

    def toText(printer: Printer): Text = printer.toText(this)

    def showLocated(implicit ctx: Context): String = ctx.printer.locatedText(this).show
    def showExtendedLocation(implicit ctx: Context): String = ctx.printer.extendedLocationText(this).show
    def showDcl(implicit ctx: Context): String = ctx.printer.dclText(this).show
    def showKind(implicit ctx: Context): String = ctx.printer.kindString(this)
    def showName(implicit ctx: Context): String = ctx.printer.nameString(this)
    def showFullName(implicit ctx: Context): String = ctx.printer.fullNameString(this)

    override def hashCode(): Int = id // for debugging.
  }

  type TermSymbol = Symbol { type ThisName = TermName }
  type TypeSymbol = Symbol { type ThisName = TypeName }

  class ClassSymbol private[Symbols] (coord: Coord, val assocFile: AbstractFile, id: Int)
    extends Symbol(coord, id) {

    type ThisName = TypeName

    type TreeOrProvider = tpd.TreeProvider | tpd.Tree

    private[this] var myTree: TreeOrProvider = tpd.EmptyTree

    /** If this is a top-level class and `-Yretain-trees` (or `-from-tasty`) is set.
      * Returns the TypeDef tree (possibly wrapped inside PackageDefs) for this class, otherwise EmptyTree.
      * This will force the info of the class.
      */
    def rootTree(implicit ctx: Context): Tree = rootTreeContaining("")

    /** Same as `tree` but load tree only if `id == ""` or the tree might contain `id`.
     *  For Tasty trees this means consulting whether the name table defines `id`.
     *  For already loaded trees, we maintain the referenced ids in an attachment.
     */
    def rootTreeContaining(id: String)(implicit ctx: Context): Tree = {
      denot.infoOrCompleter match {
        case _: NoCompleter =>
        case _ => denot.ensureCompleted()
      }
      myTree match {
        case fn: TreeProvider =>
          if (id.isEmpty || fn.mightContain(id)) {
            val tree = fn.tree
            myTree = tree
            tree
          }
          else tpd.EmptyTree
        case tree: Tree @ unchecked =>
          if (id.isEmpty || mightContain(tree, id)) tree else tpd.EmptyTree
      }
    }

    def rootTreeOrProvider: TreeOrProvider = myTree

    private[dotc] def rootTreeOrProvider_=(t: TreeOrProvider)(implicit ctx: Context): Unit =
      myTree = t

    private def mightContain(tree: Tree, id: String)(implicit ctx: Context): Boolean = {
      val ids = tree.getAttachment(Ids) match {
        case Some(ids) => ids
        case None =>
          val idSet = mutable.SortedSet[String]()
          tree.foreachSubTree {
            case tree: tpd.NameTree if tree.name.toTermName.isInstanceOf[SimpleName] =>
              idSet += tree.name.toString
            case _ =>
          }
          val ids = idSet.toArray
          tree.putAttachment(Ids, ids)
          ids
      }
      ids.binarySearch(id) >= 0
    }

    /** The source or class file from which this class was generated, null if not applicable. */
    override def associatedFile(implicit ctx: Context): AbstractFile =
      if (assocFile != null || this.owner.is(PackageClass) || this.isEffectiveRoot) assocFile
      else super.associatedFile

    private[this] var mySource: SourceFile = NoSource

    final def sourceOfClass(implicit ctx: Context): SourceFile = {
      if (!mySource.exists && !denot.is(Package))
        // this allows sources to be added in annotations after `sourceOfClass` is first called
        mySource = {
          val file = associatedFile
          if (file != null && file.extension != "class") ctx.getSource(file)
          else {
            def sourceFromTopLevel(implicit ctx: Context) =
              denot.topLevelClass.unforcedAnnotation(defn.SourceFileAnnot) match {
                case Some(sourceAnnot) => sourceAnnot.argumentConstant(0) match {
                  case Some(Constant(path: String)) => ctx.getSource(path)
                  case none => NoSource
                }
                case none => NoSource
              }
            sourceFromTopLevel(ctx.withPhaseNoLater(ctx.flattenPhase))
          }
        }//.reporting(i"source of $this # $id in ${denot.owner} = $result")
      mySource
    }

    final def classDenot(implicit ctx: Context): ClassDenotation =
      denot.asInstanceOf[ClassDenotation]

    override protected def prefixString: String = "ClassSymbol"
  }

  @sharable object NoSymbol extends Symbol(NoCoord, 0) {
    override def associatedFile(implicit ctx: Context): AbstractFile = NoSource.file
    override def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = NoDenotation
  }

  NoDenotation // force it in order to set `denot` field of NoSymbol

  implicit class Copier[N <: Name](sym: Symbol { type ThisName = N })(implicit ctx: Context) {
    /** Copy a symbol, overriding selective fields.
     *  Note that `coord` and `associatedFile` will be set from the fields in `owner`, not
     *  the fields in `sym`.
     */
    def copy(
        owner: Symbol = sym.owner,
        name: N = sym.name,
        flags: FlagSet = sym.flags,
        info: Type = sym.info,
        privateWithin: Symbol = sym.privateWithin,
        coord: Coord = NoCoord, // Can be `= owner.coord` once we boostrap
        associatedFile: AbstractFile = null // Can be `= owner.associatedFile` once we boostrap
    ): Symbol = {
      val coord1 = if (coord == NoCoord) owner.coord else coord
      val associatedFile1 = if (associatedFile == null) owner.associatedFile else associatedFile

      if (sym.isClass)
        ctx.newClassSymbol(owner, name.asTypeName, flags, _ => info, privateWithin, coord1, associatedFile1)
      else
        ctx.newSymbol(owner, name, flags, info, privateWithin, coord1)
    }
  }

  /** Makes all denotation operations available on symbols */
  implicit def toDenot(sym: Symbol)(implicit ctx: Context): SymDenotation = sym.denot

  /** Makes all class denotation operations available on class symbols */
  implicit def toClassDenot(cls: ClassSymbol)(implicit ctx: Context): ClassDenotation = cls.classDenot

  /** The Definitions object */
  def defn(implicit ctx: Context): Definitions = ctx.definitions

  /** The current class */
  def currentClass(implicit ctx: Context): ClassSymbol = ctx.owner.enclosingClass.asClass

  /* Mutable map from symbols any T */
  class MutableSymbolMap[T](private[Symbols] val value: java.util.IdentityHashMap[Symbol, T]) extends AnyVal {

    def apply(sym: Symbol): T = value.get(sym)

    def get(sym: Symbol): Option[T] = Option(value.get(sym))

    def getOrElse[U >: T](sym: Symbol, default: => U): U = {
      val v = value.get(sym)
      if (v != null) v else default
    }

    def getOrElseUpdate(sym: Symbol, op: => T): T = {
      val v = value.get(sym)
      if (v != null) v
      else {
        val v = op
        assert(v != null)
        value.put(sym, v)
        v
      }
    }

    def update(sym: Symbol, x: T): Unit = {
      assert(x != null)
      value.put(sym, x)
    }
    def put(sym: Symbol, x: T): T = {
      assert(x != null)
      value.put(sym, x)
    }

    def -=(sym: Symbol): Unit = value.remove(sym)
    def remove(sym: Symbol): Option[T] = Option(value.remove(sym))

    def contains(sym: Symbol): Boolean = value.containsKey(sym)

    def isEmpty: Boolean = value.isEmpty

    def clear(): Unit = value.clear()

    def filter(p: ((Symbol, T)) => Boolean): Map[Symbol, T] =
      value.asScala.toMap.filter(p)

    def iterator: Iterator[(Symbol, T)] = value.asScala.iterator

    def keysIterator: Iterator[Symbol] = value.keySet().asScala.iterator

    def toMap: Map[Symbol, T] = value.asScala.toMap

    override def toString: String = value.asScala.toString()
  }

  @forceInline def newMutableSymbolMap[T]: MutableSymbolMap[T] =
    new MutableSymbolMap(new java.util.IdentityHashMap[Symbol, T]())
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy