Skip to content

Commit 53f5531

Browse files
authored
Merge pull request #14919 from dotty-staging/fix-14164
Also hoist lifted arguments in super calls
2 parents 3156d50 + 077652c commit 53f5531

File tree

3 files changed

+71
-23
lines changed

3 files changed

+71
-23
lines changed

compiler/src/dotty/tools/dotc/transform/HoistSuperArgs.scala

+43-22
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,12 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
6060

6161
/** If argument is complex, hoist it out into its own method and refer to the
6262
* method instead.
63-
* @param arg The argument that might be hoisted
64-
* @param cdef The definition of the constructor from which the call is made
63+
* @param arg The argument that might be hoisted
64+
* @param cdef The definition of the constructor from which the call is made
65+
* @param lifted Argument definitions that were lifted out in a call prefix
6566
* @return The argument after possible hoisting
6667
*/
67-
private def hoistSuperArg(arg: Tree, cdef: DefDef): Tree = {
68+
private def hoistSuperArg(arg: Tree, cdef: DefDef, lifted: List[Symbol]): Tree = {
6869
val constr = cdef.symbol
6970
lazy val origParams = // The parameters that can be accessed in the supercall
7071
if (constr == cls.primaryConstructor)
@@ -92,36 +93,39 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
9293
val argTypeWrtConstr = argType.widenTermRefExpr.subst(origParams, allParamRefs(constr.info))
9394
// argType with references to paramRefs of the primary constructor instead of
9495
// local parameter accessors
96+
val abstractedArgType =
97+
if lifted.isEmpty then argTypeWrtConstr
98+
else MethodType.fromSymbols(lifted, argTypeWrtConstr)
9599
newSymbol(
96100
owner = methOwner,
97101
name = SuperArgName.fresh(cls.name.toTermName),
98102
flags = Synthetic | Private | Method | staticFlag,
99-
info = replaceResult(constr.info, argTypeWrtConstr),
103+
info = replaceResult(constr.info, abstractedArgType),
100104
coord = constr.coord
101105
).enteredAfter(thisPhase)
102106
}
103107

104108
/** Type of a reference implies that it needs to be hoisted */
105109
def refNeedsHoist(tp: Type): Boolean = tp match {
106-
case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls
110+
case tp: ThisType => !tp.cls.isStaticOwner && !cls.isContainedIn(tp.cls)
107111
case tp: TermRef => refNeedsHoist(tp.prefix)
108112
case _ => false
109113
}
110114

111115
/** Super call argument is complex, needs to be hoisted */
112-
def needsHoist(tree: Tree) = tree match {
116+
def needsHoist(tree: Tree) = tree match
113117
case _: DefDef => true
114118
case _: Template => true
115119
case _: New => !tree.tpe.typeSymbol.isStatic
116120
case _: RefTree | _: This => refNeedsHoist(tree.tpe)
117121
case _ => false
118-
}
119122

120123
/** Only rewire types that are owned by the current Hoister and is an param or accessor */
121124
def needsRewire(tp: Type) = tp match {
122125
case ntp: NamedType =>
123126
val owner = ntp.symbol.maybeOwner
124127
(owner == cls || owner == constr) && ntp.symbol.isParamOrAccessor
128+
|| lifted.contains(ntp.symbol)
125129
case _ => false
126130
}
127131

@@ -134,7 +138,7 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
134138
if pref.isType then pref.tpe.typeSymbol else pref.symbol)
135139
val tmap = new TreeTypeMap(
136140
typeMap = new TypeMap {
137-
lazy val origToParam = origParams.zip(paramSyms).toMap
141+
lazy val origToParam = (origParams ::: lifted).zip(paramSyms).toMap
138142
def apply(tp: Type) = tp match {
139143
case tp: NamedType if needsRewire(tp) =>
140144
origToParam.get(tp.symbol) match {
@@ -164,35 +168,52 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
164168
Nil
165169
}
166170
val (typeParams, termParams) = origParams.span(_.isType)
167-
val res = ref(superMeth)
171+
var res = ref(superMeth)
168172
.appliedToTypes(typeParams.map(_.typeRef))
169173
.appliedToArgss(termParamRefs(constr.info, termParams))
174+
if lifted.nonEmpty then
175+
res = res.appliedToArgs(lifted.map(ref))
170176
report.log(i"hoist $arg, cls = $cls = $res")
171177
res
172178
case _ => arg
173179
}
174180
}
175181

176182
/** Hoist complex arguments in super call out of the class. */
177-
def hoistSuperArgsFromCall(superCall: Tree, cdef: DefDef): Tree = superCall match {
183+
def hoistSuperArgsFromCall(superCall: Tree, cdef: DefDef, lifted: mutable.ListBuffer[Symbol]): Tree = superCall match
184+
case Block(defs, expr) =>
185+
cpy.Block(superCall)(
186+
stats = defs.mapconserve {
187+
case vdef: ValDef =>
188+
try cpy.ValDef(vdef)(rhs = hoistSuperArg(vdef.rhs, cdef, lifted.toList))
189+
finally lifted += vdef.symbol
190+
case ddef: DefDef =>
191+
try cpy.DefDef(ddef)(rhs = hoistSuperArg(ddef.rhs, cdef, lifted.toList))
192+
finally lifted += ddef.symbol
193+
case stat =>
194+
stat
195+
},
196+
expr = hoistSuperArgsFromCall(expr, cdef, lifted))
178197
case Apply(fn, args) =>
179-
cpy.Apply(superCall)(hoistSuperArgsFromCall(fn, cdef), args.mapconserve(hoistSuperArg(_, cdef)))
198+
cpy.Apply(superCall)(
199+
hoistSuperArgsFromCall(fn, cdef, lifted),
200+
args.mapconserve(hoistSuperArg(_, cdef, lifted.toList)))
180201
case _ =>
181202
superCall
182-
}
183203

184204
/** Hoist complex arguments in this-constructor call of secondary constructor out of the class. */
185205
def hoistSuperArgsFromConstr(stat: Tree): Tree = stat match {
186-
case stat: DefDef if stat.symbol.isClassConstructor =>
187-
cpy.DefDef(stat)(rhs =
188-
stat.rhs match {
189-
case Block(superCall :: stats, expr) =>
190-
val superCall1 = hoistSuperArgsFromCall(superCall, stat)
191-
if (superCall1 eq superCall) stat.rhs
192-
else cpy.Block(stat.rhs)(superCall1 :: stats, expr)
206+
case constr: DefDef if constr.symbol.isClassConstructor =>
207+
val lifted = new mutable.ListBuffer[Symbol]
208+
cpy.DefDef(constr)(rhs =
209+
constr.rhs match
210+
case Block(stats @ (superCall :: stats1), expr: Literal) =>
211+
cpy.Block(constr.rhs)(
212+
stats.derivedCons(hoistSuperArgsFromCall(superCall, constr, lifted), stats1),
213+
expr)
193214
case _ =>
194-
hoistSuperArgsFromCall(stat.rhs, stat)
195-
})
215+
hoistSuperArgsFromCall(constr.rhs, constr, lifted)
216+
)
196217
case _ =>
197218
stat
198219
}
@@ -202,7 +223,7 @@ class HoistSuperArgs extends MiniPhase with IdentityDenotTransformer { thisPhase
202223
tdef.rhs match {
203224
case impl @ Template(cdef, superCall :: others, _, _) =>
204225
val hoist = new Hoister(tdef.symbol)
205-
val hoistedSuperCall = hoist.hoistSuperArgsFromCall(superCall, cdef)
226+
val hoistedSuperCall = hoist.hoistSuperArgsFromCall(superCall, cdef, new mutable.ListBuffer)
206227
val hoistedBody = impl.body.mapconserve(hoist.hoistSuperArgsFromConstr)
207228
if (hoist.superArgDefs.isEmpty) tdef
208229
else {

tests/run/i14164.scala

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
object Test:
3+
class Base(a: String = "x", param: String)
4+
5+
class Child extends Base(
6+
param =
7+
for x <- Seq("a") yield x
8+
"param"
9+
)
10+
11+
new Child
12+
13+
def main(args: Array[String]) = ()
14+
15+
end Test
16+
17+
class Test2:
18+
class Inner(withDefault: String = "inner")(
19+
dependentDefault: String = withDefault) extends Object {
20+
def this(x: Int) = this(x.toString)()
21+
}
22+
23+
class Test3:
24+
class Inner(withDefault: () => String = () => "inner")(
25+
dependentDefault: String = withDefault()) extends Object {
26+
def this(x: Int) = this(() => x.toString)()
27+
}

0 commit comments

Comments
 (0)