Skip to content

Commit 9b6cd3d

Browse files
committed
Fix #9213: handle valdefs in mixin parent constructors
1 parent ebe223e commit 9b6cd3d

File tree

2 files changed

+65
-3
lines changed

2 files changed

+65
-3
lines changed

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

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
package dotty.tools.dotc
1+
package dotty.tools
2+
package dotc
23
package transform
34

45
import core._
@@ -194,7 +195,24 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
194195
for (p <- impl.parents; constr = stripBlock(p).symbol if constr.isConstructor)
195196
yield constr.owner -> transformConstructor(p)
196197
).toMap
197-
val superCalls = superCallsAndArgs.transform((_, v) => v._1)
198+
199+
/** Definitions in a parent trait constructor call (these can arise
200+
* due to reorderings with named and/or default parameters).
201+
*/
202+
def prefix(tree: Tree): List[Tree] =
203+
if stripBlock(tree).symbol.owner.is(Trait) then
204+
tree match
205+
case Block(stats, expr) => stats ::: prefix(expr)
206+
case _ => Nil
207+
else Nil
208+
209+
/** the proper trait parent constructor call, without any preceding val defs */
210+
def properCall(tree: Tree): Tree =
211+
val call = stripBlock(tree)
212+
if call.symbol.owner.is(Trait) then call else tree
213+
214+
val prefixes = superCallsAndArgs.transform((_, v) => prefix(v._1))
215+
val superCalls = superCallsAndArgs.transform((_, v) => properCall(v._1))
198216
val initArgs = superCallsAndArgs.transform((_, v) => v._2)
199217

200218
def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match {
@@ -211,6 +229,38 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
211229
def wasOneOf(sym: Symbol, flags: FlagSet) =
212230
ctx.atPhase(thisPhase) { sym.isOneOf(flags) }
213231

232+
/** The prefix definitions of a mixin parent constructor, lifted
233+
* to the enclosing class.
234+
*/
235+
def traitConstrPrefix(mixin: ClassSymbol): List[Tree] =
236+
prefixes.get(mixin) match
237+
case Some(stats) =>
238+
stats.map {
239+
case stat: ValDef =>
240+
stat.symbol.copySymDenotation(
241+
owner = cls,
242+
initFlags = stat.symbol.flags | PrivateLocal
243+
).installAfter(thisPhase)
244+
stat.symbol.enteredAfter(thisPhase)
245+
stat
246+
}
247+
case _ =>
248+
Nil
249+
250+
/** Adapt tree so that references to valdefs that are lifted to the
251+
* class now use `this` as a prefix
252+
*/
253+
def adaptToPrefix(stat: Tree, prefixSyms: List[Symbol]) =
254+
if prefixSyms.isEmpty then stat
255+
else
256+
val m = new TreeMap:
257+
override def transform(tree: Tree)(using Context) = tree match
258+
case tree: Ident if prefixSyms.contains(tree.symbol) =>
259+
This(cls).select(tree.symbol).withSpan(tree.span)
260+
case _ =>
261+
super.transform(tree)
262+
m.transform(stat)
263+
214264
def traitInits(mixin: ClassSymbol): List[Tree] = {
215265
var argNum = 0
216266
def nextArgument() = initArgs.get(mixin) match {
@@ -275,7 +325,11 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
275325
if (cls.is(Trait)) traitDefs(impl.body)
276326
else if (!cls.isPrimitiveValueClass) {
277327
val mixInits = mixins.flatMap { mixin =>
278-
flatten(traitInits(mixin)) ::: superCallOpt(mixin) ::: setters(mixin) ::: mixinForwarders(mixin)
328+
val prefix = traitConstrPrefix(mixin)
329+
val prefixSyms = prefix.map(_.symbol)
330+
val initsAndCall = (flatten(traitInits(mixin)) ::: superCallOpt(mixin))
331+
.map(adaptToPrefix(_, prefixSyms))
332+
prefix ::: initsAndCall ::: setters(mixin) ::: mixinForwarders(mixin)
279333
}
280334
superCallOpt(superCls) ::: mixInits ::: impl.body
281335
}

tests/pos/i9213.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
trait A(a: Any, b: Int) {
2+
//var x = 0
3+
}
4+
// class A(a: String, b: Int) {
5+
//
6+
// } //OK!
7+
object B extends A(b = 0, a = String(""))
8+
// object B extends A(a = String(""), b = 0) //OK!

0 commit comments

Comments
 (0)