Skip to content

Commit

Permalink
Merge pull request #9617 from scalacenter/tasty/support-3.0.0-RC3
Browse files Browse the repository at this point in the history
Tasty Reader support 3.0.0 final
  • Loading branch information
lrytz committed May 14, 2021
2 parents a23259f + 851903a commit 4fa6da5
Show file tree
Hide file tree
Showing 58 changed files with 1,809 additions and 296 deletions.
25 changes: 23 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ lazy val tastytest = configureAsSubproject(project)
.settings(
name := "scala-tastytest",
description := "Scala TASTy Integration Testing Tool",
libraryDependencies ++= List(diffUtilsDep, TastySupport.scala3Compiler),
libraryDependencies += diffUtilsDep,
Compile / scalacOptions ++= Seq("-feature", "-Xlint"),
)

Expand Down Expand Up @@ -731,7 +731,7 @@ lazy val tasty = project.in(file("test") / "tasty")
.settings(publish / skip := true)
.settings(
Test / fork := true,
libraryDependencies += junitInterfaceDep,
libraryDependencies ++= Seq(junitInterfaceDep, TastySupport.scala3Library),
testOptions += Tests.Argument(TestFrameworks.JUnit, "-a", "-v"),
Test / testOptions += Tests.Argument(
s"-Dtastytest.src=${baseDirectory.value}",
Expand All @@ -740,6 +740,27 @@ lazy val tasty = project.in(file("test") / "tasty")
Compile / unmanagedSourceDirectories := Nil,
Test / unmanagedSourceDirectories := List(baseDirectory.value/"test"),
)
.configs(TastySupport.CompilerClasspath, TastySupport.LibraryClasspath)
.settings(
inConfig(TastySupport.CompilerClasspath)(Defaults.configSettings),
inConfig(TastySupport.LibraryClasspath)(Defaults.configSettings),
libraryDependencies ++= Seq(
TastySupport.scala3Compiler % TastySupport.CompilerClasspath,
TastySupport.scala3Library % TastySupport.LibraryClasspath,
),
javaOptions ++= {
import java.io.File.pathSeparator
val scalaLibrary = (library / Compile / classDirectory).value.getAbsoluteFile()
val scalaReflect = (reflect / Compile / classDirectory).value.getAbsoluteFile()
val dottyCompiler = (TastySupport.CompilerClasspath / managedClasspath).value.seq.map(_.data) :+ scalaLibrary
val dottyLibrary = (TastySupport.LibraryClasspath / managedClasspath).value.seq.map(_.data) :+ scalaLibrary
Seq(
s"-Dtastytest.classpaths.dottyCompiler=${dottyCompiler.mkString(pathSeparator)}",
s"-Dtastytest.classpaths.dottyLibrary=${dottyLibrary.mkString(pathSeparator)}",
s"-Dtastytest.classpaths.scalaReflect=$scalaReflect",
)
},
)

lazy val scalacheck = project.in(file("test") / "scalacheck")
.dependsOn(library, reflect, compiler, scaladoc)
Expand Down
8 changes: 6 additions & 2 deletions project/DottySupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ 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-RC1" // TASTy version 28.0.1
val scala3Compiler = "org.scala-lang" % "scala3-compiler_3.0.0-RC1" % supportedTASTyRelease
val supportedTASTyRelease = "3.0.0" // TASTy version 28.0-0
val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease
val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease

val CompilerClasspath = Configuration.of("TastySupport.CompilerClasspath", "TastySupport.CompilerClasspath")
val LibraryClasspath = Configuration.of("TastySupport.LibraryClasspath", "TastySupport.LibraryClasspath")
}

