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 | |
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')
-rw-r--r-- | src/dotty/tools/dotc/core/Contexts.scala | 10 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Denotations.scala | 17 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/SymDenotations.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 6 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/ImportInfo.scala | 8 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Mode.scala (renamed from src/dotty/tools/dotc/typer/Modes.scala) | 2 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Namer.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/Typer.scala | 193 |
8 files changed, 228 insertions, 16 deletions
diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 588c18c4c..874f28bce 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -138,10 +138,10 @@ object Contexts { protected def typer_=(typer: Typer) = _typer = typer def typer: Typer = _typer - /** The currently visible imports */ - private[this] var _imports: List[ImportInfo] = _ - protected def imports_=(imports: List[ImportInfo]) = _imports = imports - def imports: List[ImportInfo] = _imports + /** The currently active import info */ + private[this] var _importInfo: ImportInfo = _ + protected def importInfo_=(importInfo: ImportInfo) = _importInfo = importInfo + def importInfo: ImportInfo = _importInfo /** The current reporter */ private[this] var _reporter: Reporter = _ @@ -263,7 +263,7 @@ object Contexts { def withScope(scope: Scope): this.type = { this.scope = scope; this } def withNewScope: this.type = { this.scope = newScope; this } def withTyper(typer: Typer): this.type = { this.typer = typer; this.scope = typer.scope; this } - def withImport(importInfo: ImportInfo): this.type = { this.imports = importInfo :: imports; this } + def withImportInfo(importInfo: ImportInfo): this.type = { this.importInfo = importInfo; this } def withReporter(reporter: Reporter): this.type = { this.reporter = reporter; this } def withDiagnostics(diagnostics: Option[StringBuilder]): this.type = { this.diagnostics = diagnostics; this } def withMoreProperties(moreProperties: Map[String, Any]): this.type = { this.moreProperties = moreProperties; this } diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index b585fb0ef..b69997836 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -161,6 +161,11 @@ object Denotations { /** Does this denotation have an alternative that satisfies the predicate `p`? */ def hasAltWith(p: Symbol => Boolean): Boolean + /** The denotation made up from the alternatives of this denotation that + * are accessible from prefix `pre`, or NoDenotation if no accessible alternative exists. + */ + def accessibleFrom(pre: Type, superAccess: Boolean = false)(implicit ctx: Context): Denotation + /** Find member of this denotation with given name and * produce a denotation that contains the type of the member * as seen from given prefix `pre`. Exclude all members that have @@ -244,6 +249,8 @@ object Denotations { if (sym2.isClass || sym2.isAliasType) denot2 else { // if sym1, sym2 exist, they are abstract types or term symbols + // we prefer concrete because they allow more things when seen as types + // e.g. new C. Question: Should we take accessibility into account? val info1 = denot1.info val info2 = denot2.info val sym1Eligible = sym1.isAsConcrete(sym2) @@ -327,6 +334,13 @@ object Denotations { } def hasAltWith(p: Symbol => Boolean): Boolean = denot1.hasAltWith(p) || denot2.hasAltWith(p) + def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = { + val d1 = denot1 accessibleFrom (pre, superAccess) + val d2 = denot2 accessibleFrom (pre, superAccess) + if (!d1.exists) d2 + else if (!d2.exists) d1 + else derivedMultiDenotation(d1, d2) + } def derivedMultiDenotation(d1: Denotation, d2: Denotation) = if ((d1 eq denot1) && (d2 eq denot2)) this else MultiDenotation(d1, d2) override def toString = alternatives.mkString(" <and> ") @@ -365,6 +379,9 @@ object Denotations { def hasAltWith(p: Symbol => Boolean): Boolean = p(symbol) + def accessibleFrom(pre: Type, superAccess: Boolean)(implicit ctx: Context): Denotation = + if (symbol isAccessibleFrom (pre, superAccess)) this else NoDenotation + def atSignature(sig: Signature)(implicit ctx: Context): SingleDenotation = if (sig == signature) this else NoDenotation diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 64119944c..3026562a3 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -208,6 +208,10 @@ object SymDenotations { final def markAbsent(): Unit = _info = NoType + /** Is symbol known to not exist? */ + final def isAbsent: Boolean = + _info == NoType + /** Is this symbol the root class or its companion object? */ final def isRoot: Boolean = name.toTermName == nme.ROOT diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index c4484c892..1a1789eeb 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -117,8 +117,10 @@ object Types { classSymbol is ModuleClass /** Is this type produced as a repair for an error? */ - final def isError(implicit ctx: Context): Boolean = - (typeSymbol is Erroneous) || (termSymbol is Erroneous) + final def isError(implicit ctx: Context): Boolean = this match { + case ErrorType => true + case _ => (typeSymbol is Erroneous) || (termSymbol is Erroneous) + } /** Is some part of this type produced as a repair for an error? */ final def isErroneous(implicit ctx: Context): Boolean = existsPart(_.isError) diff --git a/src/dotty/tools/dotc/typer/ImportInfo.scala b/src/dotty/tools/dotc/typer/ImportInfo.scala index b7c8f47e9..3f668ecb6 100644 --- a/src/dotty/tools/dotc/typer/ImportInfo.scala +++ b/src/dotty/tools/dotc/typer/ImportInfo.scala @@ -4,8 +4,12 @@ package typer import ast.untpd._ import core._ -import Symbols._ +import Symbols._, Names._, Denotations._, Types._, Contexts._ case class ImportInfo(sym: Symbol, selectors: List[Tree], scopeNestingLevel: Int) { - + /** The (TermRef) type of the qualifier of the import clause */ + def site(implicit ctx: Context): Type = { + val ImportType(expr) = sym.info + expr.tpe + } }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Modes.scala b/src/dotty/tools/dotc/typer/Mode.scala index edcbc79b4..11f2ac458 100644 --- a/src/dotty/tools/dotc/typer/Modes.scala +++ b/src/dotty/tools/dotc/typer/Mode.scala @@ -14,5 +14,7 @@ object Mode { val Pattern = Mode(1 << 2) val Type = Mode(1 << 3) + val Fun = Mode(1 << 4) + }
\ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 6ed45e4df..16d643552 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -131,7 +131,7 @@ class Namer { typer: Typer => /** A new context that summarizes an import statement */ def importContext(sym: Symbol, selectors: List[Tree])(implicit ctx: Context) = - ctx.fresh.withImport(ImportInfo(sym, selectors, ctx.scopeNestingLevel)) + ctx.fresh.withImportInfo(ImportInfo(sym, selectors, ctx.scopeNestingLevel)) /** A new context for the interior of a class */ def inClassContext(cls: ClassSymbol, selfName: TermName)(implicit ctx: Context): Context = { @@ -214,7 +214,7 @@ class Namer { typer: Typer => } } - /** Typecheck tree during completion, and remember result in yypedtree map */ + /** Typecheck tree during completion, and remember result in typedtree map */ def typedAhead(tree: Tree, mode: Mode = Mode.Expr, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree = typedTree.getOrElseUpdate(tree, typer.typedExpanded(tree, mode, pt)) 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 { |