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





                   
                                                 


                                                                                                  
                         

                                   







                                                              
                                      


                



















































                                                                                           
 




                                                            


                                                                    








                                                                             









                                                                                        









                                                                                                                           

                                                                                 

                                                                                                 
         










                                                                                      

   
                                      


                                                            








                                                                                            

                                                                                                  

                                     

                                                                                                            
 
                                                 
                                                                                              


                                            
                                                                                     



                                                                                                           
                                                                                                          



                                                                                                             


                                        
                                                        
                                                                                                        
                                                                         
                                                                                                 
                                                                 
                                                                                                  
                        
                                                                                                                           
                         
                                                                                                           
                                             
         
                         

                                                                                                                          
       






























                                                                                                 
     
 

                                                                                         
 

                                                                                               
 

                                                                                  
 


                                                                            
 


                                                         
              









                                                                                                              
         





                                                                                                              

                             
                                       




                                               

                                                                             


                                                                       
                                                        
         
                  


               
                                                 
         

   
                                                                                  






                                                                                              
         

   

                                                          
 

                                                                                             
 






































                                                                                                   
 




                                                                                       
 













                                                                                                           
           







                                                                                        
 














                                                                                      
       

                                  

     
 
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 util.Positions._
import util.SourcePosition
import collection.mutable
import language.implicitConversions

trait NamerContextOps { ctx: Context =>

