Skip to content

Commit 3a25072

Browse files
committed
Add missing and double symbol checking to TreeChecker
TreeChecker now tests that a symbol does not have two definitions that define it, and that every reference to a symbol owner by a term is in the scope of a definition of that symbol. Both tests fail on several files for pattern matcher.
1 parent 4d370b6 commit 3a25072

File tree

4 files changed

+76
-20
lines changed

4 files changed

+76
-20
lines changed

src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols.
99
import Denotations._, Decorators._
1010
import config.Printers._
1111
import typer.Mode
12+
import collection.mutable
1213
import typer.ErrorReporting._
1314

1415
import scala.annotation.tailrec
@@ -620,6 +621,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
620621
}
621622
acc(false, tree)
622623
}
624+
625+
def filterSubTrees(f: Tree => Boolean): List[Tree] = {
626+
val buf = new mutable.ListBuffer[Tree]
627+
foreachSubTree { tree => if (f(tree)) buf += tree }
628+
buf.toList
629+
}
623630
}
624631

625632
implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ object Decorators {
100100
else x1 :: xs1
101101
}
102102

103+
def foldRightBN[U](z: => U)(op: (T, => U) => U): U = xs match {
104+
case Nil => z
105+
case x :: xs1 => op(x, xs1.foldRightBN(z)(op))
106+
}
107+
103108
final def hasSameLengthAs[U](ys: List[U]): Boolean = {
104109
@tailrec def loop(xs: List[T], ys: List[U]): Boolean =
105110
if (xs.isEmpty) ys.isEmpty

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

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import reporting.ThrowingReporter
2020
import ast.Trees._
2121
import ast.{tpd, untpd}
2222
import util.SourcePosition
23+
import collection.mutable
2324
import ProtoTypes._
2425
import java.lang.AssertionError
2526

@@ -57,6 +58,32 @@ class TreeChecker {
5758
}
5859

5960
class Checker(phasesToCheck: Seq[Phase]) extends ReTyper {
61+
62+
val definedSyms = new mutable.HashSet[Symbol]
63+
64+
def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = {
65+
if (tree.isDef) {
66+
//assert(!definedSyms.contains(tree.symbol), i"doubly defined symbol: ${tree.symbol}in $tree")
67+
definedSyms += tree.symbol
68+
//println(i"defined: ${tree.symbol}")
69+
val res = op
70+
definedSyms -= tree.symbol
71+
//println(i"undefined: ${tree.symbol}")
72+
res
73+
}
74+
else op
75+
}
76+
77+
def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) =
78+
trees.foldRightBN(op)(withDefinedSym(_)(_))
79+
80+
def withDefinedSymss[T](vparamss: List[List[untpd.ValDef]])(op: => T)(implicit ctx: Context): T =
81+
vparamss.foldRightBN(op)(withDefinedSyms(_)(_))
82+
83+
def assertDefined(tree: untpd.Tree)(implicit ctx: Context) =
84+
if (tree.symbol.maybeOwner.isTerm)
85+
()//assert(definedSyms contains tree.symbol, i"undefined symbol ${tree.symbol}")
86+
6087
override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = {
6188
val res = tree match {
6289
case _: untpd.UnApply =>
@@ -88,7 +115,8 @@ class TreeChecker {
88115

89116
override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = {
90117
assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase)
91-
assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree")
118+
assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree # ${tree.uniqueId}")
119+
assertDefined(tree)
92120
super.typedIdent(tree, pt)
93121
}
94122

@@ -117,6 +145,22 @@ class TreeChecker {
117145
super.typedClassDef(cdef, cls)
118146
}
119147

148+
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) =
149+
withDefinedSyms(ddef.tparams) {
150+
withDefinedSymss(ddef.vparamss) {
151+
super.typedDefDef(ddef, sym)
152+
}
153+
}
154+
155+
override def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = {
156+
withDefinedSyms(tree.pat.asInstanceOf[tpd.Tree].filterSubTrees(_.isInstanceOf[ast.Trees.Bind[_]])) {
157+
super.typedCase(tree, pt, selType, gadtSyms)
158+
}
159+
}
160+
161+
override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) =
162+
withDefinedSyms(tree.stats) { super.typedBlock(tree, pt) }
163+
120164
/** Check that all defined symbols have legal owners.
121165
* An owner is legal if it is either the same as the context's owner
122166
* or there's an owner chain of valdefs starting at the context's owner and

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -610,29 +610,29 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
610610
accu(Set.empty, selType)
611611
}
612612

613-
def typedCase(tree: untpd.CaseDef): CaseDef = track("typedCase") {
614-
def caseRest(pat: Tree)(implicit ctx: Context) = {
615-
gadtSyms foreach (_.resetGADTFlexType)
616-
pat foreachSubTree {
617-
case b: Bind =>
618-
if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
619-
else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
620-
case _ =>
621-
}
622-
val guard1 = typedExpr(tree.guard, defn.BooleanType)
623-
val body1 = typedExpr(tree.body, pt)
624-
assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
625-
}
626-
val doCase: () => CaseDef =
627-
() => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
628-
(doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
629-
}
630-
631-
val cases1 = tree.cases mapconserve typedCase
613+
val cases1 = tree.cases mapconserve (typedCase(_, pt, selType, gadtSyms))
632614
assignType(cpy.Match(tree)(sel1, cases1), cases1)
633615
}
634616
}
635617

618+
def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") {
619+
def caseRest(pat: Tree)(implicit ctx: Context) = {
620+
gadtSyms foreach (_.resetGADTFlexType)
621+
pat foreachSubTree {
622+
case b: Bind =>
623+
if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
624+
else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
625+
case _ =>
626+
}
627+
val guard1 = typedExpr(tree.guard, defn.BooleanType)
628+
val body1 = typedExpr(tree.body, pt)
629+
assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
630+
}
631+
val doCase: () => CaseDef =
632+
() => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
633+
(doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
634+
}
635+
636636
def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") {
637637
def returnProto(owner: Symbol) =
638638
if (owner.isConstructor) defn.UnitType else owner.info.finalResultType

0 commit comments

Comments
 (0)