summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
blob: d75e119fd75b8f59c47b772bce3b1a49ebfcdf36 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                
                                              

























                                                                        










                                                                           


                     


























                                                                                          
                                                            










                                                                          
 








                                                                            
       


                                                               
     


                                                                  
     
 





                                      


                                                     
                      




























                                                                                                             



                                   

























                                                                                                                                
     
















                                                                                            
                                
                                        

                                                                            





























                                                                       
     
                                                           
                                                    
                                        

                                  

                                                          
     
                                                              
                                                   
                                       

                                   

















                                                                               

     


                                                                                
                        































































                                                                                                                      
     
   
 
/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Paul Phillips
 */
package scala.tools.nsc
package typechecker

import symtab.Flags._
import scala.collection.{ mutable, immutable }

object listutil {
  def mexists[T](xss: List[List[T]])(p: T => Boolean) =
    xss exists (_ exists p)
  def mmap[T, U](xss: List[List[T]])(f: T => U) =
    xss map (_ map f)
  def mforeach[T](xss: List[List[T]])(f: T => Unit) =
    xss foreach (_ foreach f)
  def mfind[T](xss: List[List[T]])(p: T => Boolean): Option[T] = {
    for (xs <- xss; x <- xs)
      if (p(x)) return Some(x)
    None
  }
  def mfilter[T](xss: List[List[T]])(p: T => Boolean) =
    for (xs <- xss; x <- xs; if p(x)) yield x
}

/** Logic related to method synthesis which involves cooperation between
 *  Namer and Typer.
 */
trait MethodSynthesis {
  self: Analyzer =>

  import global._
  import definitions._

  /** There are two key methods in here.
   *
   *   1) enterGetterSetter is called from Namer with a ValDef which
   *   may need accessors.  Some setup is performed.  In general this
   *   creates symbols and enters them into the scope of the owner.
   *
   *   2) finishGetterSetter is called from Typer when a Template is typed.
   *   It completes the job, returning a list of trees with their symbols
   *   set to those created in enterGetterSetter.  Those trees then become
   *   part of the typed template.
   */
  trait MethodSynth {
    self: Namer =>

    def enterGetterSetter(tree: ValDef) {
      val ValDef(mods, name, _, _) = tree
      if (nme.isSetterName(name))
        context.error(tree.pos, "Names of vals or vars may not end in `_='")

      val getter = Getter(tree).createAndEnterSymbol()

      tree.symbol = (
        if (mods.isLazy) enterLazyVal(tree, getter)
        else {
          if (mods.isPrivateLocal)
            context.error(tree.pos, "private[this] not allowed for case class parameters")
          // Create the setter if necessary.
          if (mods.isMutable)
            Setter(tree).createAndEnterSymbol()

          // If abstract, the tree gets the getter's symbol.  Otherwise, create a field.
          if (mods.isDeferred) getter setPos tree.pos
          else enterStrictVal(tree)
        }
      )

      enterBeans(tree)
    }
    def finishGetterSetter(typer: Typer, stat: Tree): List[Tree] = stat match {
      case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) =>
        // If we don't save the annotations, they seem to wander off.
        val annotations = stat.symbol.initialize.annotations
        val trees = (
          allValDefDerived(vd)
                  map (acc => atPos(vd.pos.focus)(acc derive annotations))
            filterNot (_ eq EmptyTree)
        )
        log(trees.mkString("Accessor trees:\n  ", "\n  ", "\n"))
        if (vd.symbol.isLazy) List(stat)
        else trees
      case _ =>
        List(stat)
    }