/** Settings needed to compile with Dotty,
Expand Down
11 changes: 5 additions & 6 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -953,12 +953,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
mbt.descriptor
)
}
module.attachments.get[DottyEnumSingleton] match { // TODO [tasty]: dotty enum singletons are not modules.
case Some(enumAttach) =>
val enumCompanion = symInfoTK(module.originalOwner).asClassBType
visitAccess(enumCompanion, enumAttach.name)

case _ => visitAccess(mbt, strMODULE_INSTANCE_FIELD)
if (module.isScala3Defined && module.hasAttachment[DottyEnumSingleton.type]) { // TODO [tasty]: dotty enum singletons are not modules.
val enumCompanion = symInfoTK(module.originalOwner).asClassBType
visitAccess(enumCompanion, module.rawname.toString)
} else {
visitAccess(mbt, strMODULE_INSTANCE_FIELD)
}
}
}
Expand Down
122 changes: 70 additions & 52 deletions src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class TreeUnpickler[Tasty <: TastyUniverse](
nameAtRef: NameRef => TastyName)(implicit
val tasty: Tasty) { self =>
import tasty._
import FlagSets._
import TreeUnpickler._
import MaybeCycle._
import TastyModes._
Expand Down Expand Up @@ -70,8 +69,12 @@ class TreeUnpickler[Tasty <: TastyUniverse](

//---------------- unpickling trees ----------------------------------------------------------------------------------

private def registerSym(addr: Addr, sym: Symbol)(implicit ctx: Context) = {
ctx.log(s"$addr registered ${showSym(sym)} in ${location(sym.owner)}")
private def registerSym(addr: Addr, sym: Symbol, rejected: Boolean)(implicit ctx: Context) = {
assert(!(rejected && isSymbol(sym)), "expected no symbol when rejected")
ctx.log(
if (isSymbol(sym)) s"$addr registered ${showSym(sym)} in ${location(sym.owner)}"
else s"$addr registering symbol was rejected"
)
symAtAddr(addr) = sym
}

Expand Down Expand Up @@ -415,20 +418,23 @@ class TreeUnpickler[Tasty <: TastyUniverse](
if (isType) prior.toTypeName else prior
}

private def normalizeFlags(tag: Int, tastyFlags: TastyFlagSet, name: TastyName, isAbsType: Boolean, isClass: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): TastyFlagSet = {
private def addInferredFlags(tag: Int, tastyFlags: TastyFlagSet, name: TastyName, isAbsType: Boolean, isClass: Boolean, rhsIsEmpty: Boolean)(implicit ctx: Context): TastyFlagSet = {
var flags = tastyFlags
val lacksDefinition =
rhsIsEmpty &&
name.isTermName && !name.isConstructorName && !flags.isOneOf(TermParamOrAccessor) ||
name.isTermName && !name.isConstructorName && !flags.isOneOf(FlagSets.TermParamOrAccessor) ||
isAbsType ||
flags.is(Opaque) && !isClass
if (lacksDefinition && tag != PARAM) flags |= Deferred
if (isClass && flags.is(Trait)) flags |= Abstract
if (tag === DEFDEF) flags |= Method
if (tag === VALDEF) {
if (flags.is(Inline) || ctx.owner.is(Trait)) flags |= FieldAccessor
if (flags.not(Mutable)) flags |= Stable
if (flags.is(SingletonEnumFlags)) flags |= Object // we will encode dotty enum constants as objects (this needs to be corrected in bytecode)
if (flags.is(Inline) || ctx.owner.is(Trait))
flags |= FieldAccessor
if (flags.not(Mutable))
flags |= Stable
if (flags.is(Case | Static | Enum)) // singleton enum case
flags |= Object | Stable // encode as a module (this needs to be corrected in bytecode)
}
if (ctx.owner.isClass) {
if (tag === TYPEPARAM) flags |= Param
Expand All @@ -439,7 +445,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
}
}
else if (isParamTag(tag)) flags |= Param
if (flags.is(Object)) flags |= (if (tag === VALDEF) ObjectCreationFlags else ObjectClassCreationFlags)
if (flags.is(Object)) flags |= (if (tag === VALDEF) FlagSets.Creation.ObjectDef else FlagSets.Creation.ObjectClassDef)
flags
}

Expand All @@ -462,7 +468,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
createMemberSymbol()
case TEMPLATE =>
val localDummy = ctx.newLocalDummy
registerSym(currentAddr, localDummy)
registerSym(currentAddr, localDummy, rejected = false)
localDummy
case tag =>
assert(tag != BIND, "bind pattern symbol creation from TASTy")
Expand All @@ -473,12 +479,23 @@ class TreeUnpickler[Tasty <: TastyUniverse](
* @return the created symbol
*/
def createMemberSymbol()(implicit ctx: Context): Symbol = {

def rejectSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet): Boolean = {
def isPureMixinCtor =
name == TastyName.MixinConstructor && owner.isTrait && flags.is(Stable)
def isInvisible =
flags.is(Invisible)

isPureMixinCtor || isInvisible
}

