aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/typer/Namer.scala
blob: d5b63a703de7ff558602496240618afc9041c6fe (plain) (tree)
1
2
3
4
5
6
7
8
9





                   
                                                 
                                                                                                  
                                 

                          
                         

                                   
                                        
 





                                                                


   
                                      


                



















































                                                                                           
 
                                                             



                                                            


                                                                    








                                                                             









                                                                                        









                                                                                                                           





                                                                    
                             


                                                               
                         

                                                                              


                

                                                

   




                                                                 
 









                                                                                 
 
                                                                      
 





                                                                                   
 













                                                                                    
                      
         
       
 


                                

   

                                                                                             
 

                                                           
                                                

                                                    
 
                                                                                     
                                               

                                                                                    
                             
                                               












                                                                                 
                                                                       








                                                                                                   
 




                                                                                       
 
                                                            












                                                                                                           
           
                                                            

       




                                                                         
       
 





























                                                                                                

                                                  
                                                                      
                            
                                                    
                             
                                                     
                              
                                         
                           
                                                        
                                       
       
 
                                            

     
 
package dotty.tools
package dotc
package typer

import core._
import ast._
import Trees._, Constants._, StdNames._, Scopes._
import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._
import ast.desugar, ast.desugar._
import util.Positions._
import util.SourcePosition
import collection.mutable
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
  }
}

abstract class Namer { typer: Typer =>

  import untpd._

  /** There are three maps at play here:
   *
   *
   *  Original tree ------------> Expanded tree(s)
   *                expandedTree     : ^
   *                  (weak)         : |
   *                                 : |
   *                symOfUntypedTree : | untypedTreeOfSym
   *                                 : |
   *                                 v |  typedTreeOfSym
   *                               Symbol --------------> Typed tree
   *                                      <::::::::::::::
   *                                      symOfTypedTree
   *
   *  The expandedTree map is weak, the others are strong.
   *
   *  The untypedTreeOfSym map can be inverted to a map from untyped trees
   *  to the symbols they define. The function `symOfUntypedTree` looks up a
   *  symbol in the current context with the name of the tree and which points
   *  back (via untypedTreeOfSym) to the tree. Similarly, `typedTreeOfSym` can be
   *  inverted to `symofTypedTree`.
   *
   *  Timeline:
   *
   *  During enter, trees are expanded as necessary, populating the expandedTree map.
   *  Symbols are created, and the untypedTreeOfSym link is set up.
   *
   *  Symbol completion causes some trees to be already typechecked and typedTreeOfSym
   *  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 definition tree, we make sure the corresponding symbol is completed
   *  and remove its untypedTreeOfSym link. We then consult the typedTreeOfSym map.
   *  If a typed tree exists, it replaces the original untyped tree, and the corresponding
   *  entry in the typedTree map is removed. Otherwise the untyped tree is typechecked,
   *  yielding the typed tree.
   *
   *  Dealing with shared tree nodes:
   *
   *  The scheme is designed to allow arbitrary sharing of nodes: (1) The expansion of
   *  a tree is context free, so expanding trees several times yields the same result
   *  as expanding once. No need to lock or duplicate expandedTree items. (2)
   *  Each `enterSyms` pass over a shared node creates new symbols and the two remaining
   *  maps are indexed with these symbols, so no sharing occurs for them.
   *
   *  Memory reclamation:
   *
   *  expandedTrees is a weak map, so entries will be reclaimed once the original
   *  untyped tree is no longer referenced. typedTreeOfSym and untypedTreeOfSym
   *  entries are both removed by the time a definition is integrated in the typed tree
   *  during phase typer.
   */

  val expandedTree = new mutable.WeakHashMap[MemberDef, Tree]

  val untypedTreeOfSym = mutable.Map[Symbol, Tree]()

  val typedTreeOfSym = new mutable.HashMap[Symbol, tpd.Tree]

  implicit def posToCoord(pos: Position): Coord = positionCoord(pos)

  def enclosingStats(implicit ctx: Context): List[Trees.Tree[_ >: Untyped]] =
    if (ctx == NoContext) Nil
    else ctx.tree match {
      case Template(_, _, _, stats) => stats
      case Block(stats, _) => stats
      case PackageDef(_, stats) => stats
      case _ => enclosingStats(ctx.outer)
    }

