Skip to content

Commit 4b3fdf6

Browse files
authored
Merge pull request #9216 from dotty-staging/fix-#9213
Fix #9213: handle valdefs in mixin parent constructors
2 parents a938fc8 + b99df13 commit 4b3fdf6

File tree

2 files changed

+54
-25
lines changed

2 files changed

+54
-25
lines changed

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

Lines changed: 45 additions & 25 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._
@@ -176,49 +177,60 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
176177
}) ++ initBuf
177178
}
178179

179-
/** Map constructor call to a pair of a supercall and a list of arguments
180-
* to be used as initializers of trait parameters if the target of the call
181-
* is a trait.
180+
/** Map constructor call to a triple of a supercall, and if the target
181+
* is a trait
182+
* - a list of val defs used in arguments (these can arise
183+
* due to reorderings with named and/or default parameters).
184+
* - a list of arguments to be used as initializers of trait parameters
182185
*/
183-
def transformConstructor(tree: Tree): (Tree, List[Tree]) = tree match {
186+
def transformConstructor(tree: Tree): (Tree, List[Tree], List[Tree]) = tree match {
184187
case Block(stats, expr) =>
185-
val (scall, inits) = transformConstructor(expr)
186-
(cpy.Block(tree)(stats, scall), inits)
188+
val (scall, inits, args) = transformConstructor(expr)
189+
if args.isEmpty then
190+
(cpy.Block(tree)(stats, scall), inits, args)
191+
else // it's a trait constructor with parameters, lift all prefix statements to class context
192+
// so that they precede argument definitions.
193+
stats.foreach {
194+
case stat: ValDef =>
195+
stat.symbol.copySymDenotation(
196+
owner = cls,
197+
initFlags = stat.symbol.flags | PrivateLocal
198+
).installAfter(thisPhase)
199+
stat.symbol.enteredAfter(thisPhase)
200+
}
201+
(scall, stats ::: inits, args)
187202
case _ =>
188203
val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree
189204
val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil)
190-
(superRef(tree.symbol, tree.span).appliedToArgs(callArgs), initArgs)
205+
(superRef(tree.symbol, tree.span).appliedToArgs(callArgs), Nil, initArgs)
191206
}
192207

193-
val superCallsAndArgs = (
208+
val superCallsAndArgs: Map[Symbol, (Tree, List[Tree], List[Tree])] = (
194209
for (p <- impl.parents; constr = stripBlock(p).symbol if constr.isConstructor)
195210
yield constr.owner -> transformConstructor(p)
196211
).toMap
197-
val superCalls = superCallsAndArgs.transform((_, v) => v._1)
198-
val initArgs = superCallsAndArgs.transform((_, v) => v._2)
199212

200-
def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match {
201-
case Some(call) =>
213+
def superCallOpt(baseCls: Symbol): List[Tree] = superCallsAndArgs.get(baseCls) match
214+
case Some((call, _, _)) =>
202215
if (defn.NotRuntimeClasses.contains(baseCls) || baseCls.isAllOf(NoInitsTrait)) Nil
203216
else call :: Nil
204217
case None =>
205-
if (baseCls.isAllOf(NoInitsTrait) || defn.NoInitClasses.contains(baseCls) || defn.isFunctionClass(baseCls)) Nil
218+
if baseCls.isAllOf(NoInitsTrait) || defn.NoInitClasses.contains(baseCls) || defn.isFunctionClass(baseCls) then
219+
Nil
206220
else
207221
//println(i"synth super call ${baseCls.primaryConstructor}: ${baseCls.primaryConstructor.info}")
208222
transformFollowingDeep(superRef(baseCls.primaryConstructor).appliedToNone) :: Nil
209-
}
210223

211224
def wasOneOf(sym: Symbol, flags: FlagSet) =
212225
ctx.atPhase(thisPhase) { sym.isOneOf(flags) }
213226

214227
def traitInits(mixin: ClassSymbol): List[Tree] = {
215-
var argNum = 0
216-
def nextArgument() = initArgs.get(mixin) match {
217-
case Some(arguments) =>
218-
val result = arguments(argNum)
219-
argNum += 1
220-
result
221-
case None =>
228+
val argsIt = superCallsAndArgs.get(mixin) match
229+
case Some((_, _, args)) => args.iterator
230+
case _ => Iterator.empty
231+
def nextArgument() =
232+
if argsIt.hasNext then argsIt.next
233+
else
222234
assert(
223235
impl.parents.forall(_.tpe.typeSymbol != mixin),
224236
i"missing parameters for $mixin from $impl should have been caught in typer")
@@ -227,7 +239,6 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
227239
|needs to be implemented directly so that arguments can be passed""",
228240
cls.sourcePos)
229241
EmptyTree
230-
}
231242

232243
for (getter <- mixin.info.decls.toList if getter.isGetter && !wasOneOf(getter, Deferred)) yield {
233244
val isScala2x = mixin.is(Scala2x)
@@ -275,9 +286,18 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
275286
if (cls.is(Trait)) traitDefs(impl.body)
276287
else if (!cls.isPrimitiveValueClass) {
277288
val mixInits = mixins.flatMap { mixin =>
278-
flatten(traitInits(mixin)) ::: superCallOpt(mixin) ::: setters(mixin) ::: mixinForwarders(mixin)
289+
val prefix = superCallsAndArgs.get(mixin) match
290+
case Some((_, inits, _)) => inits
291+
case _ => Nil
292+
prefix
293+
::: flatten(traitInits(mixin))
294+
::: superCallOpt(mixin)
295+
::: setters(mixin)
296+
::: mixinForwarders(mixin)
279297
}
280-
superCallOpt(superCls) ::: mixInits ::: impl.body
298+
superCallOpt(superCls)
299+
::: mixInits
300+
::: impl.body
281301
}
282302
else impl.body)
283303
}

tests/pos/i9213.scala

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

0 commit comments

Comments
 (0)