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

Update TASTy reader to support Scala 3.1 #9791

Merged
merged 5 commits into from Oct 27, 2021
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.0.0" // TASTy version 28.0-0
val supportedTASTyRelease = "3.1.0" // TASTy version 28.1-0
val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease
val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease

Expand Down
3 changes: 0 additions & 3 deletions src/compiler/scala/tools/nsc/tasty/ForceKinds.scala
Expand Up @@ -18,8 +18,6 @@ import ForceKinds._

object ForceKinds {

/** When forcing the constructor of an annotation */
final val AnnotCtor: ForceKinds.Single = of(1 << 0)
/** When forcing the companion of a module */
final val DeepForce: ForceKinds.Single = of(1 << 1)
/** When forcing the owner of a symbol */
Expand Down Expand Up @@ -51,7 +49,6 @@ class ForceKinds(val toInt: Int) extends AnyVal {

def describe: List[String] = {
var xs = List.empty[String]
if (is(AnnotCtor)) xs ::= "reading annotation constructor"
if (is(DeepForce)) xs ::= "deep"
if (is(CompleteOwner)) xs ::= "class owner is required"
if (is(OverloadedSym)) xs ::= "overload resolution"
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/scala/tools/nsc/tasty/TastyModes.scala
Expand Up @@ -34,10 +34,14 @@ object TastyModes {
final val InnerScope: TastyMode = TastyMode(1 << 5)
/** When reading the tree of an Opaque type */
final val OpaqueTypeDef: TastyMode = TastyMode(1 << 6)
/** When reading trees of an annotation */
final val ReadAnnotationCtor: TastyMode = TastyMode(1 << 7)

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

final val ReadAnnotTopLevel: TastyMode = ReadAnnotation | ReadAnnotationCtor

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

def |(other: TastyMode): TastyMode = TastyMode(toInt | other.toInt)
Expand All @@ -58,6 +62,7 @@ object TastyModes {
if (mode.is(ReadMacro)) sb += "ReadMacro"
if (mode.is(InnerScope)) sb += "InnerScope"
if (mode.is(OpaqueTypeDef)) sb += "OpaqueTypeDef"
if (mode.is(ReadAnnotationCtor)) sb += "ReadAnnotationCtor"
sb.mkString(" | ")
}
}
Expand Down
24 changes: 14 additions & 10 deletions src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala
Expand Up @@ -396,7 +396,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
val lo = readType()
if (nothingButMods(end)) readVariances(lo)
else defn.TypeBounds(lo, readVariances(readType()))
case ANNOTATEDtype => defn.AnnotatedType(readType(), readTerm()(ctx.addMode(ReadAnnotation)))
case ANNOTATEDtype => defn.AnnotatedType(readType(), readTerm()(ctx.addMode(ReadAnnotTopLevel)))
case ANDtype => defn.IntersectionType(readType(), readType())
case ORtype => unionIsUnsupported
case SUPERtype => defn.SuperType(readType(), readType())
Expand Down Expand Up @@ -461,6 +461,8 @@ class TreeUnpickler[Tasty <: TastyUniverse](

private def addInferredFlags(tag: Int, tastyFlags: TastyFlagSet, name: TastyName, isAbsType: Boolean, isClass: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): TastyFlagSet = {
var flags = tastyFlags
if (flags.is(Given))
flags |= Implicit
val lacksDefinition =
rhsIsEmpty &&
name.isTermName && !name.isConstructorName && !flags.isOneOf(FlagSets.TermParamOrAccessor) ||
Expand All @@ -474,7 +476,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
flags |= FieldAccessor
if (flags.not(Mutable))
flags |= Stable
if (flags.is(Case | Static | Enum)) // singleton enum case
if (flags.is(Case | Enum)) // singleton enum case
flags |= Object | Stable // encode as a module (this needs to be corrected in bytecode)
}
if (ctx.owner.isClass) {
Expand Down Expand Up @@ -668,7 +670,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case HASDEFAULT => addFlag(HasDefault)
case STABLE => addFlag(Stable)
case EXTENSION => addFlag(Extension)
case GIVEN => addFlag(Implicit)
case GIVEN => addFlag(Given)
case PARAMsetter => addFlag(ParamSetter)
case PARAMalias => addFlag(ParamAlias)
case EXPORTED => addFlag(Exported)
Expand All @@ -692,7 +694,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
private val readTypedWithin: Context => Symbol = implicit ctx => readType().typeSymbolDirect

private val readTypedAnnot: Context => DeferredAnnotation = { implicit ctx =>
val annotCtx = ctx.addMode(ReadAnnotation)
val annotCtx = ctx.addMode(ReadAnnotTopLevel)
val start = currentAddr
readByte() // tag
val end = readEnd()
Expand Down Expand Up @@ -803,7 +805,8 @@ class TreeUnpickler[Tasty <: TastyUniverse](

def DefDef(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = {
val isMacro = repr.tflags.is(Erased | Macro)
checkUnsupportedFlags(repr.unsupportedFlags &~ (Extension | Exported | Infix | optFlag(isMacro)(Erased)))
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 = {
Expand Down Expand Up @@ -842,7 +845,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](

def ValDef(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = {
// valdef in TASTy is either a singleton object or a method forwarder to a local value.
checkUnsupportedFlags(repr.unsupportedFlags &~ (Enum | Extension | Exported))
checkUnsupportedFlags(repr.unsupportedFlags &~ (Enum | Extension | Exported | Given))
val tpe = readTpt()(localCtx).tpe
ctx.setInfo(sym,
if (repr.tflags.is(FlagSets.SingletonEnum)) {
Expand All @@ -856,7 +859,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
}

def TypeDef(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = {
val allowedShared = Enum | Opaque | Infix
val allowedShared = Enum | Opaque | Infix | Given
val allowedTypeFlags = allowedShared | Exported
val allowedClassFlags = allowedShared | Open | Transparent
if (sym.isClass) {
Expand All @@ -881,7 +884,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
}

def TermParam(repr: TastyRepr, localCtx: Context)(implicit ctx: Context): Unit = {
checkUnsupportedFlags(repr.unsupportedFlags &~ (ParamAlias | Exported))
checkUnsupportedFlags(repr.unsupportedFlags &~ (ParamAlias | Exported | Given))
val tpt = readTpt()(localCtx)
ctx.setInfo(sym,
if (nothingButMods(end) && sym.not(ParamSetter)) tpt.tpe
Expand Down Expand Up @@ -1131,7 +1134,8 @@ class TreeUnpickler[Tasty <: TastyUniverse](
until(end)(skipTree())
tpd.TypeTree(fnResult(fn.tpe))
} else {
tpd.Apply(fn, until(end)(readTerm()))
val argsCtx = ctx.argumentCtx(fn)
tpd.Apply(fn, until(end)(readTerm()(argsCtx)))
}
case TYPEAPPLY => tpd.TypeApply(readTerm(), until(end)(readTpt()))
case TYPED => tpd.Typed(readTerm(), readTpt())
Expand All @@ -1155,7 +1159,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
// wrong number of arguments in some scenarios reading F-bounded
// types. This came up in #137 of collection strawman.
tpd.AppliedTypeTree(readTpt(), until(end)(readTpt()))
case ANNOTATEDtpt => tpd.Annotated(readTpt(), readTerm()(ctx.addMode(ReadAnnotation)))
case ANNOTATEDtpt => tpd.Annotated(readTpt(), readTerm()(ctx.addMode(ReadAnnotTopLevel)))
case LAMBDAtpt => tpd.LambdaTypeTree(readParams[NoCycle](TYPEPARAM).map(symFromNoCycle), readTpt())
case MATCHtpt => matchTypeIsUnsupported
case TYPEBOUNDStpt =>
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala
Expand Up @@ -269,7 +269,7 @@ trait ContextOps { self: TastyUniverse =>
owner.newTypeParameter(
name = u.freshTypeName("_$")(u.currentFreshNameCreator),
pos = u.NoPosition,
newFlags = FlagSets.Creation.Default
newFlags = FlagSets.Creation.Wildcard
).setInfo(info)

final def newConstructor(owner: Symbol, info: Type): Symbol = unsafeNewSymbol(
Expand Down Expand Up @@ -557,6 +557,9 @@ trait ContextOps { self: TastyUniverse =>

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

final def argumentCtx(fn: Tree): Context =
if (fn.symbol.isPrimaryConstructor) retractMode(ReadAnnotationCtor) else thisCtx

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

final def markAsEnumSingleton(sym: Symbol): Unit =
Expand Down
6 changes: 5 additions & 1 deletion src/compiler/scala/tools/nsc/tasty/bridge/FlagOps.scala
Expand Up @@ -42,6 +42,7 @@ trait FlagOps { self: TastyUniverse =>
object Creation {
val ObjectDef: TastyFlagSet = Object | Lazy | Final | Stable
val ObjectClassDef: TastyFlagSet = Object | Final
val Wildcard: u.FlagSet = newSymbolFlagSetFromEncoded(Flags.EXISTENTIAL)
val Default: u.FlagSet = newSymbolFlagSet(EmptyTastyFlags)
}
def withAccess(flags: TastyFlagSet, inheritedAccess: TastyFlagSet): TastyFlagSet =
Expand All @@ -56,7 +57,10 @@ trait FlagOps { self: TastyUniverse =>

/** For purpose of symbol initialisation, encode a `TastyFlagSet` as a `symbolTable.FlagSet`. */
private[bridge] def newSymbolFlagSet(tflags: TastyFlagSet): u.FlagSet =
unsafeEncodeTastyFlagSet(tflags) | ModifierFlags.SCALA3X
newSymbolFlagSetFromEncoded(unsafeEncodeTastyFlagSet(tflags))

private[bridge] def newSymbolFlagSetFromEncoded(flags: u.FlagSet): u.FlagSet =
flags | ModifierFlags.SCALA3X

implicit final class SymbolFlagOps(val sym: Symbol) {
def reset(tflags: TastyFlagSet)(implicit ctx: Context): sym.type =
Expand Down
15 changes: 4 additions & 11 deletions src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala
Expand Up @@ -12,7 +12,7 @@

package scala.tools.nsc.tasty.bridge

import scala.tools.nsc.tasty.{TastyUniverse, TastyModes, ForceKinds}, TastyModes._, ForceKinds._
import scala.tools.nsc.tasty.{TastyUniverse, TastyModes}, TastyModes._

import scala.tools.tasty.TastyName
import scala.reflect.internal.Flags
Expand Down Expand Up @@ -70,17 +70,10 @@ trait TreeOps { self: TastyUniverse =>
def selectCtor(qual: Tree) =
u.Select(qual, u.nme.CONSTRUCTOR).setType(qual.tpe.typeSymbol.primaryConstructor.tpe)

if (ctx.mode.is(ReadAnnotation) && name.isSignedConstructor) {
val cls = qual.tpe.typeSymbol
cls.ensureCompleted(AnnotCtor)
if (cls.isJavaAnnotation)
selectCtor(qual)
else
selectName(qual, name)(lookup)
}
else {
if (ctx.mode.is(ReadAnnotationCtor) && name.isSignedConstructor)
selectCtor(qual)
else
selectName(qual, name)(lookup)
}

}

Expand Down
1 change: 0 additions & 1 deletion src/compiler/scala/tools/nsc/transform/Erasure.scala
Expand Up @@ -1297,7 +1297,6 @@ abstract class Erasure extends InfoTransform
if (ct.tag == ClazzTag && ct.typeValue.typeSymbol != definitions.UnitClass) {
val typeValue = ct.typeValue.dealiasWiden
val erased = erasure(typeValue.typeSymbol) applyInArray typeValue

treeCopy.Literal(cleanLiteral, Constant(erased))
} else cleanLiteral

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/tasty/TastyFormat.scala
Expand Up @@ -35,7 +35,7 @@ object TastyFormat {
* compatibility, but remains backwards compatible, with all
* preceding `MinorVersion`.
*/
final val MinorVersion: Int = 0
final val MinorVersion: Int = 1

/**Natural Number. The `ExperimentalVersion` allows for
* experimentation with changes to TASTy without committing
Expand Down
11 changes: 0 additions & 11 deletions src/tastytest/dotty/tools/vulpix/ParallelTesting.scala

This file was deleted.

45 changes: 31 additions & 14 deletions src/tastytest/scala/tools/tastytest/Dotc.scala
Expand Up @@ -16,6 +16,15 @@ object Dotc extends Script.Command {
def initClassloader(): Try[Dotc.ClassLoader] =
Try(Dotc.ClassLoader(ScalaClassLoader.fromURLs(Classpaths.dottyCompiler.asURLs)))

def processIn(op: Dotc.ClassLoader => Int): Int = {
Dotc.initClassloader() match {
case Success(cl) => op(cl)
case Failure(err) =>
println(red(s"could not initialise Scala 3 classpath: $err"))
1
}
}

def loadClass(name: String)(implicit cl: Dotc.ClassLoader) =
Class.forName(name, true, cl.parent)

Expand All @@ -24,6 +33,18 @@ object Dotc extends Script.Command {
invoke(method, null, args)
}

def invokeStatic(
className: String,
methodName: String,
args: Seq[String]
)(implicit cl: Dotc.ClassLoader): Try[Object] = {
val cls = loadClass(className)
val method = cls.getMethod(methodName, classOf[Array[String]])
Try {
invokeStatic(method, Seq(args.toArray))
}
}

def invoke(method: Method, obj: AnyRef, args: Seq[Any])(implicit cl: Dotc.ClassLoader) = {
try cl.parent.asContext[AnyRef] {
method.invoke(obj, args.toArray:_*)
Expand All @@ -35,18 +56,18 @@ object Dotc extends Script.Command {

private def dotcProcess(args: Seq[String])(implicit cl: Dotc.ClassLoader) = processMethod("dotty.tools.dotc.Main")(args)

def processMethod(mainClassName: String)(args: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Boolean] = {
val mainClass = loadClass(mainClassName)
val reporterClass = loadClass("dotty.tools.dotc.reporting.Reporter")
val Main_process = mainClass.getMethod("process", classOf[Array[String]])
val Reporter_hasErrors = reporterClass.getMethod("hasErrors")
Try {
val reporter = unlockExperimentalFeatures(invokeStatic(Main_process, Seq(args.toArray)))
def processMethod(className: String)(args: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Boolean] = {
val reporterCls = loadClass("dotty.tools.dotc.reporting.Reporter")
val Reporter_hasErrors = reporterCls.getMethod("hasErrors")
for (reporter <- invokeStatic(className, "process", args)) yield {
val hasErrors = invoke(Reporter_hasErrors, reporter, Seq.empty).asInstanceOf[Boolean]
!hasErrors
}
}

def mainMethod(className: String)(args: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] =
for (_ <- invokeStatic(className, "main", args)) yield ()

def dotcVersion(implicit cl: Dotc.ClassLoader): String = {
val compilerPropertiesClass = loadClass("dotty.tools.dotc.config.Properties")
val Properties_simpleVersionString = compilerPropertiesClass.getMethod("simpleVersionString")
Expand Down Expand Up @@ -81,14 +102,10 @@ object Dotc extends Script.Command {
return 1
}
val Seq(out, src, additional @ _*) = args: @unchecked
implicit val scala3classloader: Dotc.ClassLoader = initClassloader() match {
case Success(cl) => cl
case Failure(err) =>
println(red(s"could not initialise Scala 3 classpath: $err"))
return 1
Dotc.processIn { implicit scala3classloader =>
val success = dotc(out, out, additional, src).get
if (success) 0 else 1
}
val success = dotc(out, out, additional, src).get
if (success) 0 else 1
}

}
12 changes: 4 additions & 8 deletions src/tastytest/scala/tools/tastytest/DotcDecompiler.scala
@@ -1,6 +1,6 @@
package scala.tools.tastytest

import scala.util.{Try, Success, Failure}
import scala.util.Try

object DotcDecompiler extends Script.Command {

Expand All @@ -19,14 +19,10 @@ object DotcDecompiler extends Script.Command {
return 1
}
val Seq(tasty, additionalSettings @ _*) = args: @unchecked
implicit val scala3classloader: Dotc.ClassLoader = Dotc.initClassloader() match {
case Success(cl) => cl
case Failure(err) =>
println(red(s"could not initialise Scala 3 classpath: $err"))
return 1
Dotc.processIn { implicit scala3classloader =>
val success = decompile(tasty, additionalSettings).get
if (success) 0 else 1
}
val success = decompile(tasty, additionalSettings).get
if (success) 0 else 1
}

}
24 changes: 24 additions & 0 deletions src/tastytest/scala/tools/tastytest/PrintTasty.scala
@@ -0,0 +1,24 @@
package scala.tools.tastytest

import scala.util.Try

object PrintTasty extends Script.Command {

def printTasty(tasty: String)(implicit cl: Dotc.ClassLoader): Try[Unit] =
Dotc.mainMethod("dotty.tools.dotc.core.tasty.TastyPrinter")(Seq(tasty))

val commandName: String = "printTasty"
val describe: String = s"$commandName <tasty: File>"

def process(args: String*): Int = {
if (args.length != 1) {
println(red(s"please provide 1 argument in sub-command: $describe"))
return 1
}
Dotc.processIn { implicit scala3classloader =>
val success = printTasty(tasty = args.head).isSuccess
if (success) 0 else 1
}
}

}
5 changes: 0 additions & 5 deletions src/tastytest/scala/tools/tastytest/package.scala
@@ -1,16 +1,11 @@
package scala.tools

import dotty.tools.vulpix.ParallelTesting

package object tastytest {

import scala.util.Try

import Files.{pathSep, classpathSep}

def unlockExperimentalFeatures[T](op: => T): T =
new ParallelTesting().unlockExperimentalFeatures(op)

def printerrln(str: String): Unit = System.err.println(red(str))
def printwarnln(str: String): Unit = System.err.println(yellow(str))
def printsuccessln(str: String): Unit = System.err.println(green(str))
Expand Down