Skip to content

make Ycheck a phase #377

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

Merged
merged 6 commits into from
Feb 26, 2015
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class Compiler {
*/
def rootContext(implicit ctx: Context): Context = {
ctx.definitions.init(ctx)
ctx.usePhases(phases)
ctx.setPhasePlan(phases)
val rootScope = new MutableScope
val bootstrap = ctx.fresh
.setPeriod(Period(nextRunId, FirstPhaseId))
Expand Down
16 changes: 5 additions & 11 deletions src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,19 @@ class Run(comp: Compiler)(implicit ctx: Context) {
*/
def compileSources(sources: List[SourceFile]) = Stats.monitorHeartBeat {
if (sources forall (_.exists)) {
val phases = ctx.squashPhases(ctx.phasePlan,
ctx.settings.Yskip.value, ctx.settings.YstopBefore.value, ctx.settings.YstopAfter.value, ctx.settings.Ycheck.value)
ctx.usePhases(phases)
units = sources map (new CompilationUnit(_))
def stoppedBefore(phase: Phase) =
ctx.settings.YstopBefore.value.containsPhase(phase) ||
ctx.settings.YstopAfter.value.containsPhase(phase.prev)
val phasesToRun = ctx.allPhases.init
.takeWhile(!stoppedBefore(_))
.filterNot(ctx.settings.Yskip.value.containsPhase(_)) // TODO: skip only subphase
for (phase <- phasesToRun)
for (phase <- ctx.allPhases)
if (!ctx.reporter.hasErrors) {
if (ctx.settings.verbose.value) println(s"[$phase]")
units = phase.runOn(units)
def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
if (ctx.settings.Xprint.value.containsPhase(phase))
foreachUnit(printTree)
if (ctx.settings.Ycheck.value.containsPhase(phase) && !ctx.reporter.hasErrors) {
assert(phase.isCheckable, s"phase $phase is not checkable")
foreachUnit(TreeChecker.check(phasesToRun, _))
}

}
}
}
Expand Down
11 changes: 7 additions & 4 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -487,13 +487,13 @@ object Contexts {
def rootLoader(root: TermSymbol)(implicit ctx: Context): SymbolLoader = platform.rootLoader(root)

// Set up some phases to get started */
usePhases(List(List(SomePhase)))
usePhases(List(SomePhase))

/** The standard definitions */
val definitions = new Definitions

def squashed(p: Phase): Phase = {
squashedPhases.find(_.period.containsPhaseId(p.id)).getOrElse(NoPhase)
allPhases.find(_.period.containsPhaseId(p.id)).getOrElse(NoPhase)
}
}

Expand Down Expand Up @@ -560,12 +560,15 @@ object Contexts {
* of underlying during a controlled operation exists. */
private[core] val pendingUnderlying = new mutable.HashSet[Type]


private [core] var phasesPlan: List[List[Phase]] = _

// Phases state
/** Phases by id */
private[core] var phases: Array[Phase] = _

/** Phases with consecutive Transforms groupped into a single phase */
private [core] var squashedPhases: Array[Phase] = _
/** Phases with consecutive Transforms groupped into a single phase, Empty array if squashing is disabled */
private [core] var squashedPhases: Array[Phase] = Array.empty[Phase]

/** Next denotation transformer id */
private[core] var nextDenotTransformerId: Array[Int] = _
Expand Down
6 changes: 3 additions & 3 deletions src/dotty/tools/dotc/core/Periods.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ object Periods {
*
* sign, always 0 1 bit
* runid 21 bits
* last phase id: 5 bits
* #phases before last: 5 bits
* last phase id: 6 bits
* #phases before last: 6 bits
*
* // Dmitry: sign == 0 isn't actually always true, in some cases phaseId == -1 is used for shifts, that easily creates code < 0
*/
Expand Down Expand Up @@ -153,7 +153,7 @@ object Periods {
final val FirstPhaseId = 1

/** The number of bits needed to encode a phase identifier. */
final val PhaseWidth = 5
final val PhaseWidth = 6
final val PhaseMask = (1 << PhaseWidth) - 1
final val MaxPossiblePhaseId = PhaseMask
}
142 changes: 103 additions & 39 deletions src/dotty/tools/dotc/core/Phases.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ object Phases {
this: ContextBase =>

// drop NoPhase at beginning
def allPhases = squashedPhases.tail
def allPhases = (if (squashedPhases.nonEmpty) squashedPhases else phases).tail

object NoPhase extends Phase {
override def exists = false
Expand All @@ -70,68 +70,129 @@ object Phases {
override def lastPhaseId(implicit ctx: Context) = id
}

def phasePlan = this.phasesPlan
def setPhasePlan(phasess: List[List[Phase]]) = this.phasesPlan = phasess

/** Squash TreeTransform's beloning to same sublist to a single TreeTransformer
* Each TreeTransform gets own period,
* whereas a combined TreeTransformer gets period equal to union of periods of it's TreeTransforms
*/
private def squashPhases(phasess: List[List[Phase]]): Array[Phase] = {
def squashPhases(phasess: List[List[Phase]],
phasesToSkip: List[String], stopBeforePhases: List[String], stopAfterPhases: List[String], YCheckAfter: List[String]): List[Phase] = {
val squashedPhases = ListBuffer[Phase]()
var prevPhases: Set[Class[_ <: Phase]] = Set.empty

var stop = false
val filteredPhases = phasess.map(_.filter { p =>
val pstop = stop
stop = stop | stopBeforePhases.contains(p.phaseName) | stopAfterPhases.contains(p.phaseName)
!(pstop || stopBeforePhases.contains(p.phaseName) || phasesToSkip.contains(p.phaseName))
})

var i = 0
while (i < phasess.length) {
if (phasess(i).length > 1) {
val phasesInBlock: Set[String] = phasess(i).map(_.phaseName).toSet
for(phase<-phasess(i)) {
phase match {
case p: MiniPhase =>

val unmetRequirements = p.runsAfterGroupsOf &~ prevPhases
assert(unmetRequirements.isEmpty,
s"${phase.phaseName} requires ${unmetRequirements.mkString(", ")} to be in different TreeTransformer")

case _ =>
assert(false, s"Only tree transforms can be squashed, ${phase.phaseName} can not be squashed")

while (i < filteredPhases.length) {
if (filteredPhases(i).nonEmpty) { //could be empty due to filtering
val filteredPhaseBlock = filteredPhases(i)
val phaseToAdd =
if (filteredPhaseBlock.length > 1) {
val phasesInBlock: Set[String] = filteredPhaseBlock.map(_.phaseName).toSet
for (phase <- filteredPhaseBlock) {
phase match {
case p: MiniPhase =>
val unmetRequirements = p.runsAfterGroupsOf &~ prevPhases
assert(unmetRequirements.isEmpty,
s"${phase.phaseName} requires ${unmetRequirements.mkString(", ")} to be in different TreeTransformer")

case _ =>
assert(false, s"Only tree transforms can be squashed, ${phase.phaseName} can not be squashed")
}
}
val transforms = filteredPhaseBlock.asInstanceOf[List[MiniPhase]].map(_.treeTransform)
val block = new TreeTransformer {
override def phaseName: String = transformations.map(_.phase.phaseName).mkString("TreeTransform:{", ", ", "}")

override def transformations: Array[TreeTransform] = transforms.toArray
}
prevPhases ++= filteredPhaseBlock.map(_.getClazz)
block
} else { // block of a single phase, no squashing
val phase = filteredPhaseBlock.head
prevPhases += phase.getClazz
phase
}
squashedPhases += phaseToAdd
val shouldAddYCheck = YCheckAfter.exists(nm => phaseToAdd.phaseName.contains(nm))
if (shouldAddYCheck) {
val checker = new TreeChecker

squashedPhases += checker
}
val transforms = phasess(i).asInstanceOf[List[MiniPhase]].map(_.treeTransform)
val block = new TreeTransformer {
override def phaseName: String = transformations.map(_.phase.phaseName).mkString("TreeTransform:{", ", ", "}")
override def transformations: Array[TreeTransform] = transforms.toArray
}
squashedPhases += block
prevPhases ++= phasess(i).map(_.getClazz)
block.init(this, phasess(i).head.id, phasess(i).last.id)
} else {
squashedPhases += phasess(i).head
prevPhases += phasess(i).head.getClazz
}

i += 1
}
(NoPhase :: squashedPhases.toList ::: new TerminalPhase :: Nil).toArray
squashedPhases.toList
}

/** Use the following phases in the order they are given.
* The list should never contain NoPhase.
* if squashing is enabled, phases in same subgroup will be squashed to single phase.
*/
def usePhases(phasess: List[List[Phase]], squash: Boolean = true) = {
phases = (NoPhase :: phasess.flatten ::: new TerminalPhase :: Nil).toArray
def usePhases(phasess: List[Phase], squash: Boolean = true) = {

val flatPhases = collection.mutable.ListBuffer[Phase]()

phasess.foreach(p => p match {
case t: TreeTransformer => flatPhases ++= t.transformations.map(_.phase)
case _ => flatPhases += p
})

phases = (NoPhase :: flatPhases.toList ::: new TerminalPhase :: Nil).toArray
var phasesAfter:Set[Class[_ <: Phase]] = Set.empty
nextDenotTransformerId = new Array[Int](phases.length)
denotTransformers = new Array[DenotTransformer](phases.length)
var i = 0
while (i < phases.length) {
phases(i).init(this, i)
val unmetPreceedeRequirements = phases(i).runsAfter -- phasesAfter

var phaseId = 0
def nextPhaseId = {
phaseId += 1
phaseId // starting from 1 as NoPhase is 0
}

def checkRequirements(p: Phase) = {
val unmetPreceedeRequirements = p.runsAfter -- phasesAfter
assert(unmetPreceedeRequirements.isEmpty,
s"phase ${phases(i)} has unmet requirement: ${unmetPreceedeRequirements.mkString(", ")} should precede this phase")
phasesAfter += phases(i).getClazz
s"phase ${p} has unmet requirement: ${unmetPreceedeRequirements.mkString(", ")} should precede this phase")
phasesAfter += p.getClazz

}
var i = 0

while (i < phasess.length) {
val phase = phasess(i)
phase match {
case t: TreeTransformer =>
val transforms = t.transformations
transforms.foreach{ x =>
checkRequirements(x.phase)
x.phase.init(this, nextPhaseId)}
t.init(this, transforms.head.phase.id, transforms.last.phase.id)
case _ =>
phase.init(this, nextPhaseId)
checkRequirements(phase)
}

i += 1
}

phases.last.init(this, nextPhaseId) // init terminal phase

i = phases.length
var lastTransformerId = i
while (i > 0) {
i -= 1
phases(i) match {
val phase = phases(i)
phase match {
case transformer: DenotTransformer =>
lastTransformerId = i
denotTransformers(i) = transformer
Expand All @@ -141,28 +202,31 @@ object Phases {
}

if (squash) {
this.squashedPhases = squashPhases(phasess)
this.squashedPhases = (NoPhase :: phasess).toArray
} else {
this.squashedPhases = this.phases
}

config.println(s"Phases = ${phases.deep}")
config.println(s"squashedPhases = ${squashedPhases.deep}")
config.println(s"nextDenotTransformerId = ${nextDenotTransformerId.deep}")
}

def phaseOfClass(pclass: Class[_]) = phases.find(pclass.isInstance).getOrElse(NoPhase)

private val cachedPhases = collection.mutable.Set[PhaseCache]()
private def cleanPhaseCache = cachedPhases.foreach(_.myPhase = NoPhase)

/** A cache to compute the phase with given name, which
* stores the phase as soon as phaseNamed returns something
* different from NoPhase.
*/
private class PhaseCache(pclass: Class[_ <: Phase]) {
private var myPhase: Phase = NoPhase
var myPhase: Phase = NoPhase
def phase = {
if (myPhase eq NoPhase) myPhase = phaseOfClass(pclass)
myPhase
}
cachedPhases += this
}

private val typerCache = new PhaseCache(classOf[FrontEnd])
Expand Down
9 changes: 8 additions & 1 deletion src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,16 @@ import scala.util.control.NonFatal
* - After typer, identifiers and select nodes refer to terms only (all types should be
* represented as TypeTrees then).
*/
class TreeChecker {
class TreeChecker extends Phase {
import ast.tpd._


def phaseName: String = "Ycheck"

def run(implicit ctx: Context): Unit = {
check(ctx.allPhases, ctx)
}

private def previousPhases(phases: List[Phase])(implicit ctx: Context): List[Phase] = phases match {
case (phase: TreeTransformer) :: phases1 =>
val subPhases = phase.transformations.map(_.phase)
Expand Down