From b58b90683652e1b6e2c32412f0a03ba614b61b33 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 6 Sep 2014 11:35:07 +0200 Subject: Generalize phase postcondition checking. Have a general way how a phase can establish a postcondition which will be checked each time a later phase is tree-checked. Moves erasure constraints from TreeChecker to Erasure's post condition. --- src/dotty/tools/dotc/Run.scala | 9 +++--- src/dotty/tools/dotc/core/Phases.scala | 8 ++++- src/dotty/tools/dotc/transform/Erasure.scala | 31 +++++++++++++++++++ src/dotty/tools/dotc/transform/TreeChecker.scala | 39 ++++-------------------- 4 files changed, 49 insertions(+), 38 deletions(-) (limited to 'src/dotty/tools') diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index a639b20cd..a4c862a84 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -37,17 +37,18 @@ class Run(comp: Compiler)(implicit ctx: Context) { val phasesToRun = ctx.allPhases.init .takeWhile(!stoppedBefore(_)) .filterNot(ctx.settings.Yskip.value.containsPhase(_)) - for (phase <- phasesToRun) { + for (phase <- phasesToRun) if (!ctx.reporter.hasErrors) { 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) - foreachUnit(TreeChecker.check) + if (ctx.settings.Ycheck.value.containsPhase(phase) && !ctx.reporter.hasErrors) { + assert(phase.isCheckable, s"phase $phase is not checkable") + foreachUnit(TreeChecker.check(phasesToRun, _)) + } } - } } } diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 6baec3cf6..27b7cd77d 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -12,6 +12,7 @@ import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, Tr import dotty.tools.dotc.transform.TreeTransforms import Periods._ import typer.{FrontEnd, RefChecks} +import ast.tpd import dotty.tools.dotc.transform.{Erasure, Flatten} trait Phases { @@ -191,7 +192,12 @@ object Phases { def description: String = phaseName - def checkable: Boolean = true + /** Output should be checkable by TreeChecker */ + def isCheckable: Boolean = true + + /** Check what the phase achieves, to be called at any point after it is finished. + */ + def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = () def exists: Boolean = true diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index fe5e516bf..f9e48ac5d 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -71,6 +71,37 @@ class Erasure extends Phase with DenotTransformer { thisTransformer => val unit = ctx.compilationUnit unit.tpdTree = eraser.typedExpr(unit.tpdTree)(ctx.fresh.setPhase(this.next)) } + + override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context) = { + assertErased(tree) + tree match { + case res: tpd.This => + assert(!ExplicitOuter.referencesOuter(ctx.owner.enclosingClass, res), + i"Reference to $res from ${ctx.owner.showLocated}") + case _ => + } + } + + /** Assert that tree type and its widened underlying type are erased. + * Also assert that term refs have fixed symbols (so we are sure + * they need not be reloaded using member; this would likely fail as signatures + * may change after erasure). + */ + def assertErased(tree: tpd.Tree)(implicit ctx: Context): Unit = { + assertErased(tree.typeOpt, tree) + if (!(tree.symbol == defn.Any_isInstanceOf || tree.symbol == defn.Any_asInstanceOf)) + assertErased(tree.typeOpt.widen, tree) + if (ctx.mode.isExpr) + tree.tpe match { + case ref: TermRef => + assert(ref.denot.isInstanceOf[SymDenotation], + i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree") + case _ => + } + } + + def assertErased(tp: Type, tree: tpd.Tree = tpd.EmptyTree)(implicit ctx: Context): Unit = + assert(isErasedType(tp), i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase}") } object Erasure { diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala index 5847796b3..39c5ce8b6 100644 --- a/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -13,6 +13,7 @@ import core.Constants._ import core.StdNames._ import core.Decorators._ import core.TypeErasure.isErasedType +import core.Phases.Phase import typer._ import typer.ErrorReporting._ import reporting.ThrowingReporter @@ -34,14 +35,15 @@ import java.lang.AssertionError class TreeChecker { import ast.tpd._ - def check(ctx: Context) = { + def check(phasesToRun: Seq[Phase], ctx: Context) = { println(s"checking ${ctx.compilationUnit} after phase ${ctx.phase.prev}") val checkingCtx = ctx.fresh .setTyperState(ctx.typerState.withReporter(new ThrowingReporter(ctx.typerState.reporter))) - Checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) + val checker = new Checker(phasesToRun.takeWhile(_ ne ctx.phase) :+ ctx.phase) + checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx) } - object Checker extends ReTyper { + class Checker(phasesToCheck: Seq[Phase]) extends ReTyper { override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = { val res = tree match { case _: untpd.UnApply => @@ -67,15 +69,7 @@ class TreeChecker { assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt)) tree1 } - if (ctx.erasedTypes) { - assertErased(res) - res match { - case res: This => - assert(!ExplicitOuter.referencesOuter(ctx.owner.enclosingClass, res), - i"Reference to $res from ${ctx.owner.showLocated}") - case _ => - } - } + phasesToCheck.foreach(_.checkPostCondition(res)) res } @@ -126,27 +120,6 @@ class TreeChecker { tree } } - - def assertErased(tp: Type, tree: Tree = EmptyTree)(implicit ctx: Context): Unit = - assert(isErasedType(tp), i"The type $tp - ${tp.toString} of class ${tp.getClass} of tree $tree / ${tree.getClass} is illegal after erasure, phase = ${ctx.phase}") - - /** Assert that tree type and its widened underlying type are erased. - * Also assert that term refs have fixed symbols (so we are sure - * they need not be reloaded using member; this would likely fail as signatures - * may change after erasure). - */ - def assertErased(tree: Tree)(implicit ctx: Context): Unit = { - assertErased(tree.typeOpt, tree) - if (!(tree.symbol == defn.Any_isInstanceOf || tree.symbol == defn.Any_asInstanceOf)) - assertErased(tree.typeOpt.widen, tree) - if (ctx.mode.isExpr) - tree.tpe match { - case ref: TermRef => - assert(ref.denot.isInstanceOf[SymDenotation], - i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree") - case _ => - } - } } object TreeChecker extends TreeChecker \ No newline at end of file -- cgit v1.2.3