diff options
author | Martin Odersky <odersky@gmail.com> | 2013-06-18 19:38:09 +0200 |
---|---|---|
committer | Martin Odersky <odersky@gmail.com> | 2013-06-18 19:38:09 +0200 |
commit | f8a42a0584d855a0548c20c7434ed83a59647ed9 (patch) | |
tree | 9bdb1b8367d5a692cbf26003e6b10b6122a8d03b /src/dotty/tools/dotc/typer/Typer.scala | |
parent | 3c7936515a9aaf383b453fe5844598fd53a2e551 (diff) | |
download | dotty-f8a42a0584d855a0548c20c7434ed83a59647ed9.tar.gz dotty-f8a42a0584d855a0548c20c7434ed83a59647ed9.tar.bz2 dotty-f8a42a0584d855a0548c20c7434ed83a59647ed9.zip |
Added typedIdent method.
Also some refactorings that were caused by adding this method.
Diffstat (limited to 'src/dotty/tools/dotc/typer/Typer.scala')
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 193 |
1 files changed, 188 insertions, 5 deletions
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 0d78e070d..47ffa40ce 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -4,23 +4,203 @@ package typer import core._ import ast._ -import Trees._, Constants._, StdNames._, Scopes._ +import Trees._, Constants._, StdNames._, Scopes._, Denotations._ import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ import util.Positions._ import util.SourcePosition import collection.mutable import annotation.tailrec import language.implicitConversions -import desugar.Mode trait TyperContextOps { ctx: Context => } +object Typer { + + object BindingPrec { + val definition = 4 + val namedImport = 3 + val wildImport = 2 + val packageClause = 1 + val nothingBound = 0 + def isImportPrec(prec: Int) = prec == namedImport || prec == wildImport + } +} class Typer extends Namer { import tpd._ + import Typer.BindingPrec + + def typedSelection(site: Type, name: Name, pos: Position)(implicit ctx: Context): Type = { + val ref = site.member(name) + if (ref.exists) NamedType(site, name).withDenot(ref) + else { + ctx.error(s"$name is not a member of ${site.show}", pos) + ErrorType + } + } + + def checkAccessible(tpe: Type, pos: Position) = ??? + + /** Attribute an identifier consisting of a simple name or an outer reference. + * + * @param tree The tree representing the identifier. + * Transformations: (1) Prefix class members with this. + * (2) Change imported symbols to selections + * + */ + def typedIdent(tree: untpd.Ident, mode: Mode)(implicit ctx: Context): Tree = { + val name = tree.name + + /** A symbol qualifies if it exists and is not stale. Stale symbols + * are made to disappear here. In addition, + * if we are in a constructor of a pattern, we ignore all definitions + * which are methods (note: if we don't do that + * case x :: xs in class List would return the :: method) + * unless they are stable or are accessors (the latter exception is for better error messages) + */ + def qualifies(sym: Symbol): Boolean = !( + sym.isAbsent + || (mode is Mode.Pattern | Mode.Fun) && (sym is (Method, butNot = Accessor)) + ) - def typedModifiers(mods: untpd.Modifiers): Modifiers = ??? + /** Find the denotation of enclosing `name` in given context `ctx`. + * @param previous A denotation that was found in a more deeply nested scope, + * or else `NoDenotation` if nothing was found yet. + * @param prevPrec The binding precedence of the previous denotation, + * or else `nothingBound` if nothing was found yet. + * @param prevCtx The context of the previous denotation, + * or else `NoContext` if nothing was found yet. + */ + def findRef(previous: Type, prevPrec: Int, prevCtx: Context)(implicit ctx: Context): Type = { + import BindingPrec._ + + /** A string which explains how something was bound; Depending on `prec` this is either + * imported by <tree> + * or defined in <symbol> + */ + def bindingString(prec: Int, whereFound: Context, qualifier: String = "") = + if (prec == wildImport || prec == namedImport) s"imported$qualifier by ${whereFound.tree.show}" + else s"defined$qualifier in ${whereFound.owner.show}" + + /** Check that any previously found result from an inner context + * does properly shadow the new one from an outer context. + */ + def checkNewOrShadowed(found: Type, newPrec: Int): Type = + if (!previous.exists || (previous == found)) found + else { + if (!previous.isError && !found.isError) + ctx.error( + s"""reference to $name is ambiguous; + |it is both ${bindingString(newPrec, ctx, "")} + |and ${bindingString(prevPrec, prevCtx, " subsequently")}""".stripMargin, + tree.pos) + previous + } + + /** The type representing a named import with enclosing name when imported + * from given `site` and `selectors`. + */ + def namedImportRef(site: Type, selectors: List[untpd.Tree]): Type = { + def checkUnambiguous(found: Type) = { + val other = namedImportRef(site, selectors.tail) + if (other.exists && (found != other)) + ctx.error(s"""reference to $name is ambiguous; it is imported twice in + |${ctx.tree.show}""".stripMargin, + tree.pos) + found + } + selectors match { + case Trees.Pair(Trees.Ident(from), Trees.Ident(`name`)) :: rest => + checkUnambiguous(typedSelection(site, name, tree.pos)) + case Trees.Ident(`name`) :: rest => + checkUnambiguous(typedSelection(site, name, tree.pos)) + case _ :: rest => + namedImportRef(site, rest) + case nil => + NoType + } + } + + /** The type representing a wildcard import with enclosing name when imported + * from given `site` and `selectors`. + */ + def wildImportRef(site: Type, selectors: List[untpd.Tree]): Type = { + def wildPermitted(selectors: List[untpd.Tree]): Boolean = selectors match { + case Trees.Pair(Trees.Ident(`name`), Trees.Ident(nme.WILDCARD)) :: _ => false + case Trees.Ident(nme.WILDCARD) :: _ => true + case _ :: rest => wildPermitted(rest) + case nil => false + } + if (wildPermitted(selectors)) { + val denot = site.member(name) + if (denot.exists) return NamedType(site, name).withDenot(denot) + } + NoType + } + + /** Is (some alternative of) the given predenotation `denot` + * defined in current compilation unit? + */ + def isDefinedInCurrentUnit(denot: PreDenotation): Boolean = denot match { + case DenotUnion(d1, d2) => isDefinedInCurrentUnit(d1) || isDefinedInCurrentUnit(d2) + case denot: SingleDenotation => denot.symbol.sourceFile == ctx.source + } + + // begin findRef + if (ctx eq NoContext) previous + else { + val outer = ctx.outer + val curScope = ctx.scope + val curOwner = ctx.owner + if (curScope ne outer.scope) { + val defDenots = + if (curOwner.isClass && (curOwner ne outer.owner)) curOwner.asClass.membersNamed(name) + else curScope.denotsNamed(name) + if (defDenots.exists) { + val found = NamedType(curOwner.thisType, name).withDenot(defDenots.toDenot) + if (!(curOwner is Package) || isDefinedInCurrentUnit(defDenots)) + return checkNewOrShadowed(found, definition) // no need to go further out, we found highest prec entry + else if (prevPrec < packageClause) + return findRef(found, packageClause, ctx)(outer) + } + } + val curImport = ctx.importInfo + if (prevPrec < namedImport && (curImport ne outer.importInfo)) { + val namedImp = namedImportRef(curImport.site, curImport.selectors) + if (namedImp.exists) + return findRef(checkNewOrShadowed(namedImp, namedImport), namedImport, ctx)(outer) + if (prevPrec < wildImport) { + val wildImp = wildImportRef(curImport.site, curImport.selectors) + if (wildImp.exists) + return findRef(checkNewOrShadowed(wildImp, wildImport), wildImport, ctx)(outer) + } + } + findRef(previous, prevPrec, prevCtx)(outer) + } + } + + // begin typedIdent + val startingContext = // ignore current variable scope in patterns to enforce linearity + if (mode is Mode.Pattern) ctx.outer else ctx + + var ownType = findRef(NoType, BindingPrec.nothingBound, NoContext) + if (!ownType.exists) { + ctx.error(s"not found: $name", tree.pos) + ownType = ErrorType + } + checkAccessible(ownType, tree.pos) + tree.withType(ownType) + } + + def typedModifiers(mods: untpd.Modifiers)(implicit ctx: Context): Modifiers = { + val annotations1 = mods.annotations mapconserve typedAnnotation + if (annotations1 eq mods.annotations) mods.asInstanceOf[Modifiers] + else Trees.Modifiers(mods.flags, mods.privateWithin, annotations1) + } + + def typedAnnotation(annot: untpd.Tree)(implicit ctx: Context): Tree = + typed(annot, Mode.Expr, defn.AnnotationClass.typeConstructor) def typedValDef(vdef: untpd.ValDef, sym: Symbol)(implicit ctx: Context) = { val Trees.ValDef(mods, name, tpt, rhs) = vdef @@ -77,7 +257,7 @@ class Typer extends Namer { imp.withType(sym.symRef).derivedImport(expr1, imp.selectors) } - def typedExpanded(tree: untpd.Tree, mode: Mode.Value = Mode.Expr, pt: Type = WildcardType)(implicit ctx: Context): Tree = { + def typedExpanded(tree: untpd.Tree, mode: Mode = Mode.Expr, pt: Type = WildcardType)(implicit ctx: Context): Tree = { val sym = symOfTree.remove(tree).getOrElse(NoSymbol) sym.ensureCompleted() def localContext = ctx.fresh.withOwner(sym) @@ -106,7 +286,7 @@ class Typer extends Namer { } } - def typed(tree: untpd.Tree, mode: Mode.Value = Mode.Expr, pt: Type = WildcardType)(implicit ctx: Context): Tree = { + def typed(tree: untpd.Tree, mode: Mode = Mode.Expr, pt: Type = WildcardType)(implicit ctx: Context): Tree = { val xtree = tree match { case tree: untpd.MemberDef => @@ -119,6 +299,9 @@ class Typer extends Namer { typedExpanded(xtree, mode, pt) } + def typedTrees(trees: List[untpd.Tree], mode: Mode = Mode.Expr)(implicit ctx: Context): List[Tree] = + trees mapconserve (typed(_, mode)) + def typedStats(stats: List[untpd.Tree], exprOwner: Symbol)(implicit ctx: Context): List[tpd.Tree] = { val buf = new mutable.ListBuffer[Tree] @tailrec def traverse(stats: List[untpd.Tree])(implicit ctx: Context): List[Tree] = stats match { |