aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/typer/Namer.scala
blob: 84a82044bca708dd9538f59b53fb24b032bffeb8 (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)
  }
}

class Namer(val typer: Typer) {

  import untpd._

  val expandedTrees = new mutable.WeakHashMap[Tree, Tree]()
  val symOfTree = mutable.Map[Tree, Symbol]()

  implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition =
    ctx.source.atPos(pos)

  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 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(original), privateWithin, original.pos)
      symOfTree(original) = sym
      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
        tparam.derivedTypeDef(Modifiers(TypeParam), tparam.name, tparam.tparams, tparam.rhs)

    def methParamss(cdef: ClassDef) =
      cdef.impl.constr.vparamss.nestedMap(vparam =>
        vparam.derivedValDef(Modifiers(TermParam), vparam.name, vparam.tpt, vparam.rhs))

    def expandCaseClass(cdef: ClassDef): ClassDef = {
      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 =>
            vparam.derivedValDef(Modifiers(TermParam), vparam.name, vparam.tpt, refOfDef(vparam)))
          val copyRestParamss = constr1.vparamss.tail.nestedMap(vparam =>
            vparam.derivedValDef(Modifiers(TermParam), vparam.name, vparam.tpt, EmptyTree))
          val applyParamss = constr1.vparamss.nestedMap(vparam =>
            vparam.derivedValDef(Modifiers(TermParam), vparam.name, vparam.tpt, vparam.rhs))
          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))
      }
      updateCompanion(cname.toTermName, applyMeths ::: unapplyMeth :: Nil)
      addToClass(cdef, copyMeths ::: isDefinedMeth :: productArityMeth :: productElemMeths.toList)
    }

    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 updateCompanion(name: TermName, newDefs: List[Tree]) =
      for (companion @ ModuleDef(_, `name`, _) <- enclosingStats) {
        expandedTrees(companion) = expandedTrees get companion match {
          case Some(Thicket(vdef :: (cdef: ClassDef) :: Nil)) =>
            Thicket(vdef, addToClass(cdef, newDefs))
          case none =>
            addToModule(companion, newDefs)
        }
      }

    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 mdef: ModuleDef =>
        desugarModuleDef {
          expandedTrees get mdef match {
            case Some(mdef1: ModuleDef) => mdef
            case _ => mdef
          }
        }
      case cdef: ClassDef =>
        val result = if (cdef.mods is Case) expandCaseClass(cdef) else cdef
        if (cdef.mods is Implicit) {
          if (ctx.owner is Package)
            ctx.error("implicit classes may not be toplevel", cdef.pos)
          Thicket(result :: implicitWrapper(cdef) :: Nil)
        }
        else result
      case _ =>
        tree
    }
    if (tree1 ne tree) expandedTrees(tree) = tree1
    tree1
  }

  def enterSyms(stats: List[Tree])(implicit ctx: Context): Unit = 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 =>
  }



  class Completer(tree: Tree)(implicit ctx: Context) extends LazyType {
    def complete(sym: SymDenotation): Unit = {
      for (defn <- expandedTrees.getOrElse(tree, tree).toList if symOfTree(defn) == sym)
        defn match {
          case ValDef(mods, name, tpt, rhs) =>
            val (tpt1, rhs1) =
              if (tpt.isEmpty) {
                val rhs1 = typer.typedExpr(rhs)
                (TypeTree().withType(rhs1.tpe), TypedSplice(rhs1))
              }
              else (typer.typedType(tpt), rhs)
            defn.derivedValDef(mods, name, TypedSplice(tpt1), rhs1)


          }

      }
    }
  }
}