Skip to content

Commit f7f80e8

Browse files
committed
SI-7971 Handle static field initializers correctly
Before this fix, static fields where erroneously treated like instance fields and the initialization was moved into the constructor. With this fix, the static initializer statements go into the static initializer of the class (called “<STATIC> def init” in Scala, <clinit> in Java). The statements are added to an existing static initializer method or, if no such method exists, a new static initializer method is created and added to the class.
1 parent 6c63ab1 commit f7f80e8

File tree

3 files changed

+73
-49
lines changed

3 files changed

+73
-49
lines changed

src/compiler/scala/tools/nsc/transform/CleanUp.scala

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Flags._
1111
import scala.collection._
1212
import scala.language.postfixOps
1313

14-
abstract class CleanUp extends Transform with ast.TreeDSL {
14+
abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
1515
import global._
1616
import definitions._
1717
import CODE._
@@ -35,7 +35,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
3535
protected def newTransformer(unit: CompilationUnit): Transformer =
3636
new CleanUpTransformer(unit)
3737

38-
class CleanUpTransformer(unit: CompilationUnit) extends Transformer {
38+
class CleanUpTransformer(unit: CompilationUnit) extends StaticsTransformer {
3939
private val newStaticMembers = mutable.Buffer.empty[Tree]
4040
private val newStaticInits = mutable.Buffer.empty[Tree]
4141
private val symbolsStoredAsStatic = mutable.Map.empty[String, Symbol]
@@ -49,7 +49,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
4949
clearStatics()
5050
val newBody = transformTrees(body)
5151
val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody)
52-
try addStaticInits(templ) // postprocess to include static ctors
52+
try addStaticInits(templ, newStaticInits, localTyper) // postprocess to include static ctors
5353
finally clearStatics()
5454
}
5555
private def mkTerm(prefix: String): TermName = unit.freshTermName(prefix)
@@ -557,44 +557,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
557557
})
558558
}
559559

560-
/* finds the static ctor DefDef tree within the template if it exists. */
561-
private def findStaticCtor(template: Template): Option[Tree] =
562-
template.body find {
563-
case defdef @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => defdef.symbol.hasStaticFlag
564-
case _ => false
565-
}
566-
567-
/* changes the template for the class so that it contains a static constructor with symbol fields inits,
568-
* augments an existing static ctor if one already existed.
569-
*/
570-
private def addStaticInits(template: Template): Template = {
571-
if (newStaticInits.isEmpty)
572-
template
573-
else {
574-
val newCtor = findStaticCtor(template) match {
575-
// in case there already were static ctors - augment existing ones
576-
// currently, however, static ctors aren't being generated anywhere else
577-
case Some(ctor @ DefDef(_,_,_,_,_,_)) =>
578-
// modify existing static ctor
579-
deriveDefDef(ctor) {
580-
case block @ Block(stats, expr) =>
581-
// need to add inits to existing block
582-
treeCopy.Block(block, newStaticInits.toList ::: stats, expr)
583-
case term: TermTree =>
584-
// need to create a new block with inits and the old term
585-
treeCopy.Block(term, newStaticInits.toList, term)
586-
}
587-
case _ =>
588-
// create new static ctor
589-
val staticCtorSym = currentClass.newStaticConstructor(template.pos)
590-
val rhs = Block(newStaticInits.toList, Literal(Constant(())))
591-
592-
localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs))
593-
}
594-
deriveTemplate(template)(newCtor :: _)
595-
}
596-
}
597-
598560
} // CleanUpTransformer
599561

600562
}

src/compiler/scala/tools/nsc/transform/Constructors.scala

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import symtab.Flags._
1313
/** This phase converts classes with parameters into Java-like classes with
1414
* fields, which are assigned to from constructors.
1515
*/
16-
abstract class Constructors extends Transform with ast.TreeDSL {
16+
abstract class Constructors extends Statics with Transform with ast.TreeDSL {
1717
import global._
1818
import definitions._
1919

@@ -199,7 +199,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
199199
detectUsages walk auxConstructorBuf
200200
}
201201
}
202-
def mustbeKept(sym: Symbol) = !omittables(sym)
202+
def mustBeKept(sym: Symbol) = !omittables(sym)
203203

204204
} // OmittablesHelper
205205

@@ -458,7 +458,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
458458
} // GuardianOfCtorStmts
459459

