diff options
author | Paul Phillips <paulp@improving.org> | 2012-02-25 08:54:44 -0800 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-02-25 12:20:15 -0800 |
commit | 4573e06a278e9505b7cf1b6f1390e2833fb70fec (patch) | |
tree | ed11a0452ca502b72793d250e518051f87493996 /src/compiler/scala/reflect/internal/SymbolTable.scala | |
parent | 0c2f493804db6b594d7ec68e49e76c75a316230b (diff) | |
download | scala-4573e06a278e9505b7cf1b6f1390e2833fb70fec.tar.gz scala-4573e06a278e9505b7cf1b6f1390e2833fb70fec.tar.bz2 scala-4573e06a278e9505b7cf1b6f1390e2833fb70fec.zip |
Mainstreaming phase awareness.
As I peer into various longstanding crashes involving specialization,
mixin composition, and super calls, I find a very common reason for
logic to go wrong is ignoring or misusing atPhase. This is hardly
surprising given how much of a secret black art the whole process of
atPhase-ing is. I predict with some confidence that at least half the
calls to atPhase in the compiler are at best unnecessary and more likely
wrong; similarly, we are missing at least as many calls in other places.
Herein we find the following:
1) log messages now include not only the current "at" phase, which
in isolation was very confusing because it means what is logged is
dependent on the arbitrary jumps performed by atPhase, but the entire
"phase stack", anchored by the "global phase".
The leftmost phase is the global phase, the one set in Global which
proceeds in a predictable fashion from parser to terminal. That's the
one we usually mean when we talk about during which phase something
happens. The others (prefixed with an arrow) are calls to atPhase which
have not yet returned.
// example, assuming we've given -Ylog:expl
scala> atPhase(currentRun.explicitouterPhase)(log("hi mom"))
[log terminal(->typer ->explicitouter)] hi mom
2) A message will be logged if either the globalPhase matches,
or the current "at" phase does. So -Ylog:erasure will log all the
messages from the phase we think of as erasure, and also all those
from any phase run inside of atPhase(erasurePhase) { ... }
(except for times when that block uses atPhase to alter the
phase yet again - it only looks at the top of the atPhase stack.)
// example
% scalac -Ydebug -Ylog:refchecks foo.scala
[log refchecks] overriding-pairs? method size in trait TraversableOnce ...
[log mixin(->refchecks)] rebindsuper trait GenTraversable <none> <notype> false
3) A number of debug/power oriented functions. All of these
limit their results to phase transitions during which some change took
place, as measured by the result of `op`, compared with ==.
def afterEachPhase[T](op: => T): List[(Phase, T)]
def changesAfterEachPhase[T](op: => List[T]): List[ChangeAfterPhase[T]]
def logAfterEveryPhase[T](msg: String)(op: => T)
def describeAfterEachPhase[T](op: => T): List[String]
def describeAfterEveryPhase[T](op: => T): String
def printAfterEachPhase[T](op: => T): Unit
Some examples:
scala> printAfterEachPhase(ListClass.info.members.size)
[after 1/parser ] 219
[after 11/tailcalls ] 220
[after 12/specialize ] 322
[after 14/erasure ] 361
[after 18/flatten ] 357
scala> printAfterEachPhase(termMember(PredefModule, "implicitly").defString)
[after 1/parser ] def implicitly[T](implicit e: T): T
[after 10/uncurry ] def implicitly[T](e: T): T
[after 14/erasure ] def implicitly(e: Object): Object
Try this at home:
scala> changesAfterEachPhase(ListClass.info.nonPrivateMembers map (_.defString)) foreach println
Diffstat (limited to 'src/compiler/scala/reflect/internal/SymbolTable.scala')
-rw-r--r-- | src/compiler/scala/reflect/internal/SymbolTable.scala | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/src/compiler/scala/reflect/internal/SymbolTable.scala b/src/compiler/scala/reflect/internal/SymbolTable.scala index b3c62bffbf..4bcf522a8f 100644 --- a/src/compiler/scala/reflect/internal/SymbolTable.scala +++ b/src/compiler/scala/reflect/internal/SymbolTable.scala @@ -78,16 +78,18 @@ abstract class SymbolTable extends api.Universe type RunId = Int final val NoRunId = 0 + private var phStack: List[Phase] = Nil private var ph: Phase = NoPhase private var per = NoPeriod + final def atPhaseStack: List[Phase] = phStack final def phase: Phase = ph final def phase_=(p: Phase) { //System.out.println("setting phase to " + p) - assert((p ne null) && p != NoPhase) + assert((p ne null) && p != NoPhase, p) ph = p - per = (currentRunId << 8) + p.id + per = period(currentRunId, p.id) } /** The current compiler run identifier. */ @@ -112,14 +114,18 @@ abstract class SymbolTable extends api.Universe final def phaseOf(period: Period): Phase = phaseWithId(phaseId(period)) final def period(rid: RunId, pid: Phase#Id): Period = - (currentRunId << 8) + pid + (rid << 8) + pid /** Perform given operation at given phase. */ @inline final def atPhase[T](ph: Phase)(op: => T): T = { val current = phase phase = ph + phStack ::= ph try op - finally phase = current + finally { + phase = current + phStack = phStack.tail + } } /** Since when it is to be "at" a phase is inherently ambiguous, * a couple unambiguously named methods. |