Skip to content

Commit

Permalink
simplify monitor, add tracking/cancellation to semanticdb
Browse files Browse the repository at this point in the history
  • Loading branch information
bishabosha committed Oct 25, 2023
1 parent 7fc4341 commit 9eeafdf
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 90 deletions.
46 changes: 26 additions & 20 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -172,30 +172,30 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
private var _progress: Progress | Null = null // Set if progress reporting is enabled

/** Only safe to call if progress is being tracked. */
private inline def trackProgress(using Context)(inline op: Context ?=> Progress => Unit): Unit =
private inline def trackProgress(inline op: Progress => Unit): Unit =
val local = _progress
if local != null then
op(using ctx)(local)
op(local)

private inline def foldProgress[T](using Context)(inline default: T)(inline op: Context ?=> Progress => T): T =
private inline def foldProgress[T](inline default: T)(inline op: Progress => T): T =
val local = _progress
if local != null then
op(using ctx)(local)
op(local)
else
default

def didEnterUnit()(using Context): Boolean =
foldProgress(true /* should progress by default */)(_.tryEnterUnit(ctx.compilationUnit))
def didEnterUnit(unit: CompilationUnit): Boolean =
foldProgress(true /* should progress by default */)(_.tryEnterUnit(unit))

def didEnterFinal()(using Context): Boolean =
foldProgress(true /* should progress by default */)(p => !p.checkCancellation())
def progressCancelled(): Boolean =
foldProgress(false /* should progress by default */)(_.checkCancellation())

def doAdvanceUnit()(using Context): Unit =
def doAdvanceUnit(): Unit =
trackProgress: progress =>
progress.unitc += 1 // trace that we completed a unit in the current (sub)phase
progress.refreshProgress()

def doAdvanceLate()(using Context): Unit =
def doAdvanceLate(): Unit =
trackProgress: progress =>
progress.latec += 1 // trace that we completed a late compilation
progress.refreshProgress()
Expand All @@ -211,7 +211,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
finally
Thread.currentThread().nn.interrupt()

private def doAdvancePhase(currentPhase: Phase, wasRan: Boolean)(using Context): Unit =
private def doAdvancePhase(currentPhase: Phase, wasRan: Boolean): Unit =
trackProgress: progress =>
progress.unitc = 0 // reset unit count in current (sub)phase
progress.subtraversalc = 0 // reset subphase index to initial
Expand All @@ -223,7 +223,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
// no subphases were ran, remove traversals from expected total
progress.totalTraversals -= currentPhase.traversals

