summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala
blob: 8fbd2f7ae0e0c61f26aa86886b63d33e4c9043f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* NSC -- new Scala compiler
 * Copyright 2005-2007 LAMP/EPFL
 * @author  Martin Odersky
 */
// $Id$

package scala.tools.nsc.typechecker

import scala.tools.nsc.symtab.Flags._
import scala.tools.nsc.util.{Position, NoPosition}

abstract class TreeCheckers extends Analyzer {

  import global._

  val tpeOfTree = new scala.collection.mutable.HashMap[Tree, Type]

  def checkTrees {
    if (settings.verbose.value)
      Console.println("[consistency check at the beginning of phase " + phase + "]")
    for (unit <- currentRun.units) check(unit)
  }

  def check(unit: CompilationUnit) {
    informProgress("checking "+unit)
    val context = rootContext(unit)
    context.checking = true
    tpeOfTree.clear
    val checker = new TreeChecker(context)

    val unit0 = currentRun.currentUnit
    currentRun.currentUnit = unit
    checker.precheck.traverse(unit.body)
    checker.typed(unit.body)
    checker.postcheck.traverse(unit.body)
    currentRun.advanceUnit
    assert(currentRun.currentUnit == unit)
    currentRun.currentUnit = unit0
  }

  override def newTyper(context: Context): Typer = new TreeChecker(context)

  class TreeChecker(context0: Context) extends Typer(context0) {

    import infer._

    override def typed(tree: Tree, mode: Int, pt: Type): Tree = {
      tree match {
        case EmptyTree | TypeTree() =>
          ;
        case _ =>
          try {
            if (!tpeOfTree.contains(tree)) {
              tpeOfTree.update(tree, tree.tpe)
              tree.tpe = null
            }
            val newtree = super.typed(tree, mode, pt);
            if ((newtree ne tree) && !newtree.isInstanceOf[Literal])
              error(tree.pos, "trees differ\n old: " + tree + " [" + tree.getClass() +
                    "]\n new: " + newtree + " [" + newtree.getClass() + "]")
          } catch {
            case ex: Throwable =>
              Console.println("exception while typing "+tree)
              throw ex
          }
      }
      tree
    }

    object precheck extends Traverser {
      override def traverse(tree: Tree) {
        try {
          tree match {
            case DefDef(_, _, _, _, _, _) =>
              if (tree.symbol.hasFlag(ACCESSOR) &&
                  !tree.symbol.isDeferred &&
                  !tree.symbol.tpe.resultType.isInstanceOf[ConstantType]) {
                assert(tree.symbol.accessed != NoSymbol, tree.symbol)
                assert(tree.symbol.accessed.getter(tree.symbol.owner) == tree.symbol ||
                       tree.symbol.accessed.setter(tree.symbol.owner) == tree.symbol)
              }
            case ValDef(_, _, _, _) =>
              if (tree.symbol.hasGetter) {
                assert(tree.symbol.getter(tree.symbol.owner) != NoSymbol, tree.symbol)
              }
            case Apply(_, args) =>
              assert(args forall (EmptyTree !=))
            case Select(_, _) =>
              assert(tree.symbol != NoSymbol, tree)
            case This(_) =>
              if (!(tree.symbol.isStatic && (tree.symbol hasFlag MODULE))) {
                var o = currentOwner
                while (o != tree.symbol) {
                  o = o.owner
                  if (o == NoSymbol) {
                    error(tree.pos, "tree symbol "+tree.symbol+" does not point to enclosing class; tree = "+tree)
                    return
                  }
                }
              }
            case _ =>
          }
          if (tree.pos == NoPosition && tree != EmptyTree) {
            error(tree.pos, "tree without position: " + tree)
          } else if ((tree.tpe eq null) && phase.id >= currentRun.typerPhase.id) {
            error(tree.pos, "tree without type: " + tree)
          } else if (tree.isDef && tree.symbol.owner != currentOwner) {
            var owner = currentOwner
            while (owner.isTerm && !owner.isMethod && tree.symbol.owner != owner)
              owner = owner.owner;
            if (tree.symbol.owner != owner) {
              error(tree.pos, "" + tree.symbol + " has wrong owner: " + tree.symbol.owner +
                    tree.symbol.owner.locationString + ", should be: " +
                    currentOwner + currentOwner.locationString)
            }
          } else {
            super.traverse(tree)
          }
        } catch {
          case ex: Throwable =>
            if (settings.debug.value)
              Console.println("exception when traversing " + tree);
            throw(ex)
        }
      }
    }

    object postcheck extends Traverser {
      override def traverse(tree: Tree) {
        try {
          tree match {
            case EmptyTree | TypeTree() =>
              ;
            case _ =>
              tpeOfTree.get(tree) match {
                case Some(oldtpe) =>
                  if (!(oldtpe =:= tree.tpe))
                    error(tree.pos, "types differ\n old: " + oldtpe +
                          "\n new: " + tree.tpe + "\n tree: " + tree)
                  tree.tpe = oldtpe
                  super.traverse(tree)
                case None =>
              }
          }
        } catch {
          case ex: Throwable =>
            if (settings.debug.value)
              Console.println("exception when traversing " + tree);
            throw(ex)
        }
      }
    }
  }
}