460460
private class TemplateTransformer(val unit: CompilationUnit, val impl: Template)
461-
extends Transformer
461+
extends StaticsTransformer
462462
with DelayedInitHelper
463463
with OmittablesHelper
464464
with GuardianOfCtorStmts {
@@ -607,7 +607,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
607607
// follow the primary constructor
608608
val auxConstructorBuf = new ListBuffer[Tree]
609609

610-
// The list of statements that go into constructor after and including the superclass constructor call
610+
// The list of statements that go into the constructor after and including the superclass constructor call
611611
val constrStatBuf = new ListBuffer[Tree]
612612

613613
// The list of early initializer statements that go into constructor before the superclass constructor call
@@ -616,6 +616,9 @@ abstract class Constructors extends Transform with ast.TreeDSL {
616616
// The early initialized field definitions of the class (these are the class members)
617617
val presupers = treeInfo.preSuperFields(stats)
618618

619+
// The list of statements that go into the class initializer
620+
val classInitStatBuf = new ListBuffer[Tree]
621+
619622
// generate code to copy pre-initialized fields
620623
for (stat <- constrBody.stats) {
621624
constrStatBuf += stat
@@ -644,7 +647,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
644647
else if (stat.symbol.isConstructor) auxConstructorBuf += stat
645648
else defBuf += stat
646649
}
647-
case ValDef(_, _, _, rhs) =>
650+
case ValDef(mods, _, _, rhs) if !mods.hasStaticFlag =>
648651
// val defs with constant right-hand sides are eliminated.
649652
// for all other val defs, an empty valdef goes into the template and
650653
// the initializer goes as an assignment into the constructor
@@ -659,6 +662,11 @@ abstract class Constructors extends Transform with ast.TreeDSL {
659662
}
660663
defBuf += deriveValDef(stat)(_ => EmptyTree)
661664
}
665+
case ValDef(_, _, _, rhs) =>
666+
// Add static initializer statements to classInitStatBuf and remove the rhs from the val def.
667+
classInitStatBuf += mkAssign(stat.symbol, rhs)
668+
defBuf += deriveValDef(stat)(_ => EmptyTree)
669+
662670
case ClassDef(_, _, _, _) =>
663671
// classes are treated recursively, and left in the template
664672
defBuf += new ConstructorTransformer(unit).transform(stat)
@@ -670,7 +678,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
670678
populateOmittables()
671679

672680
// Initialize all parameters fields that must be kept.
673-
val paramInits = paramAccessors filter mustbeKept map { acc =>
681+
val paramInits = paramAccessors filter mustBeKept map { acc =>
674682
// Check for conflicting symbol amongst parents: see bug #1960.
675683
// It would be better to mangle the constructor parameter name since
676684
// it can only be used internally, but I think we need more robust name
@@ -710,11 +718,13 @@ abstract class Constructors extends Transform with ast.TreeDSL {
710718
defBuf ++= auxConstructorBuf
711719

712720
// Unlink all fields that can be dropped from class scope
713-
for (sym <- clazz.info.decls ; if !mustbeKept(sym))
721+
for (sym <- clazz.info.decls ; if !mustBeKept(sym))
714722
clazz.info.decls unlink sym
715723

716724
// Eliminate all field definitions that can be dropped from template
717-
val transformed: Template = deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustbeKept(stat.symbol)))
725+
val templateWithoutOmittables: Template = deriveTemplate(impl)(_ => defBuf.toList filter (stat => mustBeKept(stat.symbol)))
726+
// Add the static initializers
727+
val transformed: Template = addStaticInits(templateWithoutOmittables, classInitStatBuf, localTyper)
718728

719729
} // TemplateTransformer
720730

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package scala.tools.nsc
2+
package transform
3+
4+
import symtab._
5+
import Flags._
6+
7+
import collection.mutable.Buffer
8+
9+
abstract class Statics extends Transform with ast.TreeDSL {
10+
import global._
11+
12+
class StaticsTransformer extends Transformer {
13+
14+
/** finds the static ctor DefDef tree within the template if it exists. */
15+
def findStaticCtor(template: Template): Option[Tree] =
16+
template.body find {
17+
case defdef @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => defdef.symbol.hasStaticFlag
18+
case _ => false
19+
}
20+
21+
/** changes the template for the class so that it contains a static constructor with symbol fields inits,
22+
* augments an existing static ctor if one already existed.
23+
*/
24+
def addStaticInits(template: Template, newStaticInits: Buffer[Tree], localTyper: analyzer.Typer): Template = {
25+
if (newStaticInits.isEmpty)
26+
template
27+
else {
28+
val newCtor = findStaticCtor(template) match {
29+
// in case there already were static ctors - augment existing ones
30+
// currently, however, static ctors aren't being generated anywhere else
31+
case Some(ctor @ DefDef(_,_,_,_,_,_)) =>
32+
// modify existing static ctor
33+
deriveDefDef(ctor) {
34+
case block @ Block(stats, expr) =>
35+
// need to add inits to existing block
36+
treeCopy.Block(block, newStaticInits.toList ::: stats, expr)
37+
case term: TermTree =>
38+
// need to create a new block with inits and the old term
39+
treeCopy.Block(term, newStaticInits.toList, term)
40+
}
41+
case _ =>
42+
// create new static ctor
43+
val staticCtorSym = currentClass.newStaticConstructor(template.pos)
44+
val rhs = Block(newStaticInits.toList, Literal(Constant(())))
45+
46+
localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs))
47+
}
48+
deriveTemplate(template)(newCtor :: _)
49+
}
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)