summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--test/checker-tests/fail12.scala20
7 files changed, 166 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 ...
diff --git a/test/checker-tests/fail12.scala b/test/checker-tests/fail12.scala
new file mode 100644
index 0000000000..7568311454
--- /dev/null
+++ b/test/checker-tests/fail12.scala
@@ -0,0 +1,20 @@
+class A {
+ def f(b: Boolean) = {
+ locally {
+ while (b == false) ()
+ // or:
+ // do () while (b == false)
+ }
+ }
+}
+//
+// [Now checking: erasure]
+// [check: erasure] New symbols: BoxedUnit UNIT runtime scala
+// /tmp/fail.scala:4: error:
+// **** ERROR DURING INTERNAL CHECKING ****
+// type mismatch;
+// found : scala.runtime.BoxedUnit
+// required: Unit
+// while (b == false) ()
+// ^
+// one error found