Skip to content

Commit

Permalink
Add compilation unit info to ClassSymbol (#19010)
Browse files Browse the repository at this point in the history
- [x] Add `compilationUnitInfo` to class symbols
- [x] Move `assocFile` into `CompilationUnitInfo`
- [x] Add tasty version to `CompilationUnitInfo`
- [x] Include #18925 to have test for `CompilationUnitInfo`
- [x] Add explicit nulls to `CompilationUnitInfo`

Fixes  #18933
  • Loading branch information
bishabosha authored Nov 23, 2023
2 parents 637ed88 + 050859e commit 08ab809
Show file tree
Hide file tree
Showing 48 changed files with 329 additions and 123 deletions.
31 changes: 31 additions & 0 deletions compiler/src/dotty/tools/dotc/core/CompilationUnitInfo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package dotty.tools.dotc.core

import dotty.tools.io.AbstractFile
import dotty.tools.tasty.TastyVersion

/** Information about the compilation unit of a class symbol.
*
* @param associatedFile The source or class file from which this class or
* the class containing this symbol was generated,
* null if not applicable.
* @param tastyVersion The TASTy version (major, minor, experimental)
* @param explicitNulls This compilation unit has explicit nulls enabled?
*/
class CompilationUnitInfo(
val associatedFile: AbstractFile,
val tastyVersion: Option[TastyVersion],
val explicitNulls: Boolean
) {

override def toString(): String =
s"CompilationUnitInfo($associatedFile, $tastyVersion)"
}

object CompilationUnitInfo:
def apply(assocFile: AbstractFile | Null, explicitNulls: Boolean = false): CompilationUnitInfo | Null =
if assocFile == null then null
else new CompilationUnitInfo(
assocFile,
tastyVersion = None,
explicitNulls = explicitNulls,
)
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,7 @@ class Definitions {
),
privateWithin = patch.privateWithin,
coord = denot.symbol.coord,
assocFile = denot.symbol.associatedFile
compUnitInfo = denot.symbol.compilationUnitInfo
)

def makeNonClassSymbol(patch: Symbol) =
Expand Down
3 changes: 1 addition & 2 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -298,14 +298,13 @@ object Denotations {
name: Name,
site: Denotation = NoDenotation,
args: List[Type] = Nil,
source: AbstractFile | Null = null,
generateStubs: Boolean = true)
(p: Symbol => Boolean)
(using Context): Symbol =
disambiguate(p) match {
case m @ MissingRef(ownerd, name) if generateStubs =>
if ctx.settings.YdebugMissingRefs.value then m.ex.printStackTrace()
newStubSymbol(ownerd.symbol, name, source)
newStubSymbol(ownerd.symbol, name)
case NoDenotation | _: NoQualifyingRef | _: MissingRef =>
def argStr = if (args.isEmpty) "" else i" matching ($args%, %)"
val msg =
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/NamerOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ object NamerOps:
ConstructorCompanionFlags, ConstructorCompanionFlags,
constructorCompanionCompleter(cls),
coord = cls.coord,
assocFile = cls.assocFile)
compUnitInfo = cls.compUnitInfo)
companion.moduleClass.registerCompanion(cls)
cls.registerCompanion(companion.moduleClass)
companion
Expand All @@ -150,7 +150,7 @@ object NamerOps:
newSymbol(tsym.owner, tsym.name.toTermName,
ConstructorCompanionFlags | StableRealizable | Method, ExprType(prefix.select(proxy)), coord = tsym.coord)

