From 1dbf020c43639b0c37e9005f2692871d39676ac7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 15 Mar 2014 17:10:24 +0100 Subject: Refactored denotation transformers Many small and large changes. Added samplePhase to demonstrate functionality. To test functioning, run the compiler with args tests/pos/uncurry.scala -Ylog:sample,terminal --- src/dotty/tools/dotc/Compiler.scala | 4 +- src/dotty/tools/dotc/Run.scala | 2 +- src/dotty/tools/dotc/config/Printers.scala | 2 +- src/dotty/tools/dotc/core/Contexts.scala | 5 ++- src/dotty/tools/dotc/core/DenotTransformers.scala | 44 ++----------------- src/dotty/tools/dotc/core/Denotations.scala | 11 +++-- src/dotty/tools/dotc/core/Periods.scala | 14 +++--- src/dotty/tools/dotc/core/Phases.scala | 53 ++++++++++++++++------- src/dotty/tools/dotc/transform/SamplePhase.scala | 49 ++++++++++++++------- 9 files changed, 96 insertions(+), 88 deletions(-) (limited to 'src') diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 9844557f5..84e11e7ed 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -11,7 +11,9 @@ import dotty.tools.dotc.core.Phases.Phase class Compiler { - def phases: List[Phase] = List(new FrontEnd) + def phases: List[Phase] = List( + new FrontEnd, + new transform.SamplePhase) var runId = 1 def nextRunId = { runId += 1; runId } diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index 160390c4f..89ca45071 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -30,7 +30,7 @@ class Run(comp: Compiler)(implicit ctx: Context) { def compileSources(sources: List[SourceFile]) = Stats.monitorHeartBeat { if (sources forall (_.exists)) { units = sources map (new CompilationUnit(_)) - for (phase <- ctx.allPhases) { + for (phase <- ctx.allPhases.init) { if (!ctx.reporter.hasErrors) phase.runOn(units) } diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index e236e34e0..853049b04 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -23,5 +23,5 @@ object Printers { val completions = noPrinter val gadts = noPrinter val incremental = noPrinter - + val config = noPrinter } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 0d6e341a6..38784ec0c 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -325,6 +325,7 @@ object Contexts { def withProperty(prop: (String, Any)): this.type = withMoreProperties(moreProperties + prop) def withPhase(pid: PhaseId): this.type = withPeriod(Period(runId, pid)) + def withPhase(phase: Phase): this.type = withPhase(phase.id) def withSetting[T](setting: Setting[T], value: T): this.type = withSettings(setting.updateIn(sstate, value)) @@ -361,7 +362,6 @@ object Contexts { * compiler run. */ class ContextBase extends ContextState - with DenotTransformers.DenotTransformerBase with Denotations.DenotationsBase with Phases.PhasesBase { @@ -460,6 +460,9 @@ object Contexts { /** Phases by id */ private[core] var phases: Array[Phase] = _ + /** Next denotation transformer id */ + private[core] var nextTransformerId: Array[Int] = _ + // Printers state /** Number of recursive invocations of a show method on cuyrrent stack */ private[dotc] var toTextRecursions = 0 diff --git a/src/dotty/tools/dotc/core/DenotTransformers.scala b/src/dotty/tools/dotc/core/DenotTransformers.scala index f41f1d171..be85b5877 100644 --- a/src/dotty/tools/dotc/core/DenotTransformers.scala +++ b/src/dotty/tools/dotc/core/DenotTransformers.scala @@ -6,62 +6,26 @@ import SymDenotations._ import Contexts._ import Types._ import Denotations._ +import Phases._ import java.lang.AssertionError import dotty.tools.dotc.util.DotClass object DenotTransformers { - trait DenotTransformerBase { self: ContextBase => - val denotTransformers = new DenotTransformerGroup - } - /** A transformer group contains a sequence of transformers, * ordered by the phase where they apply. Transformers are added * to a group via `install`. - * - * There are two transformerGroups in a context base: - * symTransformers and refTransformers. symTransformers translate - * full symbol denotations, refTransformers translate only symbol references - * of type Unique/JointRefDenotation. */ - class DenotTransformerGroup { - - private val nxTransformer = - Array.fill[DenotTransformer](MaxPossiblePhaseId + 1)(NoTransformer) - - def nextTransformer(pid: PhaseId) = nxTransformer(pid) - - def install(pid: PhaseId, transFn: DenotTransformerGroup => DenotTransformer): Unit = - if ((pid > NoPhaseId) && (nxTransformer(pid).phaseId > pid)) { - val trans = transFn(this) - trans._phaseId = pid - nxTransformer(pid) = transFn(this) - install(pid - 1, transFn) - } - - /** A sentinel transformer object */ - object NoTransformer extends DenotTransformer(this) { - _phaseId = MaxPossiblePhaseId + 1 - override def lastPhaseId = phaseId - 1 // TODO JZ Probably off-by-N error here. MO: Don't think so: we want empty validity period. - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = - unsupported("transform") - } - } /** A transformer transforms denotations at a given phase */ - abstract class DenotTransformer(group: DenotTransformerGroup) extends DotClass { - - private[DenotTransformers] var _phaseId: PhaseId = _ - - /** The phase at the start of which the denotations are transformed */ - def phaseId: PhaseId = _phaseId + trait DenotTransformer extends Phase { /** The last phase during which the transformed denotations are valid */ - def lastPhaseId = group.nextTransformer(phaseId).phaseId - 1 + def lastPhaseId(implicit ctx: Context) = ctx.nextTransformerId(id + 1) /** The validity period of the transformer in the given context */ def validFor(implicit ctx: Context): Period = - Period(ctx.runId, phaseId, lastPhaseId) + Period(ctx.runId, id, lastPhaseId) /** The transformation method */ def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index bd1aedd75..b35647ead 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -390,7 +390,6 @@ object Denotations { if ((symbol eq this.symbol) && (info eq this.info)) this else newLikeThis(symbol, info) - def orElse(that: => SingleDenotation) = if (this.exists) this else that def altsWith(p: Symbol => Boolean): List[SingleDenotation] = @@ -518,7 +517,8 @@ object Denotations { } else { // not found, cur points to highest existing variant var startPid = cur.validFor.lastPhaseId + 1 - val transformer = ctx.denotTransformers.nextTransformer(startPid) + val transformer = ctx.phases(startPid - 1).asInstanceOf[DenotTransformer] + //println(s"transforming with $transformer") next = transformer.transform(cur).syncWithParents if (next eq cur) startPid = cur.validFor.firstPhaseId @@ -527,20 +527,23 @@ object Denotations { case next: ClassDenotation => next.resetFlag(Frozen) case _ => } + next.nextInRun = cur.nextInRun cur.nextInRun = next cur = next } cur.validFor = Period( currentPeriod.runId, startPid, transformer.lastPhaseId) + //println(s"new denot: $cur, valid for ${cur.validFor}") } } else { - // currentPeriod < valid; in this case a version must exist + // currentPeriod < end of valid; in this case a version must exist // but to be defensive we check for infinite loop anyway var cnt = 0 while (!(cur.validFor contains currentPeriod)) { + //println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}") cur = cur.nextInRun cnt += 1 - assert(cnt <= MaxPossiblePhaseId, "seems to be a loop in Denotations") + assert(cnt <= MaxPossiblePhaseId, s"seems to be a loop in Denotations for $this, currentPeriod = $currentPeriod") } } cur diff --git a/src/dotty/tools/dotc/core/Periods.scala b/src/dotty/tools/dotc/core/Periods.scala index 592516531..1c9fbf0c6 100644 --- a/src/dotty/tools/dotc/core/Periods.scala +++ b/src/dotty/tools/dotc/core/Periods.scala @@ -27,20 +27,16 @@ abstract class Periods extends DotClass { self: Context => /** The period containing the current period where denotations do not change. * We compute this by taking as first phase the first phase less or equal to - * the current phase that has the same "nextTransformer". As last phase - * we take the phaseId of the nextTransformer - 1. This has the advantage that - * it works even if no transformer is installed other than the sentinel - * NoTransformer, which is always installed automatically. + * the current phase that has the same "nextTransformerId". As last phase + * we take the next transformer id following the current phase. */ def stablePeriod = { var first = phaseId - val transformers = base.denotTransformers - val nxTrans = transformers.nextTransformer(first) - while (first - 1 > NoPhaseId && - (transformers.nextTransformer(first - 1) eq nxTrans)) { + val nxTrans = ctx.base.nextTransformerId(first) + while (first - 1 > NoPhaseId && (ctx.base.nextTransformerId(first - 1) == nxTrans)) { first -= 1 } - Period(runId, first, nxTrans.phaseId - 1) + Period(runId, first, nxTrans) } } diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 14cab9821..81ab7acbf 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -4,6 +4,9 @@ package core import Periods._ import Contexts._ import util.DotClass +import DenotTransformers._ +import Denotations._ +import config.Printers._ trait Phases { self: Context => import Phases._ @@ -18,6 +21,8 @@ trait Phases { self: Context => def atPhase[T](phase: Phase)(op: Context => T): T = atPhase(phase.id)(op) + def atNextPhase[T](op: Context => T): T = atPhase(phase.next)(op) + def atPhaseNotLaterThan[T](limit: Phase)(op: Context => T): T = if (!limit.exists || phase <= limit) op(this) else atPhase(limit)(op) @@ -29,12 +34,13 @@ object Phases { trait PhasesBase { this: ContextBase => - def allPhases = phases.tail + def allPhases = phases.tail // drop NoPhase at beginning object NoPhase extends Phase { override def exists = false def name = "" def run(implicit ctx: Context): Unit = unsupported("run") + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = unsupported("transform") } object SomePhase extends Phase { @@ -42,14 +48,38 @@ object Phases { def run(implicit ctx: Context): Unit = unsupported("run") } + /** A sentinel transformer object */ + class TerminalPhase extends DenotTransformer { + def name = "terminal" + def run(implicit ctx: Context): Unit = unsupported("run") + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = + unsupported("transform") + override def lastPhaseId(implicit ctx: Context) = id + } + def phaseNamed(name: String) = phases.find(_.name == name).getOrElse(NoPhase) /** Use the following phases in the order they are given. * The list should never contain NoPhase. */ - def usePhases(phases: List[Phase]) = - this.phases = (NoPhase :: phases).toArray + def usePhases(phases: List[Phase]) = { + this.phases = (NoPhase :: phases ::: new TerminalPhase :: Nil).toArray + this.nextTransformerId = new Array[Int](this.phases.length) + var i = 0 + while (i < this.phases.length) { + this.phases(i)._id = i + i += 1 + } + var lastTransformerId = i + while (i > 0) { + i -= 1 + if (this.phases(i).isInstanceOf[DenotTransformer]) lastTransformerId = i + nextTransformerId(i) = lastTransformerId + } + config.println(s"Phases = ${this.phases.deep}") + config.println(s"nextTransformId = ${nextTransformerId.deep}") + } final val typerName = "typer" final val refchecksName = "refchecks" @@ -69,7 +99,7 @@ object Phases { def run(implicit ctx: Context): Unit def runOn(units: List[CompilationUnit])(implicit ctx: Context): Unit = - for (unit <- units) run(ctx.fresh.withCompilationUnit(unit)) + for (unit <- units) run(ctx.fresh.withPhase(this).withCompilationUnit(unit)) def description: String = name @@ -77,22 +107,13 @@ object Phases { def exists: Boolean = true - private[this] var idCache = -1 + private[Phases] var _id = -1 /** The sequence position of this phase in the given context where 0 * is reserved for NoPhase and the first real phase is at position 1. - * Returns -1 if the phase is not installed in the context. + * -1 if the phase is not installed in the context. */ - def id(implicit ctx: Context) = { - val id = idCache - val phases = ctx.phases - if (idCache >= 0 && idCache < phases.length && (phases(idCache) eq this)) - id - else { - idCache = phases indexOf this - idCache - } - } + def id = _id final def <= (that: Phase)(implicit ctx: Context) = exists && id <= that.id diff --git a/src/dotty/tools/dotc/transform/SamplePhase.scala b/src/dotty/tools/dotc/transform/SamplePhase.scala index 137ab2eda..ed4ac113f 100644 --- a/src/dotty/tools/dotc/transform/SamplePhase.scala +++ b/src/dotty/tools/dotc/transform/SamplePhase.scala @@ -4,33 +4,52 @@ package transform import TreeTransforms._ import core.DenotTransformers._ import core.Denotations._ +import core.SymDenotations._ import core.Contexts._ +import core.Types._ import ast.Trees._ import ast.tpd.{Apply, Tree, cpy} -class SamplePhase extends TreeTransformer { - - def init(implicit ctx: Context) = { - ctx.base.denotTransformers.install(id, new UncurryDenotTransform(_)) - } - +class SamplePhase extends TreeTransformer with DenotTransformer { def name = "sample" - def transformations = Array(new UncurryTreeTransform(_, _)) -} - -class UncurryDenotTransform(group: DenotTransformerGroup) extends DenotTransformer(group) { - - def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ??? + def uncurry(tp: Type)(implicit ctx: Context): Type = tp match { + case tp @ MethodType(pnames1, ptypes1) => + tp.resultType match { + case rt @ MethodType(pnames2, ptypes2) => + tp.derivedMethodType(pnames1 ++ pnames2, ptypes1 ++ ptypes2, rt.resultType) + case _ => + tp + } + case tp: PolyType => + tp.derivedPolyType(tp.paramNames, tp.paramBounds, uncurry(tp.resultType)) + case _ => + tp + } + def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = { + val info1 = uncurry(ref.info) + if (info1 eq ref.info) ref + else ref match { + case ref: SymDenotation => ref.copySymDenotation(info = info1) + case _ => ref.derivedSingleDenotation(ref.symbol, info1) + } + } } class UncurryTreeTransform(group: TreeTransformer, idx: Int) extends TreeTransform(group, idx) { override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - tree match { - case Apply(fn, args) => cpy.Apply(tree, fn, args ++ tree.args) + ctx.traceIndented(s"transforming ${tree.show}", show = true) { + tree.fun match { + case Apply(fn, args) => + def showType(implicit ctx: Context) = + ctx.log(s"at ${ctx.phase} ${fn.symbol} has type ${fn.symbol.info.widen.show}") + showType + ctx.atNextPhase(showType(_)) + showType + cpy.Apply(tree, fn, args ++ tree.args) case _ => tree - } + }} } \ No newline at end of file -- cgit v1.2.3