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

Tasty Reader: Add support for Scala 3.2 #10068

Merged
merged 7 commits into from Jul 11, 2022
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
2 changes: 1 addition & 1 deletion project/DottySupport.scala
Expand Up @@ -12,7 +12,7 @@ import sbt.librarymanagement.{
* Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version
*/
object TastySupport {
val supportedTASTyRelease = "3.1.2-RC1" // TASTy version 28.1-0
val supportedTASTyRelease = "3.2.0-RC1" // TASTy version 28.2-1
val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease
val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease

Expand Down
46 changes: 26 additions & 20 deletions src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala
Expand Up @@ -706,7 +706,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
rdr.readTerm()(ctx)
}
)(annotCtx.retractMode(IndexScopedStats))
DeferredAnnotation.fromTree(mkTree)
DeferredAnnotation.fromTree(annotSym)(mkTree)
}

private def traceAnnotation(annotStart: Addr, annotSym: Symbol, annotee: Symbol) = TraceInfo[Tree](
Expand Down Expand Up @@ -808,16 +808,18 @@ class TreeUnpickler[Tasty <: TastyUniverse](
val supportedFlags = Extension | Exported | Infix | Given | optFlag(isMacro)(Erased)
checkUnsupportedFlags(repr.unsupportedFlags &~ supportedFlags)
val isCtor = sym.isConstructor
val paramDefss = readParamss()(localCtx).map(_.map(symFromNoCycle))
val typeParams = {
val paramss = readParamss()(localCtx)
val typeClause = {
// A type parameter list must be non-empty and with type symbols
val first = paramDefss.take(1)
if (first.exists(_.exists(_.isType))) first.head else Nil
val first = paramss.take(1)
if (first.exists(_.headOption.exists(nc => symFromNoCycle(nc).isType))) first.head else Nil
}
val valueClauses = paramss.drop(if (typeClause.isEmpty) 0 else 1)
val typeParams = typeClause.map(symFromNoCycle)
val vparamss = {
val vparamSymss = valueClauses.map(_.map(symFromNoCycle))
// A value parameter list may be empty, or filled with term symbols
val valueClauses = paramDefss.drop(if (typeParams.isEmpty) 0 else 1)
val hasTypeParams = valueClauses.exists(_.exists(_.isType))
val hasTypeParams = vparamSymss.exists(_.headOption.exists(_.isType))
unsupportedWhen(hasTypeParams, {
val noun = (
if (isCtor) "constructor"
Expand All @@ -826,7 +828,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
)
s"$noun with unmergeable type parameters: $tname"
})
valueClauses
vparamSymss
}
val tpt = readTpt()(localCtx)
if (isMacro) {
Expand All @@ -838,7 +840,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
)
sym.addAnnotation(annot)
}
val valueParamss = normalizeIfConstructor(vparamss, isCtor)
val valueParamss = normalizeIfConstructor(sym.enclClass, vparamss, valueClauses, isCtor)
val resType = effectiveResultType(sym, tpt.tpe)
ctx.setInfo(sym, defn.DefDefType(if (isCtor) Nil else typeParams, valueParamss, resType))
}
Expand Down Expand Up @@ -891,7 +893,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
else defn.ExprType(tpt.tpe))
}