private def doAdvanceSubPhase()(using Context): Unit =
private def doEnterSubPhase()(using Context): Unit =
trackProgress: progress =>
progress.unitc = 0 // reset unit count in current (sub)phase
progress.seen += 1 // trace that we've seen a (sub)phase
Expand Down Expand Up @@ -351,7 +351,7 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
if (!ctx.reporter.hasErrors)
Rewrites.writeBack()
suppressions.runFinished(hasErrors = ctx.reporter.hasErrors)
while (finalizeActions.nonEmpty && didEnterFinal()) {
while (finalizeActions.nonEmpty && !progressCancelled()) {
val action = finalizeActions.remove(0)
action()
}
Expand Down Expand Up @@ -563,7 +563,7 @@ object Run {
true

/** trace the current progress out of the total, in the current (sub)phase, reporting the next (sub)phase */
private[Run] def refreshProgress()(using Context): Unit =
private[Run] def refreshProgress(): Unit =
requireInitialized()
val total = totalProgress()
if total > 0 && !cb.progress(currentProgress(), total, currPhaseName, nextPhaseName) then
Expand All @@ -572,19 +572,25 @@ object Run {
extension (run: Run | Null)

/** record that the current phase has begun for the compilation unit of the current Context */
def enterUnit()(using Context): Boolean =
if run != null then run.didEnterUnit()
def enterUnit(unit: CompilationUnit): Boolean =
if run != null then run.didEnterUnit(unit)
else true // don't check cancellation if we're not tracking progress

/** check progress cancellation, true if not cancelled */
def enterBlock(): Boolean =
if run != null then !run.progressCancelled()
else true // don't check cancellation if we're not tracking progress

/** advance the unit count and record progress in the current phase */
def advanceUnit()(using Context): Unit =
def advanceUnit(): Unit =
if run != null then run.doAdvanceUnit()

def advanceSubPhase()(using Context): Unit =
if run != null then run.doAdvanceSubPhase()
/** advance the current/next sub-phase and record progress */
def enterSubPhase()(using Context): Unit =
if run != null then run.doEnterSubPhase()

/** advance the late count and record progress in the current phase */
def advanceLate()(using Context): Unit =
def advanceLate(): Unit =
if run != null then run.doAdvanceLate()

def enrichedErrorMessage: Boolean = if run == null then false else run.myEnrichedErrorMessage
Expand Down
31 changes: 14 additions & 17 deletions compiler/src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ object Phases {
val buf = List.newBuilder[CompilationUnit]
for unit <- units do
given unitCtx: Context = runCtx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports
if ctx.run.enterUnit() then
if ctx.run.enterUnit(unit) then
try run
catch case ex: Throwable if !ctx.run.enrichedErrorMessage =>
println(ctx.run.enrichErrorMessage(s"unhandled exception while running $phaseName on $unit"))
Expand Down Expand Up @@ -458,29 +458,26 @@ object Phases {
final def iterator: Iterator[Phase] =
Iterator.iterate(this)(_.next) takeWhile (_.hasNext)

/** run the body as one iteration of a (sub)phase (see Run.Progress), Enrich crash messages */
/** Cancellable region, if not cancelled, run the body in the context of the current compilation unit.
* Enrich crash messages.
*/
final def monitor(doing: String)(body: Context ?=> Unit)(using Context): Boolean =
if ctx.run.enterUnit() then
val unit = ctx.compilationUnit
if ctx.run.enterUnit(unit) then
try {body; true}
catch
case NonFatal(ex) if !ctx.run.enrichedErrorMessage =>
report.echo(ctx.run.enrichErrorMessage(s"exception occurred while $doing ${ctx.compilationUnit}"))
throw ex
catch case NonFatal(ex) if !ctx.run.enrichedErrorMessage =>
report.echo(ctx.run.enrichErrorMessage(s"exception occurred while $doing $unit"))
throw ex
finally ctx.run.advanceUnit()
else
false

/** run the body as one iteration of a (sub)phase (see Run.Progress), Enrich crash messages */
final def monitorOpt[T](doing: String)(body: Context ?=> Option[T])(using Context): Option[T] =
if ctx.run.enterUnit() then
try body
catch
case NonFatal(ex) if !ctx.run.enrichedErrorMessage =>
report.echo(ctx.run.enrichErrorMessage(s"exception occurred while $doing ${ctx.compilationUnit}"))
throw ex
finally ctx.run.advanceUnit()
/** Do not run if compile progress has been cancelled */
final def cancellable(body: Context ?=> Unit)(using Context): Boolean =
if ctx.run.enterBlock() then
{body; true}
else
None
false

override def toString: String = phaseName
}
Expand Down
12 changes: 6 additions & 6 deletions compiler/src/dotty/tools/dotc/fromtasty/ReadTasty.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import NameOps._
import ast.Trees.Tree
import Phases.Phase


/** Load trees from TASTY files */
class ReadTasty extends Phase {

Expand All @@ -23,13 +22,14 @@ class ReadTasty extends Phase {

override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
withMode(Mode.ReadPositions) {
val unitContexts = units.map(unit => ctx.fresh.setCompilationUnit(unit))
unitContexts.flatMap(applyPhase()(using _))
val nextUnits = collection.mutable.ListBuffer.empty[CompilationUnit]
val unitContexts = units.view.map(ctx.fresh.setCompilationUnit)
for given Context <- unitContexts if addTasty(nextUnits += _) do ()
nextUnits.toList
}

private def applyPhase()(using Context): Option[CompilationUnit] = monitorOpt(phaseName):
val unit = ctx.compilationUnit
readTASTY(unit)
def addTasty(fn: CompilationUnit => Unit)(using Context): Boolean = monitor(phaseName):
readTASTY(ctx.compilationUnit).foreach(fn)

def readTASTY(unit: CompilationUnit)(using Context): Option[CompilationUnit] = unit match {
case unit: TASTYCompilationUnit =>
Expand Down
79 changes: 45 additions & 34 deletions compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import dotty.tools.dotc.{semanticdb => s}
import dotty.tools.io.{AbstractFile, JarArchive}
import dotty.tools.dotc.semanticdb.DiagnosticOps.*
import scala.util.{Using, Failure, Success}
import java.nio.file.Path
import dotty.tools.dotc.reporting.Diagnostic.Warning


/** Extract symbol references and uses to semanticdb files.
Expand Down Expand Up @@ -60,48 +62,57 @@ class ExtractSemanticDB private (phaseMode: ExtractSemanticDB.PhaseMode) extends
// Check not needed since it does not transform trees
override def isCheckable: Boolean = false

private def semanticdbOfUnit(unit: CompilationUnit, sourceRoot: String)(using Context) =
ExtractSemanticDB.semanticdbPath(unit.source, ExtractSemanticDB.semanticdbOutDir, sourceRoot)

private def extractSemanticDB(sourceRoot: String, writeSemanticdbText: Boolean)(using Context): Boolean =
monitor(phaseName) {
val unit = ctx.compilationUnit
val extractor = ExtractSemanticDB.Extractor()
extractor.extract(unit.tpdTree)
ExtractSemanticDB.write(
unit.source,
extractor.occurrences.toList,
extractor.symbolInfos.toList,
extractor.synthetics.toList,
semanticdbOfUnit(unit, sourceRoot),
sourceRoot,
writeSemanticdbText
)
}

def computeDiagnostics(
warnings: WarningsMap, sourceRoot: String, accept: SourceDiagnostics => Unit)(using Context): Boolean =
monitor(phaseName) {
val unit = ctx.compilationUnit
for ws <- warnings.get(unit.source) do
val outputPath = semanticdbOfUnit(unit, sourceRoot)
accept(SourceDiagnostics(outputPath, ws.map(_.toSemanticDiagnostic)))
}

type WarningsMap = Map[SourceFile, List[Warning]]
case class SourceDiagnostics(outputDir: Path, diagnostics: List[Diagnostic])

override def runOn(units: List[CompilationUnit])(using ctx: Context): List[CompilationUnit] = {
val sourceRoot = ctx.settings.sourceroot.value
val appendDiagnostics = phaseMode == ExtractSemanticDB.PhaseMode.AppendDiagnostics
val unitContexts = units.map(unit => ctx.fresh.setCompilationUnit(unit).withRootImports)
if (appendDiagnostics)
val warnings = ctx.reporter.allWarnings.groupBy(w => w.pos.source)
units.flatMap { unit =>
warnings.get(unit.source).map { ws =>
val unitCtx = ctx.fresh.setCompilationUnit(unit).withRootImports
val outputDir =
ExtractSemanticDB.semanticdbPath(
unit.source,
ExtractSemanticDB.semanticdbOutDir(using unitCtx),
sourceRoot
)
(outputDir, ws.map(_.toSemanticDiagnostic))
val reportBuf = mutable.ListBuffer.empty[SourceDiagnostics]
val units0 =
for given Context <- unitContexts if computeDiagnostics(warnings, sourceRoot, reportBuf += _)
yield ctx.compilationUnit
cancellable {
reportBuf.toList.asJava.parallelStream().forEach { case SourceDiagnostics(out, warnings) =>
ExtractSemanticDB.appendDiagnostics(warnings, out)
}
}.asJava.parallelStream().forEach { case (out, warnings) =>
ExtractSemanticDB.appendDiagnostics(warnings, out)
}
units0
else
val writeSemanticdbText = ctx.settings.semanticdbText.value
units.foreach { unit =>
val unitCtx = ctx.fresh.setCompilationUnit(unit).withRootImports
val outputDir =
ExtractSemanticDB.semanticdbPath(
unit.source,
ExtractSemanticDB.semanticdbOutDir(using unitCtx),
sourceRoot
)
val extractor = ExtractSemanticDB.Extractor()
extractor.extract(unit.tpdTree)(using unitCtx)
ExtractSemanticDB.write(
unit.source,
extractor.occurrences.toList,
extractor.symbolInfos.toList,
extractor.synthetics.toList,
outputDir,
sourceRoot,
writeSemanticdbText
)
}
units
for given Context <- unitContexts if extractSemanticDB(sourceRoot, writeSemanticdbText)
yield ctx.compilationUnit
}

def run(using Context): Unit = unsupported("run")
Expand Down Expand Up @@ -611,4 +622,4 @@ object ExtractSemanticDB:
traverse(vparam.tpt)
tparams.foreach(tp => traverse(tp.rhs))
end Extractor
end ExtractSemanticDB
end ExtractSemanticDB
22 changes: 11 additions & 11 deletions compiler/src/dotty/tools/dotc/transform/init/Checker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,21 @@ class Checker extends Phase:
val traverser = new InitTreeTraverser()
val unitContexts = units.map(unit => checkCtx.fresh.setCompilationUnit(unit))

val unitContexts0 =
for
given Context <- unitContexts
if traverse(traverser)
yield ctx
val units0 =
for given Context <- unitContexts if traverse(traverser) yield ctx.compilationUnit

val classes = traverser.getClasses()
cancellable {
val classes = traverser.getClasses()

if ctx.settings.YcheckInit.value then
Semantic.checkClasses(classes)(using checkCtx)
if ctx.settings.YcheckInit.value then
Semantic.checkClasses(classes)(using checkCtx)

if ctx.settings.YcheckInitGlobal.value then
Objects.checkClasses(classes)(using checkCtx)
if ctx.settings.YcheckInitGlobal.value then
Objects.checkClasses(classes)(using checkCtx)
}

unitContexts0.map(_.compilationUnit)
units0
end runOn

def run(using Context): Unit = unsupported("run")

Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/TyperPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
if enterSyms
yield ctx
finally
ctx.run.advanceSubPhase() // tick from "typer (indexing)" to "typer (typechecking)"
ctx.run.enterSubPhase() // tick from "typer (indexing)" to "typer (typechecking)"

ctx.base.parserPhase match {
case p: ParserPhase =>
Expand All @@ -98,7 +98,7 @@ class TyperPhase(addRootImports: Boolean = true) extends Phase {
if typeCheck
yield ctx
finally
ctx.run.advanceSubPhase() // tick from "typer (typechecking)" to "typer (java checking)"
ctx.run.enterSubPhase() // tick from "typer (typechecking)" to "typer (java checking)"

record("total trees after typer", ast.Trees.ntrees)

Expand Down

0 comments on commit 9eeafdf

Please sign in to comment.