  def enterSym(sym: Symbol) = ctx.owner match {
    case cls: ClassSymbol => cls.enter(sym)
    case _ => this.scope.asInstanceOf[MutableScope].enter(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[Tree, 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, original: Tree)(implicit ctx: Context): Symbol = {
    def createSym(name: Name, flags: FlagSet, privateWithin: Symbol) = {
      val sym = ctx.newSymbol(ctx.owner, name, flags, new Completer, privateWithin, original.pos)
      untypedTreeOfSym(sym) = tree
      sym
    }
    tree match {
      case tree: ModDefTree =>
        val sym = createSym(tree.name, tree.mods.flags, privateWithinClass(tree.mods))
        ctx.enterSym(sym)
        sym
      case imp: Import =>
        createSym(nme.IMPORT, Synthetic, NoSymbol)
      case _ =>
        NoSymbol
    }
  }

  val synthetic = Modifiers(Synthetic)

  def expansion(tree: Tree)(implicit ctx: Context): Tree = {

    def classTypeRef(cdef: ClassDef) = {
      val tycon = Ident(cdef.name)
      if (cdef.tparams.isEmpty) tycon else AppliedTypeTree(tycon, cdef.tparams map refOfDef)
    }

    def creator(cdef: ClassDef) =
      New(classTypeRef(cdef), cdef.impl.constr.vparamss.nestedMap(refOfDef))

    def methTypeParams(cdef: ClassDef) =
      for (tparam <- cdef.tparams) yield // don't use derivedTypeDef; parameters have to be unique
        TypeDef(Modifiers(TypeParam), tparam.name, tparam.tparams, tparam.rhs).withPos(tparam.pos)

    def methParamss(cdef: ClassDef) =
      cdef.impl.constr.vparamss.nestedMap(vparam => // don't use derivedValDef; parameters have to be unique
        ValDef(Modifiers(TermParam), vparam.name, vparam.tpt, vparam.rhs).withPos(vparam.pos))

    def expandCaseClass(cdef: ClassDef): Tree = {
      val ClassDef(mods, cname, tparams, impl @ Template(constr, parents, self, stats)) = cdef
      val constr1 =
        if (constr.vparamss.nonEmpty) constr
        else {
          ctx.error("case class needs to have at least one parameter list", cdef.pos)
          constr.derivedDefDef(constr.mods, constr.name, constr.tparams, ListOfNil, constr.tpt, constr.rhs)
        }
      val caseParams = constr1.vparamss.head
      val caseParamsArray = caseParams.toArray
      def syntheticProperty(name: TermName, rhs: Tree) = DefDef(synthetic, name, Nil, Nil, EmptyTree, rhs)
      val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true)))
      val productArityMeth = syntheticProperty(nme.productArity, Literal(Constant(caseParamsArray.length)))
      val productElemMeths = for (i <- 0 until caseParamsArray.length) yield
          syntheticProperty(("_" + (i + 1)).toTermName, Select(This(EmptyTypeName), caseParamsArray(i).name))
      val (copyMeths, applyMeths) =
        if (mods is Abstract) (Nil, Nil)
        else {
          val copyFirstParams = caseParams.map(vparam =>
            ValDef(Modifiers(TermParam), vparam.name, vparam.tpt, refOfDef(vparam)).withPos(vparam.pos))
          val copyRestParamss = constr1.vparamss.tail.nestedMap(vparam =>
            ValDef(Modifiers(TermParam), vparam.name, vparam.tpt, EmptyTree).withPos(vparam.pos))
          val applyParamss = constr1.vparamss.nestedMap(vparam =>
            ValDef(Modifiers(TermParam), vparam.name, vparam.tpt, vparam.rhs).withPos(vparam.pos))
          val copyMeth =
            DefDef(synthetic, nme.copy, methTypeParams(cdef), copyFirstParams :: copyRestParamss, EmptyTree, creator(cdef))
          val applyMeth =
            DefDef(synthetic, nme.apply, methTypeParams(cdef), methParamss(cdef), EmptyTree, creator(cdef))
          (copyMeth :: Nil, applyMeth :: Nil)
        }
      val unapplyMeth = {
        val unapplyParam = makeSyntheticParameter(tpt = classTypeRef(cdef))
        DefDef(synthetic, nme.unapply, methTypeParams(cdef), (unapplyParam :: Nil) :: Nil, EmptyTree, This(EmptyTypeName))
      }
      val classMeths = copyMeths ::: isDefinedMeth :: productArityMeth :: productElemMeths.toList
      val cls1 = addToClass(cdef, classMeths)

      // update or create companion object:
      val companionMeths = applyMeths ::: unapplyMeth :: Nil
      val companionName = cname.toTermName
      var companionFound = false
      for (companion @ ModuleDef(_, `companionName`, _) <- enclosingStats) {
        // Add `companionDefs` to either the expanded or unexpanded version of
        // the companion object with given `companionName`, and update its expandedTree map
        // with the result.
        expandedTree(companion) = expandedTree get companion match {
          case Some(Thicket(vdef :: (cdef: ClassDef) :: Nil)) =>
            Thicket(vdef, addToClass(cdef, companionMeths))
          case none =>
            addToModule(companion, companionMeths)
        }
        companionFound = true
      }
      val syntheticCompanions =
        if (companionFound) Nil
        else {
          val parent =
            if (tparams.nonEmpty) ref(defn.AnyRefAlias.typeConstructor)
            else (constr1.vparamss :\ classTypeRef(cdef)) ((vparams, restpe) =>
              Function(vparams map (_.tpt), restpe))
          ModuleDef(
            Modifiers(Synthetic), companionName,
            Template(emptyConstructor, parent :: Nil, EmptyValDef(), companionMeths)) :: Nil
        }
      Thicket.make(cls1 :: syntheticCompanions)
    }

    def addToTemplate(templ: Template, stats: List[Tree]): Template =
      templ.derivedTemplate(templ.constr, templ.parents, templ.self, templ.body ++ stats)

    def addToClass(cdef: ClassDef, stats: List[Tree]): ClassDef =
      cdef.derivedClassDef(cdef.mods, cdef.name, cdef.tparams, addToTemplate(cdef.impl, stats))

    def addToModule(mdef: ModuleDef, stats: List[Tree]): ModuleDef =
      mdef.derivedModuleDef(mdef.mods, mdef.name, addToTemplate(mdef.impl, stats))

    def implicitWrapper(cdef: ClassDef) =
      DefDef(Modifiers(Synthetic | Implicit), cdef.name.toTermName,
          methTypeParams(cdef), methParamss(cdef), EmptyTree, creator(cdef))

