Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Read inner classes in signatures, force annotations after template parents #106

Merged
merged 2 commits into from
Jul 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
85 changes: 51 additions & 34 deletions src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ class TreeUnpickler[Tasty <: TastyUniverse](
this.roots = Set(objectRoot, classRoot)
val rdr = new TreeReader(reader).fork
ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr)
def indexTopLevel(implicit ctx: Context): Unit = rdr.indexStats(reader.endAddr)
if (rdr.isTopLevel)
rdr.indexStats(reader.endAddr)
inIndexingContext(indexTopLevel(_))
}

class Completer(reader: TastyReader, originalFlagSet: TastyFlagSet)(implicit ctx: Context) extends TastyLazyType(originalFlagSet) { self =>
Expand Down Expand Up @@ -604,21 +605,19 @@ class TreeUnpickler[Tasty <: TastyUniverse](
* current address and `end`.
*/
def indexStats(end: Addr)(implicit ctx: Context): Unit = {
val statsCtx = ctx.addMode(IndexStats)
while (currentAddr.index < end.index) {
nextByte match {
case tag @ (VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM) =>
symbolAtCurrent()(statsCtx)
symbolAtCurrent()
skipTree()
case IMPORT =>
skipTree()
case PACKAGE =>
processPackage(end => implicit ctx => indexStats(end))(statsCtx)
processPackage(end => implicit ctx => indexStats(end))
case _ =>
skipTree()
}
}
statsCtx.forceAnnotations()
assert(currentAddr.index === end.index)
}

Expand Down Expand Up @@ -774,23 +773,25 @@ class TreeUnpickler[Tasty <: TastyUniverse](
assert(readByte() === TEMPLATE)
val end = readEnd()

// ** PARAMETERS **
ctx.log(s"$currentAddr Template: reading parameters of $cls:")
val tparams = readIndexedParams[NoCycle](TYPEPARAM)
if (tparams.nonEmpty) {
cls.info = defn.PolyType(tparams.map(symFromNoCycle), cls.info)
def completeTypeParameters()(implicit ctx: Context): List[Symbol] = {
ctx.log(s"$currentAddr Template: reading parameters of $cls:")
val tparams = readIndexedParams[NoCycle](TYPEPARAM).map(symFromNoCycle)
if (tparams.nonEmpty) {
cls.info = defn.PolyType(tparams, cls.info)
}
readIndexedParams[NoCycle](PARAM) // skip value parameters
tparams
}
readIndexedParams[NoCycle](PARAM) // skip value parameters

// ** MEMBERS **
ctx.log(s"$currentAddr Template: indexing members of $cls:")
val bodyIndexer = fork
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree() // skip until primary ctor
bodyIndexer.indexStats(end)
def indexMembers()(implicit ctx: Context): Unit = {
ctx.log(s"$currentAddr Template: indexing members of $cls:")
val bodyIndexer = fork
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree() // skip until primary ctor
bodyIndexer.indexStats(end)
}

// ** PARENTS **
ctx.log(s"$currentAddr Template: adding parents of $cls:")
val parents = {
def traverseParents()(implicit ctx: Context): List[Type] = {
ctx.log(s"$currentAddr Template: adding parents of $cls:")
val parentCtx = ctx.withOwner(localDummy).addMode(ReadParents)
val parentWithOuter = parentCtx.addMode(OuterTerm)
collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
Expand All @@ -801,7 +802,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
}
}

if (nextByte === SELFDEF) {
def addSelfDef()(implicit ctx: Context): Unit = {
ctx.log(s"$currentAddr Template: adding self-type of $cls:")
readByte() // read SELFDEF tag
readLongNat() // skip Name
Expand All @@ -810,21 +811,36 @@ class TreeUnpickler[Tasty <: TastyUniverse](
cls.typeOfThis = selfTpe
}

val parentTypes = ctx.adjustParents(cls, parents)

ctx.setInfo(cls, {
val classInfo = defn.ClassInfoType(parentTypes, cls)
// TODO [tasty]: if support opaque types, refine the self type with any opaque members here
if (tparams.isEmpty) classInfo
else defn.PolyType(tparams.map(symFromNoCycle), classInfo)
})
def setInfoWithParents(tparams: List[Symbol], parentTypes: List[Type])(implicit ctx: Context): Unit = {
def debugMsg = {
val addendum =
if (parentTypes.isEmpty) ""
else parentTypes.map(lzyShow).mkString(" extends ", " with ", "") // don't force types
s"$currentAddr Template: Updated info of $cls$addendum"
}
val info = {
val classInfo = defn.ClassInfoType(parentTypes, cls)
// TODO [tasty]: if support opaque types, refine the self type with any opaque members here
if (tparams.isEmpty) classInfo
else defn.PolyType(tparams, classInfo)
}
ctx.setInfo(cls, info)
ctx.log(debugMsg)
}

ctx.log {
val addendum =
if (parentTypes.isEmpty) ""
else parentTypes.map(lzyShow).mkString(" extends ", " with ", "") // don't force types
s"$currentAddr Template: Updated info of $cls$addendum"
def traverseTemplate()(implicit ctx: Context): Unit = {
val tparams = completeTypeParameters()
indexMembers()
val parents = traverseParents()
if (nextByte === SELFDEF) {
addSelfDef()
}
val parentTypes = ctx.adjustParents(cls, parents)
setInfoWithParents(tparams, parentTypes)
}

inIndexingContext(traverseTemplate()(_))

}

def isTopLevel: Boolean = nextByte === IMPORT || nextByte === PACKAGE
Expand All @@ -845,7 +861,8 @@ class TreeUnpickler[Tasty <: TastyUniverse](
until(end)(readIndexedStatAsSym(exprOwner))

def readStatsAsSyms(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[NoCycle] = {
fork.indexStats(end)
def forkAndIndexStats(implicit ctx: Context): Unit = fork.indexStats(end)
inIndexingContext(forkAndIndexStats(_))
readIndexedStatsAsSyms(exprOwner, end)
}

Expand Down
140 changes: 68 additions & 72 deletions src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import scala.reflect.io.AbstractFile

import collection.mutable

import scala.tools.tasty.{TastyName, TastyFlags}, TastyFlags._
import scala.tools.tasty.{TastyName, TastyFlags}, TastyFlags._, TastyName.ObjectName
import scala.tools.nsc.tasty.{TastyUniverse, TastyModes, SafeEq}, TastyModes._
import scala.reflect.internal.MissingRequirementError

Expand All @@ -29,7 +29,7 @@ trait ContextOps { self: TastyUniverse =>

private def describeOwner(owner: Symbol): String = {
val kind =
if (owner.is(Param)) {
if (owner.isOneOf(Param | ParamSetter)) {
if (owner.isType) "type parameter"
else "parameter"
}
Expand Down Expand Up @@ -74,64 +74,45 @@ trait ContextOps { self: TastyUniverse =>
else u.NoSymbol //throw new AssertionError(s"no module $name in ${location(owner)}")
}

/**Maintains state through traversal of a TASTy file, such as the outer scope of the defintion being traversed, the
* traversal mode, and the root owners and source path for the TASTy file.
* It also provides all operations for manipulation of the symbol table, such as creating/updating symbols and
* updating their types.
*/
sealed abstract class Context {
thisCtx =>
/**Perform an operation within a context that has the mode [[IndexStats]] will force any collected annotations
* afterwards*/
def inIndexingContext(op: Context => Unit)(implicit ctx: Context): Unit = {
val statsCtx = ctx.addMode(IndexStats)
op(statsCtx)
statsCtx.initialContext.forceAnnotations()
}

private[this] implicit final def implyThisCtx: thisCtx.type = thisCtx

private[this] var mySymbolsToForceAnnots: mutable.LinkedHashSet[Symbol] = _

private def stageSymbolToForceAnnots(sym: Symbol): Unit = {
if (sym.annotations.nonEmpty) {
if (mySymbolsToForceAnnots == null) {
mySymbolsToForceAnnots = mutable.LinkedHashSet.empty
}
mySymbolsToForceAnnots += sym
/**Forces lazy annotations, if one is [[scala.annotation.internal.Child]] then it will add the referenced type as a
* sealed child.
*/
private def analyseAnnotations(sym: Symbol)(implicit ctx: Context): Unit = {
for (annot <- sym.annotations) {
annot.completeInfo()
if (annot.tpe.typeSymbolDirect === defn.ChildAnnot) {
val child = annot.tpe.typeArgs.head.typeSymbolDirect
sym.addChild(child)
ctx.log(s"adding sealed child ${showSym(child)} to ${showSym(sym)}")
}
}
}

/** Force any lazy annotations collected from declaration statements directly in this scope.
*
* It is important to call this *after* indexing statements in a scope, otherwise calling
* [[ownertree.findOwner]] can fail, this is because [[ownertree.findOwner]] cannot traverse a definition tree at
* a given address before a symbol has been registered to that address.
*/
def forceAnnotations(): Unit = {
if (mySymbolsToForceAnnots != null) {
val toForce = mySymbolsToForceAnnots
mySymbolsToForceAnnots = null
for (sym <- toForce) {
log(s"!!! forcing annotations on ${showSym(sym)}")
analyseAnnotations(sym)
}
}
}
/**Maintains state through traversal of a TASTy file, such as the outer scope of the defintion being traversed, the
* traversal mode, and the root owners and source path for the TASTy file.
* It also provides all operations for manipulation of the symbol table, such as creating/updating symbols and
* updating their types.
*/
sealed abstract class Context { thisCtx =>

/**Forces lazy annotations, if one is [[scala.annotation.internal.Child]] then it will add the referenced type as a
* sealed child.
*/
private def analyseAnnotations(sym: Symbol): Unit = {
for (annot <- sym.annotations) {
annot.completeInfo()
if (annot.tpe.typeSymbolDirect === defn.ChildAnnot) {
val child = annot.tpe.typeArgs.head.typeSymbolDirect
sym.addChild(child)
log(s"adding sealed child ${showSym(child)} to ${showSym(sym)}")
}
}
}
protected implicit final def implyThisCtx: thisCtx.type = thisCtx

/**Associates the annotations with the symbol, and will force their evaluation if not reading statements.*/
def adjustAnnotations(sym: Symbol, annots: List[DeferredAnnotation]): Unit = {
if (annots.nonEmpty) {
if (mode.is(IndexStats)) {
log(s"lazily adding annotations to ${showSym(sym)}")
stageSymbolToForceAnnots(sym.setAnnotations(annots.map(_.lzy(sym))))
initialContext.stageSymbolToForceAnnots(sym.setAnnotations(annots.map(_.lzy(sym))))
}
else {
log(s"eagerly adding annotations to ${showSym(sym)}")
Expand Down Expand Up @@ -169,15 +150,6 @@ trait ContextOps { self: TastyUniverse =>
symOrDependencyError(false, true, fullname)(loadingMirror.getPackage(encodeTermName(fullname).toString))
}

final def requiredClass(fullname: TastyName.TypeName): Symbol =
symOrDependencyError(false, false, fullname)(loadingMirror.getRequiredClass(encodeTypeName(fullname).toString))

final def optionalClass(fullname: TastyName.TypeName): Option[Symbol] =
loadingMirror.getClassIfDefined(encodeTypeName(fullname).toString).toOption

final def requiredObject(fullname: TastyName.ObjectName): Symbol =
symOrDependencyError(true, false, fullname)(loadingMirror.getRequiredModule(encodeTermName(fullname).toString))

private def symOrDependencyError(isObject: Boolean, isPackage: Boolean, fullname: TastyName)(sym: => Symbol): Symbol = {
try sym
catch {
Expand All @@ -198,7 +170,7 @@ trait ContextOps { self: TastyUniverse =>
owner.newTypeParameter(u.nme.WILDCARD.toTypeName, u.NoPosition, u.NoFlags).setInfo(info)

final def findRootSymbol(roots: Set[Symbol], name: TastyName): Option[Symbol] = {
import TastyName._
import TastyName.TypeName

def isSameRoot(root: Symbol, selector: u.Name): Boolean =
(root.owner `eq` this.owner) && selector === root.name
Expand Down Expand Up @@ -428,36 +400,30 @@ trait ContextOps { self: TastyUniverse =>
}

final def withOwner(owner: Symbol): Context =
if (owner `ne` this.owner) fresh(owner) else this
if (owner `ne` this.owner) freshSymbol(owner) else this

final def withNewScope: Context =
fresh(newLocalDummy)
freshSymbol(newLocalDummy)

final def selectionCtx(name: TastyName): Context = this // if (name.isConstructorName) this.addMode(Mode.InSuperCall) else this
final def fresh(owner: Symbol): FreshContext = new FreshContext(owner, this, this.mode)

private def sibling(mode: TastyMode): FreshContext = new FreshContext(this.owner, outerOrThis, mode)
private def sibling: FreshContext = sibling(mode)

private def outerOrThis: Context = this match {
case ctx: FreshContext => ctx.outer
case ctx => ctx
}
final def freshSymbol(owner: Symbol): FreshContext = new FreshContext(owner, this, this.mode)
final def freshMode(mode: TastyMode): FreshContext = new FreshContext(this.owner, this, mode)
final def fresh: FreshContext = new FreshContext(this.owner, this, this.mode)

final def addMode(mode: TastyMode): Context =
if (!this.mode.is(mode)) sibling(this.mode | mode)
if (!this.mode.is(mode)) freshMode(this.mode | mode)
else this

final def retractMode(mode: TastyMode): Context =
if (this.mode.isOneOf(mode)) sibling(this.mode &~ mode)
if (this.mode.isOneOf(mode)) freshMode(this.mode &~ mode)
else this

final def withMode(mode: TastyMode): Context =
if (mode != this.mode) sibling(mode)
if (mode != this.mode) freshMode(mode)
else this

final def withSource(source: AbstractFile): Context =
if (source `ne` this.source) sibling.atSource(source)
if (source `ne` this.source) fresh.atSource(source)
else this

final def withPhaseNoLater[T](phase: String)(op: Context => T): T =
Expand All @@ -468,6 +434,36 @@ trait ContextOps { self: TastyUniverse =>
final class InitialContext(val topLevelClass: Symbol, val source: AbstractFile) extends Context {
def mode: TastyMode = EmptyTastyMode
def owner: Symbol = topLevelClass.owner

private[this] var mySymbolsToForceAnnots: mutable.LinkedHashSet[Symbol] = _

private[ContextOps] def stageSymbolToForceAnnots(sym: Symbol): Unit = {
if (sym.annotations.nonEmpty) {
if (mySymbolsToForceAnnots == null) {
mySymbolsToForceAnnots = mutable.LinkedHashSet.empty
}
mySymbolsToForceAnnots += sym
}
}

/** Force any lazy annotations collected from declaration statements directly in this scope.
*
* It is important to call this *after* indexing statements in a scope, otherwise calling
* [[ownertree.findOwner]] can fail, this is because [[ownertree.findOwner]] cannot traverse a definition tree at
* a given address before a symbol has been registered to that address.
*/
private[ContextOps] def forceAnnotations(): Unit = {
if (mySymbolsToForceAnnots != null) {
val toForce = mySymbolsToForceAnnots
mySymbolsToForceAnnots = null
for (sym <- toForce) {
log(s"!!! forcing annotations on ${showSym(sym)}")
analyseAnnotations(sym)
}
assert(mySymbolsToForceAnnots == null, "more symbols added while forcing")
}
}

}

final class FreshContext(val owner: Symbol, val outer: Context, val mode: TastyMode) extends Context {
Expand Down