summaryrefslogtreecommitdiff
path: root/sources/scala/tools/nsc/typechecker
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2005-02-16 13:15:42 +0000
committerMartin Odersky <odersky@gmail.com>2005-02-16 13:15:42 +0000
commit28c2394d012195e38308bcd6fd5049bcde635b4c (patch)
treeefc1ab19ecba057cf626af0d86e0aa85b985b941 /sources/scala/tools/nsc/typechecker
parent431abf42bda2c97ff062594a8f1d15fd7776350c (diff)
downloadscala-28c2394d012195e38308bcd6fd5049bcde635b4c.tar.gz
scala-28c2394d012195e38308bcd6fd5049bcde635b4c.tar.bz2
scala-28c2394d012195e38308bcd6fd5049bcde635b4c.zip
*** empty log message ***
Diffstat (limited to 'sources/scala/tools/nsc/typechecker')
-rw-r--r--sources/scala/tools/nsc/typechecker/Analyzer.scala5
-rwxr-xr-xsources/scala/tools/nsc/typechecker/Contexts.scala98
-rwxr-xr-xsources/scala/tools/nsc/typechecker/DeSugarizePhase.scala219
-rwxr-xr-xsources/scala/tools/nsc/typechecker/Namers.scala166
-rwxr-xr-xsources/scala/tools/nsc/typechecker/TypeCheckers.scala246
-rwxr-xr-xsources/scala/tools/nsc/typechecker/Typers.scala217
6 files changed, 951 insertions, 0 deletions
diff --git a/sources/scala/tools/nsc/typechecker/Analyzer.scala b/sources/scala/tools/nsc/typechecker/Analyzer.scala
index d519a895d8..fe01281247 100644
--- a/sources/scala/tools/nsc/typechecker/Analyzer.scala
+++ b/sources/scala/tools/nsc/typechecker/Analyzer.scala
@@ -1,3 +1,8 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+// $Id$
package scala.tools.nsc.typechecker;
import scala.tools.util.Position;
diff --git a/sources/scala/tools/nsc/typechecker/Contexts.scala b/sources/scala/tools/nsc/typechecker/Contexts.scala
new file mode 100755
index 0000000000..78112ad604
--- /dev/null
+++ b/sources/scala/tools/nsc/typechecker/Contexts.scala
@@ -0,0 +1,98 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+// $Id$
+package scala.tools.nsc.typechecker;
+
+import scala.tools.util.Position;
+
+class Contexts: Analyzer {
+ import global._;
+
+ val NoContext = new Context();
+
+ val startContext = {
+ import definitions._;
+ var sc = NoContext.make(EmptyTree, RootClass, RootClass.info.decls);
+ def addImport(pkg: Symbol): unit = {
+ sc = sc.make(sc.tree, sc.owner, new Scope(sc.scope));
+ val impTree = Import(gen.mkGlobalRef(pkg), List(Pair(nme.WILDCARD, null)));
+ impTree.setSymbol(NoSymbol.newImport(Position.NOPOS)).setType(ImportType(impTree));
+ sc.scope.enter(impTree.symbol)
+ }
+ if (!settings.noimports.value) {
+ addImport(JavaLangPackage);
+ addImport(ScalaPackage);
+ if (!settings.nopredefs.value)
+ addImport(PredefModule);
+ }
+ sc
+ }
+
+ class Context {
+ var unit: CompilationUnit = _;
+ var tree: Tree = _; // Tree associated with this context
+ var owner: Symbol = _; // The current owner
+ var scope: Scope = _; // The current scope
+ var outer: Context = _; // The next outer context
+ var enclClass: Context = this; // The next outer context whose tree
+ // is a class template
+ var variance: int = _; // Variance relative to enclosing class.
+ var constructorClass: Symbol = _; // Class for auxiliary constructor
+ var depth: int = 0;
+ val imports: List[Tree] = List();
+
+ def make(unit: CompilationUnit, tree: Tree, owner: Symbol, scope: Scope): Context = {
+ val c = new Context();
+ c.unit = unit;
+ c.tree = tree;
+ c.owner = owner;
+ c.scope = scope;
+ c.enclClass = if ((tree.isInstanceOf[Template] ||
+ tree.isInstanceOf[IntersectionTypeTree]) &&
+ tree != this.tree) c
+ else this.enclClass;
+ c.variance = this.variance;
+ c.constructorClass = this.constructorClass;
+ c.depth = this.depth + 1;
+ c.outer = this;
+ c
+ }
+
+ def make(unit: CompilationUnit): Context =
+ make(unit, EmptyTree, this.owner, new Scope(this.owner.info.decls));
+
+ def make(tree: Tree, owner: Symbol, scope: Scope): Context =
+ make(this.unit, tree, owner, scope);
+
+ def makeNewScope(tree: Tree, owner: Symbol): Context =
+ make(tree, owner, new Scope(this.scope));
+
+ def make(tree: Tree, owner: Symbol): Context =
+ make(tree, owner, this.scope);
+
+ def make(tree: Tree): Context =
+ make(tree, this.owner);
+
+ def outerContext(clazz: Symbol): Context = {
+ var c = this;
+ while (c != NoContext && c.owner != clazz) c = c.outer.enclClass;
+ c
+ }
+
+ def isLocal(): boolean = tree match {
+ case Block(_,_) => true
+ case PackageDef(_, _) => false
+ case EmptyTree => false
+ case _ => outer.isLocal()
+ }
+
+ override def toString(): String = {
+ if (this == NoContext) "NoContext";
+ else tree.toString() + "\n:: " + outer.toString()
+ }
+ }
+}
+
+
diff --git a/sources/scala/tools/nsc/typechecker/DeSugarizePhase.scala b/sources/scala/tools/nsc/typechecker/DeSugarizePhase.scala
new file mode 100755
index 0000000000..01c1789435
--- /dev/null
+++ b/sources/scala/tools/nsc/typechecker/DeSugarizePhase.scala
@@ -0,0 +1,219 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+// $Id$
+package scala.tools.nsc.typechecker;
+
+import symtab.Flags._;
+import scala.collection.mutable.ListBuffer;
+
+/** Performs the following context-free rewritings:
+ * (1) Places all pattern variables in Bind nodes. In a pattern, for identifiers `x':
+ * x => x @ _
+ * x:T => x @ (_ : T)
+ *
+ * (2) Removes pattern definitions (PatDef's) as follows:
+ * If pattern is a simple (typed) identifier:
+ * val x = e ==> val x = e
+ * val x: T = e ==> val x: T = e
+ *
+ * if there are no variables in pattern
+ * val p = e ==> e.match (case p => ())
+ *
+ * if there is exactly one variable in pattern
+ * val x_1 = e.match (case p => (x_1))
+ *
+ * if there is more than one variable in pattern
+ * val p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N))
+ * val x_1 = t$._1
+ * ...
+ * val x_N = t$._N
+ *
+ * (3) Removes function types as follows:
+ * (argtpes) => restpe ==> scala.Function_n[argtpes, restpe]
+ *
+ * (4) Wraps naked case definitions in a match as follows:
+ * { cases } ==> (x => x.match {cases}), except when already argument to match
+ */
+abstract class DeSugarizePhase(prev: Phase) extends StdPhase(prev) {
+ import global._;
+ import posAssigner.atPos;
+
+ def name = "desugarize";
+ def apply(unit: CompilationUnit): unit =
+ unit.body = new DeSugarizer(unit).transformTrees(unit.body);
+
+ class DeSugarizer(unit: CompilationUnit) extends Transformer {
+
+ /** introduce fresh variable of the form "ds$56"
+ */
+ private def getvar: Name = unit.fresh.newName("ds$");
+
+ /** in patterns x => x @ _
+ * x:T => x @ (_ : T)
+ */
+ object patvarTransformer extends Transformer {
+ override def transform(tree: Tree): Tree = tree match {
+ case Ident(name) if (treeInfo.isVariableName(name) && name != nme.WILDCARD) =>
+ atPos(tree.pos)(Bind(name, Ident(nme.WILDCARD)))
+ case Typed(id @ Ident(name), tpt) =>
+ Bind(name, atPos(tree.pos)(Typed(Ident(nme.WILDCARD), tpt))) setPos id.pos
+ case Apply(fn @ Apply(_, _), args) =>
+ copy.Apply(tree, transform(fn), transformTrees(args))
+ case Apply(fn, args) =>
+ copy.Apply(tree, fn, transformTrees(args))
+ case Sequence(_) | Alternative(_) | Bind(_, _) | Typed(_, _) =>
+ super.transform(tree)
+ case _ =>
+ tree
+ }
+ }
+
+ /** Traverse pattern and collect all variable names in buffer */
+ object getvarTraverser extends Traverser {
+ val buf = new ListBuffer[Name];
+ def init: Traverser = { buf.clear; this }
+ override def traverse(tree: Tree): unit = tree match {
+ case Bind(name, tpe) =>
+ if (buf.elements forall (name !=)) buf += name;
+ traverse(tpe)
+ case _ => super.traverse(tree)
+ }
+ }
+
+ /** Returns list of all pattern variables without duplicates */
+ private def getVariables(tree: Tree): List[Name] = {
+ getvarTraverser.init.traverse(tree);
+ getvarTraverser.buf.toList
+ }
+
+ /** Expand patdefs */
+ private def transformStat(tree: Tree): List[Tree] = {
+ def rebox(defs: List[Tree], boxf: Tree => Tree): List[Tree] = defs match {
+ case List(stat) => List(if (stat.isDef) boxf(stat) else stat);
+ case stat :: stats => stat :: (stats map boxf)
+ }
+ def mkTuple(trees: List[Tree]): Tree = trees match {
+ case List() => Literal(())
+ case List(tree) => tree
+ case _ => Apply(Select(Ident(nme.scala), newTermName("Tuple" + trees.length)), trees)
+ }
+ tree match {
+ // pattern definitions:
+ // val x = e ==> val x = e
+ case PatDef(mods, Ident(name), rhs) =>
+ List(atPos(tree.pos)(ValDef(mods, name, EmptyTypeTree(), rhs)))
+
+ // val (x @ _) = e ==> val x = e
+ case PatDef(mods, Bind(name, Ident(nme.WILDCARD)), rhs) =>
+ List(atPos(tree.pos)(ValDef(mods, name, EmptyTypeTree(), rhs)))
+
+ // val x: T = e ==> val x: T = e
+ case PatDef(mods, Typed(Ident(name), tpt), rhs) =>
+ List(atPos(tree.pos)(ValDef(mods, name, tpt, rhs)))
+
+ // val x @ (_: T) = e ==> val x: T = e
+ case PatDef(mods, Bind(name, Typed(Ident(nme.WILDCARD), tpt)), rhs) =>
+ List(atPos(tree.pos)(ValDef(mods, name, tpt, rhs)))
+
+ // in case there are no variables in pattern
+ // val p = e ==> e.match (case p => ())
+ //
+ // in case there is exactly one variable in pattern
+ // val x_1 = e.match (case p => (x_1))
+ //
+ // in case there are more variables in pattern
+ // val p = e ==> private synthetic val t$ = e.match (case p => (x_1, ..., x_N))
+ // val x_1 = t$._1
+ // ...
+ // val x_N = t$._N
+ case PatDef(mods, pat, rhs) =>
+ val vars = getVariables(pat);
+ val matchExpr = atPos(pat.pos) {
+ Apply(
+ Select(rhs, nme._match),
+ List(Visitor(List(CaseDef(pat, EmptyTree, mkTuple(vars map Ident))))))
+ }
+ vars match {
+ case List() =>
+ List(matchExpr)
+ case List(vname) =>
+ List(ValDef(mods, vname, EmptyTypeTree(), matchExpr))
+ case _ =>
+ val tmp = getvar;
+ val firstDef = ValDef(PRIVATE | LOCAL | SYNTHETIC,
+ tmp, EmptyTypeTree(), matchExpr);
+ var cnt = 0;
+ val restDefs = for (val v <- vars) yield {
+ cnt = cnt + 1;
+ val selector = newTermName("_" + cnt);
+ ValDef(mods, v, EmptyTypeTree(), Select(Ident(tmp), selector))
+ }
+ firstDef :: restDefs
+ } map atPos(tree.pos)
+
+ case DocDef(comment, defn) =>
+ rebox(transformStat(defn), t => DocDef(comment, t))
+
+ case Attributed(attribute, defn) =>
+ rebox(transformStat(defn), t => Attributed(attribute, t))
+
+ case _ =>
+ List(tree)
+ }
+ }
+
+ /** Expand patdefs in list of statements */
+ private def transformStats(trees: List[Tree]): List[Tree] = {
+ def isPatDef(tree: Tree): boolean = tree match {
+ case PatDef(_, _, _) => true
+ case DocDef(_, defn) => isPatDef(defn)
+ case Attributed(_, defn) => isPatDef(defn)
+ case _ => false
+ }
+ if (trees exists isPatDef) trees flatMap transformStat else trees
+ }
+
+ /** desugarize tree */
+ override def transform(tree: Tree): Tree = tree match {
+ // (argtpes) => restpe ==> scala.Function_n[argtpes, restpe]
+ case FunctionTypeTree(argtpes, restpe) =>
+ atPos(tree.pos) (
+ AppliedTypeTree(
+ Select(Ident(nme.scala), newTypeName("Function" + argtpes.length)),
+ transformTrees(argtpes) ::: List(transform(restpe))))
+
+ // { cases } ==> (x => x.match {cases}), except when already argument to match
+ case Apply(s @ Select(x, nme._match), List(v @ Visitor(cases))) =>
+ copy.Apply(tree,
+ copy.Select(s, transform(x), nme._match),
+ List(copy.Visitor(v, transformCaseDefs(cases))))
+
+ case Apply(Ident(nme._match), List(v @ Visitor(cases))) =>
+ copy.Apply(tree,
+ Select(This(nme.EMPTY.toTypeName), nme._match),
+ List(copy.Visitor(v, transformCaseDefs(cases))))
+
+ case Visitor(cases) =>
+ val x = getvar;
+ atPos(tree.pos)(
+ Function(
+ List(ValDef(PARAM, x, EmptyTypeTree(), EmptyTree)),
+ Apply(Select(Ident(x), nme._match), List(super.transform(tree)))))
+
+ case CaseDef(pat, guard, body) =>
+ copy.CaseDef(tree, transform(patvarTransformer.transform(pat)),
+ transform(guard), transform(body))
+
+ case PatDef(mods, pat, rhs) =>
+ copy.PatDef(tree, mods, transform(patvarTransformer.transform(pat)), transform(rhs))
+
+ case Template(parents, body) =>
+ copy.Template(tree, transformTrees(parents), transformStats(transformTrees(body)))
+
+ case Block(stats, expr) =>
+ copy.Block(tree, transformStats(transformTrees(stats)), transform(expr))
+ }
+ }
+}
diff --git a/sources/scala/tools/nsc/typechecker/Namers.scala b/sources/scala/tools/nsc/typechecker/Namers.scala
new file mode 100755
index 0000000000..1215c5ddfb
--- /dev/null
+++ b/sources/scala/tools/nsc/typechecker/Namers.scala
@@ -0,0 +1,166 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+// $Id$
+package scala.tools.nsc.typechecker;
+
+import scala.tools.util.Position;
+import symtab.Flags._;
+
+/** Methods to create symbols and to enter them into scopes. */
+trait Namers: Analyzer {
+ import global._;
+
+ class NamerPhase(prev: Phase) extends StdPhase(prev) {
+ def name = "namer";
+ val global: Namers.this.global.type = Namers.this.global;
+ def apply(unit: CompilationUnit): unit =
+ new Namer(startContext.make(unit)).enterSyms(unit.body);
+ }
+
+ class Namer(context: Context) {
+
+ val typer = new Typer(context);
+
+ private def doubleDefError(pos: int, sym: Symbol): unit =
+ context.unit.error(pos,
+ sym.name.toString() + " is already defined as " +
+ (if ((sym.rawflags & CASE) != 0) "case class " + sym.name else sym.toString()));
+
+ private def updatePosFlags(sym: Symbol, pos: int, mods: int): Symbol = {
+ val sym1 = if (sym.isExternal && !sym.isPackage) sym
+ else { doubleDefError(pos, sym); sym.cloneSymbol }
+ sym1.pos = pos;
+ val oldflags = sym1.rawflags & (INITIALIZED | LOCKED);
+ val newflags = mods & ~(INITIALIZED | LOCKED);
+ sym1.rawflags = oldflags | newflags;
+ if (sym1.isModule)
+ updatePosFlags(sym1.moduleClass, pos, (mods & MODULE2CLASSFLAGS) | MODULE | FINAL);
+ sym1
+ }
+
+ private def enterInScope(pos: int, sym: Symbol): Symbol = {
+ if (!(sym.isMethod && sym.owner.isClass)) {
+ val prev = context.scope.lookupEntry(sym.name);
+ if (prev != null && prev.owner == context.scope && !prev.sym.isMethod)
+ doubleDefError(pos, prev.sym);
+ }
+ context.scope enter sym;
+ sym
+ }
+
+ private def enterPackageSymbol(pos: int, name: Name): Symbol = {
+ val p: Symbol = context.scope.lookup(name);
+ if (p.isPackage && context.scope == p.owner.info.decls) {
+ p.pos = pos; p.moduleClass.pos = pos; p
+ } else {
+ enterInScope(pos, context.owner.newPackage(pos, name))
+ }
+ }
+
+ private def enterClassSymbol(pos: int, mods: int, name: Name): Symbol = {
+ val c: Symbol = context.scope.lookup(name);
+ if (c.isType && context.scope == c.owner.info.decls) {
+ updatePosFlags(c, pos, mods)
+ } else {
+ enterInScope(pos, context.owner.newClass(pos, name).setFlag(mods))
+ }
+ }
+
+ private def enterModuleSymbol(pos: int, mods: int, name: Name): Symbol = {
+ val m: Symbol = context.scope.lookup(name);
+ if (m.isModule && context.scope == m.owner.info.decls) {
+ updatePosFlags(m, pos, mods)
+ } else {
+ val newm = context.owner.newModule(pos, name);
+ newm.setFlag(mods);
+ newm.moduleClass.setFlag(mods);
+ enterInScope(pos, newm)
+ }
+ }
+
+ private def enterCaseFactorySymbol(pos: int, mods: int, name: Name): Symbol = {
+ val m: Symbol = context.scope.lookup(name);
+ if (m.isModule && context.scope == m.owner.info.decls) {
+ updatePosFlags(m, pos, mods)
+ } else {
+ enterInScope(pos, context.owner.newMethod(pos, name).setFlag(mods))
+ }
+ }
+
+ def enterSyms(trees: List[Tree]): unit =
+ for (val tree <- trees) enterSym(tree);
+
+ def enterSym(tree: Tree): Symbol = {
+
+ def finishWith(tparams: List[AbsTypeDef]) = {
+ val ltype = typer.typeCompleter(tree);
+ def makeParam(tparam: AbsTypeDef): Symbol =
+ tree.symbol.newTypeParameter(tparam.pos, tparam.name);
+ tree.symbol.setInfo(
+ if (tparams.isEmpty) ltype
+ else new LazyPolyType(tparams map makeParam, ltype))
+ }
+ def finish = finishWith(List());
+
+ if (tree.symbol != null) tree.symbol
+ else {
+ val owner = context.owner;
+ tree match {
+ case PackageDef(name, stats) =>
+ tree.symbol = enterPackageSymbol(tree.pos, name);
+ val namer = new Namer(
+ context.make(tree, tree.symbol.moduleClass, tree.symbol.info.decls));
+ stats map namer.enterSym;
+ tree.symbol
+ case ClassDef(mods, name, tparams, _, _) =>
+ if ((mods & (CASE | ABSTRACT)) == CASE) { // enter case factory method.
+ tree.symbol = enterCaseFactorySymbol(
+ tree.pos, mods & ACCESSFLAGS | CASE, name.toTermName);
+ finishWith(tparams);
+ }
+ tree.symbol = enterClassSymbol(tree.pos, mods, name);
+ val constr = tree.symbol.newConstructor(tree.pos)
+ .setFlag(tree.symbol.rawflags & CONSTRFLAGS)
+ .setInfo(typer.typeCompleter(tree));
+ tree.symbol.info.decls enter constr;
+ finishWith(tparams)
+ case ModuleDef(mods, name, _, _) =>
+ tree.symbol = enterModuleSymbol(tree.pos, mods, name);
+ tree.symbol.moduleClass.setInfo(typer.typeCompleter(tree));
+ finish
+ case ValDef(mods, name, tp, rhs) =>
+ tree.symbol = enterInScope(tree.pos, owner.newValue(tree.pos, name).setFlag(mods));
+ finish
+ case DefDef(mods, nme.CONSTRUCTOR, tparams, vparams, tp, rhs) =>
+ if (!(owner.isClass && context.scope == owner.info.decls) ||
+ owner.isModuleClass || owner.isAnonymousClass || owner.isRefinementClass)
+ context.unit.error(tree.pos, "constructor definition not allowed here");
+ tree.symbol = enterInScope(tree.pos, owner.newConstructor(tree.pos))
+ .setFlag(mods | owner.rawflags & CONSTRFLAGS);
+ finishWith(tparams)
+ case DefDef(mods, name, tparams, _, _, _) =>
+ tree.symbol = enterInScope(tree.pos, owner.newMethod(tree.pos, name)).setFlag(mods);
+ finishWith(tparams)
+ case AbsTypeDef(mods, name, _, _) =>
+ tree.symbol = enterInScope(tree.pos, owner.newAbstractType(tree.pos, name));
+ finish
+ case AliasTypeDef(mods, name, tparams, _) =>
+ tree.symbol = enterInScope(tree.pos, owner.newAliasType(tree.pos, name));
+ finishWith(tparams)
+ case Attributed(_, defn) =>
+ enterSym(defn)
+ case DocDef(_, defn) =>
+ enterSym(defn)
+ case Import(expr, selectors) =>
+ tree.symbol = NoSymbol.newImport(tree.pos);
+ finish
+ case _ =>
+ NoSymbol
+ }
+ }
+ }
+ }
+}
+
diff --git a/sources/scala/tools/nsc/typechecker/TypeCheckers.scala b/sources/scala/tools/nsc/typechecker/TypeCheckers.scala
new file mode 100755
index 0000000000..01f0e26f03
--- /dev/null
+++ b/sources/scala/tools/nsc/typechecker/TypeCheckers.scala
@@ -0,0 +1,246 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+// $Id$
+package scala.tools.nsc.typechecker;
+
+import scala.tools.util.Position;
+
+/** Methods to create symbols and to enter them into scopes. */
+trait TypeCheckers: Analyzer {
+ import symtab.Flags._;
+ import global._;
+
+ class TypeCheckPhase(prev: Phase) extends StdPhase(prev) {
+ def name = "typechecker";
+ val global: TypeCheckers.this.global.type = TypeCheckers.this.global;
+ def apply(unit: CompilationUnit): unit =
+ unit.body = new TypeChecker(startContext.make(unit)).transformTrees(unit.body);
+ }
+
+ class TypeChecker(context: Context) extends Transformer {
+
+ import global._;
+ import context.unit;
+
+ def error(pos: int, msg: String) = unit.error(pos, msg);
+
+ def reportTypeError(pos: int, ex: TypeError): unit = {
+ if (settings.debug.value) ex.printStackTrace();
+ ex match {
+ case CyclicReference(sym, info: TypeCompleter) =>
+ info.tree match {
+ case ValDef(_, _, EmptyTypeTree(), _) =>
+ error(pos, "recursive " + sym + " needs type")
+ case DefDef(_, _, _, _, EmptyTypeTree(), _) =>
+ error(pos, "recursive " + sym + " needs result type")
+ case _ =>
+ error(pos, ex.getMessage())
+ }
+ case _ =>
+ error(pos, ex.getMessage())
+ }
+ }
+
+ def reenterValueParams(tree: Tree): unit = tree match {
+ case DefDef(_, _, _, vparamss, _, _) =>
+ for (val vparams <- vparamss; val vparam <- vparams) context.scope enter vparam.symbol
+ }
+
+ def checkStable(tree: Tree): Tree = tree;
+ def checkNoEscape(pos: int, tpe: Type): Type = tpe;
+
+ def transformConstr(tree: Tree): Tree = tree;
+ def transformExpr(tree: Tree): Tree = tree;
+ def transformType(tree: Tree): Tree = tree;
+
+ abstract class ResolveError extends TermSymbol(NoSymbol, Position.NOPOS, nme.EMPTY) {
+ setFlag(IS_ERROR);
+ def msg(qual: Tree, name: Name): String;
+ }
+
+ object NotFoundError extends ResolveError {
+ def msg(qual: Tree, name: Name) = name.decode + " is not a member of " + qual;
+ }
+
+ object NotAccessibleError extends TermSymbol(NoSymbol, Position.NOPOS, nme.EMPTY) {
+ def msg(qual: Tree, name: Name) = name.decode + " is not accessible in " + qual;
+ }
+
+ def transform(tree: Tree, mode: int, pt: Type): Tree = {
+
+ def stabilize(tree: Tree, pre: Type): Tree = tree;
+
+ /** Attribute an identifier consisting of a simple name or an outer reference.
+ * @param tree The tree representing the identifier.
+ * @param name The name of the identifier.
+ * Transformations: (1) Prefix class members with this.
+ * (2) Change imported symbols to selections
+ */
+ def transformIdent(name: Name): Tree = {
+ def ambiguousError(msg: String) =
+ unit.error(tree.pos, "reference to " + name + " is ambiguous;\n" + msg);
+ def importTree(imp: Symbol) =
+ imp.info.asInstanceOf[ImportType].tree;
+ def importedSymbol(imp: Symbol) =
+ treeInfo.importedSymbol(importTree(imp), name);
+ def isExplicitImport(imp: Symbol) =
+ treeInfo.isExplicitImport(importTree(imp), name);
+ //System.out.println("transforming " + name);//DEBUG
+
+ // Compute values of the following 4 variables,
+ // except that `pre' is only set for directly inherited symbols.
+ // Also detect double imports.
+ var sym: Symbol = NoSymbol; // the directly found symbol
+ var sym1: Symbol = NoSymbol; // the imported symbol
+ var pre: Type = null; // if symbols are class members, their prefix type
+ var impSym = NoSymbol; // if symbols are imported, the import symbol from
+ // which they were imported.
+ var cx: Context = context;
+ while (sym == NoSymbol && cx != NoContext) {
+ val symEntry = cx.scope.lookupEntry(name);
+ var impEntry = cx.scope.lookupEntry(nme.IMPORT);
+ while (impEntry != null) {
+ var impSym1 = impEntry.sym;
+ impEntry = cx.scope.lookupNextEntry(impEntry);
+ sym1 = importedSymbol(impSym1);
+ if (sym1 != NoSymbol) { // check whether shadowed by sym, reset to NoSymbol if yes.
+ if (symEntry != null) {
+ var impEntry1 = symEntry.owner.lookupEntry(nme.IMPORT);
+ while (impEntry1 != null && sym1 != NoSymbol) {
+ if (impEntry1.sym == impSym1) sym1 = NoSymbol;
+ // directly found takes precedence
+ impEntry1 = symEntry.owner.lookupNextEntry(impEntry1);
+ }
+ }
+ }
+ if (sym1 != NoSymbol) { // check for ambiguous imports
+ var impSym2: Symbol = NoSymbol; // alternative import symbol
+ var sym2: Symbol = NoSymbol; // alternative imported symbol
+ def ambiguousImportError = ambiguousError(
+ "it is imported twice in the same scope by\n" +
+ importTree(impSym1) + "\nand " + importTree(impSym2));
+ while (impEntry != null) {
+ impSym2 = impEntry.sym;
+ impEntry = cx.scope.lookupNextEntry(impEntry);
+ if (impSym2.owner == impSym1.owner) {
+ sym2 = importedSymbol(impSym2);
+ if (sym2 != NoSymbol) {
+ if (isExplicitImport(impSym2)) {
+ if (isExplicitImport(impSym1)) ambiguousImportError;
+ sym1 = sym2;
+ impSym1 = impSym2;
+ }
+ if (isExplicitImport(impSym1)) sym2 = NoSymbol;
+ }
+ }
+ }
+ if (sym2 != NoSymbol) ambiguousImportError
+ }
+ }
+ if (symEntry != null) sym = symEntry.sym;
+ cx = cx.enclClass;
+ if (cx != NoContext) {
+ pre = cx.owner.thisType;
+ if (sym == NoSymbol) sym = pre.member(name);
+ cx = cx.outer;
+ }
+ }
+
+ // detect ambiguous definition/import,
+ // update `sym' to be the final resolved symbol,
+ // update `pre' to be `sym's prefix type in case it is a class member,
+ // and compute value of:
+ var qual: Tree = EmptyTree; // the qualififier tree if transformed tree is a select
+ if (sym != NoSymbol) {
+ if (sym1 != NoSymbol)
+ ambiguousError(
+ "it is both defined in " + sym.owner +
+ " and imported subsequently by \n" + importTree(impSym));
+ else if (sym.owner.isClass && !sym.owner.isPackageClass)
+ qual = gen.This(tree.pos, pre.symbol);
+ } else {
+ if (sym1 != NoSymbol) {
+ sym = sym1;
+ qual = importTree(impSym).expr.duplicate;
+ pre = qual.tpe;
+ } else {
+ error(tree.pos, "not found: " + name.decode);
+ sym = context.owner.newErrorSymbol(name);
+ }
+ }
+ val symtype = pre.memberType(sym);
+ if (symtype == NoType) error(tree.pos, "not found: " + name.decode);
+ if (qual == EmptyTree) stabilize(tree.setSymbol(sym).setType(symtype), pre)
+ else transform(Select(qual, name) setPos tree.pos, mode, pt)
+ }
+ tree//for now
+ }
+ }
+}
+
+/*
+ def resolveSymbol(pos: int, qual: Tree, name: Name): Symbol = {
+ def chekFeasible(sym: Symbol): Symbol = {
+
+
+ var best: Symbol = notFoundError;
+ var multiple = false;
+ var alts = pre.lookupAll(name);
+ while (alts.hasNext) {
+ val alt = checkFeasible(alts.next);
+ if (!improves(best, alt)) {
+ multiple = !best.isError;
+ best = alt;
+ }
+ }
+ if (multiple) {
+ alts = pre.lookupAll(name);
+ while (alts.hasNext) {
+ val alt = checkFeasible(alts.next);
+ if (!improves(best, alt)) {
+ error(pos,
+ "ambiguous reference to overloaded definition,\n" +
+ "both " + sym1 + ": " + pre.memberType(sym1) + "\n" +
+ "and " + sym2 + ": " + pre.memberTyoe(sym2) + "\nmatch" +
+ context.resolveContext);
+ }
+ }
+ }
+ best match {
+ case err: ResolveError => error(pos, err.msg);
+ case _ => best
+ }
+ }
+
+ /** Attribute a selection where `tree' is `qual.name'.
+ * `qual' is already attributed.
+ */
+ def transformSelect(tree: Tree, qual0: Tree, name: Name): Tree = {
+ val qual = qual0;
+ var uninst: List[Symbol] = List();
+ qual.tpe match {
+ case PolyType(tparams, restpe) =>
+ qual = mkTypeApply(qual, tparams, restype, tparams map (.tpe));
+ uninst = tparams;
+ case _ =>
+ }
+ val sym = resolveSymbol(qual, name);
+ // if (sym == NoSymbol) try to insert view.
+ if (!uninst.isEmpty) {
+ def polymorphize(tp: Type): Type = tp match {
+ case PolyType(tparams, restpe) => PolyType(tparams, polymorphize(restpe))
+ case _ => PolyType(uninst, tp)
+ }
+ symtype = polymorphize(symtype);
+ }
+ //System.out.println(qual.getType() + ".member: " + sym + ":" + symtype);//DEBUG
+ val tree1: Tree = tree match {
+ case Select(_, _) => copy.Select(tree, sym, qua);
+ case SelectFromType(_, _) => copy.SelectFromType(tree, sym, qual)
+ }
+ mkStable(tree1.setType(symtype), qualtype)
+ }
+
+*/
diff --git a/sources/scala/tools/nsc/typechecker/Typers.scala b/sources/scala/tools/nsc/typechecker/Typers.scala
new file mode 100755
index 0000000000..e14628ab90
--- /dev/null
+++ b/sources/scala/tools/nsc/typechecker/Typers.scala
@@ -0,0 +1,217 @@
+/* NSC -- new scala compiler
+ * Copyright 2005 LAMP/EPFL
+ * @author Martin Odersky
+ */
+// $Id$
+package scala.tools.nsc.typechecker;
+
+import scala.tools.util.Position;
+
+/** Methods to create symbols and to enter them into scopes. */
+trait Typers: Analyzer {
+ import symtab.Flags;
+ import symtab.Flags._;
+ import global._;
+
+ abstract class TypeCompleter(val tree: Tree) extends LazyType;
+
+ class Typer(context: Context) {
+ import context.unit;
+
+ val typechecker = new TypeChecker(context);
+
+ def typeCompleter(tree: Tree) = new TypeCompleter(tree) {
+ override def complete(sym: Symbol): unit = {
+ if (settings.debug.value) log("defining " + sym);
+ sym.setInfo(typeSig(tree));
+ if (settings.debug.value) log("defined " + sym);
+ validate(sym);
+ }
+ }
+
+ def selfTypeCompleter(tree: Tree) = new TypeCompleter(tree) {
+ override def complete(sym: Symbol): unit =
+ sym.setInfo(typechecker.transformType(tree).tpe)
+ }
+
+ private def constrType(constr: Tree): Type = constr match {
+ case Apply(fn, args) =>
+ val ctp = constrType(fn);
+ if (ctp.typeParams.isEmpty) ctp else typechecker.transformConstr(constr).tpe
+ case _ => typechecker.transformType(constr).tpe
+ }
+
+ private def deconstIfNotFinal(sym: Symbol, tpe: Type): Type =
+ if (sym.isVariable || !sym.hasFlag(FINAL)) tpe.deconst else tpe;
+
+ private def enterTypeParams(owner: Symbol, tparams: List[AbsTypeDef]): List[Symbol] = {
+ List.map2(owner.typeParams, tparams)
+ { (tpsym, tptree) => tptree.symbol = tpsym; context.scope enter tpsym; tpsym }
+ }
+
+ private def enterValueParams(owner: Symbol,
+ vparamss: List[List[ValDef]]): List[List[Symbol]] = {
+ def enterValueParam(param: ValDef): Symbol = {
+ param.symbol = owner.newValueParameter(param.pos, param.name)
+ .setInfo(typeCompleter(param));
+ context.scope enter param.symbol;
+ param.symbol
+ }
+ vparamss.map(.map(enterValueParam))
+ }
+
+ /** A creator for polytypes. If tparams is empty, simply returns result type */
+ private def makePolyType(tparams: List[Symbol], tpe: Type): Type =
+ if (tparams.isEmpty) tpe
+ else
+ PolyType(tparams, tpe match {
+ case PolyType(List(), tpe1) => tpe1
+ case _ => tpe
+ });
+
+ private def templateSig(clazz: Symbol, templ: Template): Type = {
+ // determine parent types
+ val parents = templ.parents map constrType;
+ if (!parents.isEmpty && parents.head.symbol.hasFlag(INTERFACE)
+ && parents.head.symbol.hasFlag(JAVA))
+ unit.error(templ.parents.head.pos, "cannot extend a Java interface");
+
+ // enter all members
+ val decls = new Scope();
+ new Namer(context.make(templ, clazz, decls)).enterSyms(templ.body);
+ ClassInfoType(parents, decls, clazz)
+ }
+
+ private def classSig(clazz: Symbol,
+ tparams: List[AbsTypeDef],
+ tpt: Tree, impl: Template): Type = {
+ val tparamSyms = enterTypeParams(clazz, tparams);
+ if (!tpt.isEmpty) clazz.typeOfThis = selfTypeCompleter(tpt);
+ val constrTree = treeInfo.firstConstructor(impl.body);
+ val constr = new Namer(context).enterSym(constrTree).initialize;
+ typechecker.reenterValueParams(constrTree);
+ makePolyType(tparamSyms, templateSig(clazz, impl))
+ }
+
+ private def methodSig(meth: Symbol,
+ tparams: List[AbsTypeDef], vparamss: List[List[ValDef]],
+ tpt: Tree, rhs: Tree): Type = {
+ val tparamSyms = enterTypeParams(meth, tparams);
+ val vparamSymss = enterValueParams(meth, vparamss);
+ val restype =
+ typechecker.checkNoEscape(tpt.pos,
+ deconstIfNotFinal(meth,
+ if (meth.name == nme.CONSTRUCTOR) context.enclClass.owner.tpe
+ else if (tpt.tpe != null) tpt.tpe
+ else if (tpt.isEmpty) { tpt.tpe = typechecker.transformExpr(rhs).tpe; tpt.tpe }
+ else typechecker.transformType(tpt).tpe));
+ def mkMethodType(vparams: List[Symbol], restpe: Type) =
+ MethodType(vparams map (.tpe), restpe);
+ makePolyType(
+ tparamSyms,
+ if (vparamSymss.isEmpty) PolyType(List(), restype)
+ else (vparamSymss :\ restype)(mkMethodType))
+ }
+
+ private def aliasTypeSig(tpsym: Symbol, tparams: List[AbsTypeDef], rhs: Tree): Type =
+ makePolyType(enterTypeParams(tpsym, tparams), typechecker.transformType(rhs).tpe);
+
+ private def typeSig(tree: Tree): Type =
+ try {
+ val sym: Symbol = tree.symbol;
+ tree match {
+ case ClassDef(_, _, tparams, tpt, impl) =>
+ new Typer(context.makeNewScope(tree, sym)).classSig(sym, tparams, tpt, impl)
+
+ case ModuleDef(_, _, tpt, impl) =>
+ val clazz = sym.moduleClass;
+ clazz.setInfo(new Typer(context.make(tree, clazz)).templateSig(clazz, impl));
+ if (tpt.isEmpty) { tpt.tpe = clazz.tpe; tpt.tpe }
+ else typechecker.transformType(tpt).tpe
+
+ case DefDef(_, _, tparams, vparamss, tpt, rhs) =>
+ new Typer(context.makeNewScope(tree, sym))
+ .methodSig(sym, tparams, vparamss, tpt, rhs)
+
+ case ValDef(_, _, tpt, rhs) =>
+ deconstIfNotFinal(sym,
+ if (tpt.tpe != null) tpt.tpe
+ else if (tpt.isEmpty)
+ if (rhs.isEmpty) {
+ unit.error(tpt.pos, "missing parameter type");
+ ErrorType
+ } else {
+ tpt.tpe = new TypeChecker(context.make(tree, sym))
+ .transformExpr(rhs).tpe;
+ tpt.tpe
+ }
+ else typechecker.transformType(tpt).tpe)
+
+ case AliasTypeDef(_, _, tparams, rhs) =>
+ new Typer(context.makeNewScope(tree, sym)).aliasTypeSig(sym, tparams, rhs)
+
+ case AbsTypeDef(_, _, lo, hi) =>
+ TypeBounds(typechecker.transformType(lo).tpe, typechecker.transformType(hi).tpe);
+
+ case imptree @ Import(expr, selectors) =>
+ val expr1 = typechecker.transformExpr(expr);
+ val base = expr1.tpe;
+ typechecker.checkStable(expr1);
+ for (val Pair(from, to) <- selectors) {
+ if (from != nme.WILDCARD && base != ErrorType &&
+ base.member(from) == NoSymbol && base.member(from.toTypeName) == NoSymbol)
+ unit.error(tree.pos, from.decode + " is not a member of " + expr);
+ if (to != null && to != nme.WILDCARD && (selectors exists (p => p._2 == to)))
+ unit.error(tree.pos, to.decode + " appears twice as a target of a renaming");
+ }
+ ImportType(imptree)
+ }
+ } catch {
+ case ex: TypeError =>
+ typechecker.reportTypeError(tree.pos, ex);
+ ErrorType
+ }
+
+ /** Check that symbol's definition is well-formed. This means:
+ * - no conflicting modifiers
+ * - `abstract' modifier only for classes
+ * - `override' modifier never for classes
+ * - `def' modifier never for parameters of case classes
+ * - declarations only in traits or abstract classes
+ * - todo: in desugarize: replace ABSTRACT OVERRIDE with ABSOVERRIDE
+ */
+ def validate(sym: Symbol): unit = {
+ def checkNoConflict(flag1: int, flag2: int): unit =
+ if (sym.hasFlag(flag1) && sym.hasFlag(flag2))
+ unit.error(sym.pos,
+ if (flag1 == DEFERRED)
+ "abstract member may not have " + Flags.flagsToString(flag2) + " modifier";
+ else
+ "illegal combination of modifiers: " +
+ Flags.flagsToString(flag1) + " and " + Flags.flagsToString(flag2));
+ if (sym.hasFlag(ABSTRACT) && !sym.isClass)
+ unit.error(sym.pos, "`abstract' modifier can be used only for classes; " +
+ "\nit should be omitted for abstract members");
+ if (sym.hasFlag(OVERRIDE | ABSOVERRIDE) && sym.isClass)
+ unit.error(sym.pos, "`override' modifier not allowed for classes");
+ if (sym.info.symbol == definitions.FunctionClass(0) &&
+ sym.isValueParameter && sym.owner.isClass && sym.owner.hasFlag(CASE))
+ unit.error(sym.pos, "pass-by-name arguments not allowed for case class parameters");
+ if ((sym.flags & DEFERRED) != 0) {
+ if (!sym.owner.isClass || sym.owner.isModuleClass || sym.owner.isAnonymousClass) {
+ unit.error(sym.pos,
+ "only classes can have declared but undefined members" +
+ (if (!sym.isVariable) ""
+ else "\n(Note that variables need to be initialized to be defined)"));
+ sym.resetFlag(DEFERRED);
+ }
+ }
+ checkNoConflict(DEFERRED, PRIVATE);
+ checkNoConflict(FINAL, SEALED);
+ if (!sym.hasFlag(MODULE)) checkNoConflict(FINAL, PRIVATE);
+ checkNoConflict(PRIVATE, PROTECTED);
+ checkNoConflict(PRIVATE, OVERRIDE);
+ checkNoConflict(DEFERRED, FINAL);
+ }
+ }
+}