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
|
package dotty.tools.dotc
package transform
import core._
import DenotTransformers._
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._
/** Provides the implementations of all getters and setters, introducing
* fields to hold the value accessed by them.
* TODO: Make LazyVals a part of this phase?
*
* <accessor> <stable> <mods> def x(): T = e
* --> private val x: T = e
* <accessor> <stable> <mods> def x(): T = x
*
* <accessor> <mods> def x(): T = e
* --> private var x: T = e
* <accessor> <mods> def x(): T = x
*
* <accessor> <mods> def x_=(y: T): Unit = ()
* --> <accessor> <mods> def x_=(y: T): Unit = x = y
*/
class Memoize extends MiniPhaseTransform with IdentityDenotTransformer { thisTransform =>
import ast.tpd._
override def phaseName = "memoize"
/* Makes sure that, after getters and constructors gen, there doesn't
* exist non-deferred definitions that are not implemented. */
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
def errorLackImplementation(t: Tree) = {
val firstPhaseId = t.symbol.initial.validFor.firstPhaseId
val definingPhase = ctx.withPhase(firstPhaseId).phase.prev
throw new AssertionError(
i"Non-deferred definition introduced by $definingPhase lacks implementation: $t")
}
tree match {
case ddef: DefDef
if !ddef.symbol.is(Deferred) &&
!ddef.symbol.isConstructor && // constructors bodies are added later at phase Constructors
ddef.rhs == EmptyTree =>
errorLackImplementation(ddef)
case tdef: TypeDef
if tdef.symbol.isClass && !tdef.symbol.is(Deferred) && tdef.rhs == EmptyTree =>
errorLackImplementation(tdef)
case _ =>
}
super.checkPostCondition(tree)
}
/** Should run after mixin so that fields get generated in the
* class that contains the concrete getter rather than the trait
* that defines it.
*/
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Mixin])
override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
val sym = tree.symbol
def newField = {
val fieldType =
if (sym.isGetter) sym.info.resultType
else /*sym.isSetter*/ sym.info.firstParamTypes.head
ctx.newSymbol(
owner = ctx.owner,
name = sym.name.asTermName.fieldName,
flags = Private | (if (sym is Stable) EmptyFlags else Mutable),
info = fieldType,
coord = tree.pos)
.withAnnotationsCarrying(sym, defn.FieldMetaAnnot)
.enteredAfter(thisTransform)
}
/** Can be used to filter annotations on getters and setters; not used yet */
def keepAnnotations(denot: SymDenotation, meta: ClassSymbol) = {
val cpy = sym.copySymDenotation()
cpy.filterAnnotations(_.symbol.derivesFrom(meta))
if (cpy.annotations ne denot.annotations) cpy.installAfter(thisTransform)
}
lazy val field = sym.field.orElse(newField).asTerm
def adaptToField(tree: Tree) =
if (tree.isEmpty) tree else tree.ensureConforms(field.info.widen)
if (sym.is(Accessor, butNot = NoFieldNeeded))
if (sym.isGetter) {
def skipBlocks(t: Tree): Tree = t match {
case Block(_, t1) => skipBlocks(t1)
case _ => t
}
skipBlocks(tree.rhs) match {
case lit: Literal if sym.is(Final) && isIdempotentExpr(tree.rhs) =>
// duplicating scalac behavior: for final vals that have rhs as constant, we do not create a field
// and instead return the value. This seemingly minor optimization has huge effect on initialization
// order and the values that can be observed during superconstructor call
// see remark about idempotency in PostTyper#normalizeTree
cpy.DefDef(tree)(rhs = lit)
case _ =>
var rhs = tree.rhs.changeOwnerAfter(sym, field, thisTransform)
if (isWildcardArg(rhs)) rhs = EmptyTree
val fieldDef = transformFollowing(ValDef(field, adaptToField(rhs)))
val getterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(ref(field))(ctx.withOwner(sym), info))
Thicket(fieldDef, getterDef)
}
} else if (sym.isSetter) {
if (!sym.is(ParamAccessor)) { val Literal(Constant(())) = tree.rhs } // this is intended as an assertion
field.setFlag(Mutable) // necessary for vals mixed in from Scala2 traits
val initializer = Assign(ref(field), adaptToField(ref(tree.vparamss.head.head.symbol)))
cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(ctx.withOwner(sym), info))
}
else tree // curiously, some accessors from Scala2 have ' ' suffixes. They count as
// neither getters nor setters
else tree
}
private val NoFieldNeeded = Lazy | Deferred | JavaDefined
}
|