diff options
author | Martin Odersky <odersky@gmail.com> | 2005-02-16 13:15:42 +0000 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2005-02-16 13:15:42 +0000 |
commit | 28c2394d012195e38308bcd6fd5049bcde635b4c (patch) | |
tree | efc1ab19ecba057cf626af0d86e0aa85b985b941 /sources/scala/tools/nsc/typechecker | |
parent | 431abf42bda2c97ff062594a8f1d15fd7776350c (diff) | |
download | scala-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.scala | 5 | ||||
-rwxr-xr-x | sources/scala/tools/nsc/typechecker/Contexts.scala | 98 | ||||
-rwxr-xr-x | sources/scala/tools/nsc/typechecker/DeSugarizePhase.scala | 219 | ||||
-rwxr-xr-x | sources/scala/tools/nsc/typechecker/Namers.scala | 166 | ||||
-rwxr-xr-x | sources/scala/tools/nsc/typechecker/TypeCheckers.scala | 246 | ||||
-rwxr-xr-x | sources/scala/tools/nsc/typechecker/Typers.scala | 217 |
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); + } + } +} |