package dotty.tools
package dotc
package typer
import core._
import ast._
import Trees._, Constants._, StdNames._, Scopes._, Denotations._
import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._
import ast.desugar, ast.desugar._
import Typer.AnySelectionProto
import util.Positions._
import util.SourcePosition
import collection.mutable
import annotation.tailrec
import ErrorReporting._
import language.implicitConversions
trait NamerContextOps { this: Context =>
def enter(sym: Symbol): Symbol = {
ctx.owner match {
case cls: ClassSymbol => cls.enter(sym)
case _ => this.scope.asInstanceOf[MutableScope].enter(sym)
}
sym
}
def denotsNamed(name: Name): PreDenotation =
if (owner.isClass) owner.asClass.membersNamed(name)
else scope.denotsNamed(name)
def effectiveScope =
if (owner != null && owner.isClass) owner.asClass.decls
else scope
}
/** This class attaches creates symbols from definitions and imports and gives them
* lazy types.
*
* Timeline:
*
* During enter, trees are expanded as necessary, populating the expandedTree map.
* Symbols are created, and the symOfTree map is set up.
*
* Symbol completion causes some trees to be already typechecked and typedTree
* entries are created to associate the typed trees with the untyped expanded originals.
*
* During typer, original trees are first expanded using expandedTree. For each
* expanded member definition or import we extract and remove the corresponding symbol
* from the symOfTree map and complete it. We then consult the typedTree map to see
* whether a typed tree exists already. If yes, the typed tree is returned as result.
* Otherwise, we proceed with regular type checking.
*
* The scheme is designed to allow sharing of nodes, as long as each duplicate appears
* in a different method.
*/
class Namer { typer: Typer =>
import untpd._
/** A partial map from unexpanded member defs to their expansions.
* Populated during enterSyms, emptied during typer.
*/
lazy val expandedTree = new mutable.HashMap[MemberDef, Tree]
/** A map from expanded MemberDef or Import trees to their symbols.
* Populated during enterSyms, emptied at the point a typed tree
* with the same symbol is created (this can be when the symbol is completed
* or at the latest when the tree is typechecked.
*/
lazy val symOfTree = new mutable.HashMap[Tree, Symbol]
/** A map from expanded trees their typed versions.
* Populated when trees are typechecked during completion (using method typedAhead).
* Emptied during typer.
*/
lazy val typedTree = new mutable.HashMap[Tree, tpd.Tree]
/** A map from method symbols to nested typers.
* Populated when methods are completed. Emptied when they are typechecked.
* The nested typer contains new versions of the four maps above including this
* one, so that trees that are shared between different DefDefs can be independently
* used as indices. It also contains a scope that contains nested parameters.
*/
lazy val nestedTyper = new mutable.HashMap[Symbol, Typer]
/** The scope of the typer.
* For nested typers this is a place parameters are entered during completion
* and where they survive until typechecking.
*/
val scope = newScope
/** The symbol of the given expanded tree. */
def symbolOfTree(tree: Tree)(implicit ctx: Context): Symbol = typedTree get tree match {
case Some(tree1) => tree1.denot.symbol
case _ => symOfTree(tree)
}
/** The enclosing class with given name; error if none exists */
def enclosingClassNamed(name: TypeName, pos: Position)(implicit ctx: Context): Symbol = {
if (name.isEmpty) NoSymbol
else {
val cls = ctx.owner.enclosingClassNamed(name)
if (!cls.exists) ctx.error(s"no enclosing class or object is named $name", pos)
cls
}
}
/** If this tree is a member def or an import, create a symbol of it
* and store in symOfTree map.
*/
def createSymbol(tree: Tree)(implicit ctx: Context): Symbol = {
def privateWithinClass(mods: Modifiers) =
enclosingClassNamed(mods.privateWithin, mods.pos)
def record(tree: Tree, sym: Symbol): Symbol = {
symOfTree(tree) = sym
sym
}
println(i"creating symbol for $tree")
tree match {
case tree: TypeDef if tree.isClassDef =>
record(tree, ctx.newClassSymbol(
ctx.owner, tree.name, tree.mods.flags, new ClassCompleter(tree),
privateWithinClass(tree.mods), tree.pos, ctx.source.file))
case tree: MemberDef =>
record(tree, ctx.newSymbol(
ctx.owner, tree.name, tree.mods.flags, new Completer(tree),
privateWithinClass(tree.mods), tree.pos))
case imp: Import =>
record(imp, ctx.newSymbol(
ctx.owner, nme.IMPORT, Synthetic, new Completer(tree), NoSymbol, tree.pos))
case _ =>
NoSymbol
}
}
/** If `sym` exists, enter it in effective scope. Check that
* package members are not entered twice in the same run.
*/
def enterSymbol(sym: Symbol)(implicit ctx: Context) = {
if (sym.exists) {
println(s"entered: $sym in ${ctx.owner} and ${ctx.effectiveScope}")
if (sym.owner is PackageClass) {
val preExisting = sym.owner.decls.lookup(sym.name)
if (preExisting.defRunId == ctx.runId)
ctx.error(s"${sym.showLocated} is compiled twice", sym.pos)
}
ctx.enter(sym)
}
sym
}
/** All PackageClassInfoTypes come from here. */
private def createPackageSymbol(pid: RefTree)(implicit ctx: Context): Symbol = {
val pkgOwner = pid match {
case Ident(_) => if (ctx.owner eq defn.EmptyPackageClass) defn.RootClass else ctx.owner
case Select(qual: RefTree, _) => createPackageSymbol(qual).moduleClass
}
val existing = pkgOwner.info.decls.lookup(pid.name)
if ((existing is Package) && (pkgOwner eq existing.owner)) existing
else ctx.newCompletePackageSymbol(pkgOwner, pid.name.asTermName).entered
}
/** The expansion of a member def */
def expansion(mdef: MemberDef)(implicit ctx: Context): Tree = {
val expanded = desugar.memberDef(mdef)
if (expanded ne mdef) expandedTree(mdef) = expanded
expanded
}
/** A new context that summarizes an import statement */
def importContext(sym: Symbol, selectors: List[Tree])(implicit ctx: Context) =
ctx.fresh.withImportInfo(new ImportInfo(sym, selectors))
/** A new context for the interior of a class */
def inClassContext(selfInfo: DotClass /* Should be Type | Symbol*/)(implicit ctx: Context): Context = {
val localCtx: Context = ctx.fresh.withNewScope
selfInfo match {
case sym: Symbol if sym.exists && sym.name != nme.WILDCARD => localCtx.enter(sym)
case _ =>
}
localCtx
}
/** Create top-level symbols for statement and enter them into symbol table */
def index(stat: Tree)(implicit ctx: Context): Context = stat match {
case pcl: PackageDef =>
val pkg = createPackageSymbol(pcl.pid)
index(pcl.stats)(ctx.fresh.withOwner(pkg.moduleClass))
ctx
case imp: Import =>
importContext(createSymbol(imp), imp.selectors)
case mdef: MemberDef =>
expansion(mdef).toList foreach (tree => enterSymbol(createSymbol(tree)))
ctx
case _ =>
ctx
}
/** Create top-level symbols for statements and enter them into symbol table */
def index(stats: List[Tree])(implicit ctx: Context): Context = {
@tailrec def traverse(stats: List[Tree])(implicit ctx: Context): Context = stats match {
case stat :: stats1 =>
traverse(stats1)(index(stat))
case nil =>
ctx
}
/** Merge the definitions of a synthetic companion generated by a case class
* and the real companion, if both exist.
*/
def mergeCompanionDefs() = {
val caseClassDef = mutable.Map[TypeName, TypeDef]()
for (cdef @ TypeDef(mods, name, _) <- stats)
if (mods is Case) caseClassDef(name) = cdef
for (mdef @ ModuleDef(_, name, _) <- stats)
caseClassDef get name.toTypeName match {
case Some(cdef) =>
val Thicket((mcls @ TypeDef(_, _, impl: Template)) :: mrest) = expandedTree(mdef)
val Thicket(cls :: TypeDef(_, _, compimpl: Template) :: crest) = expandedTree(cdef)
val mcls1 = cpy.TypeDef(mcls, mcls.mods, mcls.name,
cpy.Template(impl, impl.constr, impl.parents, impl.self,
compimpl.body ++ impl.body))
expandedTree(mdef) = Thicket(mcls1 :: mrest)
expandedTree(cdef) = Thicket(cls :: crest)
case none =>
}
}
val result = traverse(stats)
mergeCompanionDefs()
result
}
/** The completer of a symbol defined by a member def or import (except ClassSymbols) */
class Completer(original: Tree)(implicit ctx: Context) extends LazyType {
def complete(denot: SymDenotation): Unit = {
val sym = denot.symbol
def localContext = ctx.fresh.withOwner(sym)
def typeSig(tree: Tree): Type = tree match {
case tree: ValDef =>
valOrDefDefSig(tree, sym, identity)(localContext)
case tree: DefDef =>
val typer1 = new Typer
nestedTyper(sym) = typer1
typer1.defDefSig(tree, sym)(localContext.withTyper(typer1))
case tree: TypeDef =>
typeDefSig(tree, sym)(localContext.withNewScope)
case imp: Import =>
val expr1 = typedAheadExpr(imp.expr, AnySelectionProto)
ImportType(tpd.SharedTree(expr1))
}
sym.info = typeSig(original)
}
}
/** The completer for a symbol defined by a class definition */
class ClassCompleter(original: TypeDef)(implicit ctx: Context)
extends ClassCompleterWithDecls(newScope) {
override def complete(denot: SymDenotation): Unit = {
val cls = denot.symbol.asClass
def localContext = ctx.fresh.withOwner(cls)
println(s"completing ${cls.show}")
cls.info = classDefSig(original, cls, decls.asInstanceOf[MutableScope])(localContext)
}
}
/** Typecheck tree during completion, and remember result in typedtree map */
private def typedAheadImpl(tree: Tree, pt: Type)(implicit ctx: Context): tpd.Tree =
typedTree.getOrElseUpdate(tree, typer.typedExpanded(tree, pt))
def typedAheadType(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType addMode Mode.Type)
def typedAheadExpr(tree: Tree, pt: Type = WildcardType)(implicit ctx: Context): tpd.Tree =
typedAheadImpl(tree, pt)(ctx retractMode Mode.PatternOrType)
/** Enter and typecheck parameter list */
def completeParams(params: List[MemberDef])(implicit ctx: Context) = {
index(params)
for (param <- params) typedAheadExpr(param)
}
/** The type signature of a ValDef or DefDef
* @param mdef The definition
* @param sym Its symbol
* @param paramFn A wrapping function that produces the type of the
* defined symbol, given its final return type
*/
def valOrDefDefSig(mdef: ValOrDefDef, sym: Symbol, paramFn: Type => Type)(implicit ctx: Context): Type = {
val pt =
if (!mdef.tpt.isEmpty) WildcardType
else {
lazy val schema = paramFn(WildcardType)
val site = sym.owner.thisType
val inherited = {
// TODO: Look only at member of supertype instead?
((NoType: Type) /: sym.owner.info.baseClasses.tail) { (tp, cls) =>
val itpe = cls.info
.nonPrivateDecl(sym.name)
.matchingDenotation(site, schema)
.asSeenFrom(site)
.info.finalResultType
tp & itpe
}
}
inherited orElse typedAheadExpr(mdef.rhs).tpe
}
paramFn(typedAheadType(mdef.tpt, pt).tpe)
}
/** The type signature of a DefDef with given symbol */
def defDefSig(ddef: DefDef, sym: Symbol)(implicit ctx: Context) = {
val DefDef(_, name, tparams, vparamss, _, _) = ddef
completeParams(tparams)
vparamss foreach completeParams
val isConstructor = name == nme.CONSTRUCTOR
val isSecondaryConstructor = isConstructor && sym != sym.owner.primaryConstructor
def typeParams =
if (isSecondaryConstructor) sym.owner.primaryConstructor.typeParams
else tparams map symbolOfTree
def wrapMethType(restpe: Type): Type = {
val monotpe =
(restpe /: vparamss) { (restpe, params) =>
val make =
if (params.nonEmpty && (params.head.mods is Implicit)) ImplicitMethodType
else MethodType
make.fromSymbols(params map symbolOfTree, restpe)
}
if (typeParams.nonEmpty) PolyType.fromSymbols(typeParams, monotpe)
else if (vparamss.isEmpty) ExprType(monotpe)
else monotpe
}
if (isConstructor) {
// set result type tree to unit, but set the current class as result type of the symbol
typedAheadType(ddef.tpt, defn.UnitType)
wrapMethType(sym.owner.typeConstructor.appliedTo(typeParams map (_.symRef)))
}
else valOrDefDefSig(ddef, sym, wrapMethType)
}
def typeDefSig(tdef: TypeDef, sym: Symbol)(implicit ctx: Context): Type = {
completeParams(tdef.tparams)
val tparamSyms = tdef.tparams map symbolOfTree
val rhsType = typedAheadType(tdef.rhs).tpe
rhsType match {
case bounds: TypeBounds =>
if (tparamSyms.nonEmpty) bounds.higherKinded(tparamSyms)
else rhsType
case _ =>
if (tparamSyms.nonEmpty) rhsType.LambdaAbstract(tparamSyms)(ctx.error(_, _))
else TypeBounds(rhsType, rhsType)
}
}
/** The type signature of a ClassDef with given symbol */
def classDefSig(cdef: TypeDef, cls: ClassSymbol, decls: MutableScope)(implicit ctx: Context): Type = {
def parentType(constr: untpd.Tree): Type = {
val Trees.Select(Trees.New(tpt), _) = methPart(constr)
val ptype = typedAheadType(tpt).tpe
if (ptype.uninstantiatedTypeParams.isEmpty) ptype
else typedAheadExpr(constr).tpe
}
val TypeDef(_, name, impl @ Template(constr, parents, self, body)) = cdef
val (params, rest) = body span {
case td: TypeDef => td.mods is Param
case td: ValDef => td.mods is ParamAccessor
case _ => false
}
index(params)
val selfInfo = if (self.isEmpty) NoType else createSymbol(self)
// pre-set info, so that parent types can refer to type params
cls.info = ClassInfo(cls.owner.thisType, cls, Nil, decls, selfInfo)
val parentTypes = parents map parentType
val parentRefs = ctx.normalizeToRefs(parentTypes, cls, decls)
index(constr)
index(rest)(inClassContext(selfInfo))
ClassInfo(cls.owner.thisType, cls, parentRefs, decls, selfInfo)
}
}