def initialize(localCtx: Context)(implicit ctx: Context): Unit = ctx.trace(traceCompletion(symAddr, sym)) {
def initialize(localCtx: Context)(implicit ctx: Context) = ctx.trace(traceCompletion(symAddr, sym)) {
sym.rawInfo match {
case repr: TastyRepr =>
tag match {
Expand All @@ -900,26 +902,30 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case TYPEDEF | TYPEPARAM => TypeDef(repr, localCtx)
case PARAM => TermParam(repr, localCtx)
}
repr.tflags
case _ => // nothing to do here (assume correctly initalised)
ctx.log(s"${showSym(sym)} is already initialised, in owner ${showSym(sym.owner)}")
EmptyTastyFlags
}
}

try {
val localCtx = ctx.withOwner(sym)
if (sym.isClass) {
inIndexScopedStatsContext(localCtx0 => initialize(localCtx0)(ctx))(localCtx)
}
else {
initialize(localCtx)
val tflags = {
if (sym.isClass) {
inIndexScopedStatsContext(localCtx0 => initialize(localCtx0)(ctx))(localCtx)
}
else {
initialize(localCtx)
}
}
NoCycle(at = symAddr)
NoCycle(at = symAddr, tflags)
}
catch ctx.onCompletionError(sym)
finally goto(end)
}

private def traceCompletion(addr: Addr, sym: Symbol)(implicit ctx: Context) = TraceInfo[Unit](
private def traceCompletion(addr: Addr, sym: Symbol)(implicit ctx: Context) = TraceInfo[TastyFlagSet](
query = "begin completion",
qual = s"${showSym(sym)} in context ${showSym(ctx.owner)} $addr",
res = _ => s"completed ${showSym(sym)}: ${showType(sym.info)}"
Expand Down Expand Up @@ -1032,7 +1038,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
unsupportedTermTreeError("package statement")
case _ =>
skipTree() // readTerm()(ctx.withOwner(exprOwner))
NoCycle(at = NoAddr)
NoCycle(at = NoAddr, tflags = EmptyTastyFlags)
}

def readIndexedStatsAsSyms(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[NoCycle] =
Expand Down Expand Up @@ -1331,8 +1337,8 @@ object TreeUnpickler {

sealed trait MaybeCycle
object MaybeCycle {
case class NoCycle(at: Addr) extends MaybeCycle
case object Tombstone extends MaybeCycle
case class NoCycle(at: Addr, tflags: TastyFlagSet) extends MaybeCycle
case object Tombstone extends MaybeCycle
}

/** An enumeration indicating which subtrees should be added to an OwnerTree. */
Expand Down
66 changes: 49 additions & 17 deletions src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala
Expand Up @@ -18,33 +18,65 @@ import scala.tools.nsc.tasty.TastyUniverse
trait AnnotationOps { self: TastyUniverse =>
import self.{symbolTable => u}

private[bridge] final def mkAnnotation(tree: Tree): u.Annotation = tree match {
case u.Apply(u.Select(u.New(tpt), u.nme.CONSTRUCTOR), args) =>
u.AnnotationInfo(tpt.tpe, args, Nil)
case u.Apply(u.TypeApply(u.Select(u.New(tpt), u.nme.CONSTRUCTOR), tpargs), args) =>
u.AnnotationInfo(u.appliedType(tpt.tpe, tpargs.map(_.tpe)), args, Nil)
case u.New(tpt) =>
// this is to handle incorrectly formatted annotations in dotty - https://github.com/lampepfl/dotty/issues/10113
u.AnnotationInfo(tpt.tpe, Nil, Nil)
case _ =>
throw new Exception(s"unexpected annotation kind from TASTy: ${u.showRaw(tree)}")
trait ShowKind[T] {
def showKind(annot: String, t: T)(implicit ctx: Context): String
}

sealed abstract class DeferredAnnotation {
object ShowKind {
implicit object ShowSymbol extends ShowKind[u.Symbol] {
def showKind(annot: String, t: u.Symbol)(implicit ctx: Context): String = s"$annot ${location(t)}"
}
implicit object ShowType extends ShowKind[u.Type] {
def showKind(annot: String, t: u.Type)(implicit ctx: Context): String =
s"type ${showType(t, wrap = false)} $annot of ${location(ctx.owner)}"
}
}

private[bridge] final def mkAnnotation[T: ShowKind](tree: Tree, annotee: T)(implicit ctx: Context): u.Annotation = {
def go(tpargs: List[Type], args: List[List[Tree]], tree: Tree): u.Annotation = tree match {
case u.Select(u.New(tpt), u.nme.CONSTRUCTOR) =>
val atp = if (tpargs.isEmpty) tpt.tpe else u.appliedType(tpt.tpe, tpargs)
if (args.lengthIs > 1) {
val soFar = s"@${atp.typeSymbol.name.toString}${args.map(_.mkString("(", ", ", ")")).mkString("")}"
u.reporter.warning(u.NoPosition,
"Implementation limitation: multiple argument lists on annotations are\n"+
"currently not supported; ignoring arguments " + args(1) + " on\n"+
s"${implicitly[ShowKind[T]].showKind(soFar, annotee)}")
}
u.AnnotationInfo(atp, args.headOption.getOrElse(Nil), Nil)
case u.TypeApply(pre, newTpArgs) if tpargs.isEmpty =>
go(newTpArgs.map(_.tpe), args, pre)
case u.Apply(pre, Nil) => // skip the empty term param list
go(tpargs, args, pre)
case u.Apply(pre, newArgs) =>
go(tpargs, newArgs :: args, pre)
case _ =>
throw new Exception(s"unexpected annotation kind from TASTy: ${u.showRaw(tree)}")
}
tree match {
case u.New(tpt) =>
// this is to handle incorrectly formatted annotations in dotty - https://github.com/lampepfl/dotty/issues/10113
u.AnnotationInfo(tpt.tpe, Nil, Nil)
case _ =>
go(Nil, Nil, tree)
}
}

sealed abstract class DeferredAnnotation(annotSym: Symbol) {

private[bridge] def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo
protected def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo
private[bridge] final def lzy(annotee: Symbol)(implicit ctx: Context): u.LazyAnnotationInfo = {
u.AnnotationInfo.lazily(eager(annotee))
u.AnnotationInfo.lazily(annotSym, eager(annotee))
}
}

object DeferredAnnotation {

def fromTree(tree: Symbol => Context => Tree): DeferredAnnotation = {
new DeferredAnnotation {
private[bridge] final def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo = {
def fromTree(annotSym: Symbol)(tree: Symbol => Context => Tree): DeferredAnnotation = {
new DeferredAnnotation(annotSym) {
protected final def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo = {
val atree = tree(annotee)(ctx)
mkAnnotation(atree)
mkAnnotation(atree, annotee)
}
}
}
Expand Down
25 changes: 13 additions & 12 deletions src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala
Expand Up @@ -96,27 +96,28 @@ trait ContextOps { self: TastyUniverse =>

/**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 = {
def inIndexStatsContext[T](op: Context => T)(implicit ctx: Context): T = {
val statsCtx = ctx.addMode(IndexStats)
op(statsCtx)
statsCtx.initialContext.forceAnnotations()
try op(statsCtx)
finally 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 = {
def inInnerScopeContext[T](op: Context => T)(implicit ctx: Context): T = {
val innerCtx = ctx.addMode(InnerScope)
op(innerCtx)
innerCtx.initialContext.enterLatentDefs(innerCtx.owner)
try op(innerCtx)
finally innerCtx.initialContext.enterLatentDefs(innerCtx.owner)
}


/** an aggregate of `inInnerScopeContext` within `inIndexStatsContext` */
def inIndexScopedStatsContext(op: Context => Unit)(implicit ctx: Context): Unit = {
def inIndexScopedStatsContext[T](op: Context => T)(implicit ctx: Context): T = {
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.
/**Analyses critical annotations, critical annotations will be forced as they are necessary to
* the reading of TASTy. E.g. `scala.annotation.internal.Child` is a critical annotation that
* must be forced to add its first type argument as a sealed child.
*/
private def analyseAnnotations(sym: Symbol)(implicit ctx: Context): Unit = {

Expand All @@ -137,8 +138,7 @@ trait ContextOps { self: TastyUniverse =>
var problematic: List[String] = Nil

for (annot <- sym.annotations) {
annot.completeInfo()
if (annot.tpe.typeSymbolDirect === defn.ChildAnnot) {
if (annot.symbol === defn.ChildAnnot) {
val child = {
val child0 = lookupChild(annot.tpe.typeArgs.head)
if (child0 eq sym) {
Expand All @@ -161,6 +161,7 @@ trait ContextOps { self: TastyUniverse =>
if ((annot.symbol eq defn.TargetNameAnnotationClass) ||
(annot.symbol eq defn.StaticMethodAnnotationClass)) {
problematic ::= inOwner { implicit ctx =>
annot.completeInfo() // these should be safe to force
unsupportedMessage(s"annotation on $sym: @$annot")
}
}
Expand Down Expand Up @@ -196,7 +197,7 @@ trait ContextOps { self: TastyUniverse =>
}
else {
log(s"eagerly adding annotations to ${showSym(sym)}")
analyseAnnotations(sym.setAnnotations(annots.map(_.eager(sym))))
analyseAnnotations(sym.setAnnotations(annots.map(_.lzy(sym))))
}
}
}
Expand Down
21 changes: 15 additions & 6 deletions src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala
Expand Up @@ -17,6 +17,8 @@ import scala.tools.nsc.tasty.{SafeEq, TastyUniverse, ForceKinds, TastyModes}, Ta
import scala.tools.tasty.{TastyName, Signature, TastyFlags}, TastyName.SignedName, Signature.MethodSignature, TastyFlags._
import scala.tools.tasty.ErasedTypeRef

import scala.tools.nsc.tasty.TreeUnpickler.MaybeCycle.NoCycle

/**This layer deals with selecting a member symbol from a type using a `TastyName`,
* also contains factories for making type references to symbols.
*/
Expand Down Expand Up @@ -121,12 +123,19 @@ trait SymbolOps { self: TastyUniverse =>
def symIsExperimental(sym: Symbol) = sym.hasAnnotation(defn.ExperimentalAnnotationClass)

/** if isConstructor, make sure it has one non-implicit parameter list */
def normalizeIfConstructor(termParamss: List[List[Symbol]], isConstructor: Boolean): List[List[Symbol]] =
if (isConstructor &&
(termParamss.isEmpty || termParamss.head.nonEmpty && termParamss.head.head.isImplicit))
Nil :: termParamss
else
termParamss
def normalizeIfConstructor(owner: Symbol, termParamss: List[List[Symbol]], paramClauses: List[List[NoCycle]], isConstructor: Boolean): List[List[Symbol]] =
if (!isConstructor) termParamss
else {
paramClauses match {
case (vparam :: _) :: _ if vparam.tflags.is(Implicit, butNot=Given) => Nil :: termParamss
case _ =>
if (paramClauses.forall(paramClause => paramClause.nonEmpty && paramClause.head.tflags.is(Given))) {
termParamss :+ Nil
} else {
termParamss
}
}
}

private[bridge] def lookupSymbol(space: Type, tname: TastyName)(implicit ctx: Context): Symbol = {
deepComplete(space)
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala
Expand Up @@ -166,7 +166,7 @@ trait TreeOps { self: TastyUniverse =>
}
}

def Annotated(tpt: Tree, annot: Tree): Tree = {
def Annotated(tpt: Tree, annot: Tree)(implicit ctx: Context): Tree = {
if (annot.tpe.typeSymbol === defn.RepeatedAnnot
&& tpt.tpe.typeSymbol.isSubClass(u.definitions.SeqClass)
&& tpt.tpe.typeArgs.length == 1) {
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala
Expand Up @@ -223,9 +223,9 @@ trait TypeOps { self: TastyUniverse =>
def IntersectionType(tps: Type*): Type = u.intersectionType(tps.toList)
def IntersectionType(tps: List[Type]): Type = u.intersectionType(tps)

def AnnotatedType(tpe: Type, annot: Tree): Type = tpe match {
case u.AnnotatedType(annots, tpe) => u.AnnotatedType(annots :+ mkAnnotation(annot), tpe)
case _ => u.AnnotatedType(mkAnnotation(annot) :: Nil , tpe)
def AnnotatedType(tpe: Type, annot: Tree)(implicit ctx: Context): Type = tpe match {
case u.AnnotatedType(annots, tpe) => u.AnnotatedType(annots :+ mkAnnotation(annot, tpe), tpe)
case _ => u.AnnotatedType(mkAnnotation(annot, tpe) :: Nil , tpe)
}

def SuperType(thisTpe: Type, superTpe: Type): Type = u.SuperType(thisTpe, superTpe)
Expand Down
8 changes: 5 additions & 3 deletions src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
Expand Up @@ -889,7 +889,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
if (ms.nonEmpty && clazz.isTrait && clazz.isInterface)
clazz.resetFlag(INTERFACE)

if (normalizedMember.isMethod) {
if (normalizedMember.isMethod && !normalizedMember.isScala3Defined) {
val newTpe = subst(outerEnv, normalizedMember.info)
// only do it when necessary, otherwise the method type might be at a later phase already
if (newTpe != normalizedMember.info) {
Expand Down Expand Up @@ -930,7 +930,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
*/
private def normalizeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv): List[Symbol] = {
sym :: (
if (!sym.isMethod || enteringTyper(sym.typeParams.isEmpty)) Nil
if (!sym.isMethod || sym.isScala3Defined || enteringTyper(sym.typeParams.isEmpty)) Nil
else if (sym.hasDefault) {
/* Specializing default getters is useless, also see scala/bug#7329 . */
sym.resetFlag(SPECIALIZED)
Expand Down Expand Up @@ -1024,7 +1024,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
specMember
}

if (!sym.isMethod || sym.isConstructor || hasUnspecializableAnnotation(sym) || sym.isSuperAccessor) {
if (!sym.isMethod || sym.isConstructor || hasUnspecializableAnnotation(sym) || sym.isSuperAccessor
|| sym.isScala3Defined) { // Scala 3 does not have specialised methods yet.
// ) {
Nil
} else {
val stvars = specializedTypeVars(sym)
Expand Down