Skip to content

Commit b58b906

Browse files
committed
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.
1 parent e104093 commit b58b906

File tree

4 files changed

+49
-38
lines changed

4 files changed

+49
-38
lines changed

src/dotty/tools/dotc/Run.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,18 @@ class Run(comp: Compiler)(implicit ctx: Context) {
3737
val phasesToRun = ctx.allPhases.init
3838
.takeWhile(!stoppedBefore(_))
3939
.filterNot(ctx.settings.Yskip.value.containsPhase(_))
40-
for (phase <- phasesToRun) {
40+
for (phase <- phasesToRun)
4141
if (!ctx.reporter.hasErrors) {
4242
phase.runOn(units)
4343
def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit =
4444
for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit))
4545
if (ctx.settings.Xprint.value.containsPhase(phase))
4646
foreachUnit(printTree)
47-
if (ctx.settings.Ycheck.value.containsPhase(phase) && !ctx.reporter.hasErrors)
48-
foreachUnit(TreeChecker.check)
47+
if (ctx.settings.Ycheck.value.containsPhase(phase) && !ctx.reporter.hasErrors) {
48+
assert(phase.isCheckable, s"phase $phase is not checkable")
49+
foreachUnit(TreeChecker.check(phasesToRun, _))
50+
}
4951
}
50-
}
5152
}
5253
}
5354

src/dotty/tools/dotc/core/Phases.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, Tr
1212
import dotty.tools.dotc.transform.TreeTransforms
1313
import Periods._
1414
import typer.{FrontEnd, RefChecks}
15+
import ast.tpd
1516
import dotty.tools.dotc.transform.{Erasure, Flatten}
1617

1718
trait Phases {
@@ -191,7 +192,12 @@ object Phases {
191192

192193
def description: String = phaseName
193194

194-
def checkable: Boolean = true
195+
/** Output should be checkable by TreeChecker */
196+
def isCheckable: Boolean = true
197+
198+
/** Check what the phase achieves, to be called at any point after it is finished.
199+
*/
200+
def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = ()
195201

196202
def exists: Boolean = true
197203

src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,37 @@ class Erasure extends Phase with DenotTransformer { thisTransformer =>
7171
val unit = ctx.compilationUnit
7272
unit.tpdTree = eraser.typedExpr(unit.tpdTree)(ctx.fresh.setPhase(this.next))
7373
}
74+
75+
override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context) = {
76+
assertErased(tree)
77+
tree match {
78+
case res: tpd.This =>
79+
assert(!ExplicitOuter.referencesOuter(ctx.owner.enclosingClass, res),
80+
i"Reference to $res from ${ctx.owner.showLocated}")
81+
case _ =>
82+
}
83+
}
84+
85+
/** Assert that tree type and its widened underlying type are erased.
86+
* Also assert that term refs have fixed symbols (so we are sure
87+
* they need not be reloaded using member; this would likely fail as signatures
88+
* may change after erasure).
89+
*/
90+
def assertErased(tree: tpd.Tree)(implicit ctx: Context): Unit = {
91+
assertErased(tree.typeOpt, tree)
92+
if (!(tree.symbol == defn.Any_isInstanceOf || tree.symbol == defn.Any_asInstanceOf))
93+
assertErased(tree.typeOpt.widen, tree)
94+
if (ctx.mode.isExpr)
95+
tree.tpe match {
96+
case ref: TermRef =>
97+
assert(ref.denot.isInstanceOf[SymDenotation],
98+
i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree")
99+
case _ =>
100+
}
101+
}
102+
103+
def assertErased(tp: Type, tree: tpd.Tree = tpd.EmptyTree)(implicit ctx: Context): Unit =
104+
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}")
74105
}
75106

76107
object Erasure {

src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import core.Constants._
1313
import core.StdNames._
1414
import core.Decorators._
1515
import core.TypeErasure.isErasedType
16+
import core.Phases.Phase
1617
import typer._
1718
import typer.ErrorReporting._
1819
import reporting.ThrowingReporter
@@ -34,14 +35,15 @@ import java.lang.AssertionError
3435
class TreeChecker {
3536
import ast.tpd._
3637

37-
def check(ctx: Context) = {
38+
def check(phasesToRun: Seq[Phase], ctx: Context) = {
3839
println(s"checking ${ctx.compilationUnit} after phase ${ctx.phase.prev}")
3940
val checkingCtx = ctx.fresh
4041
.setTyperState(ctx.typerState.withReporter(new ThrowingReporter(ctx.typerState.reporter)))
41-
Checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx)
42+
val checker = new Checker(phasesToRun.takeWhile(_ ne ctx.phase) :+ ctx.phase)
43+
checker.typedExpr(ctx.compilationUnit.tpdTree)(checkingCtx)
4244
}
4345

44-
object Checker extends ReTyper {
46+
class Checker(phasesToCheck: Seq[Phase]) extends ReTyper {
4547
override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = {
4648
val res = tree match {
4749
case _: untpd.UnApply =>
@@ -67,15 +69,7 @@ class TreeChecker {
6769
assert(isSubType(tree1.tpe, tree.typeOpt), divergenceMsg(tree1.tpe, tree.typeOpt))
6870
tree1
6971
}
70-
if (ctx.erasedTypes) {
71-
assertErased(res)
72-
res match {
73-
case res: This =>
74-
assert(!ExplicitOuter.referencesOuter(ctx.owner.enclosingClass, res),
75-
i"Reference to $res from ${ctx.owner.showLocated}")
76-
case _ =>
77-
}
78-
}
72+
phasesToCheck.foreach(_.checkPostCondition(res))
7973
res
8074
}
8175

@@ -126,27 +120,6 @@ class TreeChecker {
126120
tree
127121
}
128122
}
129-
130-
def assertErased(tp: Type, tree: Tree = EmptyTree)(implicit ctx: Context): Unit =
131-
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}")
132-
133-
/** Assert that tree type and its widened underlying type are erased.
134-
* Also assert that term refs have fixed symbols (so we are sure
135-
* they need not be reloaded using member; this would likely fail as signatures
136-
* may change after erasure).
137-
*/
138-
def assertErased(tree: Tree)(implicit ctx: Context): Unit = {
139-
assertErased(tree.typeOpt, tree)
140-
if (!(tree.symbol == defn.Any_isInstanceOf || tree.symbol == defn.Any_asInstanceOf))
141-
assertErased(tree.typeOpt.widen, tree)
142-
if (ctx.mode.isExpr)
143-
tree.tpe match {
144-
case ref: TermRef =>
145-
assert(ref.denot.isInstanceOf[SymDenotation],
146-
i"non-sym type $ref of class ${ref.getClass} with denot of class ${ref.denot.getClass} of $tree")
147-
case _ =>
148-
}
149-
}
150123
}
151124

152125
object TreeChecker extends TreeChecker

0 commit comments

Comments
 (0)