Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SI-7971 Handle static field initializers correctly #3224

Merged
merged 1 commit into from Dec 19, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 3 additions & 41 deletions src/compiler/scala/tools/nsc/transform/CleanUp.scala
Expand Up @@ -11,7 +11,7 @@ import Flags._
import scala.collection._
import scala.language.postfixOps

abstract class CleanUp extends Transform with ast.TreeDSL {
abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
import global._
import definitions._
import CODE._
Expand All @@ -35,7 +35,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
protected def newTransformer(unit: CompilationUnit): Transformer =
new CleanUpTransformer(unit)

class CleanUpTransformer(unit: CompilationUnit) extends Transformer {
class CleanUpTransformer(unit: CompilationUnit) extends StaticsTransformer {
private val newStaticMembers = mutable.Buffer.empty[Tree]
private val newStaticInits = mutable.Buffer.empty[Tree]
private val symbolsStoredAsStatic = mutable.Map.empty[String, Symbol]
Expand All @@ -49,7 +49,7 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
clearStatics()
val newBody = transformTrees(body)
val templ = deriveTemplate(tree)(_ => transformTrees(newStaticMembers.toList) ::: newBody)
try addStaticInits(templ) // postprocess to include static ctors
try addStaticInits(templ, newStaticInits, localTyper) // postprocess to include static ctors
finally clearStatics()
}
private def mkTerm(prefix: String): TermName = unit.freshTermName(prefix)
Expand Down Expand Up @@ -557,44 +557,6 @@ abstract class CleanUp extends Transform with ast.TreeDSL {
})
}

/* finds the static ctor DefDef tree within the template if it exists. */
private def findStaticCtor(template: Template): Option[Tree] =
template.body find {
case defdef @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => defdef.symbol.hasStaticFlag
case _ => false
}

/* changes the template for the class so that it contains a static constructor with symbol fields inits,
* augments an existing static ctor if one already existed.
*/
private def addStaticInits(template: Template): Template = {
if (newStaticInits.isEmpty)
template
else {
val newCtor = findStaticCtor(template) match {
// in case there already were static ctors - augment existing ones
// currently, however, static ctors aren't being generated anywhere else
case Some(ctor @ DefDef(_,_,_,_,_,_)) =>
// modify existing static ctor
deriveDefDef(ctor) {
case block @ Block(stats, expr) =>
// need to add inits to existing block
treeCopy.Block(block, newStaticInits.toList ::: stats, expr)
case term: TermTree =>
// need to create a new block with inits and the old term
treeCopy.Block(term, newStaticInits.toList, term)
}
case _ =>
// create new static ctor
val staticCtorSym = currentClass.newStaticConstructor(template.pos)
val rhs = Block(newStaticInits.toList, Literal(Constant(())))

localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs))
}
deriveTemplate(template)(newCtor :: _)
}
}

} // CleanUpTransformer

}
26 changes: 18 additions & 8 deletions src/compiler/scala/tools/nsc/transform/Constructors.scala
Expand Up @@ -13,7 +13,7 @@ import symtab.Flags._
/** This phase converts classes with parameters into Java-like classes with
* fields, which are assigned to from constructors.
*/
abstract class Constructors extends Transform with ast.TreeDSL {
abstract class Constructors extends Statics with Transform with ast.TreeDSL {
import global._
import definitions._

Expand Down Expand Up @@ -199,7 +199,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
detectUsages walk auxConstructorBuf
}
}
def mustbeKept(sym: Symbol) = !omittables(sym)
def mustBeKept(sym: Symbol) = !omittables(sym)

} // OmittablesHelper

Expand Down Expand Up @@ -458,7 +458,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
} // GuardianOfCtorStmts

private class TemplateTransformer(val unit: CompilationUnit, val impl: Template)
extends Transformer
extends StaticsTransformer
with DelayedInitHelper
with OmittablesHelper
with GuardianOfCtorStmts {
Expand Down Expand Up @@ -607,7 +607,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
// follow the primary constructor
val auxConstructorBuf = new ListBuffer[Tree]

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

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

// The list of statements that go into the class initializer
val classInitStatBuf = new ListBuffer[Tree]

// generate code to copy pre-initialized fields
for (stat <- constrBody.stats) {
constrStatBuf += stat
Expand Down Expand Up @@ -644,7 +647,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
else if (stat.symbol.isConstructor) auxConstructorBuf += stat
else defBuf += stat
}
case ValDef(_, _, _, rhs) =>
case ValDef(mods, _, _, rhs) if !mods.hasStaticFlag =>
// val defs with constant right-hand sides are eliminated.
// for all other val defs, an empty valdef goes into the template and
// the initializer goes as an assignment into the constructor
Expand All @@ -659,6 +662,11 @@ abstract class Constructors extends Transform with ast.TreeDSL {
}
defBuf += deriveValDef(stat)(_ => EmptyTree)
}
case ValDef(_, _, _, rhs) =>
// Add static initializer statements to classInitStatBuf and remove the rhs from the val def.
classInitStatBuf += mkAssign(stat.symbol, rhs)
defBuf += deriveValDef(stat)(_ => EmptyTree)

case ClassDef(_, _, _, _) =>
// classes are treated recursively, and left in the template
defBuf += new ConstructorTransformer(unit).transform(stat)
Expand All @@ -670,7 +678,7 @@ abstract class Constructors extends Transform with ast.TreeDSL {
populateOmittables()

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

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

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

} // TemplateTransformer

Expand Down
52 changes: 52 additions & 0 deletions src/compiler/scala/tools/nsc/transform/Statics.scala
@@ -0,0 +1,52 @@
package scala.tools.nsc
package transform

import symtab._
import Flags._

import collection.mutable.Buffer

abstract class Statics extends Transform with ast.TreeDSL {
import global._

class StaticsTransformer extends Transformer {

/** finds the static ctor DefDef tree within the template if it exists. */
def findStaticCtor(template: Template): Option[Tree] =
template.body find {
case defdef @ DefDef(_, nme.CONSTRUCTOR, _, _, _, _) => defdef.symbol.hasStaticFlag
case _ => false
}

/** changes the template for the class so that it contains a static constructor with symbol fields inits,
* augments an existing static ctor if one already existed.
*/
def addStaticInits(template: Template, newStaticInits: Buffer[Tree], localTyper: analyzer.Typer): Template = {
if (newStaticInits.isEmpty)
template
else {
val newCtor = findStaticCtor(template) match {
// in case there already were static ctors - augment existing ones
// currently, however, static ctors aren't being generated anywhere else
case Some(ctor @ DefDef(_,_,_,_,_,_)) =>
// modify existing static ctor
deriveDefDef(ctor) {
case block @ Block(stats, expr) =>
// need to add inits to existing block
treeCopy.Block(block, newStaticInits.toList ::: stats, expr)
case term: TermTree =>
// need to create a new block with inits and the old term
treeCopy.Block(term, newStaticInits.toList, term)
}
case _ =>
// create new static ctor
val staticCtorSym = currentClass.newStaticConstructor(template.pos)
val rhs = Block(newStaticInits.toList, Literal(Constant(())))

localTyper.typedPos(template.pos)(DefDef(staticCtorSym, rhs))
}
deriveTemplate(template)(newCtor :: _)
}
}
}
}