summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-10-07 22:18:59 +0000
committerPaul Phillips <paulp@improving.org>2010-10-07 22:18:59 +0000
commit001e910f9774b2da00da2d56b7ba92d78a9c20ce (patch)
tree9216c2bc6192554894544a2e73d29da7e4e60b47 /src/compiler/scala/tools
parent68aafb29c162c117d87ef4786ee2226bd08663bc (diff)
downloadscala-001e910f9774b2da00da2d56b7ba92d78a9c20ce.tar.gz
scala-001e910f9774b2da00da2d56b7ba92d78a9c20ce.tar.bz2
scala-001e910f9774b2da00da2d56b7ba92d78a9c20ce.zip
Did a bunch of symbol oriented work on checkers.
changes in Global and Typer to accomodate this, and renamed "Checkers" to "ICodeCheckers" to make some less confusing space for the future "SymbolCheckers". No review.
Diffstat (limited to 'src/compiler/scala/tools')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala30
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala (renamed from src/compiler/scala/tools/nsc/backend/icode/Checkers.scala)8
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Types.scala14
-rw-r--r--src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala109
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala11
6 files changed, 146 insertions, 27 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index ff3a5cdc0b..d3f505d0e2 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -24,7 +24,7 @@ import ast.parser._
import typechecker._
import transform._
-import backend.icode.{ ICodes, GenICode, Checkers }
+import backend.icode.{ ICodes, GenICode, ICodeCheckers }
import backend.{ ScalaPrimitives, Platform, MSILPlatform, JavaPlatform }
import backend.jvm.GenJVM
import backend.opt.{ Inliners, ClosureElimination, DeadCodeElimination }
@@ -78,11 +78,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
val global: Global.this.type = Global.this
} with ConstantFolder
- /** Tree checker (used for testing and debugging) */
- object checker extends {
- val global: Global.this.type = Global.this
- } with TreeCheckers
-
/** ICode generator */
object icodes extends {
val global: Global.this.type = Global.this
@@ -98,11 +93,6 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
val global: Global.this.type = Global.this
} with CopyPropagation
- /** Icode verification */
- object checkers extends {
- val global: Global.this.type = Global.this
- } with Checkers
-
/** Some statistics (normally disabled) */
object statistics extends {
val global: Global.this.type = Global.this
@@ -515,7 +505,21 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
val runsRightAfter = None
} with SampleTransform
- object icodeChecker extends checkers.ICodeChecker()
+ /** The checkers are for validating the compiler data structures
+ * at phase boundaries.
+ */
+
+ /** Tree checker */
+ object treeChecker extends {
+ val global: Global.this.type = Global.this
+ } with TreeCheckers
+
+ /** Icode verification */
+ object icodeCheckers extends {
+ val global: Global.this.type = Global.this
+ } with ICodeCheckers
+
+ object icodeChecker extends icodeCheckers.ICodeChecker()
object typer extends analyzer.Typer(
analyzer.NoContext.make(EmptyTree, Global.this.definitions.RootClass, new Scope))
@@ -777,7 +781,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
phase = globalPhase
inform("[Now checking: " + phase.prev.name + "]")
if (globalPhase.id >= icodePhase.id) icodeChecker.checkICodes
- else checker.checkTrees
+ else treeChecker.checkTrees
}
else inform("[Not checkable: " + globalPhase.prev.name + "]")
}
diff --git a/src/compiler/scala/tools/nsc/backend/icode/Checkers.scala b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala
index 61f2d8dd25..53205e26ca 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/Checkers.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/ICodeCheckers.scala
@@ -10,7 +10,7 @@ package icode
import scala.collection.mutable.{Buffer, ListBuffer, Map, HashMap}
import scala.tools.nsc.symtab._
-abstract class Checkers {
+abstract class ICodeCheckers {
val global: Global
import global._
@@ -108,11 +108,11 @@ abstract class Checkers {
for (f1 <- cls.fields ; f2 <- cls.fields ; if f1 < f2)
if (isConfict(f1, f2, false))
- Checkers.this.global.error("Repetitive field name: " + f1.symbol.fullName)
+ ICodeCheckers.this.global.error("Repetitive field name: " + f1.symbol.fullName)
for (m1 <- cls.methods ; m2 <- cls.methods ; if m1 < m2)
if (isConfict(m1, m2, true))
- Checkers.this.global.error("Repetitive method: " + m1.symbol.fullName)
+ ICodeCheckers.this.global.error("Repetitive method: " + m1.symbol.fullName)
clasz.methods foreach check
}
@@ -633,7 +633,7 @@ abstract class Checkers {
//////////////// Error reporting /////////////////////////
def error(msg: String) {
- Checkers.this.global.error("!! ICode checker fatality in " + method + " at:" + blockAsString(basicBlock) + ":\n " + msg)
+ ICodeCheckers.this.global.error("!! ICode checker fatality in " + method + " at:" + blockAsString(basicBlock) + ":\n " + msg)
}
def error(msg: String, stack: TypeStack) {
diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 21da19c526..9dac3c530b 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -814,9 +814,19 @@ trait Types extends reflect.generic.Types { self: SymbolTable =>
// of a subtyping/equality judgement, which can lead to recursive types being constructed.
// See (t0851) for a situation where this happens.
if (!this.isGround) {
+ // PP: The foreach below was formerly expressed as:
+ // for(tv @ TypeVar(_, _) <- this) { suspension suspend tv }
+ //
+ // The tree checker failed this saying a TypeVar is required, but a (Type @unchecked) was found.
+ // This is a consequence of using a pattern match and variable binding + ticket #1503, which
+ // was addressed by weakening the type of bindings in pattern matches if they occur on the right.
+ // So I'm not quite sure why this works at all, as the checker is right that it is mistyped.
+ // For now I modified it as below, which achieves the same without error.
+ //
// make each type var in this type use its original type for comparisons instead of collecting constraints
- for(tv@TypeVar(_, _) <- this) {
- suspension suspend tv
+ this foreach {
+ case tv: TypeVar => suspension suspend tv
+ case _ => ()
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
index 1403377367..0e8faa1f1e 100644
--- a/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
+++ b/src/compiler/scala/tools/nsc/transform/ExplicitOuter.scala
@@ -9,6 +9,7 @@ package transform
import symtab._
import Flags.{ CASE => _, _ }
import scala.collection.mutable.ListBuffer
+import scala.collection.mutable
import matching.{ Patterns, ParallelMatching }
/** This class ...
diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
index 2bc3103854..16961db77b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
@@ -8,12 +8,108 @@ package typechecker
import scala.tools.nsc.symtab.Flags._
import scala.collection.mutable
-import mutable.HashMap
+import mutable.{ HashMap, HashSet, ListBuffer }
import util.returning
abstract class TreeCheckers extends Analyzer {
import global._
+ private def classstr(x: AnyRef) = x.getClass.getName split """\\.|\\$""" last;
+ private def typestr(x: Type) = " (tpe = " + x + ")"
+ private def treestr(t: Tree) = t + " [" + classstr(t) + "]" + typestr(t.tpe)
+ private def ownerstr(s: Symbol) = "'" + s + "'" + s.locationString
+ private def wholetreestr(t: Tree) = nodeToString(t) + "\n"
+
+ private def beststr(t: Tree) = "<" + {
+ if (t.symbol != null && t.symbol != NoSymbol) "sym=" + ownerstr(t.symbol)
+ else if (t.tpe.isComplete) "tpe=" + typestr(t.tpe)
+ else t match {
+ case x: DefTree => "name=" + x.name
+ case x: RefTree => "reference=" + x.name
+ case _ => "clazz=" + classstr(t)
+ }
+ } + ">"
+
+ /** This is a work in progress, don't take it too seriously.
+ */
+ object SymbolTracker extends Traverser {
+ type PhaseMap = HashMap[Symbol, List[Tree]]
+ val maps: ListBuffer[(Phase, PhaseMap)] = ListBuffer()
+ def prev = maps.init.last._2
+ def latest = maps.last._2
+ val defSyms = new HashMap[Symbol, List[DefTree]]
+ val newSyms = new HashSet[Symbol]
+ val movedMsgs = new ListBuffer[String]
+ def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString)
+
+ def inPrev(sym: Symbol) = {
+ (maps.size >= 2) && (prev contains sym)
+ }
+ def record(sym: Symbol, tree: Tree) = {
+ if (latest contains sym) latest(sym) = latest(sym) :+ tree
+ else latest(sym) = List(tree)
+
+ if (inPrev(sym)) {
+ val prevTrees = prev(sym)
+
+ if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) ()
+ else if (prevTrees exists (_.symbol.owner == sym.owner.implClass)) {
+ errorFn("Noticed " + ownerstr(sym) + " moving to implementation class.")
+ }
+ else {
+ val s1 = (prevTrees map wholetreestr).sorted.distinct
+ val s2 = wholetreestr(tree)
+ if (s1 contains s2) ()
+ else movedMsgs += ("\n** %s moved:\n** Previously:\n%s\n** Currently:\n%s".format(ownerstr(sym), s1 mkString ", ", s2))
+ }
+ }
+ else newSyms += sym
+ }
+ def reportChanges(): Unit = {
+ // new symbols
+ if (newSyms.nonEmpty) {
+ val str =
+ if (settings.debug.value) "New symbols: " + (sortedNewSyms mkString " ")
+ else newSyms.size + " new symbols."
+
+ newSyms.clear()
+ errorFn(str)
+ }
+
+ // moved symbols
+ movedMsgs foreach errorFn
+ movedMsgs.clear()
+
+ // duplicate defs
+ for ((sym, defs) <- defSyms ; if defs.size > 1) {
+ errorFn("%s DefTrees with symbol '%s': %s".format(defs.size, ownerstr(sym), defs map beststr mkString ", "))
+ }
+ defSyms.clear()
+ }
+
+ def check(ph: Phase, unit: CompilationUnit): Unit = {
+ if (maps.isEmpty || maps.last._1 != ph)
+ maps += ((ph, new PhaseMap))
+
+ traverse(unit.body)
+ reportChanges()
+ }
+ override def traverse(tree: Tree): Unit = {
+ val sym = tree.symbol
+ if (sym != null && sym != NoSymbol) {
+ record(sym, tree)
+ tree match {
+ case x: DefTree =>
+ if (defSyms contains sym) defSyms(sym) = defSyms(sym) :+ x
+ else defSyms(sym) = List(x)
+ case _ => ()
+ }
+ }
+
+ super.traverse(tree)
+ }
+ }
+
lazy val tpeOfTree = new HashMap[Tree, Type]
def posstr(p: Position) =
@@ -48,12 +144,12 @@ abstract class TreeCheckers extends Analyzer {
assertFn(currentRun.currentUnit == unit, "currentUnit is " + currentRun.currentUnit + ", but unit is " + unit)
currentRun.currentUnit = unit0
}
-
def check(unit: CompilationUnit) {
informProgress("checking "+unit)
val context = rootContext(unit)
context.checking = true
tpeOfTree.clear
+ SymbolTracker.check(phase, unit)
val checker = new TreeChecker(context)
runWithUnit(unit) {
checker.precheck.traverse(unit.body)
@@ -65,10 +161,11 @@ abstract class TreeCheckers extends Analyzer {
override def newTyper(context: Context): Typer = new TreeChecker(context)
class TreeChecker(context0: Context) extends Typer(context0) {
- private def classstr(x: AnyRef) = x.getClass.getName split '.' last;
- private def typestr(x: Type) = " (tpe = " + x + ")"
- private def treestr(t: Tree) = t + " [" + classstr(t) + "]" + typestr(t.tpe)
- private def ownerstr(s: Symbol) = "'" + s + "'" + s.locationString
+ override protected def typerAddSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = {
+ // If we don't intercept this all the synthetics get added at every phase,
+ // with predictably unfortunate results.
+ templ
+ }
// XXX check for tree.original on TypeTrees.
private def treesDiffer(t1: Tree, t2: Tree) =
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 6a67fecd80..647e5e422d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1301,7 +1301,7 @@ trait Typers { self: Analyzer =>
val tparams1 = cdef.tparams mapConserve (typedTypeDef)
val impl1 = newTyper(context.make(cdef.impl, clazz, new Scope))
.typedTemplate(cdef.impl, parentTypes(cdef.impl))
- val impl2 = addSyntheticMethods(impl1, clazz, context)
+ val impl2 = typerAddSyntheticMethods(impl1, clazz, context)
if ((clazz != ClassfileAnnotationClass) &&
(clazz isNonBottomSubClass ClassfileAnnotationClass))
unit.warning (cdef.pos,
@@ -1338,7 +1338,7 @@ trait Typers { self: Analyzer =>
assert(clazz != NoSymbol)
val impl1 = newTyper(context.make(mdef.impl, clazz, new Scope))
.typedTemplate(mdef.impl, parentTypes(mdef.impl))
- val impl2 = addSyntheticMethods(impl1, clazz, context)
+ val impl2 = typerAddSyntheticMethods(impl1, clazz, context)
if (mdef.name == nme.PACKAGEkw)
for (m <- mdef.symbol.info.members)
@@ -1348,6 +1348,13 @@ trait Typers { self: Analyzer =>
treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType
}
+ /** In order to override this in the TreeCheckers Typer so synthetics aren't re-added
+ * all the time, it is exposed here the module/class typing methods go through it.
+ */
+ protected def typerAddSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = {
+ addSyntheticMethods(templ, clazz, context)
+ }
+
/**
* @param stat ...
* @return ...