Skip to content

Commit 6a46b07

Browse files
authored
Merge pull request #3712 from dotty-staging/fix-#2081-noinits
Fix #2081: Don't issue an $init$ method for NoInits traits
2 parents 2164c42 + 81efdfa commit 6a46b07

File tree

6 files changed

+56
-6
lines changed

6 files changed

+56
-6
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1171,7 +1171,9 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
11711171
object Template extends TemplateDeconstructor {
11721172
def _1: List[Tree] = field.parents
11731173
def _2: ValDef = field.self
1174-
def _3: List[Tree] = field.constr :: field.body
1174+
def _3: List[Tree] =
1175+
if (field.constr.rhs.isEmpty) field.body
1176+
else field.constr :: field.body
11751177
}
11761178

11771179
object Bind extends BindDeconstructor {

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,12 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
9898
* in this phase and for other methods in memoize).
9999
*/
100100
override def checkPostCondition(tree: tpd.Tree)(implicit ctx: Context): Unit = {
101+
def emptyRhsOK(sym: Symbol) =
102+
sym.is(LazyOrDeferred) || sym.isConstructor && sym.owner.is(NoInitsTrait)
101103
tree match {
102104
case tree: ValDef if tree.symbol.exists && tree.symbol.owner.isClass && !tree.symbol.is(Lazy) && !tree.symbol.hasAnnotation(defn.ScalaStaticAnnot) =>
103105
assert(tree.rhs.isEmpty, i"$tree: initializer should be moved to constructors")
104-
case tree: DefDef if !tree.symbol.is(LazyOrDeferred) =>
106+
case tree: DefDef if !emptyRhsOK(tree.symbol) =>
105107
assert(!tree.rhs.isEmpty, i"unimplemented: $tree")
106108
case _ =>
107109
}
@@ -269,9 +271,16 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
269271
case _ => false
270272
}
271273

274+
val finalConstrStats = copyParams ::: mappedSuperCalls ::: lazyAssignments ::: stats
275+
val expandedConstr =
276+
if (cls.is(NoInitsTrait)) {
277+
assert(finalConstrStats.isEmpty)
278+
constr
279+
}
280+
else cpy.DefDef(constr)(rhs = Block(finalConstrStats, unitLiteral))
281+
272282
cpy.Template(tree)(
273-
constr = cpy.DefDef(constr)(
274-
rhs = Block(copyParams ::: mappedSuperCalls ::: lazyAssignments ::: stats, unitLiteral)),
283+
constr = expandedConstr,
275284
body = clsStats.toList)
276285
}
277286
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase =>
179179

180180
def superCallOpt(baseCls: Symbol): List[Tree] = superCalls.get(baseCls) match {
181181
case Some(call) =>
182-
if (defn.NotRuntimeClasses.contains(baseCls)) Nil else call :: Nil
182+
if (defn.NotRuntimeClasses.contains(baseCls) || baseCls.is(NoInitsTrait)) Nil
183+
else call :: Nil
183184
case None =>
184185
if (baseCls.is(NoInitsTrait) || defn.NoInitClasses.contains(baseCls)) Nil
185186
else {

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,7 +1469,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
14691469
} else {
14701470
val dummy = localDummy(cls, impl)
14711471
val body1 = typedStats(impl.body, dummy)(inClassContext(self1.symbol))
1472-
cls.setNoInitsFlags((NoInitsInterface /: body1) ((fs, stat) => fs & defKind(stat)))
1472+
if (!ctx.isAfterTyper)
1473+
cls.setNoInitsFlags((NoInitsInterface /: body1) ((fs, stat) => fs & defKind(stat)))
14731474

14741475
// Expand comments and type usecases
14751476
cookComments(body1.map(_.symbol), self1.symbol)(localContext(cdef, cls).setNewScope)

tests/run/junitForwarders/C_1.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
trait T {
22
@org.junit.Test def foo = 0
3+
println("hi") // to force an $init method
34
}
45

6+
trait U {
7+
@org.junit.Test def bar = 0
8+
// don't issue a $init method
9+
}
10+
11+
512
class C extends T
613

714
object Test extends App {
@@ -12,4 +19,5 @@ object Test extends App {
1219
check(classOf[C], "foo - @org.junit.Test()")
1320
// TODO scala-dev#213: should `foo$` really carry the @Test annotation?
1421
check(classOf[T], "$init$ - ;foo - @org.junit.Test()")
22+
check(classOf[U], "bar - @org.junit.Test()")
1523
}

tests/run/traitNoInit.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
trait NoInit {
2+
def meth(x: Int): Int
3+
}
4+
5+
trait WithInit {
6+
val i = 1
7+
def meth(x: Int): Int
8+
}
9+
10+
trait Bar(x: Int)
11+
12+
class NoInitClass extends NoInit() with Bar(1) {
13+
def meth(x: Int) = x
14+
}
15+
16+
class WithInitClass extends WithInit() with Bar(1) {
17+
def meth(x: Int) = x
18+
}
19+
20+
object Test {
21+
def hasInit(cls: Class[_]) = cls.getMethods.map(_.toString).exists(_.contains("$init$"))
22+
def main(args: Array[String]): Unit = {
23+
val noInit = new NoInitClass {}
24+
val withInit = new WithInitClass {}
25+
26+
assert(!hasInit(noInit.getClass))
27+
assert(hasInit(withInit.getClass))
28+
}
29+
}

0 commit comments

Comments
 (0)