diff options
Diffstat (limited to 'sources/scalac/typechecker/RefCheck.java')
-rw-r--r-- | sources/scalac/typechecker/RefCheck.java | 208 |
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); + } + } +} + |