  def privateWithinClass(mods: Modifiers)(implicit ctx: Context): Symbol = {
    val pw = mods.privateWithin
    if (pw.isEmpty) NoSymbol
    else {
      val cls = ctx.owner.enclosingClassNamed(pw)
      if (!cls.exists) ctx.error(s"no enclosing class or object is named $pw", mods.pos)
      cls
    }
  }

  def symOfTree(tree: Trees.NameTree[_], treeMap: collection.Map[Symbol, Trees.Tree[_]])(implicit ctx: Context): Symbol = {
    var e = ctx.scope.lookupEntry(tree.name)
    while (e != null && treeMap(e.sym) != tree)
      e = ctx.scope.lookupNextEntry(e)
    if (e == null) NoSymbol else e.sym
  }

  def symOfTypedTree(tree: tpd.NameTree)(implicit ctx: Context) = symOfTree(tree, typedTreeOfSym)(ctx)
  def symOfUntypedTree (tree: NameTree)(implicit ctx: Context) = symOfTree(tree, untypedTreeOfSym)(ctx)

  def createSymbol(tree: Tree)(implicit ctx: Context): Symbol = {
    val sym = tree match {
      case tree: ClassDef =>
        ctx.enter(ctx.newClassSymbol(
          ctx.owner, tree.name, tree.mods.flags, new Completer,
          privateWithinClass(tree.mods), tree.pos, ctx.source.file))
      case tree: MemberDef =>
        ctx.enter(ctx.newSymbol(
          ctx.owner, tree.name, tree.mods.flags, new Completer,
          privateWithinClass(tree.mods), tree.pos))
      case imp: Import =>
        ctx.newSymbol(
          ctx.owner, nme.IMPORT, Synthetic, new Completer, NoSymbol, tree.pos)
      case _ =>
        NoSymbol
    }
    if (sym.exists) untypedTreeOfSym(sym) = tree
    sym
  }

  def expansion(defn: MemberDef)(implicit ctx: Context): Tree = {
    val expanded = desugar.memberDef(defn)
    if (expanded ne defn) expandedTree(defn) = expanded
    expanded
  }

  def enterSym(stat: Tree)(implicit ctx: Context): Context = stat match {
    case imp: Import =>
      val sym = createSymbol(imp)
      ctx.fresh.withImport(ImportInfo(sym, imp.selectors, ctx.scopeNestingLevel))
    case defn: MemberDef =>
      expansion(defn).toList foreach createSymbol
      ctx
    case _ =>
      ctx
  }

  def enterSyms(stats: List[Tree])(implicit ctx: Context): Context = {

    def traverse(stats: List[Tree])(implicit ctx: Context): Context = stats match {
      case stat :: stats1 =>
        traverse(stats)(enterSym(stat))
      case nil =>
        ctx
    }

    def mergeCompanionDefs() = {
      val caseClassDef = mutable.Map[TypeName, ClassDef]()
      for (cdef @ ClassDef(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 @ ClassDef(_, _, impl)) :: mrest) = expandedTree(mdef)
            val Thicket(cls :: (companion: ClassDef) :: crest) = expandedTree(cdef)
            val mcls1 = mcls.derivedClassDef(mcls.mods, mcls.name,
              impl.derivedTemplate(impl.constr, impl.parents, impl.self,
                companion.impl.body ++ impl.body))
            expandedTree(mdef) = Thicket(mcls1 :: mrest)
            expandedTree(cdef) = Thicket(cls :: crest)
          case none =>
        }
      }

