summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
blob: 0f257d3717ba06f03b0042dec09415aa10059f12 (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
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Paul Phillips
 */
package scala.tools.nsc
package typechecker

import scala.reflect.NameTransformer
import symtab.Flags._
import scala.reflect.internal.util.StringOps.ojoin
import scala.reflect.internal.util.ListOfNil

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

  import global._
  import definitions._
  import CODE._


  class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) {
    def mkThis = This(clazz) setPos clazz.pos.focus
    def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)(
      if (clazz.isClass) Select(This(clazz), sym) else Ident(sym)
    )

    private def isOverride(name: TermName) =
      clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz))

    def newMethodFlags(name: TermName) = {
      val overrideFlag = if (isOverride(name)) OVERRIDE else 0L
      overrideFlag | SYNTHETIC
    }
    def newMethodFlags(method: Symbol) = {
      val overrideFlag = if (isOverride(method.name.toTermName)) OVERRIDE else 0L
      (method.flags | overrideFlag | SYNTHETIC) & ~DEFERRED
    }

    private def finishMethod(method: Symbol, f: Symbol => Tree): Tree =
      localTyper typed (
        if (method.isLazy) ValDef(method, f(method))
        else DefDef(method, f(method))
      )

    private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = {
      val name1 = name.toTermName
      val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1))
      finishMethod(m setInfoAndEnter info, f)
    }
    private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = {
      val name1 = name.toTermName
      val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1))
      finishMethod(m setInfoAndEnter infoFn(m), f)
    }
    private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = {
      val m = original.cloneSymbol(clazz, newMethodFlags(original), name) setPos clazz.pos.focus
      finishMethod(clazz.info.decls enter m, f)
    }

    def clazzMember(name: Name)  = clazz.info nonPrivateMember name
    def typeInClazz(sym: Symbol) = clazz.thisType memberType sym

    def deriveMethod(original: Symbol, nameFn: Name => Name)(f: Symbol => Tree): Tree =
      cloneInternal(original, f, nameFn(original.name))

    def createMethod(name: Name, paramTypes: List[Type], returnType: Type)(f: Symbol => Tree): Tree =
      createInternal(name, f, (m: Symbol) => MethodType(m newSyntheticValueParams paramTypes, returnType))

    def createMethod(name: Name, returnType: Type)(f: Symbol => Tree): Tree =
      createInternal(name, f, NullaryMethodType(returnType))

    def createMethod(original: Symbol)(f: Symbol => Tree): Tree =
      createInternal(original.name, f, original.info)

    def forwardMethod(original: Symbol, newMethod: Symbol)(transformArgs: List[Tree] => List[Tree]): Tree =
      createMethod(original)(m => gen.mkMethodCall(newMethod, transformArgs(m.paramss.head map Ident)))

    def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = {
      createMethod(name, List(IntTpe), returnType) { m =>
        val arg0    = Ident(m.firstParam)
        val default = DEFAULT ==> Throw(IndexOutOfBoundsExceptionClass.tpe_*, fn(arg0, nme.toString_))
        val cases   = range.map(num => CASE(LIT(num)) ==> f(num)).toList :+ default

        Match(arg0, cases)
      }
    }

    // def foo() = constant
    def constantMethod(name: Name, value: Any): Tree = {
      val constant = Constant(value)
      createMethod(name, Nil, constant.tpe)(_ => Literal(constant))
    }
    // def foo = constant
    def constantNullary(name: Name, value: Any): Tree = {
      val constant = Constant(value)
      createMethod(name, constant.tpe)(_ => Literal(constant))
    }
  }

  /** There are two key methods in here.
   *
   *   1) Enter methods such as enterGetterSetter are called
   *   from Namer with a tree which may generate further trees such as accessors or
   *   implicit wrappers. Some setup is performed.  In general this creates symbols
   *   and enters them into the scope of the owner.
   *
   *   2) addDerivedTrees 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 the enter methods.  Those trees then become
   *   part of the typed template.
   */
  trait MethodSynth {
    self: Namer =>

    import NamerErrorGen._


    import treeInfo.noFieldFor

    // populate synthetics for this unit with trees that will later be added by the typer
    // we get here when entering the symbol for the valdef, so its rhs has not yet been type checked
    def enterGetterSetter(tree: ValDef): Unit = {
      val fieldSym =
        if (noFieldFor(tree, owner)) NoSymbol
        else owner.newValue(tree.name append NameTransformer.LOCAL_SUFFIX_STRING, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal)

      val getter = Getter(tree)
      val getterSym = getter.createSym

      // only one symbol can have `tree.pos`, the others must focus their position
      // normally the field gets the range position, but if there is none, give it to the getter
      //
      // SI-10009 the tree's modifiers can be temporarily out of sync with the new symbol's flags.
      //          typedValDef corrects this later on.
      tree.symbol = fieldSym orElse (getterSym setPos tree.pos)

      val namer = namerOf(tree.symbol)

      // the valdef gets the accessor symbol for a lazy val (too much going on in its RHS)
      // the fields phase creates the field symbol
      if (!tree.mods.isLazy) {
        // if there's a field symbol, the getter is considered a synthetic that must be added later
        // if there's no field symbol, the ValDef tree receives the getter symbol and thus is not a synthetic
        if (fieldSym != NoSymbol) {
          context.unit.synthetics(getterSym) = getter.derivedTree(getterSym)
          getterSym setInfo namer.accessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = false)
        } else getterSym setInfo namer.valTypeCompleter(tree)

        enterInScope(getterSym)

        if (getter.needsSetter) {
          val setter = Setter(tree)
          val setterSym = setter.createSym
          context.unit.synthetics(setterSym) = setter.derivedTree(setterSym)
          setterSym setInfo namer.accessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = true)
          enterInScope(setterSym)
        }

        // TODO: delay emitting the field to the fields phase (except for private[this] vals, which only get a field and no accessors)
        if (fieldSym != NoSymbol) {
          fieldSym setInfo namer.valTypeCompleter(tree)
          enterInScope(fieldSym)
        }
      } else {
        getterSym setInfo namer.valTypeCompleter(tree)
        enterInScope(getterSym)
      }

      deriveBeanAccessors(tree, namer)
    }

    private def deriveBeanAccessors(tree: ValDef, namer: Namer): Unit = {
      // TODO: can we look at the annotations symbols? (name-based introduced in 8cc477f8b6, see neg/t3403)
      val hasBeanProperty = tree.mods hasAnnotationNamed tpnme.BeanPropertyAnnot
      val hasBoolBP = tree.mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot

      if (hasBeanProperty || hasBoolBP) {
        if (!tree.name.charAt(0).isLetter) BeanPropertyAnnotationFieldWithoutLetterError(tree)
        // avoids name clashes with private fields in traits
        else if (tree.mods.isPrivate) BeanPropertyAnnotationPrivateFieldError(tree)

        val derivedPos = tree.pos.focus
        val missingTpt = tree.tpt.isEmpty

        def deriveBeanAccessor(prefix: String): Symbol = {
          val isSetter = prefix == "set"
          val name = newTermName(prefix + tree.name.toString.capitalize)
          val setterParam = nme.syntheticParamName(1)

          // note: tree.tpt may be EmptyTree, which will be a problem when use as the tpt of a parameter
          // the completer will patch this up (we can't do this now without completing the field)
          val tptToPatch = if (missingTpt) TypeTree() else tree.tpt.duplicate

          val (vparams, tpt) =
            if (isSetter) (List(ValDef(Modifiers(PARAM | SYNTHETIC), setterParam, tptToPatch, EmptyTree)), TypeTree(UnitTpe))
            else (Nil, tptToPatch)

          val rhs =
            if (tree.mods.isDeferred) EmptyTree
            else if (isSetter) Apply(Ident(tree.name.setterName), List(Ident(setterParam)))
            else Select(This(owner), tree.name)

          val sym = createMethod(tree, name, derivedPos, tree.mods.flags & BeanPropertyFlags)
          context.unit.synthetics(sym) = newDefDef(sym, rhs)(tparams = Nil, vparamss = List(vparams), tpt = tpt)
          sym
        }

        val getterCompleter = namer.accessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = false)
        enterInScope(deriveBeanAccessor(if (hasBeanProperty) "get" else "is") setInfo getterCompleter)

        if (tree.mods.isMutable) {
          val setterCompleter = namer.accessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = true)
          enterInScope(deriveBeanAccessor("set") setInfo setterCompleter)
        }
      }
    }


    def enterImplicitWrapper(classDef: ClassDef): Unit = {
      val methDef = factoryMeth(classDef.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC, classDef.name.toTermName, classDef)
      val methSym = assignAndEnterSymbol(methDef)
      context.unit.synthetics(methSym) = methDef
      methSym setInfo implicitFactoryMethodCompleter(methDef, classDef.symbol, completerOf(methDef).asInstanceOf[LockingTypeCompleter])
    }


    trait DerivedAccessor {
      def tree: ValDef
      def derivedName: TermName
      def derivedFlags: Long
      def derivedTree(sym: Symbol): Tree

      def derivedPos = tree.pos.focus
      def createSym = createMethod(tree, derivedName, derivedPos, derivedFlags)
    }

    case class Getter(tree: ValDef) extends DerivedAccessor {
      def derivedName  = tree.name
      def derivedFlags = tree.mods.flags & GetterFlags | ACCESSOR.toLong | ( if (needsSetter) 0 else STABLE )
      def needsSetter  = tree.mods.isMutable  // implies !lazy

      override def derivedTree(derivedSym: Symbol) = {
        val missingTpt = tree.tpt.isEmpty
        val tpt = if (missingTpt) TypeTree() else tree.tpt.duplicate

        val rhs =
          if (noFieldFor(tree, owner)) tree.rhs // context.unit.transformed.getOrElse(tree.rhs, tree.rhs)
          else Select(This(tree.symbol.enclClass), tree.symbol)

        newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = Nil, tpt = tpt)
      }

//        derivedSym setPos tree.pos
//        // ValDef will have its position focused whereas DefDef will have original correct rangepos
//        // ideally positions would be correct at the creation time but lazy vals are really a special case
//        // here so for the sake of keeping api clean we fix positions manually in LazyValGetter
//        tpt.setPos(tree.tpt.pos)
//        tree.tpt.setPos(tree.tpt.pos.focus)

    }

    case class Setter(tree: ValDef) extends DerivedAccessor {
      def derivedName  = tree.setterName
      def derivedFlags = tree.mods.flags & SetterFlags | ACCESSOR
      def derivedTree(derivedSym: Symbol)  = {
        val setterParam = nme.syntheticParamName(1)

        // note: tree.tpt may be EmptyTree, which will be a problem when use as the tpt of a parameter
        // the completer will patch this up (we can't do this now without completing the field)
        val missingTpt = tree.tpt.isEmpty
        val tptToPatch = if (missingTpt) TypeTree() else tree.tpt.duplicate

        val vparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), setterParam, tptToPatch, EmptyTree))

        val tpt = TypeTree(UnitTpe)

        val rhs =
          if (noFieldFor(tree, owner)) EmptyTree
          else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam))

        newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = List(vparams), tpt = tpt)

      }
    }

  }
}