val start = currentAddr
val tag = readByte()
def isTypeTag = tag === TYPEDEF || tag === TYPEPARAM
val end = readEnd()
val parsedName: TastyName = readTastyName()
ctx.log(s"$start ::: => create ${astTagToString(tag)} ${parsedName.debug}")
def debugSymCreate: String = s"${astTagToString(tag)} ${parsedName.debug}"
ctx.log(s"$start ::: => create $debugSymCreate")
skipParams()
val ttag = nextUnsharedTag
val isAbsType = isAbstractType(ttag)
Expand All @@ -487,13 +504,11 @@ class TreeUnpickler[Tasty <: TastyUniverse](
skipTree() // tpt
val rhsIsEmpty = nothingButMods(end)
if (!rhsIsEmpty) skipTree()
val (name, flags, annotations, privateWithin) = {
val (parsedFlags, annotations, privateWithin) =
readModifiers(end, readTypedAnnot, readTypedWithin, noSymbol)
val name = normalizeName(isTypeTag, parsedName)
val flags = normalizeFlags(tag, parsedFlags, name, isAbsType, isClass, rhsIsEmpty)
(name, flags, annotations, privateWithin)
}
val (parsedFlags0, annotations, privateWithin) =
readModifiers(end, readTypedAnnot, readTypedWithin, noSymbol)
val name = normalizeName(isTypeTag, parsedName)
val flags = addInferredFlags(tag, parsedFlags0, name, isAbsType, isClass, rhsIsEmpty)
def mkCompleter = new Completer(isClass, subReader(start, end), flags)(ctx.retractMode(IndexScopedStats))
def isTypeParameter = flags.is(Param) && isTypeTag
def canEnterInClass = !isTypeParameter
ctx.log {
Expand All @@ -507,34 +522,46 @@ class TreeUnpickler[Tasty <: TastyUniverse](
}
s"""$start parsed flags $debugFlags"""
}
val rejected = rejectSymbol(ctx.owner, name, flags)
val sym = {
if (tag === TYPEPARAM && ctx.owner.isConstructor) {
// TASTy encodes type parameters for constructors
// nsc only has class type parameters
ctx.findOuterClassTypeParameter(name.toTypeName)
}
else {
val completer = new Completer(isClass, subReader(start, end), flags)(ctx.retractMode(IndexScopedStats))
ctx.findRootSymbol(roots, name) match {
case Some(rootd) =>
ctx.adjustSymbol(rootd, flags, completer, privateWithin) // dotty "removes one completion" here from the flags, which is not possible in nsc
ctx.log(s"$start replaced info of ${showSym(rootd)}")
rootd
roots -= rootd
if (rejected) {
ctx.evict(rootd)
noSymbol
}
else {
ctx.redefineSymbol(rootd, flags, mkCompleter, privateWithin)
ctx.log(s"$start replaced info of ${showSym(rootd)}")
rootd
}
case _ =>
if (isClass) ctx.delayClassCompletion(ctx.owner, name.toTypeName, completer, privateWithin)
else ctx.delayCompletion(ctx.owner, name, completer, privateWithin)
if (rejected) noSymbol
else if (isClass) ctx.delayClassCompletion(ctx.owner, name.toTypeName, mkCompleter, privateWithin)
else ctx.delayCompletion(ctx.owner, name, mkCompleter, privateWithin)
}
}
}.ensuring(isSymbol(_), s"${ctx.classRoot}: Could not create symbol at $start")
if (tag == VALDEF && flags.is(SingletonEnumFlags))
ctx.markAsEnumSingleton(sym)
registerSym(start, sym)
if (canEnterInClass && ctx.owner.isClass)
ctx.enterIfUnseen(sym)
if (isClass) {
val localCtx = ctx.withOwner(sym)
forkAt(templateStart).indexTemplateParams()(localCtx)
}
registerSym(start, sym, rejected)
if (isSymbol(sym)) {
if (tag == VALDEF && flags.is(FlagSets.SingletonEnum))
ctx.markAsEnumSingleton(sym)
if (canEnterInClass && ctx.owner.isClass)
ctx.enterIfUnseen(sym)
if (isClass) {
val localCtx = ctx.withOwner(sym)
forkAt(templateStart).indexTemplateParams()(localCtx)
}
ctx.adjustAnnotations(sym, annotations)
}
goto(start)
ctx.adjustAnnotations(sym, annotations)
sym
}