    val result = traverse(stats)
    mergeCompanionDefs()
    result
  }

  def enterParams(ddef: DefDef)(ctx: Context): Context =
    (enterSyms(ddef.tparams)(ctx) /: ddef.vparamss) ((ctx, params) => enterSyms(params)(ctx))

  class Completer(implicit ctx: Context) extends LazyType {

    def complete(denot: SymDenotation): Unit = {
      val symToComplete = denot.symbol
      val original = untypedTreeOfSym(symToComplete)

      def inheritedResultType(paramFn: Type => Type)(implicit ctx: Context): Type = {
        lazy val schema = paramFn(WildcardType)
        val site = symToComplete.owner.symTypeRef
        ((NoType: Type) /: symToComplete.owner.info.baseClasses.tail) { (tp, cls) =>
          val itpe = cls.info
            .nonPrivateDecl(symToComplete.name)
            .matchingDenotation(site, schema)
            .asSeenFrom(site)
            .info.finalResultType
          tp & itpe
        }
      }

      def typedDefn(tree: Tree, sym: Symbol)(implicit ctx: Context): tpd.Tree = {
        val tree1 = typer.typed(tree, sym.symRef)
        typedTreeOfSym(sym) = tree1
        tree1
      }

      def valOrDefDefSig[UT <: untpd.ValOrDefDef, T <: tpd.ValOrDefDef]
              (defn: UT, op: DefTyper[UT, T], paramFn: Type => Type)(implicit ctx: Context): Type =
        paramFn {
          if (!defn.tpt.isEmpty) typer.typed(defn.tpt).tpe
          else {
            val inherited = inheritedResultType(paramFn)
            if (inherited.exists) typer.typed(defn.tpt, inherited).tpe
            else aheadDef(defn, op).tpt.tpe
          }
        }

      def completeParams[UT <: untpd.NameTree, T <: tpd.Tree]
        (params: List[UT], completer: DefTyper[UT, T])(implicit ctx: Context): Unit = {
        enterSyms(params)
        for (param <- params) aheadDef(param, completer)
      }

      def defDefSig(defn: DefDef)(implicit ctx: Context) = {
        val DefDef(_, _, tparams, vparamss, _, _) = defn
          completeParams(tparams, completeTypeDef)
          for (vparams <- vparamss) completeParams(vparams, completeValDef)
          def wrapMethType(restpe: Type): Type = {
            val monotpe =
              (restpe /: vparamss) { (restpe, params) =>
                val creator =
                  if (params.nonEmpty && (params.head.mods is Implicit)) ImplicitMethodType else MethodType
                creator.fromSymbols(params map symOfUntypedTree, restpe)
              }
            if (tparams.nonEmpty) PolyType.fromSymbols(tparams map symOfUntypedTree, monotpe)
            else if (vparamss.isEmpty) ExprType(monotpe)
            else monotpe
          }
          valOrDefDefSig(defn, completeDefDef, wrapMethType)
      }

      def typeDefSig(defn: TypeDef)(implicit ctx: Context): Type = {
        val lctx = localContext
        completeParams(defn.tparams, completeTypeDef)(lctx)
        val TypeDef(_, _, _, rhs) = aheadDef(defn, completeTypeDef)(lctx)
        rhs.tpe   // !!! do something about parameters!
      }

      def classDefSig(defn: ClassDef)(implicit ctx: Context): Type = {

        def parentType(constr: untpd.Tree): Type = {
          val Trees.Select(Trees.New(tpt), _) = TreeInfo.methPart(constr)
          val ptype = typedType(tpt).tpe
          if (ptype.uninstantiatedTypeParams.isEmpty) ptype else typedExpr(constr).tpe
        }

        def enterSelfSym(name: TermName, tpe: Type): Unit =
          ctx.enter(ctx.newSymbol(ctx.owner, name, Synthetic, tpe, coord = symToComplete.coord))

        val ClassDef(_, _, impl @ Template(constr, parents, self, body)) = defn

        val decls = newScope
        val (params, rest) = body span {
          case td: TypeDef => td.mods is ParamOrAccessor
          case _ => false
        }
        enterSyms(params)
        val parentTypes = parents map parentType
        val parentRefs = ctx.normalizeToRefs(parentTypes, symToComplete.asClass, decls)
        val selfTypeOpt = if (self.tpt.isEmpty) NoType else typedType(self.tpt).tpe
        if (self.name != nme.WILDCARD)
          enterSelfSym(self.name, selfTypeOpt orElse symToComplete.typeConstructor)
        enterSyms(rest)
        ClassInfo(denot.owner.thisType, symToComplete.asClass, parentRefs, decls, selfTypeOpt)
      }

      def localContext = ctx.fresh.withOwner(symToComplete)

      def typeSig(defn: Tree): Type = defn match {
        case defn: ValDef =>
          valOrDefDefSig(defn, completeValDef, identity)(localContext)
        case defn: DefDef =>
          defDefSig(defn)(localContext.withNewScope)
        case defn: TypeDef =>
          typeDefSig(defn)(localContext.withNewScope)
        case defn: ClassDef =>
          classDefSig(defn)(localContext)
        case imp: Import =>
          val expr1 = typedDefn(imp.expr, symToComplete)
          ImportType(SharedTree(expr1))
      }

      symToComplete.info = typeSig(original)
    }
  }
}