Skip to content

Commit

Permalink
enable pipelined compilation against Scala 3
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed Jan 24, 2024
1 parent 6cfd8a9 commit 1117731
Show file tree
Hide file tree
Showing 20 changed files with 283 additions and 77 deletions.
5 changes: 3 additions & 2 deletions src/compiler/scala/tools/nsc/classpath/FileUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ object FileUtils {
implicit class AbstractFileOps(val file: AbstractFile) extends AnyVal {
def isPackage: Boolean = file.isDirectory && mayBeValidPackage(file.name)

def isClass: Boolean = !file.isDirectory && (file.hasExtension("class") || file.hasExtension("sig"))
def isClass: Boolean = !file.isDirectory && (file.hasExtension("class") || file.hasExtension("sig") || file.hasExtension("tasty"))

def isScalaOrJavaSource: Boolean = !file.isDirectory && (file.hasExtension("scala") || file.hasExtension("java"))

Expand All @@ -46,6 +46,7 @@ object FileUtils {
private val SUFFIX_SCALA = ".scala"
private val SUFFIX_JAVA = ".java"
private val SUFFIX_SIG = ".sig"
private val SUFFIX_TASTY = ".tasty"

def stripSourceExtension(fileName: String): String = {
if (endsScala(fileName)) stripClassExtension(fileName)
Expand All @@ -58,7 +59,7 @@ object FileUtils {
@inline private def ends (filename:String, suffix:String) = filename.endsWith(suffix) && filename.length > suffix.length

def endsClass(fileName: String): Boolean =
ends (fileName, SUFFIX_CLASS) || fileName.endsWith(SUFFIX_SIG)
ends (fileName, SUFFIX_CLASS) || fileName.endsWith(SUFFIX_SIG) || fileName.endsWith(SUFFIX_TASTY)

def endsScalaOrJava(fileName: String): Boolean =
endsScala(fileName) || endsJava(fileName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,18 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
this.isScala = false
this.YtastyReader = settings.YtastyReader.value

val magic = in.getInt(in.bp)
if (magic != JAVA_MAGIC && file.name.endsWith(".sig")) {
val isJavaMagic = in.getInt(in.bp) == JAVA_MAGIC
if (!isJavaMagic && file.name.endsWith(".sig")) {
currentClass = clazz.javaClassName
isScala = true
unpickler.unpickle(in.buf.take(file.sizeOption.get), 0, clazz, staticModule, file.name)
} else if (!isJavaMagic && file.name.endsWith(".tasty")) {
if (!YtastyReader)
MissingRequirementError.signal(s"Add -Ytasty-reader to scalac options to parse the TASTy in $file")

AnyRefClass // Force scala.AnyRef, otherwise we get "error: Symbol AnyRef is missing from the classpath"
val bytes = in.buf.take(file.sizeOption.get)
TastyUnpickler.unpickle(TastyUniverse)(bytes, clazz, staticModule, file.path.stripSuffix(".class") + ".tasty")
} else {
parseHeader()
this.pool = new ConstantPool
Expand Down
7 changes: 6 additions & 1 deletion src/compiler/scala/tools/nsc/tasty/TastyUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ package scala.tools.nsc.tasty

import scala.collection.mutable
import scala.tools.tasty.{ErasedTypeRef, Signature, TastyName, TastyReader, TastyRefs}
import scala.tools.tasty.{AttributeUnpickler, Attributes}
import scala.tools.tasty.{TastyFormat, TastyHeaderUnpickler, TastyVersion, UnpicklerConfig}
import TastyFormat.NameTags._
import TastyRefs.NameRef
Expand Down Expand Up @@ -43,7 +44,11 @@ object TastyUnpickler {
unpickler.readHeader()
unpickler.readNames()
val Some(astReader) = unpickler.readSection(TastyFormat.ASTsSection): @unchecked
val treeUnpickler = new TreeUnpickler[tasty.type](astReader, unpickler.nameAtRef)(tasty)
val attributes = unpickler
.readSection(TastyFormat.AttributesSection)
.map(AttributeUnpickler.attributes)
.getOrElse(Attributes.empty)
val treeUnpickler = new TreeUnpickler[tasty.type](astReader, unpickler.nameAtRef, attributes)(tasty)
treeUnpickler.enterTopLevel(classRoot, objectRoot)
}

Expand Down
36 changes: 26 additions & 10 deletions src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

package scala.tools.nsc.tasty

import scala.tools.tasty.{TastyRefs, TastyReader, TastyName, TastyFormat, TastyFlags}
import scala.tools.tasty.{Attributes, TastyRefs, TastyReader, TastyName, TastyFormat, TastyFlags}
import TastyRefs._, TastyFlags._, TastyFormat._
import ForceKinds._

Expand All @@ -36,7 +36,8 @@ import scala.collection.immutable.ArraySeq
*/
class TreeUnpickler[Tasty <: TastyUniverse](
reader: TastyReader,
nameAtRef: NameRef => TastyName)(implicit
nameAtRef: NameRef => TastyName,
attributes: Attributes)(implicit
val tasty: Tasty) { self =>
import tasty._
import TreeUnpickler._
Expand Down Expand Up @@ -70,6 +71,12 @@ class TreeUnpickler[Tasty <: TastyUniverse](
/** The root owner tree. See `OwnerTree` class definition. Set by `enterTopLevel`. */
private[this] var ownerTree: OwnerTree = _

/** JAVAattr is necessary to support pipelining in Zinc, we have to set Java erasure semantics if found.
* To support this we also need to support TASTy-only classpaths, see https://github.com/lampepfl/dotty/pull/17594
* For a test case, see test/tasty/run-pipelined
*/
private[this] val isJava = attributes.isJava

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

private def registerSym(addr: Addr, sym: Symbol, rejected: Boolean)(implicit ctx: Context) = {
Expand Down Expand Up @@ -111,7 +118,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
reader: TastyReader,
tflags: TastyFlagSet
)(implicit ctx: Context)
extends TastyCompleter(isClass, tflags) {
extends TastyCompleter(isClass, tflags, isJava) {

private val symAddr = reader.currentAddr

Expand All @@ -133,6 +140,9 @@ class TreeUnpickler[Tasty <: TastyUniverse](
def forkAt(start: Addr): TreeReader = new TreeReader(subReader(start, endAddr))
def fork: TreeReader = forkAt(currentAddr)

def skipParentTree(tag: Int): Unit = if (tag != SPLITCLAUSE) skipTree(tag)
def skipParentTree(): Unit = skipParentTree(readByte())

def skipTree(tag: Int): Unit =
if (tag >= firstLengthTreeTag) goto(readEnd())
else if (tag >= firstNatASTTreeTag) { readNat(); skipTree() }
Expand Down Expand Up @@ -383,15 +393,16 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case TERMREFin =>
defn.TermRefIn(name = readTastyName(), prefix = readType(), space = readType())
case TYPEREFin =>
defn.TypeRefIn(name = readTastyName().toTypeName, prefix = readType(), space = readType())
defn.TypeRefIn(
name = readTastyName().toTypeName, prefix = readType(), space = readType(), isJava = isJava)
case REFINEDtype =>
var name = readTastyName()
val parent = readType()
if (nextUnsharedTag === TYPEBOUNDS) name = name.toTypeName
ctx.enterRefinement(parent)(refinedCtx =>
defn.RefinedType(parent, name, refinedCtx.owner, readType())
)
case APPLIEDtype => defn.AppliedType(readType(), until(end)(readType()))
case APPLIEDtype => defn.AppliedType(readType(), until(end)(readType()), isJava)
case TYPEBOUNDS =>
val lo = readType()
if (nothingButMods(end)) readVariances(lo)
Expand All @@ -417,7 +428,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
case TYPEREFsymbol | TERMREFsymbol => defn.NamedType(sym = readSymRef(), prefix = readType())
case TYPEREFpkg => defn.NamedType(defn.NoPrefix, sym = readPackageRef().objectImplementation)
case TERMREFpkg => defn.NamedType(defn.NoPrefix, sym = readPackageRef())
case TYPEREF => defn.TypeRef(name = readTastyName().toTypeName, prefix = readType())
case TYPEREF => defn.TypeRef(name = readTastyName().toTypeName, prefix = readType(), isJava = isJava)
case TERMREF => defn.TermRef(name = readTastyName(), prefix = readType())
case THIS => defn.ThisType(readType())
case RECtype =>
Expand Down Expand Up @@ -595,7 +606,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
noSymbol
}
else {
ctx.redefineSymbol(rootd, flags, mkCompleter, privateWithin)
ctx.redefineSymbol(rootd, mkCompleter, privateWithin)
ctx.log(s"$start replaced info of root ${showSym(rootd)}")
rootd
}
Expand Down Expand Up @@ -936,6 +947,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
)

private def readTemplate()(implicit ctx: Context): Unit = {
val start = currentAddr
val cls = ctx.enterClassCompletion()
val localDummy = symbolAtCurrent()
assert(readByte() === TEMPLATE)
Expand Down Expand Up @@ -979,14 +991,15 @@ class TreeUnpickler[Tasty <: TastyUniverse](

def indexMembers()(implicit ctx: Context): Unit = ctx.trace(traceIndexMembers) {
val bodyIndexer = fork
while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree() // skip until primary ctor
while ({val tag = bodyIndexer.reader.nextByte; tag != DEFDEF && tag != SPLITCLAUSE})
bodyIndexer.skipParentTree() // skip until primary ctor
bodyIndexer.indexStats(end)
}

def collectParents()(implicit ctx: Context): List[Type] = ctx.trace(traceCollectParents) {
val parentCtx = ctx.withOwner(localDummy).addMode(ReadParents)
val parentWithOuter = parentCtx.addMode(OuterTerm)
collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) {
collectWhile({val tag = nextByte; tag != SELFDEF && tag != DEFDEF && tag != SPLITCLAUSE}) {
defn.adjustParent(
nextUnsharedTag match {
case APPLY | TYPEAPPLY | BLOCK => readTerm()(parentWithOuter).tpe
Expand Down Expand Up @@ -1022,6 +1035,9 @@ class TreeUnpickler[Tasty <: TastyUniverse](
if (nextByte === SELFDEF) {
addSelfDef()
}
if (nextByte === SPLITCLAUSE) {
assert(isJava, s"unexpected SPLITCLAUSE at $start")
}
setInfoWithParents(tparams, ctx.processParents(cls, parents))
}

Expand Down Expand Up @@ -1171,7 +1187,7 @@ class TreeUnpickler[Tasty <: TastyUniverse](
// If we do directly a tpd.AppliedType tree we might get a
// 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()))
tpd.AppliedTypeTree(readTpt(), until(end)(readTpt()), isJava)
case ANNOTATEDtpt => tpd.Annotated(readTpt(), readTerm()(ctx.addMode(ReadAnnotTopLevel)))
case LAMBDAtpt => tpd.LambdaTypeTree(readParams[NoCycle](TYPEPARAM).map(symFromNoCycle), readTpt())
case MATCHtpt => matchTypeIsUnsupported
Expand Down
63 changes: 33 additions & 30 deletions src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -280,17 +280,18 @@ trait ContextOps { self: TastyUniverse =>

final def newLocalDummy: Symbol = owner.newLocalDummy(u.NoPosition)

final def newWildcard(info: Type): Symbol =
final def newWildcard(info: Type, isJava: Boolean): Symbol =
owner.newTypeParameter(
name = u.freshTypeName("_$")(u.currentFreshNameCreator),
pos = u.NoPosition,
newFlags = FlagSets.Creation.Wildcard
newFlags = FlagSets.Creation.wildcard(isJava)
).setInfo(info)

final def newConstructor(owner: Symbol, info: Type): Symbol = unsafeNewSymbol(
final def newConstructor(owner: Symbol, isJava: Boolean, info: Type): Symbol = unsafeNewSymbol(
owner = owner,
name = TastyName.Constructor,
flags = Method,
isJava = isJava,
info = info
)

Expand All @@ -300,6 +301,7 @@ trait ContextOps { self: TastyUniverse =>
owner = cls,
typeName = TastyName.SimpleName(cls.fullName('$') + "$$localSealedChildProxy").toTypeName,
flags = tflags,
isJava = false,
info = defn.LocalSealedChildProxyInfo(cls, tflags),
privateWithin = u.NoSymbol
)
Expand All @@ -311,6 +313,7 @@ trait ContextOps { self: TastyUniverse =>
owner = owner,
name = tname,
flags = flags1,
isJava = false,
info = defn.LambdaParamInfo(flags1, idx, infoDb)
)
}
Expand Down Expand Up @@ -360,17 +363,17 @@ trait ContextOps { self: TastyUniverse =>
tpe
}
}
unsafeNewSymbol(owner, name, flags, info)
unsafeNewSymbol(owner, name, flags, isJava = false, info)
}

/** 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: TastyCompleter, privateWithin: Symbol = noSymbol): Symbol = {
def default() = unsafeNewSymbol(owner, name, completer.tflags, completer, privateWithin)
def default() = unsafeNewSymbol(owner, name, completer.tflags, completer.isJava, completer, privateWithin)
if (completer.tflags.is(Object)) {
val sourceObject = findObject(owner, encodeTermName(name))
if (isSymbol(sourceObject))
redefineSymbol(sourceObject, completer.tflags, completer, privateWithin)
redefineSymbol(sourceObject, completer, privateWithin)
else
default()
}
Expand All @@ -382,11 +385,11 @@ 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: TastyCompleter, privateWithin: Symbol): Symbol = {
def default() = unsafeNewClassSymbol(owner, typeName, completer.tflags, completer, privateWithin)
def default() = unsafeNewClassSymbol(owner, typeName, completer.tflags, completer.isJava, completer, privateWithin)
if (completer.tflags.is(Object)) {
val sourceObject = findObject(owner, encodeTermName(typeName.toTermName))
if (isSymbol(sourceObject))
redefineSymbol(sourceObject.objectImplementation, completer.tflags, completer, privateWithin)
redefineSymbol(sourceObject.objectImplementation, completer, privateWithin)
else
default()
}
Expand Down Expand Up @@ -423,31 +426,31 @@ trait ContextOps { self: TastyUniverse =>

/** Unsafe to call for creation of a object val, prefer `delayCompletion` if info is a LazyType
*/
private def unsafeNewSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet, info: Type, privateWithin: Symbol = noSymbol): Symbol =
unsafeSetInfoAndPrivate(unsafeNewUntypedSymbol(owner, name, flags), info, privateWithin)
private def unsafeNewSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet, isJava: Boolean, info: Type, privateWithin: Symbol = noSymbol): Symbol =
unsafeSetInfoAndPrivate(unsafeNewUntypedSymbol(owner, name, flags, isJava), info, privateWithin)

/** Unsafe to call for creation of a object class, prefer `delayClassCompletion` if info is a LazyType
*/
private def unsafeNewClassSymbol(owner: Symbol, typeName: TastyName.TypeName, flags: TastyFlagSet, info: Type, privateWithin: Symbol): Symbol =
unsafeSetInfoAndPrivate(unsafeNewUntypedClassSymbol(owner, typeName, flags), info, privateWithin)
private def unsafeNewClassSymbol(owner: Symbol, typeName: TastyName.TypeName, flags: TastyFlagSet, isJava: Boolean, info: Type, privateWithin: Symbol): Symbol =
unsafeSetInfoAndPrivate(unsafeNewUntypedClassSymbol(owner, typeName, flags, isJava), info, privateWithin)

private final def unsafeNewUntypedSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet): Symbol = {
private final def unsafeNewUntypedSymbol(owner: Symbol, name: TastyName, flags: TastyFlagSet, isJava: Boolean): Symbol = {
if (flags.isOneOf(Param | ParamSetter)) {
if (name.isTypeName) {
owner.newTypeParameter(encodeTypeName(name.toTypeName), u.NoPosition, newSymbolFlagSet(flags))
owner.newTypeParameter(encodeTypeName(name.toTypeName), u.NoPosition, newSymbolFlagSet(flags, isJava))
}
else {
if (owner.isClass && flags.is(FlagSets.FieldGetter)) {
val fieldFlags = flags &~ FlagSets.FieldGetter | FlagSets.LocalField
val termName = encodeTermName(name)
val getter = owner.newMethodSymbol(termName, u.NoPosition, newSymbolFlagSet(flags))
val fieldSym = owner.newValue(termName, u.NoPosition, newSymbolFlagSet(fieldFlags))
val getter = owner.newMethodSymbol(termName, u.NoPosition, newSymbolFlagSet(flags, isJava))
val fieldSym = owner.newValue(termName, u.NoPosition, newSymbolFlagSet(fieldFlags, isJava))
fieldSym.info = defn.CopyInfo(getter, fieldFlags)
owner.rawInfo.decls.enter(fieldSym)
getter
}
else {
owner.newValueParameter(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags))
owner.newValueParameter(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags, isJava))
}
}
}
Expand All @@ -456,36 +459,36 @@ trait ContextOps { self: TastyUniverse =>
if (!isEnum) {
log(s"!!! visited module value $name first")
}
val module = owner.newModule(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags))
val module = owner.newModule(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags, isJava))
module.moduleClass.info =
if (isEnum) defn.SingletonEnumClassInfo(module, flags)
else defn.DefaultInfo
module
}
else if (name.isTypeName) {
owner.newTypeSymbol(encodeTypeName(name.toTypeName), u.NoPosition, newSymbolFlagSet(flags))
owner.newTypeSymbol(encodeTypeName(name.toTypeName), u.NoPosition, newSymbolFlagSet(flags, isJava))
}
else if (name === TastyName.Constructor) {
owner.newConstructor(u.NoPosition, newSymbolFlagSet(flags &~ Stable))
owner.newConstructor(u.NoPosition, newSymbolFlagSet(flags &~ Stable, isJava))
}
else if (name === TastyName.MixinConstructor) {
owner.newMethodSymbol(u.nme.MIXIN_CONSTRUCTOR, u.NoPosition, newSymbolFlagSet(flags &~ Stable))
owner.newMethodSymbol(u.nme.MIXIN_CONSTRUCTOR, u.NoPosition, newSymbolFlagSet(flags &~ Stable, isJava))
}
else {
owner.newMethodSymbol(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags))
owner.newMethodSymbol(encodeTermName(name), u.NoPosition, newSymbolFlagSet(flags, isJava))
}
}

private final def unsafeNewUntypedClassSymbol(owner: Symbol, typeName: TastyName.TypeName, flags: TastyFlagSet): Symbol = {
private final def unsafeNewUntypedClassSymbol(owner: Symbol, typeName: TastyName.TypeName, flags: TastyFlagSet, isJava: Boolean): Symbol = {
if (flags.is(FlagSets.Creation.ObjectClassDef)) {
log(s"!!! visited module class $typeName first")
val module = owner.newModule(encodeTermName(typeName), u.NoPosition, FlagSets.Creation.Default)
val module = owner.newModule(encodeTermName(typeName), u.NoPosition, FlagSets.Creation.initial(isJava))
module.info = defn.DefaultInfo
module.moduleClass.flags = newSymbolFlagSet(flags)
module.moduleClass.flags = newSymbolFlagSet(flags, isJava)
module.moduleClass
}
else {
owner.newClassSymbol(encodeTypeName(typeName), u.NoPosition, newSymbolFlagSet(flags))
owner.newClassSymbol(encodeTypeName(typeName), u.NoPosition, newSymbolFlagSet(flags, isJava))
}
}

Expand Down Expand Up @@ -532,15 +535,15 @@ trait ContextOps { self: TastyUniverse =>
inheritedAccess = sym.repr.tflags
)
val selfTpe = defn.SingleType(sym.owner.thisPrefix, sym)
val ctor = newConstructor(moduleCls, selfTpe)
val ctor = newConstructor(moduleCls, isJava = false, selfTpe) // TODO: test Java Enum
moduleCls.typeOfThis = selfTpe
moduleCls.flags = newSymbolFlagSet(moduleClsFlags)
moduleCls.flags = newSymbolFlagSet(moduleClsFlags, isJava = false) // TODO: test Java Enum
moduleCls.info = defn.ClassInfoType(intersectionParts(tpe), ctor :: Nil, moduleCls)
moduleCls.privateWithin = sym.privateWithin
}

final def redefineSymbol(symbol: Symbol, flags: TastyFlagSet, completer: TastyCompleter, privateWithin: Symbol): symbol.type = {
symbol.flags = newSymbolFlagSet(flags)
final def redefineSymbol(symbol: Symbol, completer: TastyCompleter, privateWithin: Symbol): symbol.type = {
symbol.flags = newSymbolFlagSet(completer.tflags, completer.isJava)
unsafeSetInfoAndPrivate(symbol, completer, privateWithin)
}

Expand Down

0 comments on commit 1117731

Please sign in to comment.