Expand All @@ -554,7 +581,6 @@ class TreeUnpickler[Tasty <: TastyUniverse](
}
nextByte match {
case PRIVATE => addFlag(Private)
case INTERNAL => addFlag(Internal)
case PROTECTED => addFlag(Protected)
case ABSTRACT =>
readByte()
Expand Down Expand Up @@ -595,6 +621,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case PARAMalias => addFlag(ParamAlias)
case EXPORTED => addFlag(Exported)
case OPEN => addFlag(Open)
case INVISIBLE => addFlag(Invisible)
case PRIVATEqualified =>
readByte()
privateWithin = readWithin(ctx)
Expand Down Expand Up @@ -759,18 +786,9 @@ class TreeUnpickler[Tasty <: TastyUniverse](
checkUnsupportedFlags(repr.tastyOnlyFlags &~ (Enum | Extension | Exported))
val tpe = readTpt()(localCtx).tpe
ctx.setInfo(sym,
if (repr.originalFlagSet.is(SingletonEnumFlags)) {
val enumClass = sym.objectImplementation
val selfTpe = defn.SingleType(sym.owner.thisPrefix, sym)
val ctor = ctx.unsafeNewSymbol(
owner = enumClass,
name = TastyName.Constructor,
flags = Method,
info = defn.DefDefType(Nil, Nil :: Nil, selfTpe)
)
enumClass.typeOfThis = selfTpe
ctx.setInfo(enumClass, defn.ClassInfoType(intersectionParts(tpe), ctor :: Nil, enumClass))
prefixedRef(sym.owner.thisPrefix, enumClass)
if (repr.originalFlagSet.is(FlagSets.SingletonEnum)) {
ctx.completeEnumSingleton(sym, tpe)
prefixedRef(sym.owner.thisPrefix, sym.objectImplementation)
}
else if (sym.isFinal && isConstantType(tpe)) defn.InlineExprType(tpe)
else if (sym.isMethod) defn.ExprType(tpe)
Expand Down Expand Up @@ -1001,7 +1019,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
(tag: @switch) match {
case SELECTin =>
val name = readTastyName()
val qual = readTerm()
val qual = readTerm()
if (inParentCtor) {
assert(name.isSignedConstructor, s"Parent of ${ctx.owner} is not a constructor.")
skipTree()
Expand Down Expand Up @@ -1032,7 +1050,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
tpd.SeqLiteral(until(end)(readTerm()), elemtpt)
case REFINEDtpt =>
val refineCls = symAtAddr.getOrElse(start, ctx.newRefinementClassSymbol)
registerSym(start, refineCls)
registerSym(start, refineCls, rejected = false)
typeAtAddr(start) = refineCls.ref
val parent = readTpt()
ctx.withOwner(refineCls).enterRefinement(parent.tpe) { refinedCtx =>
Expand Down Expand Up @@ -1081,7 +1099,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case UNAPPLY => unsupportedTermTreeError("unapply pattern")
case INLINED => unsupportedTermTreeError("inlined expression")
case SELECTouter => metaprogrammingIsUnsupported // only within inline
case HOLE => assertNoMacroHole
case HOLE => abortMacroHole
case _ => readPathTerm()
}
assert(currentAddr === end, s"$start $currentAddr $end ${astTagToString(tag)}")
Expand All @@ -1098,7 +1116,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
forkAt(readAddr()).readTpt()
case BLOCK => // BLOCK appears in type position when quoting a type, but only in the body of a method
metaprogrammingIsUnsupported
case HOLE => assertNoMacroHole
case HOLE => abortMacroHole
case tag =>
if (isTypeTreeTag(tag)) readTerm()(ctx.retractMode(OuterTerm))
else {
Expand All @@ -1112,7 +1130,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
/**
* A HOLE should never appear in TASTy for a top level class, only in quotes.
*/
private def assertNoMacroHole[T]: T = assertError("Scala 3 macro hole in pickled TASTy")
private def abortMacroHole[T]: T = abortWith(msg = "Scala 3 macro hole in pickled TASTy")

private def metaprogrammingIsUnsupported[T](implicit ctx: Context): T =
unsupportedError("Scala 3 metaprogramming features")
Expand Down

0 comments on commit 4fa6da5

Please sign in to comment.