summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala130
-rw-r--r--test/files/buildmanager/t3059/A.scala4
-rw-r--r--test/files/buildmanager/t3059/B.scala4
-rw-r--r--test/files/buildmanager/t3059/t3059.check6
-rw-r--r--test/files/buildmanager/t3059/t3059.test2
-rw-r--r--test/files/run/t3895.check2
-rw-r--r--test/files/run/t3895.flags1
-rw-r--r--test/files/run/t3895.scala30
-rw-r--r--test/files/run/t3895b.scala30
10 files changed, 169 insertions, 42 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index 02da067619..9baec23129 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -12,6 +12,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
import CODE._
val phaseName: String = "lazyvals"
+ val FLAGS_PER_WORD: Int
def newTransformer(unit: CompilationUnit): Transformer =
new LazyValues(unit)
@@ -201,7 +202,6 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
private def mkSetFlag(bmp: Symbol, mask: Tree): Tree =
Ident(bmp) === (Ident(bmp) INT_| mask)
- final val FLAGS_PER_WORD = 32
val bitmaps = new mutable.HashMap[Symbol, List[Symbol]] {
override def default(meth: Symbol) = Nil
}
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 71472e1fcf..e1e4375378 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -206,6 +206,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
/** Map a lazy, mixedin field accessor to it's trait member accessor */
val initializer = new mutable.HashMap[Symbol, Symbol]
+ /** Deferred bitmaps that will be added during the transformation of a class */
+ val deferredBitmaps: collection.mutable.Map[Symbol, List[Tree]] = new collection.mutable.HashMap[Symbol, List[Tree]]
+
/** Add all members to be mixed in into a (non-trait-) class
* These are:
* for every mixin trait T that is not also inherited by the superclass:
@@ -614,6 +617,11 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
else newDefs ::: stats.filter(isNotDuplicate)
}
+ def addDeferredBitmap(clazz: Symbol, tree: Tree) {
+ // Append the set of deffered defs
+ deferredBitmaps(clazz) = typedPos(clazz.pos)(tree)::deferredBitmaps.getOrElse(clazz, List())
+ }
+
/** If `stat' is a superaccessor, complete it by adding a right-hand side.
* Note: superaccessors are always abstract until this point.
* The method to call in a superaccessor is stored in the accessor symbol's alias field.
@@ -638,23 +646,64 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
import lazyVals._
- /** Return the bitmap field for 'offset', create one if not inheriting it already. */
- def bitmapFor(clazz: Symbol, offset: Int): Symbol = {
- var sym = clazz.info.member(nme.bitmapName(offset / FLAGS_PER_WORD))
- assert(!sym.hasFlag(OVERLOADED))
- if (sym == NoSymbol) {
- sym = clazz.newVariable(clazz.pos, nme.bitmapName(offset / FLAGS_PER_WORD))
+ /**
+ * Return the bitmap field for 'offset'. Depending on the hierarchy it is possible to reuse
+ * the bitmap of its parents. If that does not exist yet we create one.
+ */
+ def bitmapFor(clazz0: Symbol, offset: Int, searchParents:Boolean = true): Symbol = {
+ def createBitmap: Symbol = {
+ val sym = clazz0.newVariable(clazz0.pos, nme.bitmapName(offset / FLAGS_PER_WORD))
.setInfo(IntClass.tpe)
.setFlag(PROTECTED)
atPhase(currentRun.typerPhase) {
sym addAnnotation AnnotationInfo(VolatileAttr.tpe, Nil, Nil)
}
- clazz.info.decls.enter(sym)
- addDef(clazz.pos, VAL(sym) === ZERO)
+ clazz0.info.decls.enter(sym)
+ if (clazz0 == clazz)
+ addDef(clazz.pos, VAL(sym) === ZERO)
+ else
+ addDeferredBitmap(clazz0, VAL(sym) === ZERO)
+ sym
+ }
+ var sym = clazz0.info.member(nme.bitmapName(offset / FLAGS_PER_WORD))
+ assert(!sym.hasFlag(OVERLOADED))
+ if (sym == NoSymbol) {
+ if (searchParents)
+ bitmapForParents(clazz0, offset) match {
+ case Some(bitmap) =>
+ sym = bitmap
+ case None =>
+ sym = createBitmap
+ }
+ else
+ sym = createBitmap
}
+ //assert(sym != NoSymbol)
sym
}
+ def bitmapForParents(clazz0: Symbol, offset: Int): Option[Symbol] = {
+ def requiredBitmaps(fs: Int): Int = if (fs == 0) -1 else (fs - 1) / FLAGS_PER_WORD
+
+ var res:Option[Symbol] = None
+ val bitmapNum = offset / FLAGS_PER_WORD
+
+ // on what else should we filter?
+ for (cl <- clazz0.info.baseClasses.tail.filter(c => !c.isTrait && !c.hasFlag(JAVA))
+ if res == None) {
+ val fields0 = usedBits(cl)
+
+ if (requiredBitmaps(fields0) < bitmapNum) {
+ val fields1 = cl.info.decls.filter(fieldWithBitmap).size
+ if (requiredBitmaps(fields0 + fields1) >= bitmapNum)
+ // assert(cl.sourceFile != null)
+ res = Some(bitmapFor(cl, offset, false))
+ else return None // Don't waste time, since we won't find bitmap anyway
+ }
+ }
+ res
+ }
+
/** Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */
def mkSetFlag(clazz: Symbol, offset: Int): Tree = {
val bmp = bitmapFor(clazz, offset)
@@ -705,7 +754,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
Select(This(clazz), sym1) === LIT(null)
}
-
val bitmapSym = bitmapFor(clazz, offset)
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
def cond = mkTest(clazz, mask, bitmapSym, true)
@@ -734,10 +782,6 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* the class constructor is changed to set the initialized bits.
*/
def addCheckedGetters(clazz: Symbol, stats: List[Tree]): List[Tree] = {
- // TODO: not used?
- def findLazyAssignment(stats: List[Tree]): Tree = (
- for (s @ Assign(lhs, _) <- stats ; if lhs.symbol hasFlag LAZY) yield s
- ) head // if there's no assignment then it's a bug and we crash
val stats1 = for (stat <- stats; sym = stat.symbol) yield stat match {
case DefDef(mods, name, tp, vp, tpt, rhs)
@@ -818,50 +862,54 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
}.transform(rhs)
}
- /** Return the number of bits used by superclass fields.
- * This number is a conservative approximation of what is actually used:
- * - fields initialized to the default value don't get a checked initializer
- * but there is no way to recover this information from types alone.
- */
- def usedBits(clazz: Symbol): Int = {
- def isField(sym: Symbol) =
- sym.hasFlag(PRIVATE) && sym.isTerm && !sym.isMethod
-
- def needsBitmapField(sc: Type, field: Symbol) =
- !sc.typeSymbol.isTrait &&
- field.owner != clazz &&
- (settings.checkInit.value && isField(field) ||
- field.hasFlag(LAZY))
-
- // parents != baseClasses.map(_.tpe): bug #1535
- val fields = for {
- sc <- clazz.info.baseClasses.map(_.tpe)
- field <- sc.decls.iterator.toList
- if needsBitmapField(sc, field)
- } yield field
-
- if (settings.debug.value) log("Found inherited fields in " + clazz + " : " + fields)
- fields.length
+ def fieldWithBitmap(field: Symbol) = {
+ field.info // ensure that nested objects are tranformed
+ // For checkinit consider normal value getters
+ // but for lazy values only take into account lazy getters
+ (needsInitFlag(field) || field.hasFlag(LAZY) && field.isMethod) && !field.isDeferred
}
+ /**
+ * Return the number of bits used by superclass fields.
+ */
+ def usedBits(clazz0: Symbol): Int = {
+ def needsBitmap(field: Symbol) =
+ field.owner != clazz0 &&
+ fieldWithBitmap(field)
+
+ val fs = for {
+ cl <- clazz0.info.baseClasses.tail.filter(cl => !cl.isTrait && !cl.hasFlag(JAVA))
+ field <- cl.info.decls.iterator.toList if needsBitmap(field)
+ } yield field
+ fs.length
+ }
+
/** Fill the map from fields to offset numbers.
* Instead of field symbols, the map keeps their getter symbols. This makes
* code generation easier later.
*/
- def buildFieldPositions(clazz: Symbol) {
- var fields = usedBits(clazz)
- for (f <- clazz.info.decls.iterator if needsInitFlag(f) || f.hasFlag(LAZY)) {
+ def buildFieldPositions(clazz0: Symbol) {
+ var fields = usedBits(clazz0)
+ for (f <- clazz0.info.decls.iterator if fieldWithBitmap(f)) {
if (settings.debug.value) log(f.fullName + " -> " + fields)
+
fieldOffset(f) = fields
fields += 1
}
}
- buildFieldPositions(clazz)
// begin addNewDefs
+ buildFieldPositions(clazz)
var stats1 = addCheckedGetters(clazz, stats)
+ // add deffered bitmaps
+ deferredBitmaps.remove(clazz) match {
+ case Some(deferred) =>
+ stats1 = add(stats1, deferred)
+ case None =>
+ }
+
// for all symbols `sym' in the class definition, which are mixed in:
for (sym <- clazz.info.decls.toList) {
if (sym hasFlag MIXEDIN) {
diff --git a/test/files/buildmanager/t3059/A.scala b/test/files/buildmanager/t3059/A.scala
new file mode 100644
index 0000000000..0dd25f6647
--- /dev/null
+++ b/test/files/buildmanager/t3059/A.scala
@@ -0,0 +1,4 @@
+class A extends B {
+ private def getBar = List(1,2,3)
+ lazy val bar: List[Int] = getBar
+}
diff --git a/test/files/buildmanager/t3059/B.scala b/test/files/buildmanager/t3059/B.scala
new file mode 100644
index 0000000000..46596870ac
--- /dev/null
+++ b/test/files/buildmanager/t3059/B.scala
@@ -0,0 +1,4 @@
+abstract class B {
+ private def getFoo = 12
+ lazy val foo: Int = getFoo
+}
diff --git a/test/files/buildmanager/t3059/t3059.check b/test/files/buildmanager/t3059/t3059.check
new file mode 100644
index 0000000000..4a8076aae1
--- /dev/null
+++ b/test/files/buildmanager/t3059/t3059.check
@@ -0,0 +1,6 @@
+builder > A.scala B.scala
+compiling Set(A.scala, B.scala)
+Changes: Map()
+builder > A.scala
+compiling Set(A.scala)
+Changes: Map(class A -> List()) \ No newline at end of file
diff --git a/test/files/buildmanager/t3059/t3059.test b/test/files/buildmanager/t3059/t3059.test
new file mode 100644
index 0000000000..6f3749dc4b
--- /dev/null
+++ b/test/files/buildmanager/t3059/t3059.test
@@ -0,0 +1,2 @@
+>>compile A.scala B.scala
+>>compile A.scala \ No newline at end of file
diff --git a/test/files/run/t3895.check b/test/files/run/t3895.check
new file mode 100644
index 0000000000..3045ebf016
--- /dev/null
+++ b/test/files/run/t3895.check
@@ -0,0 +1,2 @@
+17
+17 \ No newline at end of file
diff --git a/test/files/run/t3895.flags b/test/files/run/t3895.flags
new file mode 100644
index 0000000000..ae08446055
--- /dev/null
+++ b/test/files/run/t3895.flags
@@ -0,0 +1 @@
+-Xcheckinit \ No newline at end of file
diff --git a/test/files/run/t3895.scala b/test/files/run/t3895.scala
new file mode 100644
index 0000000000..dfc4a34a32
--- /dev/null
+++ b/test/files/run/t3895.scala
@@ -0,0 +1,30 @@
+class C extends A{
+
+ val a = 10
+ //object bb
+ lazy val bb = 17
+ val b = 12
+}
+
+abstract class A{
+ val a: Int
+ val b: Int
+ val c: Int = 12
+}
+
+class B extends A{
+
+ val a = 10
+ //object bb
+ lazy val bb = 17
+ val b = 12
+}
+
+
+object Test {
+ def main(args: Array[String]) {
+ println(new B().bb)
+ println(new C().bb)
+ }
+}
+
diff --git a/test/files/run/t3895b.scala b/test/files/run/t3895b.scala
new file mode 100644
index 0000000000..fd74aab125
--- /dev/null
+++ b/test/files/run/t3895b.scala
@@ -0,0 +1,30 @@
+class DryRun
+{
+ import scala.tools.nsc.{Global, Settings, CompilerCommand}
+ import scala.tools.nsc.reporters.ConsoleReporter
+
+ val settings = new Settings()
+ settings.classpath.value = System.getProperty("java.class.path")
+ val command = new CompilerCommand(List(), settings)
+ val reporter = new ConsoleReporter(settings, scala.Console.in, new java.io.PrintWriter(new java.io.PrintStream(scala.Console.out)))
+ object compiler extends Global(command.settings, reporter)
+ {
+ object test1
+ lazy val test2 = 1
+ object test3
+
+ }
+ def test {
+ compiler.test1
+ compiler.test2
+ compiler.test3
+ val run = new compiler.Run
+ run compile command.files
+ }
+}
+
+object Test {
+ def main(args: Array[String]) {
+ new DryRun().test
+ }
+}