    val tree1 = tree match {
      case ValDef(mods, name, tpt, rhs) =>
        if (!ctx.owner.isClass || (mods is Private)) tree
        else {
          val lname = name.toLocalName
          val field = tree.derivedValDef(mods, lname, tpt, rhs)
          val getter = tree.derivedDefDef(mods, name, Nil, Nil, tpt, Ident(lname))
          if (!(mods is Mutable)) Thicket(field, getter)
          else {
            val setterParam = makeSyntheticParameter(tpt = TypeTree(field))
            val setter = tree.derivedDefDef(
                mods, name.getterToSetter, Nil, (setterParam :: Nil) :: Nil, EmptyTree, refOfDef(setterParam))
            Thicket(field, getter, setter)
          }
        }
      case tdef: TypeDef if tdef.mods is PrivateLocalParamAccessor =>
        val tparam = tdef.derivedTypeDef(
          tdef.mods &~ PrivateLocal | ExpandedName, tdef.name.expandedName(ctx.owner), tdef.tparams, tdef.rhs)
        val alias = tdef.derivedTypeDef(
          Modifiers(PrivateLocal | Synthetic), tdef.name, Nil, refOfDef(tparam))
        Thicket(tparam :: alias :: Nil)
      case mdef: ModuleDef =>
        desugarModuleDef {
          expandedTree get mdef match {
            case Some(mdef1: ModuleDef) => mdef
            case _ => mdef
          }
        }
      case cdef: ClassDef =>
        val cdef1: ClassDef = desugarClassDef(cdef)
        val cdef2 = if (cdef1.mods is Case) expandCaseClass(cdef1) else cdef1
        if (cdef.mods is Implicit) {
          if (ctx.owner is Package)
            ctx.error("implicit classes may not be toplevel", cdef.pos)
          Thicket(cdef2 :: implicitWrapper(cdef) :: Nil)
        }
        else cdef2
      case _ =>
        tree
    }
    if (tree1 ne tree) expandedTree(tree) = tree1
    tree1
  }

  def enterSyms(stats: List[Tree])(implicit ctx: Context): Context = stats match {
    case (imp @ Import(expr, selectors)) :: rest =>
      val sym = createSymbol(imp, imp)
      enterSyms(rest)(ctx.fresh.withImport(ImportInfo(sym, selectors, ctx.scopeNestingLevel)))
    case stat :: rest =>
      for (expanded <- expansion(stat).toList) createSymbol(expanded, stat)
      enterSyms(rest)
    case Nil =>
      ctx
  }

  def localContext(owner: Symbol)(implicit ctx: Context) =
    ctx.fresh.withOwner(owner).withScope(newScope)

  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 registerTyped(originals: List[NameTree], trees: List[tpd.Tree]): Unit =
      for ((original, tree) <- (originals, trees).zipped)
        typedTreeOfSym(symOfTree(original, untypedTreeOfSym)) = tree

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

      def inheritedResultType(paramFn: Type => Type): Type = {
        lazy val schema = paramFn(WildcardType)
        val site = sym.owner.symTypeRef
        ((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
        }
      }

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

      def valOrDefDefTypeSig[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 defDefTypeSig(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
          }
          valOrDefDefTypeSig(defn, completeDefDef, wrapMethType)
      }

      def classDefTypeSig(defn: ClassDef)(implicit ctx: Context): Type = {
        val ClassDef(_, _, tparams, impl @ Template(constr, parents, self, body)) = defn
        val localCtx = ctx.fresh.withOwner(sym)
        ???
      }

      def typeSig(defn: Tree): Type = defn match {
        case defn: ValDef =>
          valOrDefDefTypeSig(defn, completeValDef, identity)(ctx.fresh.withOwner(sym))
        case defn: DefDef =>
          defDefTypeSig(defn)(localContext(sym))
        case defn: TypeDef =>
          val localCtx = localContext(sym)
          completeParams(defn.tparams, completeTypeDef)(localCtx)
          val TypeDef(_, _, _, rhs) = aheadDef(defn, completeTypeDef)(localCtx)
          rhs.tpe   // !!! do something about parameters!
        case defn: ClassDef =>
          classDefTypeSig(defn)(ctx.fresh.withOwner(sym))
        case imp: Import =>
          val expr1 = typedDefn(imp.expr, sym)
          ImportType(SharedTree(expr1))
      }

      sym.info = typeSig(original)
    }
  }
}