/** Add all necesssary constructor proxy symbols for members of class `cls`. This means:
/** Add all necessary constructor proxy symbols for members of class `cls`. This means:
*
* - if a member is a class, or type alias, that needs a constructor companion, add one,
* provided no member with the same name exists.
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2588,7 +2588,7 @@ object SymDenotations {
for (sym <- scope.toList.iterator)
// We need to be careful to not force the denotation of `sym` here,
// otherwise it will be brought forward to the current run.
if (sym.defRunId != ctx.runId && sym.isClass && sym.asClass.assocFile == file)
if (sym.defRunId != ctx.runId && sym.isClass && sym.asClass.compUnitInfo != null && sym.asClass.compUnitInfo.nn.associatedFile == file)
scope.unlink(sym, sym.lastKnownDenotation.name)
}
}
Expand Down
63 changes: 40 additions & 23 deletions compiler/src/dotty/tools/dotc/core/SymbolLoaders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@ import ast.desugar

import parsing.JavaParsers.OutlineJavaParser
import parsing.Parsers.OutlineParser
import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException, UnpicklerConfig}
import dotty.tools.tasty.{TastyHeaderUnpickler, UnpickleException, UnpicklerConfig, TastyVersion}
import dotty.tools.dotc.core.tasty.TastyUnpickler


object SymbolLoaders {
import ast.untpd.*

Expand All @@ -52,7 +51,7 @@ object SymbolLoaders {
def enterClass(
owner: Symbol, name: PreName, completer: SymbolLoader,
flags: FlagSet = EmptyFlags, scope: Scope = EmptyScope)(using Context): Symbol = {
val cls = newClassSymbol(owner, name.toTypeName.unmangleClassName.decode, flags, completer, assocFile = completer.sourceFileOrNull)
val cls = newClassSymbol(owner, name.toTypeName.unmangleClassName.decode, flags, completer, compUnitInfo = completer.compilationUnitInfo)
enterNew(owner, cls, completer, scope)
}

Expand All @@ -64,7 +63,7 @@ object SymbolLoaders {
val module = newModuleSymbol(
owner, name.toTermName.decode, modFlags, clsFlags,
(module, _) => completer.proxy.withDecls(newScope).withSourceModule(module),
assocFile = completer.sourceFileOrNull)
compUnitInfo = completer.compilationUnitInfo)
enterNew(owner, module, completer, scope)
enterNew(owner, module.moduleClass, completer, scope)
}
Expand Down Expand Up @@ -213,7 +212,7 @@ object SymbolLoaders {
/** Load contents of a package
*/
class PackageLoader(_sourceModule: TermSymbol, classPath: ClassPath) extends SymbolLoader {
override def sourceFileOrNull: AbstractFile | Null = null
def compilationUnitInfo: CompilationUnitInfo | Null = null
override def sourceModule(using Context): TermSymbol = _sourceModule
def description(using Context): String = "package loader " + sourceModule.fullName

Expand Down Expand Up @@ -317,7 +316,7 @@ abstract class SymbolLoader extends LazyType { self =>
/** Load source or class file for `root`, return */
def doComplete(root: SymDenotation)(using Context): Unit

def sourceFileOrNull: AbstractFile | Null
def compilationUnitInfo: CompilationUnitInfo | Null

/** Description of the resource (ClassPath, AbstractFile)
* being processed by this loader
Expand All @@ -328,7 +327,7 @@ abstract class SymbolLoader extends LazyType { self =>
* but provides fresh slots for scope/sourceModule/moduleClass
*/
def proxy: SymbolLoader = new SymbolLoader {
export self.{doComplete, sourceFileOrNull}
export self.{doComplete, compilationUnitInfo}
def description(using Context): String = s"proxy to ${self.description}"
}

Expand Down Expand Up @@ -405,7 +404,8 @@ abstract class SymbolLoader extends LazyType { self =>

class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {

override def sourceFileOrNull: AbstractFile | Null = classfile
def compilationUnitInfo: CompilationUnitInfo | Null = CompilationUnitInfo(classfile)


def description(using Context): String = "class file " + classfile.toString

Expand All @@ -417,38 +417,55 @@ class ClassfileLoader(val classfile: AbstractFile) extends SymbolLoader {

class TastyLoader(val tastyFile: AbstractFile) extends SymbolLoader {

override def sourceFileOrNull: AbstractFile | Null = tastyFile
private val unpickler: tasty.DottyUnpickler =
handleUnpicklingExceptions:
val tastyBytes = tastyFile.toByteArray
new tasty.DottyUnpickler(tastyBytes) // reads header and name table

val compilationUnitInfo: CompilationUnitInfo | Null =
val tastyHeader = unpickler.unpickler.header
val tastyVersion = TastyVersion(
tastyHeader.majorVersion,
tastyHeader.minorVersion,
tastyHeader.experimentalVersion,
)
val attributes = unpickler.tastyAttributes
new CompilationUnitInfo(
tastyFile,
tastyVersion = Some(tastyVersion),
explicitNulls = attributes.explicitNulls,
)

def description(using Context): String = "TASTy file " + tastyFile.toString

override def doComplete(root: SymDenotation)(using Context): Unit =
try
handleUnpicklingExceptions:
checkTastyUUID()
val (classRoot, moduleRoot) = rootDenots(root.asClass)
val tastyBytes = tastyFile.toByteArray
val unpickler = new tasty.DottyUnpickler(tastyBytes)
unpickler.enter(roots = Set(classRoot, moduleRoot, moduleRoot.sourceModule))(using ctx.withSource(util.NoSource))
if mayLoadTreesFromTasty then
classRoot.classSymbol.rootTreeOrProvider = unpickler
moduleRoot.classSymbol.rootTreeOrProvider = unpickler
checkTastyUUID(tastyFile, tastyBytes)

private def handleUnpicklingExceptions[T](thunk: =>T): T =
try thunk
catch case e: RuntimeException =>
val message = e match
case e: UnpickleException =>
i"""TASTy file ${tastyFile.canonicalPath} could not be read, failing with:
| ${Option(e.getMessage).getOrElse("")}"""
s"""TASTy file ${tastyFile.canonicalPath} could not be read, failing with:
| ${Option(e.getMessage).getOrElse("")}""".stripMargin
case _ =>
i"""TASTy file ${tastyFile.canonicalPath} is broken, reading aborted with ${e.getClass}
| ${Option(e.getMessage).getOrElse("")}"""
if (ctx.debug) e.printStackTrace()
throw IOException(message)
s"""TASTy file ${tastyFile.canonicalPath} is broken, reading aborted with ${e.getClass}
| ${Option(e.getMessage).getOrElse("")}""".stripMargin
throw IOException(message, e)


private def checkTastyUUID(tastyFile: AbstractFile, tastyBytes: Array[Byte])(using Context): Unit =
private def checkTastyUUID()(using Context): Unit =
val classfile =
val className = tastyFile.name.stripSuffix(".tasty")
tastyFile.resolveSibling(className + ".class")
if classfile != null then
val tastyUUID = new TastyHeaderUnpickler(TastyUnpickler.scala3CompilerConfig, tastyBytes).readHeader()
val tastyUUID = unpickler.unpickler.header.uuid
new ClassfileTastyUUIDParser(classfile)(ctx).checkTastyUUID(tastyUUID)
else
// This will be the case in any of our tests that compile with `-Youtput-only-tasty`
Expand All @@ -460,15 +477,15 @@ class TastyLoader(val tastyFile: AbstractFile) extends SymbolLoader {

class SourcefileLoader(val srcfile: AbstractFile) extends SymbolLoader {
def description(using Context): String = "source file " + srcfile.toString
override def sourceFileOrNull: AbstractFile | Null = srcfile
def compilationUnitInfo: CompilationUnitInfo | Null = CompilationUnitInfo(srcfile)
def doComplete(root: SymDenotation)(using Context): Unit =
ctx.run.nn.lateCompile(srcfile, typeCheck = ctx.settings.YretainTrees.value)
}

/** A NoCompleter which is also a SymbolLoader. */
class NoLoader extends SymbolLoader with NoCompleter {
def description(using Context): String = "NoLoader"
override def sourceFileOrNull: AbstractFile | Null = null
def compilationUnitInfo: CompilationUnitInfo | Null = null
override def complete(root: SymDenotation)(using Context): Unit =
super[NoCompleter].complete(root)
def doComplete(root: SymDenotation)(using Context): Unit =
Expand Down
Loading

0 comments on commit 08ab809

Please sign in to comment.