aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-10-24 18:32:48 +0200
committerMartin Odersky <odersky@gmail.com>2014-10-26 16:24:02 +0100
commit3a250720a0833d42ed6b23b5837de64b6ce34aed (patch)
tree008c0b58a9f598da559286deb3fd9f8ab94a2d55
parent4d370b6073bec9706d427f82c4a6a40fa22fe6d0 (diff)
downloaddotty-3a250720a0833d42ed6b23b5837de64b6ce34aed.tar.gz
dotty-3a250720a0833d42ed6b23b5837de64b6ce34aed.tar.bz2
dotty-3a250720a0833d42ed6b23b5837de64b6ce34aed.zip
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.
-rw-r--r--src/dotty/tools/dotc/ast/tpd.scala7
-rw-r--r--src/dotty/tools/dotc/core/Decorators.scala5
-rw-r--r--src/dotty/tools/dotc/transform/TreeChecker.scala46
-rw-r--r--src/dotty/tools/dotc/typer/Typer.scala38
4 files changed, 76 insertions, 20 deletions
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index 9b7c9cbae..4c21fcf49 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -9,6 +9,7 @@ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols.
import Denotations._, Decorators._
import config.Printers._
import typer.Mode
+import collection.mutable
import typer.ErrorReporting._
import scala.annotation.tailrec
@@ -620,6 +621,12 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
}
acc(false, tree)
}
+
+ def filterSubTrees(f: Tree => Boolean): List[Tree] = {
+ val buf = new mutable.ListBuffer[Tree]
+ foreachSubTree { tree => if (f(tree)) buf += tree }
+ buf.toList
+ }
}
implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {
diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala
index 99af4d0cb..e0d7fae33 100644
--- a/src/dotty/tools/dotc/core/Decorators.scala
+++ b/src/dotty/tools/dotc/core/Decorators.scala
@@ -100,6 +100,11 @@ object Decorators {
else x1 :: xs1
}
+ def foldRightBN[U](z: => U)(op: (T, => U) => U): U = xs match {
+ case Nil => z
+ case x :: xs1 => op(x, xs1.foldRightBN(z)(op))
+ }
+
final def hasSameLengthAs[U](ys: List[U]): Boolean = {
@tailrec def loop(xs: List[T], ys: List[U]): Boolean =
if (xs.isEmpty) ys.isEmpty
diff --git a/src/dotty/tools/dotc/transform/TreeChecker.scala b/src/dotty/tools/dotc/transform/TreeChecker.scala
index 1448e8bf9..77e504e25 100644
--- a/src/dotty/tools/dotc/transform/TreeChecker.scala
+++ b/src/dotty/tools/dotc/transform/TreeChecker.scala
@@ -20,6 +20,7 @@ import reporting.ThrowingReporter
import ast.Trees._
import ast.{tpd, untpd}
import util.SourcePosition
+import collection.mutable
import ProtoTypes._
import java.lang.AssertionError
@@ -57,6 +58,32 @@ class TreeChecker {
}
class Checker(phasesToCheck: Seq[Phase]) extends ReTyper {
+
+ val definedSyms = new mutable.HashSet[Symbol]
+
+ def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = {
+ if (tree.isDef) {
+ //assert(!definedSyms.contains(tree.symbol), i"doubly defined symbol: ${tree.symbol}in $tree")
+ definedSyms += tree.symbol
+ //println(i"defined: ${tree.symbol}")
+ val res = op
+ definedSyms -= tree.symbol
+ //println(i"undefined: ${tree.symbol}")
+ res
+ }
+ else op
+ }
+
+ def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) =
+ trees.foldRightBN(op)(withDefinedSym(_)(_))
+
+ def withDefinedSymss[T](vparamss: List[List[untpd.ValDef]])(op: => T)(implicit ctx: Context): T =
+ vparamss.foldRightBN(op)(withDefinedSyms(_)(_))
+
+ def assertDefined(tree: untpd.Tree)(implicit ctx: Context) =
+ if (tree.symbol.maybeOwner.isTerm)
+ ()//assert(definedSyms contains tree.symbol, i"undefined symbol ${tree.symbol}")
+
override def typed(tree: untpd.Tree, pt: Type)(implicit ctx: Context) = {
val res = tree match {
case _: untpd.UnApply =>
@@ -88,7 +115,8 @@ class TreeChecker {
override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context): Tree = {
assert(tree.isTerm || !ctx.isAfterTyper, tree.show + " at " + ctx.phase)
- assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree")
+ assert(tree.isType || !needsSelect(tree.tpe), i"bad type ${tree.tpe} for $tree # ${tree.uniqueId}")
+ assertDefined(tree)
super.typedIdent(tree, pt)
}
@@ -117,6 +145,22 @@ class TreeChecker {
super.typedClassDef(cdef, cls)
}
+ override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) =
+ withDefinedSyms(ddef.tparams) {
+ withDefinedSymss(ddef.vparamss) {
+ super.typedDefDef(ddef, sym)
+ }
+ }
+
+ override def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = {
+ withDefinedSyms(tree.pat.asInstanceOf[tpd.Tree].filterSubTrees(_.isInstanceOf[ast.Trees.Bind[_]])) {
+ super.typedCase(tree, pt, selType, gadtSyms)
+ }
+ }
+
+ override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) =
+ withDefinedSyms(tree.stats) { super.typedBlock(tree, pt) }
+
/** Check that all defined symbols have legal owners.
* An owner is legal if it is either the same as the context's owner
* or there's an owner chain of valdefs starting at the context's owner and
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index 1cab2fa46..1afa5f9f3 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -610,29 +610,29 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
accu(Set.empty, selType)
}
- def typedCase(tree: untpd.CaseDef): CaseDef = track("typedCase") {
- def caseRest(pat: Tree)(implicit ctx: Context) = {
- gadtSyms foreach (_.resetGADTFlexType)
- pat foreachSubTree {
- case b: Bind =>
- if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
- else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
- case _ =>
- }
- val guard1 = typedExpr(tree.guard, defn.BooleanType)
- val body1 = typedExpr(tree.body, pt)
- assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
- }
- val doCase: () => CaseDef =
- () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
- (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
- }
-
- val cases1 = tree.cases mapconserve typedCase
+ val cases1 = tree.cases mapconserve (typedCase(_, pt, selType, gadtSyms))
assignType(cpy.Match(tree)(sel1, cases1), cases1)
}
}
+ def typedCase(tree: untpd.CaseDef, pt: Type, selType: Type, gadtSyms: Set[Symbol])(implicit ctx: Context): CaseDef = track("typedCase") {
+ def caseRest(pat: Tree)(implicit ctx: Context) = {
+ gadtSyms foreach (_.resetGADTFlexType)
+ pat foreachSubTree {
+ case b: Bind =>
+ if (ctx.scope.lookup(b.name) == NoSymbol) ctx.enter(b.symbol)
+ else ctx.error(d"duplicate pattern variable: ${b.name}", b.pos)
+ case _ =>
+ }
+ val guard1 = typedExpr(tree.guard, defn.BooleanType)
+ val body1 = typedExpr(tree.body, pt)
+ assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1)
+ }
+ val doCase: () => CaseDef =
+ () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope)
+ (doCase /: gadtSyms)((op, tsym) => tsym.withGADTFlexType(op))()
+ }
+
def typedReturn(tree: untpd.Return)(implicit ctx: Context): Return = track("typedReturn") {
def returnProto(owner: Symbol) =
if (owner.isConstructor) defn.UnitType else owner.info.finalResultType