@@ -16,9 +16,11 @@ import SymDenotations._
16
16
import Types ._
17
17
import Decorators ._
18
18
import DenotTransformers ._
19
- import ExplicitOuter . outerParamAccessor
19
+ import collection . mutable
20
20
21
- /** This transform moves initializers from body to constructor.
21
+ /** This transform
22
+ * - moves initializers from body to constructor.
23
+ * - makes all supercalls explicit
22
24
*/
23
25
class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform =>
24
26
import tpd ._
@@ -28,101 +30,166 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
28
30
29
31
override def treeTransformPhase = thisTransform.next
30
32
33
+ /** Symbols that are owned by either <local dummy> or a class field move into the
34
+ * primary constructor.
35
+ */
31
36
override def transformSym (sym : SymDenotation )(implicit ctx : Context ): SymDenotation = {
32
37
def ownerBecomesConstructor (owner : Symbol ): Boolean =
33
- (owner.isLocalDummy ||
34
- owner.isTerm && ! owner.is(Method ) && owner.owner.isClass) &&
35
- ! owner.enclosingClass.is(Trait ) // TODO: Remove qualification once Mixin is operational
38
+ (owner.isLocalDummy || owner.isTerm && ! owner.is(Method )) &&
39
+ owner.owner.isClass
36
40
if (ownerBecomesConstructor(sym.owner))
37
41
sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor)
38
42
else sym
39
43
}
40
44
41
- private def intoConstr (accessors : List [Symbol ], params : List [Symbol ]) = new TreeMap {
45
+ /** @return true if after ExplicitOuter, all references from this tree go via an
46
+ * outer link, so no parameter accessors need to be rewired to parameters
47
+ */
48
+ private def noDirectRefsFrom (tree : Tree )(implicit ctx : Context ) =
49
+ tree.isDef && tree.symbol.isClass && ! tree.symbol.is(InSuperCall )
50
+
51
+ /** Adjustments performed when moving code into the constructor:
52
+ * (1) Replace references to param accessors by constructor parameters
53
+ * except possibly references to mutable variables, if `excluded = Mutable`.
54
+ * (Mutable parameters should be replaced only during the super call)
55
+ * (2) If the parameter accessor reference was to an alias getter,
56
+ * drop the () when replacing by the parameter.
57
+ */
58
+ class IntoConstrMap (accessors : List [Symbol ], params : List [Symbol ]) extends TreeMap {
59
+ private var excluded : FlagSet = _
42
60
override def transform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
43
61
case Ident (_) | Select (This (_), _) =>
44
62
val sym = tree.symbol
45
- if (sym is ParamAccessor ) {
63
+ if (sym is ( ParamAccessor , butNot = excluded) ) {
46
64
val param = sym.subst(accessors, params)
47
65
if (param ne sym) ref(param).withPos(tree.pos)
48
66
else tree
49
67
}
50
68
else tree
51
69
case Apply (fn, Nil ) =>
52
70
val fn1 = transform(fn)
53
- if ((fn1 ne fn) &&
54
- fn1.symbol.is(Param ) &&
55
- fn1.symbol.owner.isPrimaryConstructor) {
56
- // Two possible cases, which each need their adaptation:
57
- if (fn1.symbol.initial.info.isInstanceOf [ExprType ])
58
- // it's either a call-by-name parameter, which is erased to Function0,
59
- // then we need to insert an apply.
60
- cpy.Apply (tree)(Select (fn1, nme.apply), Nil ).ensureConforms(tree.tpe)
61
- else
62
- // or original accessor was an alias accessor, then we need to drop the ()
63
- fn1
64
- }
71
+ if ((fn1 ne fn) && fn1.symbol.is(Param ) && fn1.symbol.owner.isPrimaryConstructor)
72
+ fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass
65
73
else cpy.Apply (tree)(fn1, Nil )
66
74
case _ =>
67
- super .transform(tree)
75
+ if (noDirectRefsFrom(tree)) tree else super .transform(tree)
68
76
}
69
- }
70
77
71
- private def splitStats (stats : List [Tree ])(implicit ctx : Context ): (List [Tree ], List [Tree ]) = stats match {
72
- case stat :: stats1 =>
73
- val (constrStats, clsStats) = splitStats(stats1)
74
- stat match {
75
- case stat @ ValDef (mods, name, tpt, rhs) if ! rhs.isEmpty =>
76
- val inits =
77
- if (isWildcardArg(rhs)) Nil
78
- else Assign (ref(stat.symbol), rhs).withPos(stat.pos) :: Nil
79
- (inits ::: constrStats, cpy.ValDef (stat)(rhs = EmptyTree ) :: clsStats)
80
- case _ : DefTree =>
81
- (constrStats, stat :: clsStats)
82
- case _ =>
83
- (stat :: constrStats, clsStats)
84
- }
85
- case Nil =>
86
- (Nil , Nil )
78
+ def apply (tree : Tree , excluded : FlagSet )(implicit ctx : Context ): Tree = {
79
+ this .excluded = excluded
80
+ transform(tree)
81
+ }
87
82
}
88
83
89
84
override def transformTemplate (tree : Template )(implicit ctx : Context , info : TransformerInfo ): Tree = {
90
85
val cls = ctx.owner.asClass
91
- if (cls is Trait ) tree
92
- else {
93
- val constr @ DefDef (_, nme.CONSTRUCTOR , Nil , vparams :: Nil , _, EmptyTree ) = tree.constr
94
- val (superApp @ Apply (
95
- superSel @ Select (
96
- superNew @ New (superType),
97
- nme.CONSTRUCTOR ),
98
- superArgs)) :: traitParents = tree.parents
99
- var accessors = cls.paramAccessors.filterNot(_.isSetter)
100
- var vparamsWithOuter = vparams
101
- if (! accessors.hasSameLengthAs(vparams)) {
102
- accessors.reverse match {
103
- case last :: _ if (last.name == nme.OUTER ) =>
104
- accessors = last :: accessors.init
105
- vparamsWithOuter = ValDef (last.asTerm) :: vparams
86
+ val constr @ DefDef (_, nme.CONSTRUCTOR , Nil , vparams :: Nil , _, EmptyTree ) = tree.constr
87
+
88
+ // Produce aligned accessors and constructor parameters. We have to adjust
89
+ // for any outer parameters, which are last in the sequence of original
90
+ // parameter accessors but should come first in the constructor parameter list.
91
+ var accessors = cls.paramAccessors.filterNot(_.isSetter)
92
+ var vparamsWithOuter = vparams
93
+ if (! accessors.hasSameLengthAs(vparams)) {
94
+ accessors.reverse match {
95
+ case last :: _ if (last.name == nme.OUTER ) =>
96
+ accessors = last :: accessors.init // align wth calling convention
97
+ vparamsWithOuter = ValDef (last.asTerm) :: vparams
98
+ case _ =>
99
+ }
100
+ assert(accessors.hasSameLengthAs(vparamsWithOuter),
101
+ i " lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter" )
102
+ }
103
+ val paramSyms = vparamsWithOuter map (_.symbol)
104
+
105
+ val intoConstr = new IntoConstrMap (accessors, paramSyms)
106
+ val superCalls = new mutable.ListBuffer [Tree ]
107
+
108
+ // If parent is a constructor call, pull out the call into a separate
109
+ // supercall constructor, which gets appended to `superCalls`, and keep
110
+ // only the type.
111
+ def normalizeParent (tree : Tree ) = tree match {
112
+ case superApp @ Apply (
113
+ superSel @ Select (
114
+ superNew @ New (superType),
115
+ nme.CONSTRUCTOR ),
116
+ superArgs) =>
117
+ val toClass = ! superType.symbol.is(Trait )
118
+ val mappedArgs = superArgs.map(
119
+ intoConstr(_, excluded = if (toClass) Mutable else EmptyFlags ))
120
+ val receiver =
121
+ if (toClass) Super (This (cls), tpnme.EMPTY , inConstrCall = true )
122
+ else This (cls)
123
+ superCalls += cpy.Apply (superApp)(
124
+ receiver.withPos(superNew.pos)
125
+ .select(superSel.symbol).withPos(superSel.pos),
126
+ mappedArgs)
127
+ superType
128
+ case tree : TypeTree => tree
129
+ }
130
+ val parentTypeTrees = tree.parents.map(normalizeParent)
131
+
132
+ // Split class body into statements that go into constructor and
133
+ // definitions that are kept as members of the class.
134
+ def splitStats (stats : List [Tree ]): (List [Tree ], List [Tree ]) = stats match {
135
+ case stat :: stats1 =>
136
+ val (constrStats, clsStats) = splitStats(stats1)
137
+ stat match {
138
+ case stat @ ValDef (mods, name, tpt, rhs) =>
139
+ val inits =
140
+ if (rhs.isEmpty || isWildcardArg(rhs)) Nil
141
+ else Assign (ref(stat.symbol), intoConstr(rhs, excluded = Mutable ))
142
+ .withPos(stat.pos) :: Nil
143
+ (inits ::: constrStats, cpy.ValDef (stat)(rhs = EmptyTree ) :: clsStats)
144
+ case _ : DefTree =>
145
+ (constrStats, stat :: clsStats)
106
146
case _ =>
147
+ (intoConstr(stat, excluded = Mutable ) :: constrStats, clsStats)
107
148
}
108
- assert(accessors.hasSameLengthAs(vparamsWithOuter),
109
- i " lengths differ for $cls, param accs = $accessors, params = $vparamsWithOuter" )
110
- }
111
- val mappedArgs = superArgs.map(
112
- intoConstr(accessors, vparamsWithOuter.map(_.symbol)).transform)
113
- val superCall =
114
- cpy.Apply (superApp)(
115
- cpy.Select (superSel)(
116
- Super (This (cls), tpnme.EMPTY , inConstrCall = true ).withPos(superNew.pos),
117
- nme.CONSTRUCTOR ),
118
- mappedArgs)
119
- val (constrStats, clsStats) = splitStats(tree.body)
120
- def normalizeOwner (stat : Tree ) = {
149
+ case Nil =>
150
+ (Nil , Nil )
151
+ }
152
+ val (constrStats, clsStats) = splitStats(tree.body)
153
+
154
+ // Collect all private parameter accessors that need to be retained
155
+ // because they are accessed after the constructor has finished.
156
+ val collectRetained = new TreeAccumulator [Set [Symbol ]] {
157
+ override def apply (retained : Set [Symbol ], tree : Tree ) = tree match {
158
+ case tree : RefTree =>
159
+ val sym = tree.symbol
160
+ foldOver(
161
+ if (sym.is(PrivateParamAccessor ) && sym.owner == cls) retained + sym else retained,
162
+ tree)
163
+ case _ =>
164
+ if (noDirectRefsFrom(tree)) retained else foldOver(retained, tree)
121
165
}
122
- cpy.Template (tree)(
123
- constr = cpy.DefDef (constr)(rhs = Block (superCall :: constrStats, unitLiteral)),
124
- parents = superType :: traitParents,
125
- body = clsStats)
126
166
}
167
+ val retainedPrivate = collectRetained(collectRetained(Set [Symbol ](), constrStats), clsStats)
168
+ def isRetained (acc : Symbol ) =
169
+ (! acc.is(Private ) || acc.is(NotJavaPrivate ) || retainedPrivate(acc))
170
+
171
+ val accessorFields = accessors.filterNot(_ is Method )
172
+ val (retainedAccessors, droppedAccessors) = accessorFields.partition(isRetained)
173
+
174
+ // The initializers for the retained accessors */
175
+ val copyParams = retainedAccessors.map(acc =>
176
+ Assign (ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos))
177
+
178
+ // Drop accessors that are not retained from class scope
179
+ if (droppedAccessors.nonEmpty) {
180
+ val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error
181
+ cls.copy(
182
+ info = clsInfo.derivedClassInfo(
183
+ decls = clsInfo.decls.filteredScope(! droppedAccessors.contains(_))))
184
+ }
185
+
186
+ cpy.Template (tree)(
187
+ constr = cpy.DefDef (constr)(
188
+ rhs = Block (superCalls.toList ::: copyParams ::: constrStats, unitLiteral)),
189
+ parents = parentTypeTrees,
190
+ body = clsStats filter {
191
+ case vdef : ValDef => ! droppedAccessors.contains(vdef.symbol)
192
+ case _ => true
193
+ })
127
194
}
128
195
}
0 commit comments