summaryrefslogtreecommitdiff
path: root/sources/scalac/typechecker/RefCheck.java
diff options
context:
space:
mode:
Diffstat (limited to 'sources/scalac/typechecker/RefCheck.java')
-rw-r--r--sources/scalac/typechecker/RefCheck.java208
1 files changed, 208 insertions, 0 deletions
diff --git a/sources/scalac/typechecker/RefCheck.java b/sources/scalac/typechecker/RefCheck.java
new file mode 100644
index 0000000000..b45a9d3a2f
--- /dev/null
+++ b/sources/scalac/typechecker/RefCheck.java
@@ -0,0 +1,208 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+**
+** $Id$
+\* */
+// todo: Any cannot be inherited.
+// todo: check that only stable defs override stable defs
+// todo: admit vardefs w/o rhs
+// ClassTemplate ::= [extends Constr {with Constr}]
+// [with `(' TemplateStatSeq `)']
+// todo: pretty printer prints wrong precedence for `new' (-> lambdalift.scala).
+
+package scalac.typechecker;
+
+import java.util.HashMap;
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import Tree.*;
+
+/** Check that no forward reference to a term symbol extends beyond a value definition.
+ */
+public class RefCheck extends Transformer implements Modifiers, Kinds {
+
+ public RefCheck(Global global, RefCheckPhase descr) {
+ super(global, descr);
+ }
+
+ private Unit unit;
+ private Scope[] scopes = new Scope[4];
+ private int[] maxindex = new int[4];
+ private int[] refpos = new int[4];
+ private Symbol[] refsym = new Symbol[4];
+ private int level;
+ private HashMap symIndex = new HashMap();
+
+ void pushLevel() {
+ level++;
+ if (level == scopes.length) {
+ Scope[] scopes1 = new Scope[scopes.length * 2];
+ int[] maxindex1 = new int[scopes.length * 2];
+ int[] refpos1 = new int[scopes.length * 2];
+ Symbol[] refsym1 = new Symbol[scopes.length * 2];
+ System.arraycopy(scopes, 0, scopes1, 0, scopes.length);
+ System.arraycopy(maxindex, 0, maxindex1, 0, scopes.length);
+ System.arraycopy(refpos, 0, refpos1, 0, scopes.length);
+ System.arraycopy(refsym, 0, refsym1, 0, scopes.length);
+ scopes = scopes1;
+ maxindex = maxindex1;
+ refpos = refpos1;
+ refsym = refsym1;
+ }
+ scopes[level] = new Scope(scopes[level - 1]);
+ maxindex[level] = Integer.MIN_VALUE;
+ }
+
+ void popLevel() {
+ scopes[level] = null;
+ level --;
+ }
+
+ public void apply(Unit unit) {
+ this.unit = unit;
+ level = 0;
+ scopes[0] = new Scope();
+ maxindex[0] = Integer.MIN_VALUE;
+ unit.body = transformStats(unit.body);
+ scopes[0] = null;
+ symIndex.clear();
+ }
+
+ /** compensate for renaming during addition of access functions
+ */
+ Name normalize(Name name) {
+ return (name.endsWith(Name.fromString("$")))
+ ? name.subName(0, name.length() - 1)
+ : name;
+ }
+
+ void enterSyms(Tree[] stats) {
+ for (int i = 0; i < stats.length; i++) {
+ enterSym(stats[i], i);
+ }
+ }
+
+ void enterSym(Tree stat, int index) {
+ Symbol sym = null;
+ switch (stat) {
+ case ClassDef(_, _, _, _, _, _):
+ sym = stat.symbol().constructor();
+ break;
+ case DefDef(_, _, _, _, _, _):
+ case ModuleDef(_, _, _, _):
+ case ValDef(_, _, _, _):
+ sym = stat.symbol();
+ }
+ if (sym != null) {
+ scopes[level].enter(sym);
+ symIndex.put(sym, new Integer(index));
+ }
+ }
+
+ public Tree[] transformStats(Tree[] stats) {
+ pushLevel();
+ enterSyms(stats);
+ int i = 0;
+ while (i < stats.length) {
+ Tree[] newstat = transformStat(stats[i], i);
+ if (newstat != null) {
+ Tree[] newstats = new Tree[stats.length + newstat.length - 1];
+ System.arraycopy(stats, 0, newstats, 0, i);
+ System.arraycopy(newstat, 0, newstats, i, newstat.length);
+ System.arraycopy(stats, i + 1, newstats, i + newstat.length,
+ stats.length - i - 1);
+ i = i + newstat.length;
+ stats = newstats;
+ } else {
+ i = i + 1;
+ }
+ }
+ popLevel();
+ return stats;
+ }
+
+ /** The main checking functions
+ */
+ public Tree[] transformStat(Tree tree, int index) {
+ Tree resultTree;
+ switch (tree) {
+ case ModuleDef(int mods, Name name, Tree tpe, Tree.Template templ):
+ Symbol sym = tree.symbol();
+ // local modules are not yet supported but the interpreter
+ // accepts modules at the console level
+ if (sym.isLocal()) unit.error("local modules are not yet supported");
+
+ Tree[] result = new Tree[2];
+ result[0] = make.ClassDef(
+ tree.pos,
+ mods | FINAL | MODUL,
+ name.toTypeName(),
+ new Tree.TypeDef[0],
+ new Tree.ValDef[][]{{}},
+ Tree.Empty,
+ templ)
+ .setSymbol(sym.moduleClass()).setType(tree.type);
+ result[1] = make.ValDef(
+ tree.pos,
+ mods | MODUL,
+ name,
+ (tpe == Tree.Empty) ? gen.mkType(tree.pos, sym.type()) : tpe,
+ gen.New(
+ tree.pos,
+ sym.type().prefix(),
+ sym.moduleClass(),
+ Tree.EMPTY_ARRAY))
+ .setSymbol(sym).setType(tree.type);
+ return transform(result);
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ Symbol sym = tree.symbol();
+ resultTree = transform(tree);
+ //todo: handle variables
+ if (index <= maxindex[level]) {
+ if (Global.instance.debug)
+ System.out.println(refsym[level] + ":" + refsym[level].type());
+ unit.error(
+ refpos[level],
+ "forward reference extends over definition of value " +
+ normalize(name));
+ }
+ break;
+ default:
+ resultTree = transform(tree);
+ }
+ return (resultTree == tree) ? null : new Tree[]{resultTree};
+ }
+
+ public Tree transform(Tree tree) {
+ switch (tree) {
+ case Template(Tree[] bases, Tree[] body):
+ Tree[] bases1 = transform(bases);
+ Tree[] body1 = transformStats(body);
+ return copy.Template(tree, bases1, body1);
+ case Block(Tree[] stats):
+ Tree[] stats1 = transformStats(stats);
+ return copy.Block(tree, stats1);
+ case Ident(Name name):
+ Scope.Entry e = scopes[level].lookupEntry(name);
+ if (tree.symbol() == e.sym) {
+ int i = level;
+ while (scopes[i] != e.owner) i--;
+ int symindex = ((Integer) symIndex.get(tree.symbol())).intValue();
+ if (maxindex[i] < symindex) {
+ refpos[i] = tree.pos;
+ refsym[i] = e.sym;
+ maxindex[i] = symindex;
+ }
+ }
+ return tree;
+ default:
+ return super.transform(tree);
+ }
+ }
+}
+