    def standardAccessors(vd: ValDef): List[DerivedFromValDef] = (
      if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd))
      else List(Getter(vd))
    )
    def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
      if (forMSIL) Nil
      else if (vd.symbol hasAnnotation BeanPropertyAttr) {
        if (vd.mods.isMutable) List(BeanGetter(vd), BeanSetter(vd))
        else List(BeanGetter(vd))
      }
      else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr)
        List(BooleanBeanGetter(vd))
      else Nil
    }
    def allValDefDerived(vd: ValDef) = {
      val field = if (vd.mods.isDeferred) Nil else List(Field(vd))
      field ::: standardAccessors(vd) ::: beanAccessors(vd)
    }

    trait Derived {
      def name: TermName
      def flagsMask: Long
      def flagsExtra: Long
      def completer(sym: Symbol): Type
    }
    trait DerivedFromValDef extends Derived {
      /** The declaration from which we are deriving.
       */
      def tree: ValDef

      /** Which meta-annotation is associated with this kind of entity.
       *  Presently one of: field, getter, setter, beanGetter, beanSetter, param.
       */
      def category: Symbol

      // Final methods to make the rest easier to reason about.
      final def mods      = tree.mods
      final def basisSym  = tree.symbol
      final def enclClass = basisSym.enclClass

      final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter)
      final def fieldSelection         = Select(This(enclClass), basisSym)
      final def derivedFlags: Long     = basisSym.flags & flagsMask | flagsExtra
      final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil)

      def derivedSym: Symbol = tree.symbol
      def derivedTree: Tree  = EmptyTree

      def isSetter   = false
      def isDeferred = mods.isDeferred
      def keepClean  = false  // whether annotations whose definitions are not meta-annotated should be kept.
      def validate() { }
      def createAndEnterSymbol(): Symbol = {
        val sym = (
          owner.newMethod(tree.pos.focus, name)
            setFlag tree.mods.flags & flagsMask
            setFlag flagsExtra
        )
        setPrivateWithin(tree, sym)
        enterInScope(sym)
        sym setInfo completer(sym)
      }
      /** The annotations amongst those found on the original symbol which
       *  should be propagated to this kind of accessor.
       */
      private def deriveAnnotations(initial: List[AnnotationInfo]): List[AnnotationInfo] = {
        initial filter { ann =>
          // There are no meta-annotation arguments attached to `ann`
          if (ann.metaAnnotations.isEmpty) {
            // A meta-annotation matching `annotKind` exists on `ann`'s definition.
            (ann.defaultTargets contains category) ||
            // `ann`'s definition has no meta-annotations, and `keepClean` is true.
            (ann.defaultTargets.isEmpty && keepClean)
          }
          // There are meta-annotation arguments, and one of them matches `annotKind`
          else ann.metaAnnotations exists (_ matches category)
        }
      }
      private def logDerived(result: Tree): Tree = {
        val id = List(mods.defaultFlagString, basisSym.accurateKindString, basisSym.getterName) filterNot (_ == "") mkString " "
        log("[+derived] " + id + " (" + derivedSym + ")\n        " + result)
        result
      }
      final def derive(initial: List[AnnotationInfo]): Tree = {
        validate()
        derivedSym setAnnotations deriveAnnotations(initial)
        logDerived(derivedTree)
      }
    }
    trait DerivedGetter extends DerivedFromValDef {
      // TODO
    }
    trait DerivedSetter extends DerivedFromValDef {
      override def isSetter = true
      private def setterParam = derivedSym.paramss match {
        case (p :: Nil) :: _  => p
        case _                => NoSymbol
      }
      private def setterRhs = (
        if (mods.isDeferred || derivedSym.isOverloaded) EmptyTree
        else Assign(fieldSelection, Ident(setterParam))
      )
      private def setterDef = DefDef(derivedSym, setterRhs)
      override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef
    }
    case class Getter(tree: ValDef) extends DerivedGetter {
      def name       = tree.name
      def category   = GetterTargetClass
      def flagsMask  = GetterFlags
      def flagsExtra = ACCESSOR | ( if (tree.mods.isMutable) 0 else STABLE )

      override def derivedSym = (
        if (mods.isDeferred) basisSym
        else basisSym.getter(enclClass)
      )
      override def validate() {
        assert(derivedSym != NoSymbol, tree)
        if (derivedSym.isOverloaded)
          context.error(derivedSym.pos, derivedSym+" is defined twice")

        super.validate()
      }
      // keep type tree of original abstract field
      private def fixTypeTree(dd: DefDef): DefDef = {
        dd.tpt match {
          case tt: TypeTree if dd.rhs == EmptyTree  =>
            tt setOriginal tree.tpt
          case tpt =>
            tpt setPos tree.tpt.pos.focus
        }
        dd
      }
      override def derivedTree: DefDef = {
        fixTypeTree {
          DefDef(derivedSym,
            if (mods.isDeferred) EmptyTree
            else gen.mkCheckInit(fieldSelection)
          )
        }
      }
    }
    case class Setter(tree: ValDef) extends DerivedSetter {
      def name       = nme.getterToSetter(tree.name)
      def category   = SetterTargetClass
      def flagsMask  = SetterFlags
      def flagsExtra = ACCESSOR

      override def derivedSym = basisSym.setter(enclClass)
    }
    case class Field(tree: ValDef) extends DerivedFromValDef {
      def name       = nme.getterToLocal(tree.name)
      def category   = FieldTargetClass
      def flagsMask  = FieldFlags
      def flagsExtra = PrivateLocal
      // By default annotations go to the field, except if the field is
      // generated for a class parameter (PARAMACCESSOR).
      override def keepClean = !mods.isParamAccessor
      override def derivedTree = (
        if (mods.isDeferred) EmptyTree
        else treeCopy.ValDef(tree, mods | flagsExtra, name, tree.tpt, tree.rhs)
      )
    }
    case class Param(tree: ValDef) extends DerivedFromValDef {
      def name       = tree.name
      def category   = ParamTargetClass
      def flagsMask  = -1L
      def flagsExtra = 0L
      override def keepClean = true
      override def derivedTree = EmptyTree
    }
    def validateParam(tree: ValDef) {
      Param(tree).derive(tree.symbol.annotations)
    }

    sealed abstract class BeanAccessor(bean: String) extends DerivedFromValDef {
      def name       = bean + tree.name.toString.capitalize
      def flagsMask  = BeanPropertyFlags
      def flagsExtra = 0
      override def derivedSym = enclClass.info decl name
    }
    trait AnyBeanGetter extends BeanAccessor with DerivedGetter {
      def category = BeanGetterTargetClass
      override def validate() {
        if (derivedSym == NoSymbol) {
          // the namer decides whether to generate these symbols or not. at that point, we don't
          // have symbolic information yet, so we only look for annotations named "BeanProperty".
          context.error(tree.pos,
            "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import")
        }
        super.validate()
      }
    }
    trait NoSymbolBeanGetter extends AnyBeanGetter {
      // Derives a tree without attempting to use the original tree's symbol.
      override def derivedTree = {
        atPos(tree.pos.focus) {
          DefDef(derivedMods, name, Nil, List(Nil), tree.tpt.duplicate,
            if (isDeferred) EmptyTree else Select(This(owner), tree.name)
          )
        }
      }
      override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree)
    }
    case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { }
    case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { }
    case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter {
      def category = BeanSetterTargetClass
    }

    // No Symbols available.
    private def beanAccessorsFromNames(tree: ValDef) = {
      val ValDef(mods, name, tpt, _) = tree
      val hasBP     = mods hasAnnotationNamed tpnme.BeanPropertyAnnot
      val hasBoolBP = mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot

      if (hasBP || hasBoolBP) {
        val getter = (
          if (hasBP) new BeanGetter(tree) with NoSymbolBeanGetter
          else new BooleanBeanGetter(tree) with NoSymbolBeanGetter
        )
        getter :: {
          if (mods.isMutable) List(BeanSetter(tree)) else Nil
        }
      }
      else Nil
    }

    protected def enterBeans(tree: ValDef) {
      if (forMSIL)
        return

      val ValDef(mods, name, _, _) = tree
      val beans = beanAccessorsFromNames(tree)
      if (beans.nonEmpty) {
        if (!name(0).isLetter)
          context.error(tree.pos, "`BeanProperty' annotation can be applied only to fields that start with a letter")
        else if (mods.isPrivate)  // avoids name clashes with private fields in traits
          context.error(tree.pos, "`BeanProperty' annotation can be applied only to non-private fields")

        // Create and enter the symbols here, add the trees in finishGetterSetter.
        beans foreach (_.createAndEnterSymbol())
      }
    }
  }
}