Skip to content

Commit

Permalink
Added typedIdent method.
Browse files Browse the repository at this point in the history
Also some refactorings that were caused by adding this method.
  • Loading branch information
odersky committed Jun 18, 2013
1 parent 3c79365 commit f8a42a0
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 16 deletions.
10 changes: 5 additions & 5 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = _
Expand Down Expand Up @@ -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 }
Expand Down
17 changes: 17 additions & 0 deletions src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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> ")
Expand Down Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 4 additions & 2 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 6 additions & 2 deletions src/dotty/tools/dotc/typer/ImportInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ object Mode {
val Pattern = Mode(1 << 2)
val Type = Mode(1 << 3)

val Fun = Mode(1 << 4)


}
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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))

Expand Down
193 changes: 188 additions & 5 deletions src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 =>
Expand All @@ -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 {
Expand Down
21 changes: 21 additions & 0 deletions test/x/names.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package test

object Foo {
def foo = 1
}

object Bar {
def foo = 2
}

object Test {

import Foo.foo

object Inner {

import Bar._
//!! println(foo)
}

}

0 comments on commit f8a42a0

Please sign in to comment.