aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/transform/GettersSetters.scala
blob: bbe5740ffde05dd0976412b40e0f4310c9d19440 (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
package dotty.tools.dotc
package transform

import core._
import DenotTransformers.SymTransformer
import Phases.Phase
import Contexts.Context
import SymDenotations.SymDenotation
import Types._
import Symbols._
import SymUtils._
import Constants._
import ast.Trees._
import TreeTransforms._
import NameOps._
import Flags._
import Decorators._

/** Performs the following rewritings on fields of classes, where `x_L` is the "local name" of `x`:
 *
 *    val x: T = e
 *      -->  private val x_L: T = e
 *           <stable> def x: T = x_L
 *
 *    var x: T = e
 *    def x_=(y: T) = ()
 *      -->  private var x_L: T = e
 *           def x: T = x_L
 *           def x_=(x: T): Unit = x_L = x                            (if in class or trait)
 *
 *    lazy val x: T = e
 *      --> def x: T = e
 *
 *    val x: T
 *      -->  <stable> def x: T
 *
 *    var x: T
 *      -->  def x: T
 *
 *  Omitted from the rewritings are
 *
 *   - private[this] fields in non-trait classes
 *   - fields generated for static modules (TODO: needed?)
 *   - parameters, static fields, and fields coming from Java
 *
 *  Furthermore, assignements to mutable vars are replaced by setter calls
 *
 *     p.x = e
 *      -->  p.x_=(e)
 */
 class GettersSetters extends MiniPhaseTransform with SymTransformer { thisTransform =>
  import ast.tpd._

  override def phaseName = "gettersSetters"

  override def treeTransformPhase = thisTransform.next

  override def transformSym(d: SymDenotation)(implicit ctx: Context): SymDenotation = {
    def noGetterNeeded =
      d.is(NoGetterNeeded) ||
      d.initial.asInstanceOf[SymDenotation].is(PrivateLocal) && !d.owner.is(Trait) ||
      d.is(Module) && d.isStatic ||
      d.isSelfSym
    if (d.isTerm && d.owner.isClass && d.info.isValueType && !noGetterNeeded) {
      val maybeStable = if (d.isStable) Stable else EmptyFlags
      //if (d.name.toString == "_") println(i"make accessor $d in ${d.owner} ${d.symbol.id}")
      d.copySymDenotation(
        initFlags = d.flags | maybeStable | AccessorCreationFlags,
        info = ExprType(d.info))
    }
    else d
  }
  private val NoGetterNeeded = Method | Param | JavaDefined | JavaStatic
  private val NoFieldNeeded  = Lazy | Deferred | ParamAccessor

  override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
    if (tree.symbol is Method) {
      val getter = tree.symbol.asTerm
      assert(getter is Accessor)
      if (getter is NoFieldNeeded)
        DefDef(getter, tree.rhs)
      else {
        val inTrait = getter.owner.is(Trait)
        val field = ctx.newSymbol(
          owner = ctx.owner,
          name = getter.name.fieldName,
          flags = Private | (getter.flags & Mutable),
          info = getter.info.resultType).enteredAfter(thisTransform)
        assert(tree.rhs.tpe.exists, tree.show)
        val fieldDef =
          cpy.ValDef(tree)(
            name = field.name,
            rhs = tree.rhs.changeOwner(getter, field).ensureConforms(field.info.widen)
          ).withType(field.valRef)
        val rhs = ref(field)
        assert(rhs.hasType)
        val getterDef = DefDef(getter, rhs.ensureConforms(getter.info.widen))
        Thicket(fieldDef, getterDef)
      }
    }
    else tree
  }

  override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree =
    if (tree.symbol.isSetter && !tree.symbol.is(Deferred | ParamAccessor | JavaDefined)) {
      val Literal(Constant(())) = tree.rhs
      assert(tree.symbol.field.exists, i"no field for ${tree.symbol.showLocated}")
      val initializer = Assign(ref(tree.symbol.field), ref(tree.vparamss.head.head.symbol))
      assert(initializer.hasType)
      cpy.DefDef(tree)(rhs = initializer)
    }
    else tree

  override def transformAssign(tree: Assign)(implicit ctx: Context, info: TransformerInfo): Tree =
    if (tree.lhs.symbol is Method) tree.lhs.becomes(tree.rhs)
    else tree
}