Skip to content

Commit 8ec91d4

Browse files
oderskyDarkDimius
authored andcommitted
Completed constructors phase
1) Type parameters are now copied to accessors 2) Constructors also work for traits 2) makes it possible do to mixin after constructors.
1 parent 478b97a commit 8ec91d4

File tree

2 files changed

+141
-71
lines changed

2 files changed

+141
-71
lines changed

src/dotty/tools/dotc/core/Flags.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -543,12 +543,15 @@ object Flags {
543543
/** Labeled private[this] */
544544
final val PrivateLocal = allOf(Private, Local)
545545

546-
/** A private parameter accessor */
546+
/** A private[this] parameter accessor */
547547
final val PrivateLocalParamAccessor = allOf(Private, Local, ParamAccessor)
548548

549-
/** A private parameter */
549+
/** A private[this] parameter */
550550
final val PrivateLocalParam = allOf(Private, Local, Param)
551551

552+
/** A private parameter accessor */
553+
final val PrivateParamAccessor = allOf(Private, ParamAccessor)
554+
552555
/** A local parameter */
553556
final val ParamAndLocal = allOf(Param, Local)
554557

src/dotty/tools/dotc/transform/Constructors.scala

Lines changed: 136 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ import SymDenotations._
1616
import Types._
1717
import Decorators._
1818
import DenotTransformers._
19-
import ExplicitOuter.outerParamAccessor
19+
import collection.mutable
2020

21-
/** This transform moves initializers from body to constructor.
21+
/** This transform
22+
* - moves initializers from body to constructor.
23+
* - makes all supercalls explicit
2224
*/
2325
class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform =>
2426
import tpd._
@@ -28,101 +30,166 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
2830

2931
override def treeTransformPhase = thisTransform.next
3032

33+
/** Symbols that are owned by either <local dummy> or a class field move into the
34+
* primary constructor.
35+
*/
3136
override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation = {
3237
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
3640
if (ownerBecomesConstructor(sym.owner))
3741
sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor)
3842
else sym
3943
}
4044

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 = _
4260
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
4361
case Ident(_) | Select(This(_), _) =>
4462
val sym = tree.symbol
45-
if (sym is ParamAccessor) {
63+
if (sym is (ParamAccessor, butNot = excluded)) {
4664
val param = sym.subst(accessors, params)
4765
if (param ne sym) ref(param).withPos(tree.pos)
4866
else tree
4967
}
5068
else tree
5169
case Apply(fn, Nil) =>
5270
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
6573
else cpy.Apply(tree)(fn1, Nil)
6674
case _ =>
67-
super.transform(tree)
75+
if (noDirectRefsFrom(tree)) tree else super.transform(tree)
6876
}
69-
}
7077

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+
}
8782
}
8883

8984
override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = {
9085
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)
106146
case _ =>
147+
(intoConstr(stat, excluded = Mutable) :: constrStats, clsStats)
107148
}
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)
121165
}
122-
cpy.Template(tree)(
123-
constr = cpy.DefDef(constr)(rhs = Block(superCall :: constrStats, unitLiteral)),
124-
parents = superType :: traitParents,
125-
body = clsStats)
126166
}
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+
})
127194
}
128195
}

0 commit comments

Comments
 (0)