dotty.tools.dotc.core.SymDenotations.scala Maven / Gradle / Ivy
The newest version!
package dotty.tools
package dotc
package core
import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._
import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._
import NameOps._, NameKinds._, Phases._
import Constants.Constant
import TypeApplications.TypeParamInfo
import Scopes.Scope
import dotty.tools.io.AbstractFile
import Decorators.SymbolIteratorDecorator
import ast._
import Trees.Literal
import annotation.tailrec
import util.SimpleIdentityMap
import util.Stats
import java.util.WeakHashMap
import config.Config
import reporting.diagnostic.Message
import reporting.diagnostic.messages.BadSymbolicReference
import reporting.trace
import collection.mutable
import transform.TypeUtils._
import scala.annotation.internal.sharable
trait SymDenotations { this: Context =>
import SymDenotations._
/** Factory method for SymDenotion creation. All creations
* should be done via this method.
*/
def SymDenotation(
symbol: Symbol,
owner: Symbol,
name: Name,
initFlags: FlagSet,
initInfo: Type,
initPrivateWithin: Symbol = NoSymbol)(implicit ctx: Context): SymDenotation = {
val result =
if (symbol.isClass)
if (initFlags.is(Package)) new PackageClassDenotation(symbol, owner, name, initFlags, initInfo, initPrivateWithin)
else new ClassDenotation(symbol, owner, name, initFlags, initInfo, initPrivateWithin)
else new SymDenotation(symbol, owner, name, initFlags, initInfo, initPrivateWithin)
result.validFor = stablePeriod
result
}
def stillValid(denot: SymDenotation): Boolean =
if (denot.isOneOf(ValidForeverFlags) || denot.isRefinementClass || denot.isImport) true
else {
val initial = denot.initial
val firstPhaseId = initial.validFor.firstPhaseId.max(ctx.typerPhase.id)
if ((initial ne denot) || ctx.phaseId != firstPhaseId)
ctx.withPhase(firstPhaseId).stillValidInOwner(initial)
else
stillValidInOwner(denot)
}
private[SymDenotations] def stillValidInOwner(denot: SymDenotation): Boolean = try {
val owner = denot.owner.denot
stillValid(owner) && (
!owner.isClass
|| owner.isRefinementClass
|| owner.is(Scala2x)
|| (owner.unforcedDecls.lookupAll(denot.name) contains denot.symbol)
|| denot.isSelfSym
|| denot.isLocalDummy)
} catch {
case ex: StaleSymbol => false
}
/** Explain why symbol is invalid; used for debugging only */
def traceInvalid(denot: Denotation): Boolean = {
def show(d: Denotation) = s"$d#${d.symbol.id}"
def explain(msg: String) = {
println(s"${show(denot)} is invalid at ${this.period} because $msg")
false
}
denot match {
case denot: SymDenotation =>
def explainSym(msg: String) = explain(s"$msg\ndefined = ${denot.definedPeriodsString}")
if (denot.isOneOf(ValidForeverFlags) || denot.isRefinementClass) true
else {
implicit val ctx = this
val initial = denot.initial
if ((initial ne denot) || ctx.phaseId != initial.validFor.firstPhaseId) {
ctx.withPhase(initial.validFor.firstPhaseId).traceInvalid(initial)
} else try {
val owner = denot.owner.denot
if (!traceInvalid(owner)) explainSym("owner is invalid")
else if (!owner.isClass || owner.isRefinementClass || denot.isSelfSym) true
else if (owner.unforcedDecls.lookupAll(denot.name) contains denot.symbol) true
else explainSym(s"decls of ${show(owner)} are ${owner.unforcedDecls.lookupAll(denot.name).toList}, do not contain ${denot.symbol}")
} catch {
case ex: StaleSymbol => explainSym(s"$ex was thrown")
}
}
case _ =>
explain("denotation is not a SymDenotation")
}
}
/** Configurable: Accept stale symbol with warning if in IDE */
def staleOK: Boolean = Config.ignoreStaleInIDE && mode.is(Mode.Interactive)
/** Possibly accept stale symbol with warning if in IDE */
def acceptStale(denot: SingleDenotation): Boolean =
staleOK && {
ctx.echo(denot.staleSymbolMsg)
true
}
}
object SymDenotations {
/** A sym-denotation represents the contents of a definition
* during a period.
*/
class SymDenotation private[SymDenotations] (
symbol: Symbol,
final val maybeOwner: Symbol,
final val name: Name,
initFlags: FlagSet,
initInfo: Type,
initPrivateWithin: Symbol = NoSymbol) extends SingleDenotation(symbol, initInfo) {
//assert(symbol.id != 4940, name)
override def hasUniqueSym: Boolean = exists
/** Debug only
override def validFor_=(p: Period) = {
super.validFor_=(p)
}
*/
if (Config.checkNoSkolemsInInfo) assertNoSkolems(initInfo)
// ------ Getting and setting fields -----------------------------
private[this] var myFlags: FlagSet = adaptFlags(initFlags)
private[this] var myPrivateWithin: Symbol = initPrivateWithin
private[this] var myAnnotations: List[Annotation] = Nil
/** The owner of the symbol; overridden in NoDenotation */
def owner: Symbol = maybeOwner
/** The flag set */
final def flags(implicit ctx: Context): FlagSet = { ensureCompleted(); myFlags }
/** The flag set without forcing symbol completion.
* Should be used only for printing.
*/
private[dotc] final def flagsUNSAFE: FlagSet = myFlags
final def flagsString(implicit ctx: Context): String = flags.flagsString
/** Adapt flag set to this denotation's term or type nature */
private def adaptFlags(flags: FlagSet) = if (isType) flags.toTypeFlags else flags.toTermFlags
/** Update the flag set */
final def flags_=(flags: FlagSet): Unit =
myFlags = adaptFlags(flags)
/** Set given flags(s) of this denotation */
final def setFlag(flags: FlagSet): Unit = { myFlags |= flags }
/** Unset given flags(s) of this denotation */
final def resetFlag(flags: FlagSet): Unit = { myFlags &~= flags }
/** Set applicable flags in {NoInits, PureInterface}
* @param parentFlags The flags that match the class or trait's parents
* @param bodyFlags The flags that match the class or trait's body
*/
final def setNoInitsFlags(parentFlags: FlagSet, bodyFlags: FlagSet): Unit =
setFlag(
if (myFlags.is(Trait)) NoInitsInterface & bodyFlags // no parents are initialized from a trait
else NoInits & bodyFlags & parentFlags)
private def isCurrent(fs: FlagSet) =
fs <= (
if (myInfo.isInstanceOf[SymbolLoader]) FromStartFlags
else AfterLoadFlags)
final def relevantFlagsFor(fs: FlagSet)(implicit ctx: Context) =
if (isCurrent(fs)) myFlags else flags
/** Has this denotation one of given flag set? */
final def is(flag: Flag)(implicit ctx: Context): Boolean =
(if (isCurrent(flag)) myFlags else flags).is(flag)
/** Has this denotation one of the flags in `fs` set? */
final def isOneOf(fs: FlagSet)(implicit ctx: Context): Boolean =
(if (isCurrent(fs)) myFlags else flags).isOneOf(fs)
/** Has this denotation the given flag set, whereas none of the flags
* in `butNot` are set?
*/
final def is(flag: Flag, butNot: FlagSet)(implicit ctx: Context): Boolean =
(if (isCurrent(flag) && isCurrent(butNot)) myFlags else flags).is(flag, butNot)
/** Has this denotation one of the flags in `fs` set, whereas none of the flags
* in `butNot` are set?
*/
final def isOneOf(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context): Boolean =
(if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags).isOneOf(fs, butNot)
/** Has this denotation all of the flags in `fs` set? */
final def isAllOf(fs: FlagSet)(implicit ctx: Context): Boolean =
(if (isCurrent(fs)) myFlags else flags).isAllOf(fs)
/** Has this denotation all of the flags in `fs` set, whereas none of the flags
* in `butNot` are set?
*/
final def isAllOf(fs: FlagSet, butNot: FlagSet)(implicit ctx: Context): Boolean =
(if (isCurrent(fs) && isCurrent(butNot)) myFlags else flags).isAllOf(fs, butNot)
/** The type info, or, if symbol is not yet completed, the completer */
final def infoOrCompleter: Type = myInfo
/** Optionally, the info if it is completed */
final def unforcedInfo: Option[Type] = myInfo match {
case myInfo: LazyType => None
case _ => Some(myInfo)
}
final def completeFrom(completer: LazyType)(implicit ctx: Context): Unit =
if (Config.showCompletions) {
println(i"${" " * indent}completing ${if (isType) "type" else "val"} $name")
indent += 1
if (myFlags.is(Touched)) throw CyclicReference(this)
myFlags |= Touched
// completions.println(s"completing ${this.debugString}")
try completer.complete(this)(ctx.withPhase(validFor.firstPhaseId))
catch {
case ex: CyclicReference =>
println(s"error while completing ${this.debugString}")
throw ex
}
finally {
indent -= 1
println(i"${" " * indent}completed $name in $owner")
}
}
else {
if (myFlags.is(Touched)) throw CyclicReference(this)
myFlags |= Touched
completer.complete(this)(ctx.withPhase(validFor.firstPhaseId))
}
protected[dotc] def info_=(tp: Type): Unit = {
/* // DEBUG
def illegal: String = s"illegal type for $this: $tp"
if (this is Module) // make sure module invariants that allow moduleClass and sourceModule to work are kept.
tp match {
case tp: ClassInfo => assert(tp.selfInfo.isInstanceOf[TermRefBySym], illegal)
case tp: NamedType => assert(tp.isInstanceOf[TypeRefBySym], illegal)
case tp: ExprType => assert(tp.resultType.isInstanceOf[TypeRefBySym], illegal)
case _ =>
}
*/
if (Config.checkNoSkolemsInInfo) assertNoSkolems(tp)
myInfo = tp
}
/** The name, except
* - if this is a module class, strip the module class suffix
* - if this is a companion object with a clash-avoiding name, strip the
* "avoid clash" suffix
*/
def effectiveName(implicit ctx: Context): Name =
if (this.is(ModuleClass)) name.stripModuleClassSuffix
else name.exclude(AvoidClashName)
/** The privateWithin boundary, NoSymbol if no boundary is given.
*/
final def privateWithin(implicit ctx: Context): Symbol = { ensureCompleted(); myPrivateWithin }
/** Set privateWithin. */
protected[dotc] final def privateWithin_=(sym: Symbol): Unit =
myPrivateWithin = sym
/** The annotations of this denotation */
final def annotations(implicit ctx: Context): List[Annotation] = {
ensureCompleted(); myAnnotations
}
/** Update the annotations of this denotation */
final def annotations_=(annots: List[Annotation]): Unit =
myAnnotations = annots
/** Does this denotation have an annotation matching the given class symbol? */
final def hasAnnotation(cls: Symbol)(implicit ctx: Context): Boolean =
dropOtherAnnotations(annotations, cls).nonEmpty
/** Apply transform `f` to all annotations of this denotation */
final def transformAnnotations(f: Annotation => Annotation)(implicit ctx: Context): Unit =
annotations = annotations.mapConserve(f)
/** Keep only those annotations that satisfy `p` */
final def filterAnnotations(p: Annotation => Boolean)(implicit ctx: Context): Unit =
annotations = annotations.filterConserve(p)
/** Optionally, the annotation matching the given class symbol */
final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] =
dropOtherAnnotations(annotations, cls) match {
case annot :: _ => Some(annot)
case nil => None
}
/** The same as getAnnotation, but without ensuring
* that the symbol carrying the annotation is completed
*/
final def unforcedAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] =
dropOtherAnnotations(myAnnotations, cls) match {
case annot :: _ => Some(annot)
case nil => None
}
/** Add given annotation to the annotations of this denotation */
final def addAnnotation(annot: Annotation): Unit =
annotations = annot :: myAnnotations
/** Remove annotation with given class from this denotation */
final def removeAnnotation(cls: Symbol)(implicit ctx: Context): Unit =
annotations = myAnnotations.filterNot(_ matches cls)
/** Remove any annotations with same class as `annot`, and add `annot` */
final def updateAnnotation(annot: Annotation)(implicit ctx: Context): Unit = {
removeAnnotation(annot.symbol)
addAnnotation(annot)
}
/** Add all given annotations to this symbol */
final def addAnnotations(annots: TraversableOnce[Annotation])(implicit ctx: Context): Unit =
annots.foreach(addAnnotation)
@tailrec
private def dropOtherAnnotations(anns: List[Annotation], cls: Symbol)(implicit ctx: Context): List[Annotation] = anns match {
case ann :: rest => if (ann matches cls) anns else dropOtherAnnotations(rest, cls)
case Nil => Nil
}
/** The denotation is completed: info is not a lazy type and attributes have defined values */
final def isCompleted: Boolean = !myInfo.isInstanceOf[LazyType]
/** The denotation is in train of being completed */
final def isCompleting: Boolean = myFlags.is(Touched) && !isCompleted
/** The completer of this denotation. @pre: Denotation is not yet completed */
final def completer: LazyType = myInfo.asInstanceOf[LazyType]
/** Make sure this denotation is completed */
final def ensureCompleted()(implicit ctx: Context): Unit = info
/** The symbols defined in this class or object.
* Careful! This does not force the type, so is compilation order dependent.
* This method should be used only in the following circumstances:
*
* 1. When accessing type parameters or type parameter accessors (both are entered before
* completion).
* 2. When obtaining the current scope in order to enter, rename or delete something there.
* 3. When playing it safe in order not to raise CylicReferences, e.g. for printing things
* or taking more efficient shortcuts (e.g. the stillValid test).
*/
final def unforcedDecls(implicit ctx: Context): Scope = myInfo match {
case cinfo: LazyType =>
val knownDecls = cinfo.decls
if (knownDecls ne EmptyScope) knownDecls
else { completeFrom(cinfo); unforcedDecls } // complete-once
case _ => info.decls
}
/** If this is a package class, the symbols entered in it
* before it is completed. (this is needed to eagerly enter synthetic
* aliases such as AnyRef into a package class without forcing it.
* Right now, the only usage is for the AnyRef alias in Definitions.
*/
final private[core] def currentPackageDecls(implicit ctx: Context): MutableScope = myInfo match {
case pinfo: SymbolLoaders.PackageLoader => pinfo.currentDecls
case _ => unforcedDecls.openForMutations
}
/** If this is a synthetic opaque type alias, mark it as Deferred with bounds
* as given by the right hand side's `WithBounds` annotation, if one is present,
* or with empty bounds of the right kind, otherwise.
* At the same time, integrate the original alias as a refinement of the
* self type of the enclosing class.
*/
final def normalizeOpaque()(implicit ctx: Context) = {
def abstractRHS(tp: Type): Type = tp match {
case tp: HKTypeLambda => tp.derivedLambdaType(resType = abstractRHS(tp.resType))
case _ => defn.AnyType
}
if (isOpaqueAlias) {
info match {
case TypeAlias(alias) =>
val (refiningAlias, bounds) = alias match {
case AnnotatedType(alias1, Annotation.WithBounds(bounds)) =>
(alias1, bounds)
case _ =>
(alias, TypeBounds(defn.NothingType, abstractRHS(alias)))
}
def refineSelfType(selfType: Type) =
RefinedType(selfType, name, TypeAlias(refiningAlias))
val enclClassInfo = owner.asClass.classInfo
enclClassInfo.selfInfo match {
case self: Type =>
owner.info = enclClassInfo.derivedClassInfo(selfInfo = refineSelfType(self))
case self: Symbol =>
self.info = refineSelfType(self.info)
}
info = bounds
setFlag(Deferred)
typeRef.recomputeDenot()
case _ =>
}
}
}
// ------ Names ----------------------------------------------
/** The expanded name of this denotation. */
final def expandedName(implicit ctx: Context): Name =
if (name.is(ExpandedName) || isConstructor) name
else name.expandedName(initial.owner)
// need to use initial owner to disambiguate, as multiple private symbols with the same name
// might have been moved from different origins into the same class
/** The effective name with which the denoting symbol was created */
final def originalName(implicit ctx: Context): Name = initial.effectiveName
/** The owner with which the denoting symbol was created. */
final def originalOwner(implicit ctx: Context): Symbol = initial.maybeOwner
/** The encoded full path name of this denotation, where outer names and inner names
* are separated by `separator` strings as indicated by the given name kind.
* Drops package objects. Represents each term in the owner chain by a simple `_$`.
*/
def fullNameSeparated(kind: QualifiedNameKind)(implicit ctx: Context): Name =
maybeOwner.fullNameSeparated(kind, kind, name)
/** The encoded full path name of this denotation (separated by `prefixKind`),
* followed by the separator implied by `kind` and the given `name`.
* Drops package objects. Represents each term in the owner chain by a simple `_$`.
*/
def fullNameSeparated(prefixKind: QualifiedNameKind, kind: QualifiedNameKind, name: Name)(implicit ctx: Context): Name =
if (symbol == NoSymbol || isEffectiveRoot || kind == FlatName && is(PackageClass))
name
else {
var filler = ""
var encl = symbol
while (!encl.isClass && !encl.isPackageObject) {
encl = encl.owner
filler += "_$"
}
var prefix = encl.fullNameSeparated(prefixKind)
if (kind.separator == "$")
// duplicate scalac's behavior: don't write a double '$$' for module class members.
prefix = prefix.exclude(ModuleClassName)
def qualify(n: SimpleName) =
kind(prefix.toTermName, if (filler.isEmpty) n else termName(filler + n))
val fn = name replace {
case name: SimpleName => qualify(name)
case name @ AnyQualifiedName(_, _) => qualify(name.mangled.toSimpleName)
}
if (name.isTypeName) fn.toTypeName else fn.toTermName
}
/** The encoded flat name of this denotation, where joined names are separated by `separator` characters. */
def flatName(implicit ctx: Context): Name = fullNameSeparated(FlatName)
/** `fullName` where `.' is the separator character */
def fullName(implicit ctx: Context): Name = fullNameSeparated(QualifiedName)
/** The name given in an `@alpha` annotation if one is present, `name` otherwise */
final def erasedName(implicit ctx: Context): Name =
getAnnotation(defn.AlphaAnnot) match {
case Some(ann) =>
ann.arguments match {
case Literal(Constant(str: String)) :: Nil =>
if (isType) str.toTypeName else str.toTermName
case _ => name
}
case _ => name
}
// ----- Tests -------------------------------------------------
/** Is this denotation a type? */
override def isType: Boolean = name.isTypeName
/** Is this denotation a class? */
final def isClass: Boolean = isInstanceOf[ClassDenotation]
/** Is this denotation a non-trait class? */
final def isRealClass(implicit ctx: Context): Boolean = isClass && !is(Trait)
/** Cast to class denotation */
final def asClass: ClassDenotation = asInstanceOf[ClassDenotation]
/** is this symbol the result of an erroneous definition? */
def isError: Boolean = false
/** Make denotation not exist */
final def markAbsent(): Unit =
myInfo = NoType
/** Is symbol known to not exist, or potentially not completed yet? */
final def unforcedIsAbsent(implicit ctx: Context): Boolean =
myInfo == NoType ||
(this.is(ModuleVal, butNot = Package)) && moduleClass.unforcedIsAbsent
/** Is symbol known to not exist? */
final def isAbsent(implicit ctx: Context): Boolean = {
ensureCompleted()
(myInfo `eq` NoType) ||
(this.is(ModuleVal, butNot = Package)) && moduleClass.isAbsent
}
/** Is this symbol the root class or its companion object? */
final def isRoot: Boolean =
(maybeOwner eq NoSymbol) && (name.toTermName == nme.ROOT || name == nme.ROOTPKG)
/** Is this symbol the empty package class or its companion object? */
final def isEmptyPackage(implicit ctx: Context): Boolean =
name.toTermName == nme.EMPTY_PACKAGE && owner.isRoot
/** Is this symbol the empty package class or its companion object? */
final def isEffectiveRoot(implicit ctx: Context): Boolean = isRoot || isEmptyPackage
/** Is this symbol an anonymous class? */
final def isAnonymousClass(implicit ctx: Context): Boolean =
isClass && initial.name.isAnonymousClassName
final def isAnonymousFunction(implicit ctx: Context): Boolean =
this.symbol.is(Method) && initial.name.isAnonymousFunctionName
final def isAnonymousModuleVal(implicit ctx: Context): Boolean =
this.symbol.is(ModuleVal) && initial.name.isAnonymousClassName
/** Is this a synthetic method that represents conversions between representations of a value class
* These methods are generated in ExtensionMethods
* and used in ElimErasedValueType.
*/
final def isValueClassConvertMethod(implicit ctx: Context): Boolean =
name.toTermName == nme.U2EVT ||
name.toTermName == nme.EVT2U
/** Is symbol a primitive value class? */
def isPrimitiveValueClass(implicit ctx: Context): Boolean =
maybeOwner == defn.ScalaPackageClass && defn.ScalaValueClasses().contains(symbol)
/** Is symbol a primitive numeric value class? */
def isNumericValueClass(implicit ctx: Context): Boolean =
maybeOwner == defn.ScalaPackageClass && defn.ScalaNumericValueClasses().contains(symbol)
/** Is symbol a class for which no runtime representation exists? */
def isNotRuntimeClass(implicit ctx: Context): Boolean = defn.NotRuntimeClasses contains symbol
/** Is this symbol a class representing a refinement? These classes
* are used only temporarily in Typer and Unpickler as an intermediate
* step for creating Refinement types.
*/
final def isRefinementClass(implicit ctx: Context): Boolean =
name == tpnme.REFINE_CLASS
/** Is this symbol a package object or its module class? */
def isPackageObject(implicit ctx: Context): Boolean =
name.isPackageObjectName && owner.is(Package) && this.is(Module)
/** Is this symbol an abstract type? */
final def isAbstractType(implicit ctx: Context): Boolean = this.is(DeferredType)
/** Is this symbol an alias type? */
final def isAliasType(implicit ctx: Context): Boolean = isAbstractOrAliasType && !this.is(Deferred)
/** Is this symbol an abstract or alias type? */
final def isAbstractOrAliasType: Boolean = isType & !isClass
/** Is this symbol an abstract type or type parameter? */
final def isAbstractOrParamType(implicit ctx: Context): Boolean = this.isOneOf(DeferredOrTypeParam)
/** Is this symbol a user-defined opaque alias type? */
def isOpaqueAlias(implicit ctx: Context): Boolean = is(Opaque) && !isClass
/** Is this symbol a module that contains opaque aliases? */
def containsOpaques(implicit ctx: Context): Boolean = is(Opaque) && isClass
def seesOpaques(implicit ctx: Context): Boolean =
containsOpaques ||
is(Module, butNot = Package) && owner.containsOpaques
/** Is this the denotation of a self symbol of some class?
* This is the case if one of two conditions holds:
* 1. It is the symbol referred to in the selfInfo part of the ClassInfo
* which is the type of this symbol's owner.
* 2. This symbol is owned by a class, it's selfInfo field refers to a type
* (indicating the self definition does not introduce a name), and the
* symbol's name is "_".
* TODO: Find a more robust way to characterize self symbols, maybe by
* spending a Flag on them?
*/
final def isSelfSym(implicit ctx: Context): Boolean = owner.infoOrCompleter match {
case ClassInfo(_, _, _, _, selfInfo) =>
selfInfo == symbol ||
selfInfo.isInstanceOf[Type] && name == nme.WILDCARD
case _ => false
}
/** Is this definition contained in `boundary`?
* Same as `ownersIterator contains boundary` but more efficient.
*/
final def isContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean = {
def recur(sym: Symbol): Boolean =
if (sym eq boundary) true
else if (sym eq NoSymbol) false
else if (sym.is(PackageClass) && !boundary.is(PackageClass)) false
else recur(sym.owner)
recur(symbol)
}
final def isProperlyContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean =
symbol != boundary && isContainedIn(boundary)
/** Is this denotation static (i.e. with no outer instance)? */
final def isStatic(implicit ctx: Context): Boolean =
(if (maybeOwner eq NoSymbol) isRoot else maybeOwner.originDenotation.isStaticOwner) ||
myFlags.is(JavaStatic)
/** Is this a package class or module class that defines static symbols? */
final def isStaticOwner(implicit ctx: Context): Boolean =
myFlags.is(ModuleClass) && (myFlags.is(PackageClass) || isStatic)
/** Is this denotation defined in the same scope and compilation unit as that symbol? */
final def isCoDefinedWith(other: Symbol)(implicit ctx: Context): Boolean =
(this.effectiveOwner == other.effectiveOwner) &&
( !this.effectiveOwner.is(PackageClass)
|| this.unforcedIsAbsent || other.unforcedIsAbsent
|| { // check if they are defined in the same file(or a jar)
val thisFile = this.symbol.associatedFile
val thatFile = other.associatedFile
( thisFile == null
|| thatFile == null
|| thisFile.path == thatFile.path // Cheap possibly wrong check, then expensive normalization
|| thisFile.canonicalPath == thatFile.canonicalPath
)
}
)
/** Is this a denotation of a stable term (or an arbitrary type)?
* Terms are stable if they are idempotent (as in TreeInfo.Idempotent): that is, they always return the same value,
* if any.
*
* A *member* is stable, basically, if it behaves like a field projection: that is, it projects a constant result
* out of its owner.
*
* However, a stable member might not yet be initialized (if it is an object or anyhow lazy).
* So the first call to a stable member might fail and/or produce side effects.
*/
final def isStableMember(implicit ctx: Context): Boolean = {
def isUnstableValue = isOneOf(UnstableValueFlags) || info.isInstanceOf[ExprType]
isType || is(StableRealizable) || !isUnstableValue
}
/** Is this a denotation of a class that does not have - either direct or inherited -
* initaliazion code?
*/
def isNoInitsClass(implicit ctx: Context): Boolean =
isClass &&
(asClass.baseClasses.forall(_.is(NoInits)) || defn.isAssuredNoInits(symbol))
/** Is this a "real" method? A real method is a method which is:
* - not an accessor
* - not an anonymous function
*/
final def isRealMethod(implicit ctx: Context): Boolean =
this.is(Method, butNot = Accessor) && !isAnonymousFunction
/** Is this a getter? */
final def isGetter(implicit ctx: Context): Boolean =
this.is(Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix
/** Is this a setter? */
final def isSetter(implicit ctx: Context): Boolean =
this.is(Accessor) &&
originalName.isSetterName &&
(!isCompleted || info.firstParamTypes.nonEmpty) // to avoid being fooled by var x_= : Unit = ...
/** is this a symbol representing an import? */
final def isImport: Boolean = name == nme.IMPORT
/** is this the constructor of a class? */
final def isClassConstructor: Boolean = name == nme.CONSTRUCTOR
/** Is this the constructor of a trait or a class */
final def isConstructor: Boolean = name.isConstructorName
/** Is this a local template dummmy? */
final def isLocalDummy: Boolean = name.isLocalDummyName
/** Does this symbol denote the primary constructor of its enclosing class? */
final def isPrimaryConstructor(implicit ctx: Context): Boolean =
isConstructor && owner.primaryConstructor == symbol
/** Does this symbol denote the static constructor of its enclosing class? */
final def isStaticConstructor(implicit ctx: Context): Boolean =
name.isStaticConstructorName
/** Is this a subclass of the given class `base`? */
def isSubClass(base: Symbol)(implicit ctx: Context): Boolean = false
/** Is this a subclass of `base`,
* and is the denoting symbol also different from `Null` or `Nothing`?
* @note erroneous classes are assumed to derive from all other classes
* and all classes derive from them.
*/
def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean = false
/** Is this symbol a class that extends `java.io.Serializable` ? */
def isSerializable(implicit ctx: Context): Boolean =
isClass && derivesFrom(defn.JavaSerializableClass)
/** Is this symbol a class that extends `AnyVal`? */
final def isValueClass(implicit ctx: Context): Boolean = {
val di = initial
di.isClass &&
di.derivesFrom(defn.AnyValClass)(ctx.withPhase(di.validFor.firstPhaseId))
// We call derivesFrom at the initial phase both because AnyVal does not exist
// after Erasure and to avoid cyclic references caused by forcing denotations
}
/** Is this symbol a class references to which that are supertypes of null? */
final def isNullableClass(implicit ctx: Context): Boolean =
isClass && !isValueClass && !is(ModuleClass) && symbol != defn.NothingClass
/** Is this definition accessible as a member of tree with type `pre`?
* @param pre The type of the tree from which the selection is made
* @param superAccess Access is via super
* Everything is accessible if `pre` is `NoPrefix`.
* A symbol with type `NoType` is not accessible for any other prefix.
*/
final def isAccessibleFrom(pre: Type, superAccess: Boolean = false, whyNot: StringBuffer = null)(implicit ctx: Context): Boolean = {
/** Are we inside definition of `boundary`? */
def accessWithin(boundary: Symbol) = ctx.owner.isContainedIn(boundary)
/** Are we within definition of linked class of `boundary`? */
def accessWithinLinked(boundary: Symbol) = {
val linked = boundary.linkedClass
(linked ne NoSymbol) && accessWithin(linked)
}
/** Is `pre` the same as C.thisThis, where C is exactly the owner of this symbol,
* or, if this symbol is protected, a subclass of the owner?
*/
def isCorrectThisType(pre: Type): Boolean = pre match {
case pre: ThisType =>
(pre.cls eq owner) || this.is(Protected) && pre.cls.derivesFrom(owner)
case pre: TermRef =>
pre.symbol.moduleClass == owner
case _ =>
false
}
/** Is protected access to target symbol permitted? */
def isProtectedAccessOK = {
def fail(str: => String): Boolean = {
if (whyNot != null) whyNot append str
false
}
val cls = owner.enclosingSubClass
if (!cls.exists)
fail(
i"""
| Access to protected $this not permitted because enclosing ${ctx.owner.enclosingClass.showLocated}
| is not a subclass of ${owner.showLocated} where target is defined""")
else if (
!( isType // allow accesses to types from arbitrary subclasses fixes #4737
|| pre.derivesFrom(cls)
|| isConstructor
|| owner.is(ModuleClass) // don't perform this check for static members
))
fail(
i"""
| Access to protected ${symbol.show} not permitted because prefix type ${pre.widen.show}
| does not conform to ${cls.showLocated} where the access takes place""")
else true
}
if (pre eq NoPrefix) true
else if (info eq NoType) false
else {
val boundary = accessBoundary(owner)
( boundary.isTerm
|| boundary.isRoot
|| (accessWithin(boundary) || accessWithinLinked(boundary)) &&
( !this.is(Local)
|| isCorrectThisType(pre)
)
|| this.is(Protected) &&
( superAccess
|| pre.isInstanceOf[ThisType]
|| ctx.phase.erasedTypes
|| isProtectedAccessOK
)
)
}
}
/** Do members of this symbol need translation via asSeenFrom when
* accessed via prefix `pre`?
*/
def membersNeedAsSeenFrom(pre: Type)(implicit ctx: Context): Boolean =
!( this.isTerm
|| this.isStaticOwner && !this.seesOpaques
|| ctx.erasedTypes
|| (pre eq NoPrefix)
|| (pre eq thisType)
)
/** Is this symbol concrete, or that symbol deferred? */
def isAsConcrete(that: Symbol)(implicit ctx: Context): Boolean =
!this.is(Deferred) || that.is(Deferred)
/** Does this symbol have defined or inherited default parameters? */
def hasDefaultParams(implicit ctx: Context): Boolean =
if (this.isOneOf(HasDefaultParamsFlags)) true
else if (this.is(NoDefaultParams)) false
else {
val result = allOverriddenSymbols exists (_.hasDefaultParams)
setFlag(if (result) InheritedDefaultParams else NoDefaultParams)
result
}
/** Symbol is an owner that would be skipped by effectiveOwner. Skipped are
* - package objects
* - non-lazy valdefs
*/
def isWeakOwner(implicit ctx: Context): Boolean =
isPackageObject ||
isTerm && !isOneOf(MethodOrLazy) && !isLocalDummy
def isSkolem: Boolean = name == nme.SKOLEM
def isInlineMethod(implicit ctx: Context): Boolean =
isAllOf(InlineMethod, butNot = Accessor) &&
!name.isUnapplyName // unapply methods do not count as inline methods
// we need an inline flag on them only do that
// reduceProjection gets access to their rhs
/** Is this a Scala 2 macro */
final def isScala2Macro(implicit ctx: Context): Boolean = is(Macro) && symbol.owner.is(Scala2x)
/** An erased value or an inline method, excluding @forceInline annotated methods.
* The latter have to be kept around to get to parity with Scala.
* This is necessary at least until we have full bootstrap. Right now
* dotty-bootstrapped involves running the Dotty compiler compiled with Scala 2 with
* a Dotty runtime library compiled with Dotty. If we erase @forceInline annotated
* methods, this means that the support methods in dotty.runtime.LazyVals vanish.
* But they are needed for running the lazy val implementations in the Scala-2 compiled compiler.
*/
def isEffectivelyErased(implicit ctx: Context): Boolean =
is(Erased) ||
isInlineMethod && unforcedAnnotation(defn.ForceInlineAnnot).isEmpty
/** ()T and => T types should be treated as equivalent for this symbol.
* Note: For the moment, we treat Scala-2 compiled symbols as loose matching,
* because the Scala library does not always follow the right conventions.
* Examples are: isWhole(), toInt(), toDouble() in BigDecimal, Numeric, RichInt, ScalaNumberProxy.
*/
def matchNullaryLoosely(implicit ctx: Context): Boolean = {
def test(sym: Symbol) =
sym.is(JavaDefined) ||
sym.owner == defn.AnyClass ||
sym == defn.Object_clone ||
sym.owner.is(Scala2x)
this.exists && (test(symbol) || allOverriddenSymbols.exists(test))
}
// ------ access to related symbols ---------------------------------
/* Modules and module classes are represented as follows:
*
* object X extends Y { def f() }
*
* lazy val X: X$ = new X$
* class X$ extends Y { this: X.type => def f() }
*
* During completion, references to moduleClass and sourceModules are stored in
* the completers.
*/
/** If this a module, return the corresponding class, if this is a module, return itself,
* otherwise NoSymbol
*/
final def moduleClass(implicit ctx: Context): Symbol = {
def notFound = {
if (Config.showCompletions) println(s"missing module class for $name: $myInfo")
NoSymbol
}
if (this.is(ModuleVal))
myInfo match {
case info: TypeRef => info.symbol
case ExprType(info: TypeRef) => info.symbol // needed after uncurry, when module terms might be accessor defs
case info: LazyType => info.moduleClass
case t: MethodType =>
t.resultType match {
case info: TypeRef => info.symbol
case _ => notFound
}
case _ => notFound
}
else if (this.is(ModuleClass))
symbol
else
NoSymbol
}
/** If this a module class, return the corresponding module, if this is a module, return itself,
* otherwise NoSymbol
*/
final def sourceModule(implicit ctx: Context): Symbol =
if (this.is(ModuleClass))
myInfo match {
case ClassInfo(_, _, _, _, selfType) =>
def sourceOfSelf(tp: TypeOrSymbol): Symbol = (tp: @unchecked) match {
case tp: TermRef => tp.symbol
case tp: Symbol => sourceOfSelf(tp.info)
case tp: RefinedType => sourceOfSelf(tp.parent)
}
sourceOfSelf(selfType)
case info: LazyType =>
info.sourceModule
case _ =>
NoSymbol
}
else if (this.is(ModuleVal))
symbol
else
NoSymbol
/** The field accessed by this getter or setter, or if it does not exist, the getter */
def accessedFieldOrGetter(implicit ctx: Context): Symbol = {
val fieldName = if (isSetter) name.asTermName.getterName else name
val d = owner.info.decl(fieldName)
val field = d.suchThat(!_.is(Method)).symbol
def getter = d.suchThat(_.info.isParameterless).symbol
field orElse getter
}
/** The field accessed by a getter or setter, or
* if it does not exists, the getter of a setter, or
* if that does not exist the symbol itself.
*/
def underlyingSymbol(implicit ctx: Context): Symbol =
if (is(Accessor)) accessedFieldOrGetter orElse symbol else symbol
/** The chain of owners of this denotation, starting with the denoting symbol itself */
final def ownersIterator(implicit ctx: Context): Iterator[Symbol] = new Iterator[Symbol] {
private[this] var current = symbol
def hasNext = current.exists
def next: Symbol = {
val result = current
current = current.owner
result
}
}
/** If this is a weak owner, its owner, otherwise the denoting symbol. */
final def skipWeakOwner(implicit ctx: Context): Symbol =
if (isWeakOwner) owner.skipWeakOwner else symbol
/** The owner, skipping package objects and non-lazy valdefs. */
final def effectiveOwner(implicit ctx: Context): Symbol = owner.skipWeakOwner
/** The class containing this denotation.
* If this denotation is already a class, return itself
* Definitions flagged with JavaStatic are treated specially.
* Their enclosing class is not the lexically enclosing class,
* but in turn the enclosing class of the latter. This reflects
* the context created by `Context#superCallContext`, `Context#thisCallArgContext`
* for these definitions.
*
* Note, that as packages have ClassSymbols, top level classes will have an `enclosingClass`
* with Package flag set.
*/
final def enclosingClass(implicit ctx: Context): Symbol = {
def enclClass(sym: Symbol, skip: Boolean): Symbol = {
def newSkip = sym.is(JavaStaticTerm)
if (!sym.exists)
NoSymbol
else if (sym.isClass)
if (skip) enclClass(sym.owner, newSkip) else sym
else
enclClass(sym.owner, skip || newSkip)
}
enclClass(symbol, false)
}
/** A class that in source code would be lexically enclosing */
final def lexicallyEnclosingClass(implicit ctx: Context): Symbol =
if (!exists || isClass) symbol else owner.lexicallyEnclosingClass
/** A symbol is effectively final if it cannot be overridden in a subclass */
final def isEffectivelyFinal(implicit ctx: Context): Boolean =
isOneOf(EffectivelyFinalFlags) || !owner.isClass || owner.isOneOf(FinalOrModuleClass) || owner.isAnonymousClass
/** The class containing this denotation which has the given effective name. */
final def enclosingClassNamed(name: Name)(implicit ctx: Context): Symbol = {
val cls = enclosingClass
if (cls.effectiveName == name || !cls.exists) cls else cls.owner.enclosingClassNamed(name)
}
/** The closest enclosing method containing this definition.
* A local dummy owner is mapped to the primary constructor of the class.
*/
final def enclosingMethod(implicit ctx: Context): Symbol =
if (this.is(Method)) symbol
else if (this.isClass) primaryConstructor
else if (this.exists) owner.enclosingMethod
else NoSymbol
/** The top-level class containing this denotation,
* except for a toplevel module, where its module class is returned.
*/
final def topLevelClass(implicit ctx: Context): Symbol = {
@tailrec def topLevel(d: SymDenotation): Symbol = {
if (d.isTopLevelClass) d.symbol
else topLevel(d.owner)
}
val sym = topLevel(this)
if (sym.isClass) sym else sym.moduleClass
}
final def isTopLevelClass(implicit ctx: Context): Boolean =
!this.exists || this.isEffectiveRoot || this.is(PackageClass) || this.owner.is(PackageClass)
/** The package class containing this denotation */
final def enclosingPackageClass(implicit ctx: Context): Symbol =
if (this.is(PackageClass)) symbol else owner.enclosingPackageClass
/** Register target as a companion; overridden in ClassDenotation */
def registerCompanion(target: Symbol)(implicit ctx: Context) = ()
/** The registered companion; overridden in ClassDenotation */
def registeredCompanion(implicit ctx: Context): Symbol = NoSymbol
def registeredCompanion_=(c: Symbol): Unit = ()
/** The module object with the same (term-) name as this class or module class,
* and which is also defined in the same scope and compilation unit.
* NoSymbol if this module does not exist.
*/
final def companionModule(implicit ctx: Context): Symbol =
if (is(Module)) sourceModule
else registeredCompanion.sourceModule
private def companionType(implicit ctx: Context): Symbol =
if (is(Package)) NoSymbol
else if (is(ModuleVal)) moduleClass.denot.companionType
else registeredCompanion
/** The class with the same (type-) name as this module or module class,
* and which is also defined in the same scope and compilation unit.
* NoSymbol if this class does not exist.
*/
final def companionClass(implicit ctx: Context): Symbol =
companionType.suchThat(_.isClass).symbol
final def scalacLinkedClass(implicit ctx: Context): Symbol =
if (this.is(ModuleClass)) companionNamed(effectiveName.toTypeName)
else if (this.isClass) companionNamed(effectiveName.moduleClassName).sourceModule.moduleClass
else NoSymbol
/** Find companion class symbol with given name, or NoSymbol if none exists.
* Three alternative strategies:
* 1. If owner is a class, look in its members, otherwise
* 2. If current compilation unit has a typed tree,
* determine the defining statement sequence and search its trees, otherwise
* 3. If context has an enclosing scope which defines this symbol,
* lookup its companion in the same scope.
*/
private def companionNamed(name: TypeName)(implicit ctx: Context): Symbol =
if (owner.isClass)
owner.unforcedDecls.lookup(name).suchThat(_.isCoDefinedWith(symbol)).symbol
else if (!owner.exists || ctx.compilationUnit == null)
NoSymbol
else if (!ctx.compilationUnit.tpdTree.isEmpty)
tpd.definingStats(symbol).iterator
.map(tpd.definedSym)
.find(_.name == name)
.getOrElse(NoSymbol)
else if (ctx.scope == null)
NoSymbol
else if (ctx.scope.lookup(this.name) == symbol)
ctx.scope.lookup(name)
else
companionNamed(name)(ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next())
/** Is this symbol the same or a linked class of `sym`? */
final def isLinkedWith(sym: Symbol)(implicit ctx: Context): Boolean =
(symbol eq sym) || (linkedClass eq sym)
/** If this is a class, the module class of its companion object.
* If this is a module class, its companion class.
* NoSymbol otherwise.
*/
final def linkedClass(implicit ctx: Context): Symbol =
if (this.is(ModuleClass)) companionClass
else if (this.isClass) companionModule.moduleClass
else NoSymbol
/** The class that encloses the owner of the current context
* and that is a subclass of this class. NoSymbol if no such class exists.
*/
final def enclosingSubClass(implicit ctx: Context): Symbol =
ctx.owner.ownersIterator.findSymbol(_.isSubClass(symbol))
/** The alias of an opaque type alias that's stored in the self type of the
* containing object.
*/
def opaqueAlias(implicit ctx: Context): Type = {
def recur(tp: Type): Type = tp match {
case RefinedType(parent, rname, TypeAlias(alias)) =>
if (rname == name) alias else recur(parent)
case _ =>
NoType
}
recur(owner.asClass.classInfo.selfType)
}
/** The non-private symbol whose name and type matches the type of this symbol
* in the given class.
* @param inClass The class containing the result symbol's definition
* @param site The base type from which member types are computed
*
* inClass <-- find denot.symbol class C { <-- symbol is here
*
* site: Subtype of both inClass and C
*/
final def matchingDecl(inClass: Symbol, site: Type)(implicit ctx: Context): Symbol = {
var denot = inClass.info.nonPrivateDecl(name)
if (denot.isTerm) // types of the same name always match
denot = denot.matchingDenotation(site, site.memberInfo(symbol))
denot.symbol
}
/** The non-private member of `site` whose name and type matches the type of this symbol
*/
final def matchingMember(site: Type)(implicit ctx: Context): Symbol = {
var denot = site.nonPrivateMember(name)
if (denot.isTerm) // types of the same name always match
denot = denot.matchingDenotation(site, site.memberInfo(symbol))
denot.symbol
}
/** If false, this symbol cannot possibly participate in an override,
* either as overrider or overridee.
*/
final def canMatchInheritedSymbols(implicit ctx: Context): Boolean =
maybeOwner.isClass && memberCanMatchInheritedSymbols
/** If false, this class member cannot possibly participate in an override,
* either as overrider or overridee.
*/
final def memberCanMatchInheritedSymbols(implicit ctx: Context): Boolean =
!isConstructor && !is(Private)
/** The symbol, in class `inClass`, that is overridden by this denotation in class `siteClass`.*/
final def overriddenSymbol(inClass: ClassSymbol, siteClass: ClassSymbol = owner.asClass)(implicit ctx: Context): Symbol =
if (!canMatchInheritedSymbols && (owner ne inClass)) NoSymbol
else matchingDecl(inClass, siteClass.thisType)
/** All symbols overridden by this denotation. */
final def allOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] =
if (!canMatchInheritedSymbols) Iterator.empty
else overriddenFromType(owner.info)
/** Equivalent to `allOverriddenSymbols.headOption.getOrElse(NoSymbol)` but more efficient. */
final def nextOverriddenSymbol(implicit ctx: Context): Symbol = {
val overridden = allOverriddenSymbols
if (overridden.hasNext)
overridden.next
else
NoSymbol
}
/** Returns all matching symbols defined in parents of the selftype. */
final def extendedOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] =
if (!canMatchInheritedSymbols) Iterator.empty
else overriddenFromType(owner.asClass.classInfo.selfType)
private def overriddenFromType(tp: Type)(implicit ctx: Context): Iterator[Symbol] =
tp.baseClasses match {
case _ :: inherited => inherited.iterator.map(overriddenSymbol(_)).filter(_.exists)
case Nil => Iterator.empty
}
/** The symbol overriding this symbol in given subclass `ofclazz`.
*
* @param ofclazz is a subclass of this symbol's owner
*/
final def overridingSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol =
if (canMatchInheritedSymbols) matchingDecl(inClass, inClass.thisType)
else NoSymbol
/** The symbol accessed by a super in the definition of this symbol when
* seen from class `base`. This symbol is always concrete.
* pre: `this.owner` is in the base class sequence of `base`.
*/
final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = {
@tailrec def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
case bc :: bcs1 =>
val sym = matchingDecl(bcs.head, base.thisType)
.suchThat(alt => !alt.is(Deferred)).symbol
if (sym.exists) sym else loop(bcs.tail)
case _ =>
NoSymbol
}
loop(base.info.baseClasses.dropWhile(owner != _).tail)
}
/** A member of class `base` is incomplete if
* (1) it is declared deferred or
* (2) it is abstract override and its super symbol in `base` is
* nonexistent or incomplete.
*/
@tailrec final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
this.is(Deferred) ||
this.is(AbsOverride) && {
val supersym = superSymbolIn(base)
supersym == NoSymbol || supersym.isIncompleteIn(base)
}
/** The class or term symbol up to which this symbol is accessible,
* or RootClass if it is public. As java protected statics are
* otherwise completely inaccessible in scala, they are treated
* as public.
* @param base The access boundary to assume if this symbol is protected
*/
final def accessBoundary(base: Symbol)(implicit ctx: Context): Symbol = {
val fs = flags
if (fs.is(Private)) owner
else if (fs.isAllOf(StaticProtected)) defn.RootClass
else if (privateWithin.exists && !ctx.phase.erasedTypes) privateWithin
else if (fs.is(Protected)) base
else defn.RootClass
}
final def isPublic(implicit ctx: Context): Boolean =
accessBoundary(owner) == defn.RootClass
/** The primary constructor of a class or trait, NoSymbol if not applicable. */
def primaryConstructor(implicit ctx: Context): Symbol = NoSymbol
/** The current declaration in this symbol's class owner that has the same name
* as this one, and, if there are several, also has the same signature.
*/
def currentSymbol(implicit ctx: Context): Symbol = {
val candidates = owner.info.decls.lookupAll(name)
def test(sym: Symbol): Symbol =
if (sym == symbol || sym.signature == signature) sym
else if (candidates.hasNext) test(candidates.next)
else NoSymbol
if (candidates.hasNext) {
val sym = candidates.next
if (candidates.hasNext) test(sym) else sym
}
else NoSymbol
}
// ----- type-related ------------------------------------------------
/** The type parameters of a class symbol, Nil for all other symbols */
def typeParams(implicit ctx: Context): List[TypeSymbol] = Nil
/** The type This(cls), where cls is this class, NoPrefix for all other symbols */
def thisType(implicit ctx: Context): Type = NoPrefix
def typeRef(implicit ctx: Context): TypeRef =
TypeRef(owner.thisType, symbol)
def termRef(implicit ctx: Context): TermRef =
TermRef(owner.thisType, symbol)
/** The typeRef applied to its own type parameters */
def appliedRef(implicit ctx: Context): Type =
typeRef.appliedTo(symbol.typeParams.map(_.typeRef))
/** The NamedType representing this denotation at its original location.
* Same as either `typeRef` or `termRef` depending whether this denotes a type or not.
*/
def namedType(implicit ctx: Context): NamedType =
if (isType) typeRef else termRef
/** The variance of this type parameter or type member as an Int, with
* +1 = Covariant, -1 = Contravariant, 0 = Nonvariant, or not a type parameter
*/
final def variance(implicit ctx: Context): Int =
if (this.is(Covariant)) 1
else if (this.is(Contravariant)) -1
else 0
/** The flags to be used for a type parameter owned by this symbol.
* Overridden by ClassDenotation.
*/
def typeParamCreationFlags: FlagSet = TypeParam
override def toString: String = {
val kindString =
if (myFlags.is(ModuleClass)) "module class"
else if (isClass) "class"
else if (isType) "type"
else if (myFlags.is(Module)) "module"
else if (myFlags.is(Method)) "method"
else "val"
s"$kindString $name"
}
// ----- Sanity checks and debugging */
def debugString: String = toString + "#" + symbol.id // !!! DEBUG
def hasSkolems(tp: Type): Boolean = tp match {
case tp: SkolemType => true
case tp: NamedType => hasSkolems(tp.prefix)
case tp: RefinedType => hasSkolems(tp.parent) || hasSkolems(tp.refinedInfo)
case tp: RecType => hasSkolems(tp.parent)
case tp: TypeBounds => hasSkolems(tp.lo) || hasSkolems(tp.hi)
case tp: TypeVar => hasSkolems(tp.inst)
case tp: ExprType => hasSkolems(tp.resType)
case tp: AppliedType => hasSkolems(tp.tycon) || tp.args.exists(hasSkolems)
case tp: LambdaType => tp.paramInfos.exists(hasSkolems) || hasSkolems(tp.resType)
case tp: AndType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)
case tp: OrType => hasSkolems(tp.tp1) || hasSkolems(tp.tp2)
case tp: AnnotatedType => hasSkolems(tp.parent)
case _ => false
}
def assertNoSkolems(tp: Type): Unit =
if (!this.isSkolem)
assert(!hasSkolems(tp), s"assigning type $tp containing skolems to $this")
// ----- copies and transforms ----------------------------------------
protected def newLikeThis(s: Symbol, i: Type): SingleDenotation = new UniqueRefDenotation(s, i, validFor)
/** Copy this denotation, overriding selective fields */
final def copySymDenotation(
symbol: Symbol = this.symbol,
owner: Symbol = this.owner,
name: Name = this.name,
initFlags: FlagSet = UndefinedFlags,
info: Type = null,
privateWithin: Symbol = null,
annotations: List[Annotation] = null)(implicit ctx: Context): SymDenotation =
{ // simulate default parameters, while also passing implicit context ctx to the default values
val initFlags1 = (if (initFlags != UndefinedFlags) initFlags else this.flags)
val info1 = if (info != null) info else this.info
if (ctx.isAfterTyper && changedClassParents(info, info1, completersMatter = false))
assert(ctx.phase.changesParents, i"undeclared parent change at ${ctx.phase} for $this, was: $info, now: $info1")
val privateWithin1 = if (privateWithin != null) privateWithin else this.privateWithin
val annotations1 = if (annotations != null) annotations else this.annotations
val d = ctx.SymDenotation(symbol, owner, name, initFlags1, info1, privateWithin1)
d.annotations = annotations1
d.registeredCompanion = registeredCompanion
d
}
/** Copy mamberNames and baseData caches from given denotation, provided
* they are valid at given `phase`.
*/
def copyCaches(from: SymDenotation, phase: Phase)(implicit ctx: Context): this.type = this
/** Are `info1` and `info2` ClassInfo types with different parents?
* @param completersMatter if `true`, consider parents changed if `info1` or `info2 `is a type completer
*/
protected def changedClassParents(info1: Type, info2: Type, completersMatter: Boolean): Boolean =
info2 match {
case info2: ClassInfo =>
info1 match {
case info1: ClassInfo => info1.classParents ne info2.classParents
case _ => completersMatter
}
case _ => completersMatter
}
override def initial: SymDenotation = super.initial.asSymDenotation
/** Install this denotation as the result of the given denotation transformer. */
override def installAfter(phase: DenotTransformer)(implicit ctx: Context): Unit =
super.installAfter(phase)
/** 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.
*/
override def transformAfter(phase: DenotTransformer, f: SymDenotation => SymDenotation)(implicit ctx: Context): Unit =
super.transformAfter(phase, f)
/** If denotation is private, remove the Private flag and expand the name if necessary */
def ensureNotPrivate(implicit ctx: Context): SymDenotation =
if (is(Private))
copySymDenotation(name = expandedName, initFlags = this.flags &~ Private)
else this
}
/** The contents of a class definition during a period
*/
class ClassDenotation private[SymDenotations] (
symbol: Symbol,
maybeOwner: Symbol,
name: Name,
initFlags: FlagSet,
initInfo: Type,
initPrivateWithin: Symbol)
extends SymDenotation(symbol, maybeOwner, name, initFlags, initInfo, initPrivateWithin) {
import util.LRUCache
// ----- caches -------------------------------------------------------
private[this] var myTypeParams: List[TypeSymbol] = null
private[this] var fullNameCache: SimpleIdentityMap[QualifiedNameKind, Name] = SimpleIdentityMap.Empty
private[this] var myMemberCache: LRUCache[Name, PreDenotation] = null
private[this] var myMemberCachePeriod: Period = Nowhere
/** A cache from types T to baseType(T, C) */
type BaseTypeMap = java.util.IdentityHashMap[CachedType, Type]
private[this] var myBaseTypeCache: BaseTypeMap = null
private[this] var myBaseTypeCachePeriod: Period = Nowhere
private var baseDataCache: BaseData = BaseData.None
private var memberNamesCache: MemberNames = MemberNames.None
private def memberCache(implicit ctx: Context): LRUCache[Name, PreDenotation] = {
if (myMemberCachePeriod != ctx.period) {
myMemberCache = new LRUCache
myMemberCachePeriod = ctx.period
}
myMemberCache
}
private def baseTypeCache(implicit ctx: Context): BaseTypeMap = {
if (!ctx.hasSameBaseTypesAs(myBaseTypeCachePeriod)) {
myBaseTypeCache = new BaseTypeMap
myBaseTypeCachePeriod = ctx.period
}
myBaseTypeCache
}
private def invalidateBaseDataCache() = {
baseDataCache.invalidate()
baseDataCache = BaseData.None
}
private def invalidateMemberNamesCache() = {
memberNamesCache.invalidate()
memberNamesCache = MemberNames.None
}
def invalidateBaseTypeCache(): Unit = {
myBaseTypeCache = null
myBaseTypeCachePeriod = Nowhere
}
override def copyCaches(from: SymDenotation, phase: Phase)(implicit ctx: Context): this.type = {
from match {
case from: ClassDenotation =>
if (from.memberNamesCache.isValidAt(phase)) memberNamesCache = from.memberNamesCache
if (from.baseDataCache.isValidAt(phase)) {
baseDataCache = from.baseDataCache
myBaseTypeCache = from.baseTypeCache
}
case _ =>
}
this
}
// ----- denotation fields and accessors ------------------------------
if (initFlags.is(Module, butNot = Package))
assert(name.is(ModuleClassName), s"module naming inconsistency: ${name.debugString}")
/** The symbol asserted to have type ClassSymbol */
def classSymbol: ClassSymbol = symbol.asInstanceOf[ClassSymbol]
/** The info asserted to have type ClassInfo */
def classInfo(implicit ctx: Context): ClassInfo = info.asInstanceOf[ClassInfo]
/** The type parameters in this class, in the order they appear in the current
* scope `decls`. This might be temporarily the incorrect order when
* reading Scala2 pickled info. The problem is fixed by `ensureTypeParamsInCorrectOrder`,
* which is called once an unpickled symbol has been completed.
*/
private def typeParamsFromDecls(implicit ctx: Context) =
unforcedDecls.filter(sym =>
sym.is(TypeParam) && sym.owner == symbol).asInstanceOf[List[TypeSymbol]]
/** The type parameters of this class */
override final def typeParams(implicit ctx: Context): List[TypeSymbol] = {
if (myTypeParams == null)
myTypeParams =
if (ctx.erasedTypes || is(Module)) Nil // fast return for modules to avoid scanning package decls
else {
val di = initial
if (this ne di) di.typeParams
else infoOrCompleter match {
case info: TypeParamsCompleter => info.completerTypeParams(symbol)
case _ => typeParamsFromDecls
}
}
myTypeParams
}
override protected[dotc] final def info_=(tp: Type): Unit = {
if (changedClassParents(infoOrCompleter, tp, completersMatter = true))
invalidateBaseDataCache()
invalidateMemberNamesCache()
myTypeParams = null // changing the info might change decls, and with it typeParams
super.info_=(tp)
}
def classParents(implicit ctx: Context): List[Type] = info match {
case classInfo: ClassInfo => classInfo.parents
case _ => Nil
}
/** The symbol of the superclass, NoSymbol if no superclass exists */
def superClass(implicit ctx: Context): Symbol = classParents match {
case parent :: _ =>
val cls = parent.classSymbol
if (cls.is(Trait)) NoSymbol else cls
case _ =>
NoSymbol
}
/** The explicitly given self type (self types of modules are assumed to be
* explcitly given here).
*/
def givenSelfType(implicit ctx: Context): Type = classInfo.selfInfo match {
case tp: Type => tp
case self: Symbol => self.info
}
// ------ class-specific operations -----------------------------------
private[this] var myThisType: Type = null
/** The this-type depends on the kind of class:
* - for a package class `p`: ThisType(TypeRef(Noprefix, p))
* - for a module class `m`: A term ref to m's source module.
* - for all other classes `c` with owner `o`: ThisType(TypeRef(o.thisType, c))
*/
override def thisType(implicit ctx: Context): Type = {
if (myThisType == null) myThisType = computeThisType
myThisType
}
private def computeThisType(implicit ctx: Context): Type = {
val cls = symbol.asType
val pre = if (this.is(Package)) NoPrefix else owner.thisType
ThisType.raw(TypeRef(pre, cls))
}
private[this] var myTypeRef: TypeRef = null
override def typeRef(implicit ctx: Context): TypeRef = {
if (myTypeRef == null) myTypeRef = super.typeRef
myTypeRef
}
override def appliedRef(implicit ctx: Context): Type = classInfo.appliedRef
private def baseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = {
if (!baseDataCache.isValid) baseDataCache = BaseData.newCache()
baseDataCache(this)
}
/** The base classes of this class in linearization order,
* with the class itself as first element.
*/
def baseClasses(implicit onBehalf: BaseData, ctx: Context): List[ClassSymbol] =
baseData._1
/** A bitset that contains the superId's of all base classes */
private def baseClassSet(implicit onBehalf: BaseData, ctx: Context): BaseClassSet =
baseData._2
def computeBaseData(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet) = {
def emptyParentsExpected =
is(Package) || (symbol == defn.AnyClass) || ctx.erasedTypes && (symbol == defn.ObjectClass)
if (classParents.isEmpty && !emptyParentsExpected)
onBehalf.signalProvisional()
val builder = new BaseDataBuilder
def traverse(parents: List[Type]): Unit = parents match {
case p :: parents1 =>
p.classSymbol match {
case pcls: ClassSymbol => builder.addAll(pcls.baseClasses)
case _ => assert(isRefinementClass || p.isError || ctx.mode.is(Mode.Interactive), s"$this has non-class parent: $p")
}
traverse(parents1)
case nil =>
}
traverse(classParents)
(classSymbol :: builder.baseClasses, builder.baseClassSet)
}
final override def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean =
!isAbsent &&
base.isClass &&
( (symbol eq base)
|| (baseClassSet contains base)
)
final override def isSubClass(base: Symbol)(implicit ctx: Context): Boolean =
derivesFrom(base) ||
base.isClass && (
(symbol eq defn.NothingClass) ||
(symbol eq defn.NullClass) && (base ne defn.NothingClass))
/** Is it possible that a class inherits both `this` and `that`?
*
* @note The test is based on single-class inheritance and the closed
* hierarchy of final classes.
*
* @return The result may contain false positives, but never false negatives.
*/
final def mayHaveCommonChild(that: ClassSymbol)(implicit ctx: Context): Boolean =
!this.is(Final) && !that.is(Final) && (this.is(Trait) || that.is(Trait)) ||
this.derivesFrom(that) || that.derivesFrom(this.symbol)
final override def typeParamCreationFlags: FlagSet = ClassTypeParamCreationFlags
/** Hook to do a pre-enter test. Overridden in PackageDenotation */
protected def proceedWithEnter(sym: Symbol, mscope: MutableScope)(implicit ctx: Context): Boolean = true
/** Enter a symbol in current scope, and future scopes of same denotation.
* Note: We require that this does not happen after the first time
* someone does a findMember on a subclass.
* @param scope The scope in which symbol should be entered.
* If this is EmptyScope, the scope is `decls`.
*/
def enter(sym: Symbol, scope: Scope = EmptyScope)(implicit ctx: Context): Unit = {
val mscope = scope match {
case scope: MutableScope =>
// if enter gets a scope as an argument,
// than this is a scope that will eventually become decls of this symbol.
// And this should only happen if this is first time the scope of symbol
// is computed, ie symbol yet has no future.
assert(this.nextInRun.validFor.code <= this.validFor.code)
scope
case _ => unforcedDecls.openForMutations
}
if (proceedWithEnter(sym, mscope)) {
enterNoReplace(sym, mscope)
val nxt = this.nextInRun
if (nxt.validFor.code > this.validFor.code) {
this.nextInRun.asSymDenotation.asClass.enter(sym)
}
if (defn.isScalaShadowingPackageClass(sym.owner))
defn.ScalaPackageClass.enter(sym) // ScalaShadowing members are mirrored in ScalaPackage
}
}
/** Enter a symbol in given `scope` without potentially replacing the old copy. */
def enterNoReplace(sym: Symbol, scope: MutableScope)(implicit ctx: Context): Unit = {
scope.enter(sym)
if (myMemberCache != null) myMemberCache.invalidate(sym.name)
if (!sym.flagsUNSAFE.is(Private)) invalidateMemberNamesCache()
}
/** Replace symbol `prev` (if defined in current class) by symbol `replacement`.
* If `prev` is not defined in current class, do nothing.
* @pre `prev` and `replacement` have the same name.
*/
def replace(prev: Symbol, replacement: Symbol)(implicit ctx: Context): Unit = {
unforcedDecls.openForMutations.replace(prev, replacement)
if (myMemberCache != null) myMemberCache.invalidate(replacement.name)
}
/** Delete symbol from current scope.
* Note: We require that this does not happen after the first time
* someone does a findMember on a subclass.
*/
def delete(sym: Symbol)(implicit ctx: Context): Unit = {
info.decls.openForMutations.unlink(sym)
if (myMemberCache != null) myMemberCache.invalidate(sym.name)
if (!sym.flagsUNSAFE.is(Private)) invalidateMemberNamesCache()
}
/** Make sure the type parameters of this class appear in the order given
* by `typeParams` in the scope of the class. Reorder definitions in scope if necessary.
*/
def ensureTypeParamsInCorrectOrder()(implicit ctx: Context): Unit = {
val tparams = typeParams
if (!ctx.erasedTypes && !typeParamsFromDecls.corresponds(tparams)(_.name == _.name)) {
val decls = info.decls
val decls1 = newScope
for (tparam <- typeParams) decls1.enter(decls.lookup(tparam.name))
for (sym <- decls) if (!tparams.contains(sym)) decls1.enter(sym)
info = classInfo.derivedClassInfo(decls = decls1)
myTypeParams = null
}
}
/** All members of this class that have the given name.
* The elements of the returned pre-denotation all
* have existing symbols.
*/
final def membersNamed(name: Name)(implicit ctx: Context): PreDenotation = {
val privates = info.decls.denotsNamed(name, selectPrivate)
privates union nonPrivateMembersNamed(name).filterDisjoint(privates)
}
/** All non-private members of this class that have the given name.
* The elements of the returned pre-denotation all
* have existing symbols.
* @param inherited The method is called on a parent class from computeNPMembersNamed
*/
final def nonPrivateMembersNamed(name: Name)(implicit ctx: Context): PreDenotation = {
Stats.record("nonPrivateMembersNamed")
if (Config.cacheMembersNamed) {
var denots: PreDenotation = memberCache lookup name
if (denots == null) {
denots = computeNPMembersNamed(name)
memberCache.enter(name, denots)
} else if (Config.checkCacheMembersNamed) {
val denots1 = computeNPMembersNamed(name)
assert(denots.exists == denots1.exists, s"cache inconsistency: cached: $denots, computed $denots1, name = $name, owner = $this")
}
denots
} else computeNPMembersNamed(name)
}
private[core] def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation = /*>|>*/ Stats.track("computeNPMembersNamed") /*<|<*/ {
Stats.record("computeNPMembersNamed after fingerprint")
ensureCompleted()
val ownDenots = info.decls.denotsNamed(name, selectNonPrivate)
if (debugTrace) // DEBUG
println(s"$this.member($name), ownDenots = $ownDenots")
def collect(denots: PreDenotation, parents: List[Type]): PreDenotation = parents match {
case p :: ps =>
val denots1 = collect(denots, ps)
p.classSymbol.denot match {
case parentd: ClassDenotation =>
denots1.union(
parentd.nonPrivateMembersNamed(name)
.mapInherited(ownDenots, denots1, thisType))
case _ =>
denots1
}
case nil =>
denots
}
if (name.isConstructorName) ownDenots
else collect(ownDenots, classParents)
}
override final def findMember(name: Name, pre: Type, required: FlagSet, excluded: FlagSet)(implicit ctx: Context): Denotation = {
val raw = if (excluded.is(Private)) nonPrivateMembersNamed(name) else membersNamed(name)
raw.filterWithFlags(required, excluded).asSeenFrom(pre).toDenot(pre)
}
/** Compute tp.baseType(this) */
final def baseTypeOf(tp: Type)(implicit ctx: Context): Type = {
val btrCache = baseTypeCache
def inCache(tp: Type) = btrCache.get(tp) != null
def record(tp: CachedType, baseTp: Type) = {
if (Stats.monitored) {
Stats.record("basetype cache entries")
if (!baseTp.exists) Stats.record("basetype cache NoTypes")
}
if (!tp.isProvisional)
btrCache.put(tp, baseTp)
else
btrCache.remove(tp) // Remove any potential sentinel value
}
def ensureAcyclic(baseTp: Type) = {
if (baseTp `eq` NoPrefix) throw CyclicReference(this)
baseTp
}
def recur(tp: Type): Type = try {
tp match {
case tp: CachedType =>
val baseTp = btrCache.get(tp)
if (baseTp != null) return ensureAcyclic(baseTp)
case _ =>
}
if (Stats.monitored) {
Stats.record("computeBaseType, total")
Stats.record(s"computeBaseType, ${tp.getClass}")
}
val normed = tp.tryNormalize
if (normed.exists) return recur(normed)
tp match {
case tp @ TypeRef(prefix, _) =>
def foldGlb(bt: Type, ps: List[Type]): Type = ps match {
case p :: ps1 => foldGlb(bt & recur(p), ps1)
case _ => bt
}
def computeTypeRef = {
btrCache.put(tp, NoPrefix)
val tpSym = tp.symbol
tpSym.denot match {
case clsd: ClassDenotation =>
def isOwnThis = prefix match {
case prefix: ThisType => prefix.cls `eq` clsd.owner
case NoPrefix => true
case _ => false
}
val baseTp =
if (tpSym eq symbol)
tp
else if (isOwnThis)
if (clsd.baseClassSet.contains(symbol))
if (symbol.isStatic && symbol.typeParams.isEmpty) symbol.typeRef
else foldGlb(NoType, clsd.classParents)
else NoType
else
recur(clsd.typeRef).asSeenFrom(prefix, clsd.owner)
record(tp, baseTp)
baseTp
case _ =>
val superTp = tp.superType
val baseTp = recur(superTp)
if (inCache(superTp))
record(tp, baseTp)
else
btrCache.remove(tp)
baseTp
}
}
computeTypeRef
case tp @ AppliedType(tycon, args) =>
def computeApplied = {
btrCache.put(tp, NoPrefix)
val baseTp =
if (tycon.typeSymbol eq symbol) tp
else (tycon.typeParams: @unchecked) match {
case LambdaParam(_, _) :: _ =>
recur(tp.superType)
case tparams: List[Symbol @unchecked] =>
recur(tycon).substApprox(tparams, args)
}
record(tp, baseTp)
baseTp
}
computeApplied
case tp: TypeParamRef => // uncachable, since baseType depends on context bounds
recur(ctx.typeComparer.bounds(tp).hi)
case tp: TypeProxy =>
def computeTypeProxy = {
val superTp = tp.superType
val baseTp = recur(superTp)
tp match {
case tp: CachedType if baseTp.exists && inCache(superTp) =>
record(tp, baseTp)
case _ =>
}
baseTp
}
computeTypeProxy
case tp: AndOrType =>
def computeAndOrType = {
val tp1 = tp.tp1
val tp2 = tp.tp2
val baseTp =
if (symbol.isStatic && tp.derivesFrom(symbol) && symbol.typeParams.isEmpty)
symbol.typeRef
else {
val baseTp1 = recur(tp1)
val baseTp2 = recur(tp2)
val combined = if (tp.isAnd) baseTp1 & baseTp2 else baseTp1 | baseTp2
combined match {
case combined: AndOrType
if (combined.tp1 eq tp1) && (combined.tp2 eq tp2) && (combined.isAnd == tp.isAnd) => tp
case _ => combined
}
}
if (baseTp.exists && inCache(tp1) && inCache(tp2)) record(tp, baseTp)
baseTp
}
computeAndOrType
case JavaArrayType(_) if symbol == defn.ObjectClass =>
this.typeRef
case _ =>
NoType
}
}
catch {
case ex: Throwable =>
btrCache.remove(tp)
throw ex
}
/*>|>*/ trace.onDebug(s"$tp.baseType($this)") /*<|<*/ {
Stats.record("baseTypeOf")
recur(tp)
}
}
def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] =
if (this.is(PackageClass) || !Config.cacheMemberNames)
computeMemberNames(keepOnly) // don't cache package member names; they might change
else {
if (!memberNamesCache.isValid) memberNamesCache = MemberNames.newCache()
memberNamesCache(keepOnly, this)
}
def computeMemberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = {
var names = Set[Name]()
def maybeAdd(name: Name) = if (keepOnly(thisType, name)) names += name
try {
for (p <- classParents)
for (name <- p.classSymbol.asClass.memberNames(keepOnly))
maybeAdd(name)
val ownSyms =
if (keepOnly eq implicitFilter)
if (this.is(Package)) Iterator.empty
// implicits in package objects are added by the overriding `memberNames` in `PackageClassDenotation`
else info.decls.iterator.filter(_.isOneOf(DelegateOrGivenOrImplicit))
else info.decls.iterator
for (sym <- ownSyms) maybeAdd(sym.name)
names
}
catch {
case ex: Throwable =>
handleRecursive("member names", i"of $this", ex)
}
}
override final def fullNameSeparated(kind: QualifiedNameKind)(implicit ctx: Context): Name = {
val cached = fullNameCache(kind)
if (cached != null) cached
else {
val fn = super.fullNameSeparated(kind)
fullNameCache = fullNameCache.updated(kind, fn)
fn
}
}
// to avoid overloading ambiguities
override def fullName(implicit ctx: Context): Name = super.fullName
override def primaryConstructor(implicit ctx: Context): Symbol = {
def constrNamed(cname: TermName) = info.decls.denotsNamed(cname).last.symbol
// denotsNamed returns Symbols in reverse order of occurrence
if (this.is(Package)) NoSymbol
else constrNamed(nme.CONSTRUCTOR).orElse(constrNamed(nme.TRAIT_CONSTRUCTOR))
}
/** The term parameter accessors of this class.
* Both getters and setters are returned in this list.
*/
def paramAccessors(implicit ctx: Context): List[Symbol] =
unforcedDecls.filter(_.is(ParamAccessor))
/** If this class has the same `decls` scope reference in `phase` and
* `phase.next`, install a new denotation with a cloned scope in `phase.next`.
*/
def ensureFreshScopeAfter(phase: DenotTransformer)(implicit ctx: Context): Unit =
if (ctx.phaseId != phase.next.id) ensureFreshScopeAfter(phase)(ctx.withPhase(phase.next))
else {
val prevCtx = ctx.withPhase(phase)
val prevClassInfo = current(prevCtx).asInstanceOf[ClassDenotation].classInfo(prevCtx)
val ClassInfo(pre, _, ps, decls, selfInfo) = classInfo
if (prevClassInfo.decls eq decls)
copySymDenotation(info = ClassInfo(pre, classSymbol, ps, decls.cloneScope, selfInfo))
.copyCaches(this, phase.next)
.installAfter(phase)
}
private[this] var myCompanion: Symbol = NoSymbol
/** Register companion class */
override def registerCompanion(companion: Symbol)(implicit ctx: Context) =
if (companion.isClass && !unforcedIsAbsent && !companion.unforcedIsAbsent)
myCompanion = companion
override def registeredCompanion(implicit ctx: Context) = { ensureCompleted(); myCompanion }
override def registeredCompanion_=(c: Symbol) = { myCompanion = c }
}
/** The denotation of a package class.
* It overrides ClassDenotation to take account of package objects when looking for members
*/
final class PackageClassDenotation private[SymDenotations] (
symbol: Symbol,
ownerIfExists: Symbol,
name: Name,
initFlags: FlagSet,
initInfo: Type,
initPrivateWithin: Symbol)
extends ClassDenotation(symbol, ownerIfExists, name, initFlags, initInfo, initPrivateWithin) {
private[this] var packageObjsCache: List[ClassDenotation] = _
private[this] var packageObjsRunId: RunId = NoRunId
/** The package objects in this class */
def packageObjs(implicit ctx: Context): List[ClassDenotation] = {
if (packageObjsRunId != ctx.runId) {
packageObjsRunId = ctx.runId
packageObjsCache = Nil // break cycle in case we are looking for package object itself
packageObjsCache = {
val pkgObjBuf = new mutable.ListBuffer[ClassDenotation]
for (sym <- info.decls) { // don't use filter, since that loads classes with `$`s in their name
val denot = sym.lastKnownDenotation // don't use `sym.denot`, as this brings forward classes too early
if (denot.isType && denot.name.isPackageObjectName)
pkgObjBuf += sym.asClass.classDenot
}
pkgObjBuf.toList
}
}
packageObjsCache
}
/** The package object (as a term symbol) in this package that might contain
* `sym` as a member.
*/
def packageObjFor(sym: Symbol)(implicit ctx: Context): Symbol = {
val owner = sym.maybeOwner
if (owner.is(Package)) NoSymbol
else if (owner.isPackageObject) owner.sourceModule
else // owner could be class inherited by package object (until package object inheritance is removed)
packageObjs.find(_.name == packageTypeName) match {
case Some(pobj) => pobj.sourceModule
case _ => NoSymbol
}
}
/** Looks in both the package object and the package for members. The precise algorithm
* is as follows:
*
* If this is the scala package look in the package first, and if nothing is found
* there, look in the package object second. Otherwise, look in the both the package object
* and the package and form a union of the results.
*
* The reason for the special treatment of the scala package is that if we
* complete it too early, we freeze its superclass Any, so that no members can
* be entered in it. As a consequence, there should be no entry in the scala package
* object that hides a class or object in the scala package of the same name, because
* the behavior would then be unintuitive for such members.
*/
override def computeNPMembersNamed(name: Name)(implicit ctx: Context): PreDenotation = {
def recur(pobjs: List[ClassDenotation], acc: PreDenotation): PreDenotation = pobjs match {
case pcls :: pobjs1 =>
if (pcls.isCompleting) recur(pobjs1, acc)
else {
// A package object inherits members from `Any` and `Object` which
// should not be accessible from the package prefix.
val pmembers = pcls.computeNPMembersNamed(name).filterWithPredicate { d =>
val owner = d.symbol.maybeOwner
(owner ne defn.AnyClass) && (owner ne defn.ObjectClass)
}
recur(pobjs1, acc.union(pmembers))
}
case nil =>
val directMembers = super.computeNPMembersNamed(name)
if (acc.exists) acc.union(directMembers.filterWithPredicate(!_.symbol.isAbsent))
else directMembers
}
if (symbol `eq` defn.ScalaPackageClass) {
val denots = super.computeNPMembersNamed(name)
if (denots.exists) denots
else recur(packageObjs, NoDenotation)
}
else recur(packageObjs, NoDenotation)
}
/** The union of the member names of the package and the package object */
override def memberNames(keepOnly: NameFilter)(implicit onBehalf: MemberNames, ctx: Context): Set[Name] = {
def recur(pobjs: List[ClassDenotation], acc: Set[Name]): Set[Name] = pobjs match {
case pcls :: pobjs1 =>
recur(pobjs1, acc.union(pcls.memberNames(keepOnly)))
case nil =>
acc
}
recur(packageObjs, super.memberNames(keepOnly))
}
/** If another symbol with the same name is entered, unlink it,
* and, if symbol is a package object, invalidate the packageObj cache.
* @return `sym` is not already entered
*/
override def proceedWithEnter(sym: Symbol, mscope: MutableScope)(implicit ctx: Context): Boolean = {
val entry = mscope.lookupEntry(sym.name)
if (entry != null) {
if (entry.sym == sym) return false
mscope.unlink(entry)
if (sym.name.isPackageObjectName) packageObjsRunId = NoRunId
}
true
}
/** Unlink all package members defined in `file` in a previous run. */
def unlinkFromFile(file: AbstractFile)(implicit ctx: Context): Unit = {
val scope = unforcedDecls.openForMutations
for (sym <- scope.toList.iterator) {
// We need to be careful to not force the denotation of `sym` here,
// otherwise it will be brought forward to the current run.
if (sym.defRunId != ctx.runId && sym.isClass && sym.asClass.assocFile == file)
scope.unlink(sym, sym.lastKnownDenotation.name)
}
}
}
@sharable object NoDenotation
extends SymDenotation(NoSymbol, NoSymbol, "".toTermName, Permanent, NoType) {
override def isType: Boolean = false
override def isTerm: Boolean = false
override def exists: Boolean = false
override def owner: Symbol = throw new AssertionError("NoDenotation.owner")
override def computeAsSeenFrom(pre: Type)(implicit ctx: Context): SingleDenotation = this
override def mapInfo(f: Type => Type)(implicit ctx: Context): SingleDenotation = this
NoSymbol.denot = this
validFor = Period.allInRun(NoRunId)
}
// ---- Completion --------------------------------------------------------
/** Instances of LazyType are carried by uncompleted symbols.
* Note: LazyTypes double up as (constant) functions from Symbol and
* from (TermSymbol, ClassSymbol) to LazyType. That way lazy types can be
* directly passed to symbol creation methods in Symbols that demand instances
* of these function types.
*/
abstract class LazyType extends UncachedGroundType
with (Symbol => LazyType)
with ((TermSymbol, ClassSymbol) => LazyType) { self =>
/** Sets all missing fields of given denotation */
def complete(denot: SymDenotation)(implicit ctx: Context): Unit
def apply(sym: Symbol): LazyType = this
def apply(module: TermSymbol, modcls: ClassSymbol): LazyType = this
private[this] val NoSymbolFn = (_: Context) => NoSymbol
private[this] var myDecls: Scope = EmptyScope
private[this] var mySourceModuleFn: Context => Symbol = NoSymbolFn
private[this] var myModuleClassFn: Context => Symbol = NoSymbolFn
/** A proxy to this lazy type that keeps the complete operation
* but provides fresh slots for scope/sourceModule/moduleClass
*/
def proxy: LazyType = new LazyType {
override def complete(denot: SymDenotation)(implicit ctx: Context) = self.complete(denot)
}
/** The type parameters computed by the completer before completion has finished */
def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeParamInfo] =
if (sym.is(Touched)) Nil // return `Nil` instead of throwing a cyclic reference
else sym.info.typeParams
def decls: Scope = myDecls
def sourceModule(implicit ctx: Context): Symbol = mySourceModuleFn(ctx)
def moduleClass(implicit ctx: Context): Symbol = myModuleClassFn(ctx)
def withDecls(decls: Scope): this.type = { myDecls = decls; this }
def withSourceModule(sourceModuleFn: Context => Symbol): this.type = { mySourceModuleFn = sourceModuleFn; this }
def withModuleClass(moduleClassFn: Context => Symbol): this.type = { myModuleClassFn = moduleClassFn; this }
}
/** A subtrait of LazyTypes where completerTypeParams yields a List[TypeSymbol], which
* should be completed independently of the info.
*/
trait TypeParamsCompleter extends LazyType {
override def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] =
unsupported("completerTypeParams") // should be abstract, but Scala-2 will then compute the wrong type for it
}
/** A missing completer */
@sharable class NoCompleter extends LazyType {
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = unsupported("complete")
}
object NoCompleter extends NoCompleter
/** A lazy type for modules that points to the module class.
* Needed so that `moduleClass` works before completion.
* Completion of modules is always completion of the underlying
* module class, followed by copying the relevant fields to the module.
*/
class ModuleCompleter(_moduleClass: ClassSymbol) extends LazyType {
override def moduleClass(implicit ctx: Context): ClassSymbol = _moduleClass
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
val from = moduleClass.denot.asClass
denot.setFlag(from.flags.toTermFlags & RetainedModuleValFlags)
denot.annotations = from.annotations filter (_.appliesToModule)
// !!! ^^^ needs to be revised later. The problem is that annotations might
// only apply to the module but not to the module class. The right solution
// is to have the module class completer set the annotations of both the
// class and the module.
denot.info = moduleClass.typeRef
denot.privateWithin = from.privateWithin
}
}
/** A completer for missing references */
class StubInfo() extends LazyType {
def initializeToDefaults(denot: SymDenotation, errMsg: => Message)(implicit ctx: Context): Unit = {
denot.info = denot match {
case denot: ClassDenotation =>
ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope)
case _ =>
ErrorType(errMsg)
}
denot.privateWithin = NoSymbol
}
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = {
val sym = denot.symbol
def errMsg = BadSymbolicReference(denot)
ctx.error(errMsg, sym.sourcePos)
if (ctx.debug) throw new scala.Error()
initializeToDefaults(denot, errMsg)
}
}
// ---- Caches for inherited info -----------------------------------------
/** Base trait for caches that keep info dependent on inherited classes */
trait InheritedCache {
/** Is the cache valid in current period? */
def isValid(implicit ctx: Context): Boolean
/** is the cache valid in current run at given phase? */
def isValidAt(phase: Phase)(implicit ctx: Context): Boolean
/** Render invalid this cache and all cache that depend on it */
def invalidate(): Unit
}
/** A cache for sets of member names, indexed by a NameFilter */
trait MemberNames extends InheritedCache {
def apply(keepOnly: NameFilter, clsd: ClassDenotation)
(implicit onBehalf: MemberNames, ctx: Context): Set[Name]
}
object MemberNames {
implicit val None: MemberNames = new InvalidCache with MemberNames {
def apply(keepOnly: NameFilter, clsd: ClassDenotation)(implicit onBehalf: MemberNames, ctx: Context) = ???
}
def newCache()(implicit ctx: Context): MemberNames = new MemberNamesImpl(ctx.period)
}
/** A cache for baseclasses, as a sequence in linearization order and as a set that
* can be queried efficiently for containment.
*/
trait BaseData extends InheritedCache {
def apply(clsd: ClassDenotation)
(implicit onBehalf: BaseData, ctx: Context): (List[ClassSymbol], BaseClassSet)
def signalProvisional(): Unit
}
object BaseData {
implicit val None: BaseData = new InvalidCache with BaseData {
def apply(clsd: ClassDenotation)(implicit onBehalf: BaseData, ctx: Context) = ???
def signalProvisional() = ()
}
def newCache()(implicit ctx: Context): BaseData = new BaseDataImpl(ctx.period)
}
private abstract class InheritedCacheImpl(val createdAt: Period) extends InheritedCache {
protected def sameGroup(p1: Phase, p2: Phase): Boolean
private[this] var dependent: WeakHashMap[InheritedCache, Unit] = null
private[this] var checkedPeriod: Period = Nowhere
protected def invalidateDependents() = {
if (dependent != null) {
val it = dependent.keySet.iterator()
while (it.hasNext()) it.next().invalidate()
}
dependent = null
}
protected def addDependent(dep: InheritedCache) = {
if (dependent == null) dependent = new WeakHashMap
dependent.put(dep, ())
}
def isValidAt(phase: Phase)(implicit ctx: Context) =
checkedPeriod == ctx.period ||
createdAt.runId == ctx.runId &&
createdAt.phaseId < ctx.phases.length &&
sameGroup(ctx.phases(createdAt.phaseId), phase) &&
{ checkedPeriod = ctx.period; true }
}
private class InvalidCache extends InheritedCache {
def isValid(implicit ctx: Context) = false
def isValidAt(phase: Phase)(implicit ctx: Context) = false
def invalidate(): Unit = ()
}
private class MemberNamesImpl(createdAt: Period) extends InheritedCacheImpl(createdAt) with MemberNames {
private[this] var cache: SimpleIdentityMap[NameFilter, Set[Name]] = SimpleIdentityMap.Empty
final def isValid(implicit ctx: Context): Boolean =
cache != null && isValidAt(ctx.phase)
private[this] var locked = false
/** Computing parent member names might force parents, which could invalidate
* the cache itself. In that case we should cancel invalidation and
* proceed as usual. However, all cache entries should be cleared.
*/
def invalidate(): Unit =
if (cache != null)
if (locked) cache = SimpleIdentityMap.Empty
else {
cache = null
invalidateDependents()
}
def apply(keepOnly: NameFilter, clsd: ClassDenotation)(implicit onBehalf: MemberNames, ctx: Context) = {
assert(isValid)
val cached = cache(keepOnly)
try
if (cached != null) cached
else {
locked = true
val computed =
try clsd.computeMemberNames(keepOnly)(this, ctx)
finally locked = false
cache = cache.updated(keepOnly, computed)
computed
}
finally addDependent(onBehalf)
}
def sameGroup(p1: Phase, p2: Phase) = p1.sameMembersStartId == p2.sameMembersStartId
}
private class BaseDataImpl(createdAt: Period) extends InheritedCacheImpl(createdAt) with BaseData {
private[this] var cache: (List[ClassSymbol], BaseClassSet) = null
private[this] var valid = true
private[this] var locked = false
private[this] var provisional = false
final def isValid(implicit ctx: Context): Boolean = valid && isValidAt(ctx.phase)
def invalidate(): Unit =
if (valid && !locked) {
cache = null
valid = false
invalidateDependents()
}
def signalProvisional() = provisional = true
def apply(clsd: ClassDenotation)(implicit onBehalf: BaseData, ctx: Context)
: (List[ClassSymbol], BaseClassSet) = {
assert(isValid)
try {
if (cache != null) cache
else {
if (locked) throw CyclicReference(clsd)
locked = true
provisional = false
val computed =
try clsd.computeBaseData(this, ctx)
finally locked = false
if (!provisional) cache = computed
else onBehalf.signalProvisional()
computed
}
}
finally addDependent(onBehalf)
}
def sameGroup(p1: Phase, p2: Phase) = p1.sameParentsStartId == p2.sameParentsStartId
}
class BaseClassSet(val classIds: Array[Int]) extends AnyVal {
def contains(sym: Symbol, limit: Int): Boolean = {
val id = sym.id
var i = 0
while (i < limit && classIds(i) != id) i += 1
i < limit && {
if (i > 0) {
val t = classIds(i)
classIds(i) = classIds(i - 1)
classIds(i - 1) = t
}
true
}
}
def contains(sym: Symbol): Boolean = contains(sym, classIds.length)
}
object BaseClassSet {
def apply(bcs: List[ClassSymbol]): BaseClassSet =
new BaseClassSet(bcs.toArray.map(_.id))
}
/** A class to combine base data from parent types */
class BaseDataBuilder {
private[this] var classes: List[ClassSymbol] = Nil
private[this] var classIds = new Array[Int](32)
private[this] var length = 0
private def resize(size: Int) = {
val classIds1 = new Array[Int](size)
System.arraycopy(classIds, 0, classIds1, 0, classIds.length min size)
classIds = classIds1
}
private def add(sym: Symbol): Unit = {
if (length == classIds.length) resize(length * 2)
classIds(length) = sym.id
length += 1
}
def addAll(bcs: List[ClassSymbol]): this.type = {
val len = length
bcs match {
case bc :: bcs1 =>
addAll(bcs1)
if (!new BaseClassSet(classIds).contains(bc, len)) {
add(bc)
classes = bc :: classes
}
case nil =>
}
this
}
def baseClassSet: BaseClassSet = {
if (length != classIds.length) resize(length)
new BaseClassSet(classIds)
}
def baseClasses: List[ClassSymbol] = classes
}
private val packageTypeName = ModuleClassName(nme.PACKAGE).toTypeName
@sharable private[this] var indent = 0 // for completions printing
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy