summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
blob: d75e119fd75b8f59c47b772bce3b1a49ebfcdf36 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/* 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())
      }
    }
  }
}