Skip to content

Commit

Permalink
Merge pull request #9156 from scalacenter/tasty/development
Browse files Browse the repository at this point in the history
Update TASTy Reader to support macro defs and trait mixins (Dotty 0.26) [ci: last-only]
  • Loading branch information
lrytz committed Sep 16, 2020
2 parents d5a1010 + 128b523 commit 431994b
Show file tree
Hide file tree
Showing 85 changed files with 1,096 additions and 425 deletions.
4 changes: 2 additions & 2 deletions project/DottySupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import sbt.librarymanagement.{
* Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version
*/
object TastySupport {
val supportedTASTyRelease = "0.23.0-RC1" // TASTy version 20
val dottyCompiler = "ch.epfl.lamp" % "dotty-compiler_0.23" % supportedTASTyRelease
val supportedTASTyRelease = "0.26.0-RC1" // TASTy version 23
val dottyCompiler = "ch.epfl.lamp" % "dotty-compiler_0.26" % supportedTASTyRelease
}

/** Settings needed to compile with Dotty,
Expand Down
15 changes: 12 additions & 3 deletions src/compiler/scala/tools/nsc/tasty/TastyModes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@ object TastyModes {

final val EmptyTastyMode: TastyMode = TastyMode(0)
/** When reading the parents of a class template */
final val ReadParents: TastyMode = TastyMode(1 << 0)
final val ReadParents: TastyMode = TastyMode(1 << 0)
/** When reading trees of an annotation */
final val ReadAnnotation: TastyMode = TastyMode(1 << 1)
/** When reading the outermost tree of an term */
final val OuterTerm: TastyMode = TastyMode(1 << 2)
final val OuterTerm: TastyMode = TastyMode(1 << 2)
/** When reading statements in a sequence */
final val IndexStats: TastyMode = TastyMode(1 << 3)
final val IndexStats: TastyMode = TastyMode(1 << 3)
/** When reading a macro definition body */
final val ReadMacro: TastyMode = TastyMode(1 << 4)
/** When not at the package scope */
final val InnerScope: TastyMode = TastyMode(1 << 5)

/** The union of [[IndexStats]] and [[InnerScope]] */
final val IndexScopedStats: TastyMode = IndexStats | InnerScope

case class TastyMode(val toInt: Int) extends AnyVal { mode =>

Expand All @@ -46,6 +53,8 @@ object TastyModes {
if (mode.is(ReadAnnotation)) sb += "ReadAnnotation"
if (mode.is(OuterTerm)) sb += "OuterTerm"
if (mode.is(IndexStats)) sb += "IndexStats"
if (mode.is(ReadMacro)) sb += "ReadMacro"
if (mode.is(InnerScope)) sb += "InnerScope"
sb.mkString(" | ")
}
}
Expand Down
12 changes: 7 additions & 5 deletions src/compiler/scala/tools/nsc/tasty/TastyUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ private class TastyUnpickler[Tasty <: TastyUniverse](reader: TastyReader)(implic
debugName(SimpleName(new String(bytes.slice(start.index, start.index + length), "UTF-8")))
case tag @ (QUALIFIED | EXPANDED | EXPANDPREFIX) =>
val sep = tag match {
case QUALIFIED => TastyName.PathSep
case EXPANDED => TastyName.ExpandedSep
case EXPANDPREFIX => TastyName.ExpandPrefixSep
case QUALIFIED => PathSep
case EXPANDED => ExpandedSep
case EXPANDPREFIX => ExpandPrefixSep
}
debugName(QualifiedName(readName(), sep, readName().asSimpleName))
case UNIQUE =>
Expand All @@ -110,10 +110,12 @@ private class TastyUnpickler[Tasty <: TastyUniverse](reader: TastyReader)(implic
debugName(SignedName(original, sig))
case OBJECTCLASS =>
debugName(ObjectName(readName()))
case BODYRETAINER =>
debugName(SuffixName(readName(), BodyRetainerSuffix))
case INLINEACCESSOR | SUPERACCESSOR =>
val prefix = tag match {
case INLINEACCESSOR => TastyName.InlinePrefix
case SUPERACCESSOR => TastyName.SuperPrefix
case INLINEACCESSOR => InlinePrefix
case SUPERACCESSOR => SuperPrefix
}
debugName(PrefixName(prefix, readName()))
case _ =>
Expand Down
190 changes: 116 additions & 74 deletions src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala

Large diffs are not rendered by default.

146 changes: 123 additions & 23 deletions src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ package scala.tools.nsc.tasty.bridge
import scala.annotation.tailrec
import scala.reflect.io.AbstractFile

import collection.mutable

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

/**This contains the definition for [[Context]], along with standard error throwing capabilities with user friendly
* formatted errors that can change their output depending on the context mode.
Expand Down Expand Up @@ -74,16 +73,27 @@ trait ContextOps { self: TastyUniverse =>
else u.NoSymbol //throw new AssertionError(s"no module $name in ${location(owner)}")
}

/**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 = {
/**Perform an operation within a context that has the mode `IndexStats` will force any collected annotations
* afterwards */
def inIndexStatsContext(op: Context => Unit)(implicit ctx: Context): Unit = {
val statsCtx = ctx.addMode(IndexStats)
op(statsCtx)
statsCtx.initialContext.forceAnnotations()
}

/** Perform an operation within a context that has the mode `InnerScope` will enter any inline methods afterwards */
def inInnerScopeContext(op: Context => Unit)(implicit ctx: Context): Unit = {
val innerCtx = ctx.addMode(InnerScope)
op(innerCtx)
innerCtx.initialContext.enterLatentDefs(innerCtx.owner)
}


/** an aggregate of `inInnerScopeContext` within `inIndexStatsContext` */
def inIndexScopedStatsContext(op: Context => Unit)(implicit ctx: Context): Unit = {
inIndexStatsContext(inInnerScopeContext(op)(_))(ctx)
}

/**Forces lazy annotations, if one is [[scala.annotation.internal.Child]] then it will add the referenced type as a
* sealed child.
*/
Expand Down Expand Up @@ -126,6 +136,9 @@ trait ContextOps { self: TastyUniverse =>
final def ignoreAnnotations: Boolean = u.settings.YtastyNoAnnotations
final def verboseDebug: Boolean = u.settings.debug

def requiresLatentEntry(decl: Symbol): Boolean = decl.isScala3Macro || decl.isTraitParamAccessor
def neverEntered(decl: Symbol): Boolean = decl.isPureMixinCtor

def canEnterOverload(decl: Symbol): Boolean = {
!(decl.isModule && isSymbol(findObject(thisCtx.owner, decl.name)))
}
Expand All @@ -144,10 +157,11 @@ trait ContextOps { self: TastyUniverse =>

private final def loadingMirror: u.Mirror = u.mirrorThatLoaded(owner)

final def requiredPackage(fullname: TastyName): Symbol = {
if (fullname === TastyName.Root || fullname === TastyName.RootPkg) loadingMirror.RootPackage
else if (fullname === TastyName.EmptyPkg) loadingMirror.EmptyPackage
symOrDependencyError(false, true, fullname)(loadingMirror.getPackage(encodeTermName(fullname).toString))
final def requiredPackage(fullname: TastyName): Symbol = fullname match {
case TastyName.Root | TastyName.RootPkg => loadingMirror.RootPackage
case TastyName.EmptyPkg => loadingMirror.EmptyPackage
case fullname =>
symOrDependencyError(false, true, fullname)(loadingMirror.getPackage(encodeTermName(fullname).toString))
}

private def symOrDependencyError(isObject: Boolean, isPackage: Boolean, fullname: TastyName)(sym: => Symbol): Symbol = {
Expand Down Expand Up @@ -218,7 +232,7 @@ trait ContextOps { self: TastyUniverse =>

/** Guards the creation of an object val by checking for an existing definition in the owner's scope
*/
final def delayCompletion(owner: Symbol, name: TastyName, completer: TastyLazyType, privateWithin: Symbol = noSymbol): Symbol = {
final def delayCompletion(owner: Symbol, name: TastyName, completer: TastyCompleter, privateWithin: Symbol = noSymbol): Symbol = {
def default() = unsafeNewSymbol(owner, name, completer.originalFlagSet, completer, privateWithin)
if (completer.originalFlagSet.is(Object)) {
val sourceObject = findObject(owner, encodeTermName(name))
Expand All @@ -234,7 +248,7 @@ trait ContextOps { self: TastyUniverse =>

/** Guards the creation of an object class by checking for an existing definition in the owner's scope
*/
final def delayClassCompletion(owner: Symbol, typeName: TastyName.TypeName, completer: TastyLazyType, privateWithin: Symbol): Symbol = {
final def delayClassCompletion(owner: Symbol, typeName: TastyName.TypeName, completer: TastyCompleter, privateWithin: Symbol): Symbol = {
def default() = unsafeNewClassSymbol(owner, typeName, completer.originalFlagSet, completer, privateWithin)
if (completer.originalFlagSet.is(Object)) {
val sourceObject = findObject(owner, encodeTermName(typeName.toTermName))
Expand All @@ -248,8 +262,15 @@ trait ContextOps { self: TastyUniverse =>
}
}

final def enterIfUnseen(decl: Symbol): Unit = {
val decls = owner.rawInfo.decls
final def enterIfUnseen(sym: Symbol): Unit = {
if (mode.is(IndexScopedStats))
initialContext.collectLatentEvidence(owner, sym)
val decl = declaringSymbolOf(sym)
if (!(requiresLatentEntry(decl) || neverEntered(decl)))
enterIfUnseen0(owner.rawInfo.decls, decl)
}

protected final def enterIfUnseen0(decls: u.Scope, decl: Symbol): Unit = {
if (allowsOverload(decl)) {
if (canEnterOverload(decl)) {
decls.enter(decl)
Expand Down Expand Up @@ -282,11 +303,14 @@ trait ContextOps { self: TastyUniverse =>
else if (name === TastyName.Constructor) {
owner.newConstructor(u.NoPosition, encodeFlagSet(flags &~ Stable))
}
else if (name === TastyName.MixinConstructor) {
owner.newMethodSymbol(u.nme.MIXIN_CONSTRUCTOR, u.NoPosition, encodeFlagSet(flags &~ Stable))
}
else if (flags.is(FlagSets.ObjectCreationFlags)) {
log(s"!!! visited module value $name first")
assert(!owner.rawInfo.decls.lookupAll(encodeTermName(name)).exists(_.isModule))
val module = owner.newModule(encodeTermName(name), u.NoPosition, encodeFlagSet(flags))
module.moduleClass.info = u.NoType
module.moduleClass.info = defn.DefaultInfo
module
}
else if (name.isTypeName) {
Expand All @@ -300,7 +324,7 @@ trait ContextOps { self: TastyUniverse =>
if (flags.is(FlagSets.ObjectClassCreationFlags)) {
log(s"!!! visited module class $typeName first")
val module = owner.newModule(encodeTermName(typeName), u.NoPosition, encodeFlagSet(FlagSets.ObjectCreationFlags))
module.info = u.NoType
module.info = defn.DefaultInfo
module.moduleClass.flags = encodeFlagSet(flags)
module.moduleClass
}
Expand All @@ -314,7 +338,7 @@ trait ContextOps { self: TastyUniverse =>
val assumedSelfType =
if (cls.is(Object) && cls.owner.isClass) defn.SingleType(cls.owner.thisType, cls.sourceModule)
else u.NoType
cls.info = u.ClassInfoType(cls.completer.parents, cls.completer.decls, assumedSelfType.typeSymbolDirect)
cls.info = u.ClassInfoType(cls.repr.parents, cls.repr.decls, assumedSelfType.typeSymbolDirect)
cls
}

Expand Down Expand Up @@ -379,9 +403,6 @@ trait ContextOps { self: TastyUniverse =>

final def newRefinementClassSymbol: Symbol = owner.newRefinementClass(u.NoPosition)

final def initialiseClassScope(clazz: Symbol): Unit =
clazz.completer.withDecls(u.newScope)

final def setInfo(sym: Symbol, info: Type): Unit = sym.info = info

final def markAsEnumSingleton(sym: Symbol): Unit =
Expand All @@ -405,7 +426,6 @@ trait ContextOps { self: TastyUniverse =>
final def withNewScope: Context =
freshSymbol(newLocalDummy)

final def selectionCtx(name: TastyName): Context = this // if (name.isConstructorName) this.addMode(Mode.InSuperCall) else this
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)
Expand Down Expand Up @@ -454,16 +474,96 @@ trait ContextOps { self: TastyUniverse =>
*/
private[ContextOps] def forceAnnotations(): Unit = {
if (mySymbolsToForceAnnots != null) {
val toForce = mySymbolsToForceAnnots
mySymbolsToForceAnnots = null
val toForce = mySymbolsToForceAnnots.toList
mySymbolsToForceAnnots.clear()
for (sym <- toForce) {
log(s"!!! forcing annotations on ${showSym(sym)}")
analyseAnnotations(sym)
}
assert(mySymbolsToForceAnnots == null, "more symbols added while forcing")
assert(mySymbolsToForceAnnots.isEmpty, "more symbols added while forcing")
}
}

private[this] var myInlineDefs: mutable.Map[Symbol, mutable.ArrayBuffer[Symbol]] = null
private[this] var myMacros: mutable.Map[Symbol, mutable.ArrayBuffer[Symbol]] = null
private[this] var myTraitParamAccessors: mutable.Map[Symbol, mutable.ArrayBuffer[Symbol]] = null

/** Collect evidence from definitions that is required by `enterLatentDefs`. */
private[ContextOps] def collectLatentEvidence(owner: Symbol, sym: Symbol): Unit = {

def macroMap() = {
if (myMacros == null) myMacros = mutable.HashMap.empty
myMacros
}

def inlineMap() = {
if (myInlineDefs == null) myInlineDefs = mutable.HashMap.empty
myInlineDefs
}

def traitParamAccessors() = {
if (myTraitParamAccessors == null) myTraitParamAccessors = mutable.HashMap.empty
myTraitParamAccessors
}

def append(map: mutable.Map[Symbol, mutable.ArrayBuffer[Symbol]])(owner: Symbol, sym: Symbol) =
map.getOrElseUpdate(owner, mutable.ArrayBuffer.empty) += sym

if (sym.isScala2Macro) append(macroMap())(owner, sym)
else if (sym.isScala3Inline) append(inlineMap())(owner, sym)
else if (sym.isTraitParamAccessor) append(traitParamAccessors())(owner, sym)

}

/**Should be called after indexing all symbols in the given owners scope.
*
* Enters qualifying definitions into the given owners scope, according to the following rules:
* - an `inline macro` method (Scala 3 macro) without a corresponding `erased macro` method (Scala 2 macro).
*
* Reports illegal definitions:
* - trait constructors with parameters
*
* @param cls should be a symbol associated with a non-empty scope
*/
private[ContextOps] def enterLatentDefs(cls: Symbol): Unit = {

def macroDefs(cls: Symbol): Option[Iterable[Symbol]] = {
if (myMacros != null) myMacros.remove(cls)
else None
}

def inlineDefs(cls: Symbol): Option[Iterable[Symbol]] = {
if (myInlineDefs != null) myInlineDefs.remove(cls)
else None
}

def traitParamAccessors(cls: Symbol): Option[Iterable[Symbol]] = {
if (myTraitParamAccessors != null) myTraitParamAccessors.remove(cls)
else None
}

def enterInlineDefs(cls: Symbol, decls: u.Scope): Unit = {
val macros = macroDefs(cls).getOrElse(Iterable.empty)
val defs = inlineDefs(cls).getOrElse(Iterable.empty)

for (d <- defs if !macros.exists(_.name == d.name))
enterIfUnseen0(decls, d)
}

def reportParameterizedTrait(cls: Symbol, decls: u.Scope): Unit = {
val traitParams = traitParamAccessors(cls).getOrElse(Iterable.empty)
if (traitParams.nonEmpty) {
val parameters = traitParams.map(_.nameString)
val msg = s"parameterized trait ${parameters.mkString(s"${cls.nameString}(", ", ", ")")}"
unsupportedError(msg)(this.withOwner(cls.owner))
}
}

val decls = cls.rawInfo.decls
enterInlineDefs(cls, decls)
reportParameterizedTrait(cls, decls)

}
}

final class FreshContext(val owner: Symbol, val outer: Context, val mode: TastyMode) extends Context {
Expand Down
31 changes: 15 additions & 16 deletions src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ trait FlagOps { self: TastyUniverse =>

object FlagSets {
val TastyOnlyFlags: TastyFlagSet = (
Erased | Internal | Inline | InlineProxy | Opaque | Scala2x | Extension | Given | Exported | Macro | Enum
Erased | Internal | Inline | InlineProxy | Opaque | Extension | Given | Exported | SuperTrait | Enum
| Open | ParamAlias
)
val TermParamOrAccessor: TastyFlagSet = Param | ParamSetter
Expand All @@ -44,11 +44,11 @@ trait FlagOps { self: TastyUniverse =>
if (tflags.is(AbsOverride)) flags |= Flag.ABSOVERRIDE
if (tflags.is(Abstract)) flags |= Flag.ABSTRACT
if (tflags.is(Final)) flags |= Flag.FINAL
if (tflags.is(Interface)) flags |= Flag.INTERFACE
if (tflags.is(Sealed)) flags |= Flag.SEALED
if (tflags.is(Case)) flags |= Flag.CASE
if (tflags.is(Implicit)) flags |= ModifierFlags.IMPLICIT
if (tflags.is(Lazy)) flags |= Flag.LAZY
if (tflags.is(Macro)) flags |= Flag.MACRO
if (tflags.is(Override)) flags |= Flag.OVERRIDE
if (tflags.is(Static)) flags |= ModifierFlags.STATIC
if (tflags.is(Object)) flags |= Flags.MODULE
Expand All @@ -61,7 +61,7 @@ trait FlagOps { self: TastyUniverse =>
if (tflags.is(CaseAccessor)) flags |= Flag.CASEACCESSOR
if (tflags.is(Covariant)) flags |= Flag.COVARIANT
if (tflags.is(Contravariant)) flags |= Flag.CONTRAVARIANT
if (tflags.is(DefaultParameterized)) flags |= Flag.DEFAULTPARAM
if (tflags.is(HasDefault)) flags |= Flag.DEFAULTPARAM
if (tflags.is(Stable)) flags |= Flag.STABLE
if (tflags.is(ParamSetter)) flags |= Flag.PARAMACCESSOR
if (tflags.is(Param)) flags |= Flag.PARAM
Expand All @@ -75,19 +75,18 @@ trait FlagOps { self: TastyUniverse =>
if (!tflags) "EmptyTastyFlags"
else (tflags).toSingletonSets.map { f =>
(f: @unchecked) match {
case Erased => "erased"
case Internal => "<internal>"
case Inline => "inline"
case InlineProxy => "<inlineproxy>"
case Opaque => "opaque"
case Scala2x => "<scala2x>"
case Extension => "<extension>"
case Given => "given"
case Exported => "<exported>"
case Macro => "<tastymacro>"
case Enum => "enum"
case Open => "open"
case ParamAlias => "<paramalias>"
case Erased => "erased"
case Internal => "<internal>"
case Inline => "inline"
case InlineProxy => "<inlineproxy>"
case Opaque => "opaque"
case Extension => "<extension>"
case Given => "given"
case Exported => "<exported>"
case SuperTrait => "<supertrait>"
case Enum => "enum"
case Open => "open"
case ParamAlias => "<paramalias>"
}
} mkString(" | ")
}
Expand Down
Loading

0 comments on commit 431994b

Please sign in to comment.