Skip to content

Commit 2813b01

Browse files
committed
Implement trait parameters
Add necessary logic to Mixin. Also add tests that all parameterized traits are called with parameters set.
1 parent 8c28293 commit 2813b01

File tree

5 files changed

+85
-16
lines changed

5 files changed

+85
-16
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ object SymDenotations {
516516
!isAnonymousFunction &&
517517
!isCompanionMethod
518518

519-
/** Is this a setter? */
519+
/** Is this a getter? */
520520
final def isGetter(implicit ctx: Context) =
521521
(this is Accessor) && !originalName.isSetterName && !originalName.isScala2LocalSuffix
522522

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

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import collection.mutable
2020

2121
/** This phase performs the following transformations:
2222
*
23-
* 1. (done in `traitDefs`) Map every concrete trait getter
23+
* 1. (done in `traitDefs` and `transformSym`) Map every concrete trait getter
2424
*
2525
* <mods> def x(): T = expr
2626
*
@@ -46,32 +46,43 @@ import collection.mutable
4646
* For every trait M directly implemented by the class (see SymUtils.mixin), in
4747
* reverse linearization order, add the following definitions to C:
4848
*
49-
* 3.1 (done in `traitInits`) For every concrete trait getter `<mods> def x(): T` in M,
50-
* in order of textual occurrence, produce the following:
49+
* 3.1 (done in `traitInits`) For every parameter accessor `<mods> def x(): T` in M,
50+
* in order of textual occurrence, add
5151
*
52-
* 3.1.1 If `x` is also a member of `C`, and M is a Dotty trait:
52+
* <mods> def x() = e
53+
*
54+
* where `e` is the constructor argument in C that corresponds to `x`. Issue
55+
* an error if no such argument exists.
56+
*
57+
* 3.2 (done in `traitInits`) For every concrete trait getter `<mods> def x(): T` in M
58+
* which is not a parameter accessor, in order of textual occurrence, produce the following:
59+
*
60+
* 3.2.1 If `x` is also a member of `C`, and M is a Dotty trait:
5361
*
5462
* <mods> def x(): T = super[M].initial$x()
5563
*
56-
* 3.1.2 If `x` is also a member of `C`, and M is a Scala 2.x trait:
64+
* 3.2.2 If `x` is also a member of `C`, and M is a Scala 2.x trait:
5765
*
5866
* <mods> def x(): T = _
5967
*
60-
* 3.1.3 If `x` is not a member of `C`, and M is a Dotty trait:
68+
* 3.2.3 If `x` is not a member of `C`, and M is a Dotty trait:
6169
*
6270
* super[M].initial$x()
6371
*
64-
* 3.1.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added.
72+
* 3.2.4 If `x` is not a member of `C`, and M is a Scala2.x trait, nothing gets added.
6573
*
6674
*
67-
* 3.2 (done in `superCallOpt`) The call:
75+
* 3.3 (done in `superCallOpt`) The call:
6876
*
6977
* super[M].<init>
7078
*
71-
* 3.3 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M:
79+
* 3.4 (done in `setters`) For every concrete setter `<mods> def x_=(y: T)` in M:
7280
*
7381
* <mods> def x_=(y: T) = ()
7482
*
83+
* 4. (done in `transformTemplate` and `transformSym`) Drop all parameters from trait
84+
* constructors.
85+
*
7586
* Conceptually, this is the second half of the previous mixin phase. It needs to run
7687
* after erasure because it copies references to possibly private inner classes and objects
7788
* into enclosing classes where they are not visible. This can only be done if all references
@@ -87,6 +98,8 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
8798
override def transformSym(sym: SymDenotation)(implicit ctx: Context): SymDenotation =
8899
if (sym.is(Accessor, butNot = Deferred) && sym.owner.is(Trait))
89100
sym.copySymDenotation(initFlags = sym.flags | Deferred).ensureNotPrivate
101+
else if (sym.isConstructor && sym.owner.is(Trait) && sym.info.firstParamTypes.nonEmpty)
102+
sym.copySymDenotation(info = MethodType(Nil, sym.info.resultType))
90103
else
91104
sym
92105

@@ -131,15 +144,22 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
131144
}) ++ initBuf
132145
}
133146

134-
def transformSuper(tree: Tree): Tree = {
147+
/** Map constructor call to a pair of a supercall and a list of arguments
148+
* to be used as initializers of trait parameters if the target of the call
149+
* is a trait.
150+
*/
151+
def transformConstructor(tree: Tree): (Tree, List[Tree]) = {
135152
val Apply(sel @ Select(New(_), nme.CONSTRUCTOR), args) = tree
136-
superRef(tree.symbol, tree.pos).appliedToArgs(args)
153+
val (callArgs, initArgs) = if (tree.symbol.owner.is(Trait)) (Nil, args) else (args, Nil)
154+
(superRef(tree.symbol, tree.pos).appliedToArgs(callArgs), initArgs)
137155
}
138156

139-
val superCalls = (
157+
val superCallsAndArgs = (
140158
for (p <- impl.parents if p.symbol.isConstructor)
141-
yield p.symbol.owner -> transformSuper(p)
159+
yield p.symbol.owner -> transformConstructor(p)
142160
).toMap
161+
val superCalls = superCallsAndArgs.mapValues(_._1)
162+
val initArgs = superCallsAndArgs.mapValues(_._2)
143163

144164
def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match {
145165
case Some(call) =>
@@ -155,12 +175,31 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
155175
def wasDeferred(sym: Symbol) =
156176
ctx.atPhase(thisTransform) { implicit ctx => sym is Deferred }
157177

158-
def traitInits(mixin: ClassSymbol): List[Tree] =
178+
def traitInits(mixin: ClassSymbol): List[Tree] = {
179+
var argNum = 0
180+
def nextArgument() = initArgs.get(mixin) match {
181+
case Some(arguments) =>
182+
try arguments(argNum) finally argNum += 1
183+
case None =>
184+
val (msg, pos) = impl.parents.find(_.tpe.typeSymbol == mixin) match {
185+
case Some(parent) => ("lacks argument list", parent.pos)
186+
case None =>
187+
("""is indirectly implemented,
188+
|needs to be implemented directly so that arguments can be passed""".stripMargin,
189+
cls.pos)
190+
}
191+
ctx.error(i"parameterized $mixin $msg", pos)
192+
EmptyTree
193+
}
159194
for (getter <- mixin.info.decls.filter(getr => getr.isGetter && !wasDeferred(getr)).toList) yield {
160195
val isScala2x = mixin.is(Scala2x)
161196
def default = Underscore(getter.info.resultType)
162197
def initial = transformFollowing(superRef(initializer(getter)).appliedToNone)
163-
if (isCurrent(getter) || getter.is(ExpandedName))
198+
if (isCurrent(getter) || getter.is(ExpandedName)) {
199+
val rhs =
200+
if (getter.is(ParamAccessor)) nextArgument()
201+
else if (isScala2x) Underscore(getter.info.resultType)
202+
else transformFollowing(superRef(initializer(getter)).appliedToNone)
164203
// transformFollowing call is needed to make memoize & lazy vals run
165204
transformFollowing(
166205
DefDef(implementation(getter.asTerm),
@@ -178,12 +217,17 @@ class Mixin extends MiniPhaseTransform with SymTransformer { thisTransform =>
178217
else if (isScala2x) EmptyTree
179218
else initial
180219
}
220+
}
181221

182222
def setters(mixin: ClassSymbol): List[Tree] =
183223
for (setter <- mixin.info.decls.filter(setr => setr.isSetter && !wasDeferred(setr)).toList)
184224
yield DefDef(implementation(setter.asTerm), unitLiteral.withPos(cls.pos))
185225

186226
cpy.Template(impl)(
227+
constr =
228+
if (cls.is(Trait) && impl.constr.vparamss.flatten.nonEmpty)
229+
cpy.DefDef(impl.constr)(vparamss = Nil :: Nil)
230+
else impl.constr,
187231
parents = impl.parents.map(p => TypeTree(p.tpe).withPos(p.pos)),
188232
body =
189233
if (cls is Trait) traitDefs(impl.body)

test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ class tests extends CompilerTest {
139139
@Test def neg_selfInheritance = compileFile(negDir, "selfInheritance", xerrors = 5)
140140
@Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2)
141141
@Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5)
142+
@Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2)
142143

143144
@Test def run_all = runFiles(runDir)
144145

tests/neg/traitParamsMixin.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
trait T(x: Int) {
2+
def f = x
3+
}
4+
5+
class C extends T // error
6+
7+
trait U extends T
8+
9+
class D extends U { // error
10+
11+
}
12+

tests/pos/traitParams.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
trait T(x: Int) {
2+
def f = x
3+
}
4+
5+
trait U extends T
6+
7+
class C extends U with T(2) {
8+
9+
}
10+
11+
class D extends C with T
12+

0 commit comments

Comments
 (0)