diff options
Diffstat (limited to 'src/dotty/tools/dotc/core/SymDenotations.scala')
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala new file mode 100644 index 000000000..8dbf43f00 --- /dev/null +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -0,0 +1,662 @@ +package dotty.tools.dotc +package core + +import Periods._, Contexts._, Symbols._, Denotations._, Names._, Annotations._ +import Types._, Flags._, Decorators._, Transformers._ +import Scopes.Scope +import collection.mutable +import collection.immutable.BitSet +import scala.reflect.io.AbstractFile +import Decorators.SymbolIteratorDecorator +import annotation.tailrec + +object SymDenotations { + + /** A denotation represents the contents of a definition + * during a period. + */ + abstract class SymDenotation(initFlags: FlagSet) extends SingleDenotation { + + def owner: Symbol + + def name: Name + + def symbol: Symbol + + def info: Type + + private[this] var _flags: FlagSet = initFlags + + def flags: FlagSet = _flags + + def flags_=(flags: FlagSet): Unit = + _flags |= flags + + def setFlags(flags: FlagSet): Unit = + _flags |= flags + + def resetFlags(flags: FlagSet): Unit = + _flags &~= flags + + private[this] var _privateWithin: Symbol = NoSymbol + + def privateWithin: Symbol = _privateWithin + + def privateWithin_=(sym: Symbol): Unit = + _privateWithin = sym + + final def isLoaded = _privateWithin != null + + private[this] var _annotations: List[Annotation] = Nil + + def annotations: List[Annotation] = _annotations + + def annotations_=(annots: List[Annotation]): Unit = + _annotations = annots + + def hasAnnotation(cls: Symbol) = dropOtherAnnotations(annotations, cls).nonEmpty + + @tailrec + private def dropOtherAnnotations(anns: List[Annotation], cls: Symbol): List[Annotation] = anns match { + case ann :: rest => if (ann matches cls) anns else dropOtherAnnotations(rest, cls) + case Nil => Nil + } + + final def isCompleted = _annotations != null + + /** is this denotation a class? */ + final def isClass: Boolean = symbol.isInstanceOf[ClassSymbol] + + /** Special case tests for flags that are known a-priori and do not need loading + * flags. + */ + final def isModule = _flags is Module + final def isModuleObj = _flags is ModuleObj + final def isModuleClass = _flags is ModuleClass + final def isPackage = _flags is Package + final def isPackageObj = _flags is PackageObj + final def isPackageClass = _flags is PackageClass + + /** is this denotation a method? */ + //def isMethod: Boolean = false + + def isSubClass(cls: Symbol)(implicit ctx: Context) = false + + def isNonBottomSubClass(cls: Symbol)(implicit ctx: Context) = false + + final def isSubClassOrCompanion(base: Symbol)(implicit ctx: Context): Boolean = + isNonBottomSubClass(base) || + isModuleClass && linkedClass.isNonBottomSubClass(base) + + final def enclosingSubClass(implicit ctx: Context) = { + val thissym = symbol + ctx.owner.ownersIterator.findSymbol(_.isSubClass(thissym)) + } + + /** is this symbol the result of an erroneous definition? */ + def isError: Boolean = false + + final def ownersIterator(implicit ctx: Context) = new Iterator[Symbol] { + private var current = symbol + def hasNext = current.exists + def next: Symbol = { + val result = current + current = current.owner + result + } + } + + final def hasTransOwner(sym: Symbol)(implicit ctx: Context): Boolean = { + var o = symbol + while ((o ne sym) && (o ne NoSymbol)) o = o.owner + (o eq sym) + } + + def withType(tp: Type): SymDenotation = ??? + + override protected def copy(s: Symbol, i: Type): SingleDenotation = new UniqueRefDenotation(s, i, validFor) + + def moduleClass(implicit ctx: Context): Symbol = + if (this.isModuleObj) info.typeSymbol else NoSymbol + + /** Desire to re-use the field in ClassSymbol which stores the source + * file to also store the classfile, but without changing the behavior + * of sourceFile (which is expected at least in the IDE only to + * return actual source code.) So sourceFile has classfiles filtered out. + */ + private def sourceFileOnly(file: AbstractFile): AbstractFile = + if ((file eq null) || (file.path endsWith ".class")) null else file + + private def binaryFileOnly(file: AbstractFile): AbstractFile = + if ((file eq null) || !(file.path endsWith ".class")) null else file + + final def topLevelClass(implicit ctx: Context): Symbol = + if (!(owner.isPackageClass)) owner.topLevelClass + else if (isClass) symbol + else moduleClass + + final def enclosingPackage(implicit ctx: Context): Symbol = + if (isPackageClass) symbol else owner.enclosingPackage + + def associatedFile(implicit ctx: Context): AbstractFile = topLevelClass.associatedFile + final def binaryFile(implicit ctx: Context): AbstractFile = binaryFileOnly(associatedFile) + final def sourceFile(implicit ctx: Context): AbstractFile = sourceFileOnly(associatedFile) + + /** Is this symbol a type or stable term? */ + final def isStable(implicit ctx: Context) = !( + isTerm && + this.is(UnstableValue, butNot = Stable) || + info.isVolatile && !hasAnnotation(defn.uncheckedStableClass) + ) + + final def matchingSymbol(inClass: Symbol, site: Type)(implicit ctx: Context): Symbol = { + var denot = inClass.info.nonPrivateDecl(name) + if (denot.isTerm) { + val targetType = site.memberInfo(this) + if (denot.isOverloaded) + denot = denot.atSignature(targetType.signature) + if (!(site.memberInfo(denot.asInstanceOf[SymDenotation]) matches targetType)) + denot = NoDenotation + } + denot.symbol + } + + final def overriddenSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol = + if (owner isSubClass inClass) matchingSymbol(inClass, owner.thisType) + else NoSymbol + + final def allOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] = + info.baseClasses.tail.iterator map overriddenSymbol filter (_.exists) + + /** Is this symbol defined in the same scope and compilation unit as `that` symbol? */ + private def isCoDefinedWith(that: Symbol)(implicit ctx: Context) = + (this.owner == that.owner) && + ( !(this.owner.isPackageClass) + || (this.sourceFile == null) + || (that.sourceFile == null) + || (this.sourceFile.path == that.sourceFile.path) // Cheap possibly wrong check, then expensive normalization + || (this.sourceFile.canonicalPath == that.sourceFile.canonicalPath) + ) + + def companionModule(implicit ctx: Context): Symbol = + owner.info.decl(name.toTermName).filter(_.isModule).symbol + + def companionClass(implicit ctx: Context): Symbol = + owner.info.decl(name.toTypeName).filter(_.isClass).symbol + + def linkedClass(implicit ctx: Context): Symbol = + if (this.isModuleClass) companionClass + else if (this.isClass) companionModule.moduleClass + else NoSymbol + + final def accessBoundary(base: Symbol)(implicit ctx: Context): Symbol = { + val fs = flags + if (fs is PrivateOrLocal) owner + else if (fs is StaticProtected) defn.RootClass + else if (privateWithin.exists && !ctx.phase.erasedTypes) privateWithin + else if (fs is Protected) base + else defn.RootClass + } + + final def isContainedIn(boundary: Symbol)(implicit ctx: Context): Boolean = + if (symbol eq boundary) true + else if (!this.exists || + (this.isPackageClass) && !(boundary.isPackageClass)) false + else owner.isContainedIn(boundary) + + def isAsAccessibleAs(that: Symbol)(implicit ctx: Context): Boolean = + (that.accessBoundary(NoSymbol) isContainedIn this.accessBoundary(NoSymbol)) && + (this.isStable || !that.isStable) + + def isAccessibleFrom(pre: Type, superAccess: Boolean = false)(implicit ctx: Context): Boolean = { + + def accessWithinLinked(boundary: Symbol) = { + val linked = boundary.linkedClass + (linked ne NoSymbol) && accessWithin(linked) + } + + /** Are we inside definition of `boundary`? */ + def accessWithin(boundary: Symbol) = + owner.hasTransOwner(boundary) && + (!(this is JavaDefined) || + owner.enclosingPackage == boundary.enclosingPackage) + + def isCorrectThisType(pre: Type): Boolean = pre match { + case ThisType(pclazz) => + (pclazz eq owner) || + (this is Protected) && pclazz.isNonBottomSubClass(owner) + case _ => false + } + + /** Is protected access to target symbol permitted? */ + def isProtectedAccessOK = { + def fail(diagnostics: () => String): Boolean = { + ctx match { + case ctx: DiagnosticsContext => ctx.diagnostics = diagnostics + case _ => + } + false + } + val cls = owner.enclosingSubClass + if (!cls.exists) + fail(() => + s"""Access to protected $this not permitted because + |enclosing ${ctx.enclClass.owner.showLocated} is not a subclass of + |${owner.showLocated} where target is defined""".stripMargin) + else if (!(isType || // allow accesses to types from arbitrary subclasses fixes #4737 + pre.widen.typeSymbol.isSubClassOrCompanion(cls) || + cls.isModuleClass && + pre.widen.typeSymbol.isSubClassOrCompanion(cls.linkedClass))) + fail(() => + s"""Access to protected $show not permitted because + |prefix type ${pre.widen.show} does not conform to + |${cls.showLocated} where the access takes place""".stripMargin) + else true + } + + (pre == NoPrefix) || { + val boundary = accessBoundary(owner) + + ( (boundary.isTerm + || (boundary eq defn.RootClass)) + || (accessWithin(boundary) || accessWithinLinked(boundary)) && + ( !(this is Local) + || (owner is ImplClass) // allow private local accesses to impl class members + || isCorrectThisType(pre) + ) + || (this is Protected) && + ( superAccess + || pre.isInstanceOf[ThisType] + || ctx.phase.erasedTypes + || isProtectedAccessOK + ) + ) + } + } + + def isNonValueClass(implicit ctx: Context): Boolean = + isClass && !isSubClass(defn.AnyValClass) + + def show(implicit ctx: Context): String = ??? + def showLocated(implicit ctx: Context): String = ??? + } + + class CompleteSymDenotation( + val symbol: Symbol, + val owner: Symbol, + val name: Name, + initFlags: FlagSet, + val info: Type + ) extends SymDenotation(initFlags) + + trait LazyCompletion extends SymDenotation { + privateWithin = null + annotations = null + + override final def flags = { + if (!isLoaded) tryLoad() + super.flags + } + + override final def privateWithin = { + if (!isLoaded) tryLoad() + super.privateWithin + } + + override final def annotations: List[Annotation] = { + val annots = super.annotations + if (annots != null) annots else { tryComplete(); annotations } + } + + protected def tryLoad(): Unit = try { + if (flags is Locked) throw new CyclicReference(symbol) + setFlags(Locked) + load() + } catch { + case ex: CyclicReference => handleCycle() + } finally { + flags &~= Locked + } + + protected def tryComplete() = try { + if (flags is Locked) throw new CyclicReference(symbol) + complete() + } catch { + case ex: CyclicReference => handleCycle() + } finally { + flags &~= Locked + } + + protected def handleCycle(): Unit + protected def load(): Unit + protected def complete(): Unit + } + + abstract class LazySymDenotation( + val symbol: Symbol, + val owner: Symbol, + val name: Name, + initFlags: FlagSet + ) extends SymDenotation(initFlags) with LazyCompletion { + + private var currentInfo: Type = null + + override def info = { + if (currentInfo == null) complete() + currentInfo + } + } + + abstract class ClassDenotation(initFlags: FlagSet, assocFile: AbstractFile)(implicit ctx: Context) + extends SymDenotation(initFlags) { + import NameFilter._ + import util.LRU8Cache + + val symbol: ClassSymbol + + def typeParams: List[TypeSymbol] + + def parents: List[TypeRef] + + def decls: Scope + + val info = ClassInfo(owner.thisType, this) + + override def associatedFile(implicit ctx: Context): AbstractFile = assocFile + + private var memberCacheVar: LRU8Cache[Name, DenotationSet] = null + + private def memberCache: LRU8Cache[Name, DenotationSet] = { + if (memberCacheVar == null) memberCacheVar = new LRU8Cache + memberCacheVar + } + + private var thisTypeCache: ThisType = null + + def thisType(implicit ctx: Context): Type = { + if (thisTypeCache == null) + thisTypeCache = ThisType(symbol) + thisTypeCache + } + + private var typeConstructorCache: Type = null + + def typeConstructor(implicit ctx: Context): Type = { + if (typeConstructorCache == null) + typeConstructorCache = NamedType(thisType, symbol.name) + typeConstructorCache + } + + /* + private var typeTemplateCache: Type = null + + def typeTemplate(implicit ctx: Context): Type = { + if (typeTemplateCache == null) + AppliedType.make(typeConstructor, typeParams map (_.typeConstructor)) + typeTemplateCache + } +*/ + private var baseClassesVar: List[ClassSymbol] = null + private var superClassBitsVar: BitSet = null + + private def computeSuperClassBits(implicit ctx: Context): Unit = { + val seen = new mutable.BitSet + val locked = new mutable.BitSet + def addBaseClasses(bcs: List[ClassSymbol], to: List[ClassSymbol]) + : List[ClassSymbol] = bcs match { + case bc :: bcs1 => + val id = bc.superId + if (seen contains id) to + else if (locked contains id) throw new CyclicReference(symbol) + else { + locked += id + val bcs1added = addBaseClasses(bcs1, to) + seen += id + if (bcs1added eq bcs1) bcs else bc :: bcs1added + } + case _ => + to + } + def addParentBaseClasses(ps: List[Type], to: List[ClassSymbol]): List[ClassSymbol] = ps match { + case p :: ps1 => + addBaseClasses(p.baseClasses, addParentBaseClasses(ps1, to)) + case _ => + to + } + baseClassesVar = symbol :: addParentBaseClasses(parents, Nil) + superClassBitsVar = ctx.root.uniqueBits.findEntryOrUpdate(seen.toImmutable) + } + + def superClassBits(implicit ctx: Context): BitSet = { + if (superClassBitsVar == null) computeSuperClassBits + superClassBitsVar + } + + def baseClasses(implicit ctx: Context): List[ClassSymbol] = { + if (baseClassesVar == null) computeSuperClassBits + baseClassesVar + } + + final override def isNonBottomSubClass(cls: Symbol)(implicit ctx: Context): Boolean = + (symbol eq cls) || + (superClassBits contains cls.superId) || + (this is Erroneous) || + (cls is Erroneous) && cls.isClass + + final override def isSubClass(cls: Symbol)(implicit ctx: Context) = + isNonBottomSubClass(cls) || + cls.isClass && ((symbol eq defn.NothingClass) || (symbol eq defn.NullClass)) + + private var definedFingerPrintCache: FingerPrint = null + + private def computeDefinedFingerPrint(implicit ctx: Context): FingerPrint = { + var bits = newNameFilter + var e = decls.lastEntry + while (e != null) { + includeName(bits, name) + e = e.prev + } + var ps = parents + while (ps.nonEmpty) { + val parent = ps.head.typeSymbol + parent.denot match { + case classd: ClassDenotation => + includeFingerPrint(bits, classd.definedFingerPrint) + parent.denot.setFlags(Frozen) + case _ => + } + ps = ps.tail + } + definedFingerPrintCache = bits + bits + } + + /** Enter a symbol in current scope. + * Note: We require that this does not happen after the first time + * someone does a findMember on a subclass. + */ + def enter(sym: Symbol)(implicit ctx: Context) = { + require(!(this is Frozen)) + decls enter sym + if (definedFingerPrintCache != null) + includeName(definedFingerPrintCache, sym.name) + if (memberCacheVar != null) + memberCache invalidate sym.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) = { + require(!(this is Frozen)) + decls unlink sym + if (definedFingerPrintCache != null) + computeDefinedFingerPrint + if (memberCacheVar != null) + memberCache invalidate sym.name + } + + def definedFingerPrint(implicit ctx: Context): FingerPrint = { + val fp = definedFingerPrintCache + if (fp != null) fp else computeDefinedFingerPrint + } + + final def membersNamed(name: Name)(implicit ctx: Context): DenotationSet = { + var denots: DenotationSet = memberCache lookup name + if (denots == null) { + if (containsName(definedFingerPrint, name)) { + val ownDenots = decls.denotsNamed(name) + denots = ownDenots + var ps = parents + while (ps.nonEmpty) { + val parentSym = ps.head.typeSymbol + parentSym.denot match { + case parentd: ClassDenotation => + denots = denots union + parentd.membersNamed(name) + .filterExcluded(Flags.Private) + .asSeenFrom(thisType, parentSym) + .filterDisjoint(ownDenots) + case _ => + } + } + } else { + denots = NoDenotation + } + memberCache enter (name, denots) + } + denots + } + + private var baseTypeCache: java.util.HashMap[CachedType, Type] = null + private var baseTypeValid: RunId = NoRunId + + final def baseTypeOf(tp: Type)(implicit ctx: Context): Type = { + + def computeBaseTypeOf(tp: Type): Type = tp match { + case tp: TypeProxy => + baseTypeOf(tp.underlying) + case AndType(tp1, tp2) => + baseTypeOf(tp1) & baseTypeOf(tp2) + case OrType(tp1, tp2) => + baseTypeOf(tp1) | baseTypeOf(tp2) + case tp @ ClassInfo(pre, classd) => + def reduce(bt: Type, ps: List[Type]): Type = ps match { + case p :: ps1 => reduce(bt & baseTypeOf(p), ps1) + case _ => bt + } + if (classd.symbol == symbol) tp.typeConstructor // was: typeTemplate + else reduce(NoType, classd.parents).substThis(classd.symbol, tp.prefix) + } + + if (symbol.isStaticMono) symbol.typeConstructor + else tp match { + case tp: CachedType => + if (baseTypeValid != ctx.runId) { + baseTypeCache = new java.util.HashMap[CachedType, Type] + baseTypeValid = ctx.runId + } + var basetp = baseTypeCache get tp + if (basetp == null) { + baseTypeCache.put(tp, NoType) + basetp = computeBaseTypeOf(tp) + baseTypeCache.put(tp, basetp) + } else if (basetp == NoType) { + throw new CyclicReference(symbol) + } + basetp + case _ => + computeBaseTypeOf(tp) + } + } + + private var memberNamesCache: Map[NameFilter, Set[Name]] = Map() + + def memberNames(keepOnly: NameFilter)(implicit ctx: Context): Set[Name] = + memberNamesCache get keepOnly match { + case Some(names) => + names + case _ => + val inheritedNames = (parents flatMap (_.memberNames(thisType, keepOnly))).toSet + val ownNames = decls.iterator map (_.name) + val candidates = inheritedNames ++ ownNames + val names = candidates filter (keepOnly(thisType, _)) + memberNamesCache += (keepOnly -> names) + names + } + } + + class CompleteClassDenotation( + val symbol: ClassSymbol, + val owner: Symbol, + val name: Name, + initFlags: FlagSet, + val typeParams: List[TypeSymbol], + val parents: List[TypeRef], + val decls: Scope, + assocFile: AbstractFile = null + )(implicit ctx: Context) extends ClassDenotation(initFlags, assocFile) + + abstract class LazyClassDenotation( + val symbol: ClassSymbol, + val owner: Symbol, + val name: Name, + initFlags: FlagSet, + assocFile: AbstractFile = null + )(implicit ctx: Context) extends ClassDenotation(initFlags, assocFile) with LazyCompletion { + + protected var _typeParams: List[TypeSymbol] = null + protected var _parents: List[TypeRef] = null + protected var _decls: Scope = null + + final def typeParams: List[TypeSymbol] = { + val tparams = _typeParams + if (tparams != null) tparams else { tryLoad(); typeParams } + } + + final def parents: List[TypeRef] = { + val ps = _parents + if (ps != null) ps else { tryComplete(); parents } + } + + final def decls: Scope = { + val ds = _decls + if (ds != null) ds else { tryComplete(); decls } + } + } + + object NoDenotation extends SymDenotation(Flags.Empty) { + override def symbol: Symbol = NoSymbol + override def owner: Symbol = throw new AssertionError("NoDenotation.owner") + override def name: Name = BootNameTable.newTermName("<none>") + override def info: Type = NoType + } + + object NameFilter { + final val WordSizeLog = 6 + final val DefinedNamesWords = 16 + final val DefinedNamesSize = DefinedNamesWords << WordSizeLog + final val DefinedNamesMask = DefinedNamesSize - 1 + + type FingerPrint = Array[Long] + + def includeName(bits: FingerPrint, name: Name): Unit = { + val hash = name.start & DefinedNamesMask + bits(hash >> 6) |= (1 << hash) + } + + def includeFingerPrint(bits1: FingerPrint, bits2: FingerPrint): Unit = + for (i <- 0 until DefinedNamesWords) bits1(i) |= bits2(i) + + def containsName(bits: FingerPrint, name: Name): Boolean = { + val hash = name.start & DefinedNamesMask + (bits(hash >> 6) & (1 << hash)) != 0 + } + + def newNameFilter: FingerPrint = new Array[Long](DefinedNamesWords) + } + + implicit def toFlagSet(denot: SymDenotation): FlagSet = denot.flags + +} |