summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2007-06-27 14:26:47 +0000
committerIulian Dragos <jaguarul@gmail.com>2007-06-27 14:26:47 +0000
commitb4b91dcb58b5804f7ec1513e8f4d1e4534fd8018 (patch)
tree758a2817dd728101980c7ffc3f206d484ad77ba0 /src/compiler/scala/tools/nsc
parenta1ec75c2642396a83854510ba7e88d8a302a67fd (diff)
downloadscala-b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018.tar.gz
scala-b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018.tar.bz2
scala-b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018.zip
Merged lazy values branch to trunk.
Diffstat (limited to 'src/compiler/scala/tools/nsc')
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala6
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreeGen.scala4
-rw-r--r--src/compiler/scala/tools/nsc/ast/TreePrinters.scala2
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Parsers.scala52
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala1
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Tokens.scala33
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala7
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Flags.scala26
-rw-r--r--src/compiler/scala/tools/nsc/symtab/StdNames.scala4
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala14
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala121
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala128
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala25
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala29
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala4
18 files changed, 392 insertions, 73 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index 65b358610c..07421f02d4 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -308,6 +308,11 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
val global: Global.this.type = Global.this
}
+ object lazyVals extends LazyVals {
+ val global: Global.this.type = Global.this
+ final val FLAGS_PER_WORD = 32
+ }
+
object lambdaLift extends LambdaLift {
val global: Global.this.type = Global.this
}
@@ -388,6 +393,7 @@ class Global(var settings: Settings, var reporter: Reporter) extends SymbolTable
explicitOuter, // replace C.this by explicit outer pointers, eliminate pattern matching
// checkDefined,
erasure, // erase generic types to Java 1.4 types, add interfaces for traits
+ lazyVals,
lambdaLift, // move nested functions to top level
// detach,
constructors, // move field definitions into constructors
diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
index 5a2ffbf875..4532980248 100644
--- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala
@@ -223,6 +223,10 @@ abstract class TreeGen {
def mkRuntimeCall(meth: Name, args: List[Tree]): Tree =
Apply(Select(mkAttributedRef(ScalaRunTimeModule), meth), args)
+ /** Make a synchronized block on 'monitor'. */
+ def mkSynchronized(monitor: Tree, body: Tree): Tree =
+ Apply(Select(monitor, definitions.Object_synchronized), List(body))
+
def evalOnce(expr: Tree, owner: Symbol, unit: CompilationUnit)(within: (() => Tree) => Tree): Tree =
if (treeInfo.isPureExpr(expr)) {
within(() => expr);
diff --git a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
index 37899949db..71929eb68d 100644
--- a/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
+++ b/src/compiler/scala/tools/nsc/ast/TreePrinters.scala
@@ -98,7 +98,7 @@ abstract class TreePrinters {
}
def printFlags(flags: long, privateWithin: String): unit = {
- var mask = if (settings.debug.value) -1 else PrintableFlags
+ var mask: long = if (settings.debug.value) -1L else PrintableFlags
val s = flagsToString(flags & mask, privateWithin.toString)
if (s.length() != 0) print(s + " ")
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
index e49153f732..5f0c0ee7ff 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
@@ -310,12 +310,12 @@ trait Parsers {
/////// TOKEN CLASSES //////////////////////////////////////////////////////
def isModifier: Boolean = inToken match {
- case ABSTRACT | FINAL | SEALED | PRIVATE | PROTECTED | OVERRIDE | IMPLICIT => true
+ case ABSTRACT | FINAL | SEALED | PRIVATE | PROTECTED | OVERRIDE | IMPLICIT | LAZY => true
case _ => false
}
def isLocalModifier: Boolean = inToken match {
- case ABSTRACT | FINAL | SEALED | IMPLICIT => true
+ case ABSTRACT | FINAL | SEALED | IMPLICIT | LAZY => true
case _ => false
}
@@ -1540,6 +1540,8 @@ trait Parsers {
loop(addMod(mods, Flags.OVERRIDE))
case IMPLICIT =>
loop(addMod(mods, Flags.IMPLICIT))
+ case LAZY =>
+ loop(addMod(mods, Flags.LAZY))
case _ =>
mods
}
@@ -1547,7 +1549,7 @@ trait Parsers {
}
/** LocalModifiers ::= {LocalModifier}
- * LocalModifier ::= abstract | final | sealed | implicit
+ * LocalModifier ::= abstract | final | sealed | implicit | lazy
*/
def localModifiers(): Modifiers = {
def loop(mods: Modifiers): Modifiers = inToken match {
@@ -1559,6 +1561,8 @@ trait Parsers {
loop(addMod(mods, Flags.SEALED))
case IMPLICIT =>
loop(addMod(mods, Flags.IMPLICIT))
+ case LAZY =>
+ loop(addMod(mods, Flags.LAZY))
case _ =>
mods
}
@@ -1659,6 +1663,7 @@ trait Parsers {
var mods = Modifiers(Flags.PARAM)
if (owner.isTypeName) {
mods = modifiers() | Flags.PARAMACCESSOR
+ if (mods.hasFlag(Flags.LAZY)) syntaxError("lazy modifier not allowed here. Use call-by-name parameters instead", false)
if (inToken == VAL) {
inNextToken
} else if (inToken == VAR) {
@@ -1906,7 +1911,9 @@ trait Parsers {
* | type [nl] TypeDcl
* XXX: Hook for IDE.
*/
- def defOrDcl(mods: Modifiers): List[Tree] =
+ def defOrDcl(mods: Modifiers): List[Tree] = {
+ if ((mods.hasFlag(Flags.LAZY)) && in.token != VAL)
+ syntaxError("lazy not allowed here. Only vals can be lazy", false)
inToken match {
case VAL =>
patDefOrDcl(mods)
@@ -1921,6 +1928,7 @@ trait Parsers {
case _ =>
List(tmplDef(mods))
}
+ }
/** PatDef ::= Pattern2 {`,' Pattern2} [`:' Type] `=' Expr
* ValDcl ::= Id {`,' Id} `:' Type
@@ -1951,6 +1959,7 @@ trait Parsers {
if (rhs == EmptyTree) {
trees match {
case List(ValDef(_, _, _, EmptyTree)) =>
+ if (mods.hasFlag(Flags.LAZY)) syntaxError(p.pos, "lazy values may not be abstract", false)
case _ => syntaxError(p.pos, "pattern definition may not be abstract", false)
}
}
@@ -2088,20 +2097,23 @@ trait Parsers {
* | [case] object ObjectDef
* | trait TraitDef
*/
- def tmplDef(mods: Modifiers): Tree = inToken match {
- case TRAIT =>
- classDef(mods | Flags.TRAIT | Flags.ABSTRACT)
- case CLASS =>
- classDef(mods)
- case CASECLASS =>
- classDef(mods | Flags.CASE)
- case OBJECT =>
- objectDef(mods)
- case CASEOBJECT =>
- objectDef(mods | Flags.CASE)
- case _ =>
- syntaxErrorOrIncomplete("expected start of definition", true)
- EmptyTree
+ def tmplDef(mods: Modifiers): Tree = {
+ if (mods.hasFlag(Flags.LAZY)) syntaxError("classes cannot be lazy", false)
+ inToken match {
+ case TRAIT =>
+ classDef(mods | Flags.TRAIT | Flags.ABSTRACT)
+ case CLASS =>
+ classDef(mods)
+ case CASECLASS =>
+ classDef(mods | Flags.CASE)
+ case OBJECT =>
+ objectDef(mods)
+ case CASEOBJECT =>
+ objectDef(mods | Flags.CASE)
+ case _ =>
+ syntaxErrorOrIncomplete("expected start of definition", true)
+ EmptyTree
+ }
}
/** ClassDef ::= Id [TypeParamClause] Annotations
@@ -2353,14 +2365,14 @@ trait Parsers {
/** BlockStatSeq ::= { BlockStat semi } [ResultExpr]
* BlockStat ::= Import
- * | [implicit] Def
+ * | [implicit] [lazy] Def
* | LocalModifiers TmplDef
* | Expr1
* |
*/
def blockStatSeq(stats: ListBuffer[Tree]): List[Tree] = checkNoEscapingPlaceholders {
def localDef(mods: Modifiers) = {
- if (!(mods hasFlag ~Flags.IMPLICIT)) stats ++= defOrDcl(mods)
+ if (!(mods hasFlag ~(Flags.IMPLICIT | Flags.LAZY))) stats ++= defOrDcl(mods)
else stats += tmplDefHooked(mods)
if (inToken == RBRACE || inToken == CASE)
syntaxError("block must end in result expression, not in definition", false)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index a68188c3d7..6081bc568d 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -124,6 +124,7 @@ trait Scanners {
enterKeyword(nme.IFkw, IF)
enterKeyword(nme.IMPLICITkw, IMPLICIT)
enterKeyword(nme.IMPORTkw, IMPORT)
+ enterKeyword(nme.LAZYkw, LAZY)
enterKeyword(nme.MATCHkw, MATCH)
enterKeyword(nme.NEWkw, NEW)
enterKeyword(nme.NULLkw, NULL)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
index 49a497a669..959fba033e 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Tokens.scala
@@ -74,27 +74,28 @@ object Tokens {
final val MATCH = 58
final val FORSOME = 59
final val REQUIRES = 60
+ final val LAZY = 61
def isKeyword(code : Int) =
- code >= IF && code <= REQUIRES
+ code >= IF && code <= LAZY
/** special symbols */
- final val COMMA = 61
- final val SEMI = 62
- final val DOT = 63
- final val USCORE = 64
- final val COLON = 65
- final val EQUALS = 66
- final val LARROW = 67
- final val ARROW = 68
- final val NEWLINE = 69
- final val NEWLINES = 70
- final val SUBTYPE = 71
- final val SUPERTYPE = 72
- final val HASH = 73
- final val AT = 74
- final val VIEWBOUND = 75
+ final val COMMA = 70
+ final val SEMI = 71
+ final val DOT = 72
+ final val USCORE = 73
+ final val COLON = 74
+ final val EQUALS = 75
+ final val LARROW = 76
+ final val ARROW = 77
+ final val NEWLINE = 78
+ final val NEWLINES = 79
+ final val SUBTYPE = 80
+ final val SUPERTYPE = 81
+ final val HASH = 82
+ final val AT = 83
+ final val VIEWBOUND = 84
def isSymbol(code : Int) =
code >= COMMA && code <= VIEWBOUND
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 5dd43f1438..136e9d2c0a 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -60,6 +60,10 @@ trait Definitions {
var ShortClass: Symbol = _
var CharClass: Symbol = _
var IntClass: Symbol = _
+ def Int_Or = definitions.getMember(definitions.IntClass, nme.OR)
+ def Int_And = definitions.getMember(definitions.IntClass, nme.AND)
+ def Int_== = definitions.getMember(definitions.IntClass, nme.EQEQ)
+
var LongClass: Symbol = _
var FloatClass: Symbol = _
var DoubleClass: Symbol = _
@@ -369,6 +373,8 @@ trait Definitions {
var BeanPropertyAttr: Symbol = _
var AnnotationDefaultAttr: Symbol = _
var NativeAttr: Symbol = _
+ var VolatileAttr: Symbol = _
+
def getModule(fullname: Name): Symbol = getModuleOrClass(fullname, true)
@@ -939,6 +945,7 @@ trait Definitions {
BeanPropertyAttr = if (forCLDC || forMSIL) null else getClass("scala.reflect.BeanProperty")
DeprecatedAttr = getClass("scala.deprecated")
NativeAttr = getClass("scala.native")
+ VolatileAttr = getClass("scala.volatile")
}
var nbScalaCallers: Int = 0
diff --git a/src/compiler/scala/tools/nsc/symtab/Flags.scala b/src/compiler/scala/tools/nsc/symtab/Flags.scala
index a7c3e5b5d7..6a31a94444 100644
--- a/src/compiler/scala/tools/nsc/symtab/Flags.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Flags.scala
@@ -60,6 +60,7 @@ object Flags extends Enumeration {
// for parameters: is a val parameter
final val MODULEVAR = 0x40000000 // for term symbols: is the variable caching a module value
final val MONOMORPHIC = 0x40000000 // for type symbols: does not have type parameters
+ final val LAZY = 0x80000000L // symbol is a lazy val. can't have MUTABLE unless transformed by typer
final val IS_ERROR = 0x100000000L // symbol is an error symbol
final val OVERLOADED = 0x200000000L // symbol is overloaded
@@ -103,29 +104,29 @@ object Flags extends Enumeration {
// masks
/** This flags can be set when class or module symbol is first created. */
- final val TopLevelCreationFlags =
+ final val TopLevelCreationFlags: Long =
MODULE | PACKAGE | FINAL | JAVA
/** These modifiers can be set explicitly in source programs. */
- final val ExplicitFlags =
+ final val ExplicitFlags: Long =
PRIVATE | PROTECTED | ABSTRACT | FINAL | SEALED |
- OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE
+ OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY
/** These modifiers appear in TreePrinter output. */
- final val PrintableFlags =
+ final val PrintableFlags: Long =
ExplicitFlags | LOCAL | SYNTHETIC | STABLE | CASEACCESSOR |
ACCESSOR | SUPERACCESSOR | PARAMACCESSOR | BRIDGE | STATIC
- final val FieldFlags =
- MUTABLE | CASEACCESSOR | PARAMACCESSOR | STATIC | FINAL | PRESUPER
+ final val FieldFlags: Long =
+ MUTABLE | CASEACCESSOR | PARAMACCESSOR | STATIC | FINAL | PRESUPER | LAZY
- final val AccessFlags = PRIVATE | PROTECTED
+ final val AccessFlags: Long = PRIVATE | PROTECTED
final val VARIANCES = COVARIANT | CONTRAVARIANT
- final val ConstrFlags = JAVA
- final val PickledFlags = 0xFFFFFFFF
+ final val ConstrFlags: Long = JAVA
+ final val PickledFlags: Long = 0xFFFFFFFFL
/** Module flags inherited by their module-class */
- final val ModuleToClassFlags = AccessFlags | PACKAGE | CASE
+ final val ModuleToClassFlags: Long = AccessFlags | PACKAGE | CASE
private def listToString(ss: List[String]): String =
ss.filter("" !=).mkString("", " ", "")
@@ -164,6 +165,7 @@ object Flags extends Enumeration {
else if (flag == IMPLCLASS ) "<presuper/implclass>"
else if (flag == TRANS_FLAG ) "<trans-flag>"
else if (flag == LOCKED ) "<locked>"
+ else if (flag == LAZY ) "lazy"
else flag.asInstanceOf[Int] match {
case IMPLICIT => "implicit"
case FINAL => "final"
@@ -207,13 +209,13 @@ object Flags extends Enumeration {
}
}
- class Flag(mods: Int) {
+ class Flag(mods: Long) {
def isPrivate = (mods & PRIVATE ) != 0
def isProtected = (mods & PROTECTED) != 0
def isVariable = (mods & MUTABLE) != 0
def isPublic = !isPrivate && !isProtected
}
- case class FlagEnum(mask: Int) extends Val(maskToBit(mask), flagToString(mask))
+ case class FlagEnum(mask: Long) extends Val(maskToBit(mask), flagToString(mask))
val Implicit = FlagEnum(IMPLICIT)
val Final = FlagEnum(FINAL)
diff --git a/src/compiler/scala/tools/nsc/symtab/StdNames.scala b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
index b11c09b478..6bcc6e4126 100644
--- a/src/compiler/scala/tools/nsc/symtab/StdNames.scala
+++ b/src/compiler/scala/tools/nsc/symtab/StdNames.scala
@@ -30,6 +30,7 @@ trait StdNames {
val IFkw = newTermName("if")
val IMPLICITkw = newTermName("implicit")
val IMPORTkw = newTermName("import")
+ val LAZYkw = newTermName("lazy")
val MATCHkw = newTermName("match")
val MIXINkw = newTermName("mixin")
val NEWkw = newTermName("new")
@@ -151,6 +152,9 @@ trait StdNames {
/** The name of a setter for protected symbols. Used for inherited Java fields. */
def protSetterName(name: Name): Name = newTermName("protected$set" + name)
+ /** The name of bitmaps for initialized lazy vals. */
+ def bitmapName(n: Int): Name = newTermName("bitmap$" + n)
+
val ERROR = newTermName("<error>")
val ERRORtype = newTypeName("<error>")
val LOCALCHILD = newTypeName("<local child>")
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index 5dd47ad8c3..1edc9fd737 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -738,6 +738,9 @@ trait Symbols {
* is an alias, NoSymbol for all others */
def alias: Symbol = NoSymbol
+ /** For a lazy value, it's lazy accessor. NoSymbol for all others */
+ def lazyAccessor: Symbol = NoSymbol
+
/** For an outer accessor: The class from which the outer originates.
* For all other symbols: NoSymbol
*/
@@ -1139,6 +1142,17 @@ trait Symbols {
referenced = clazz
this
}
+
+ def setLazyAccessor(sym: Symbol): TermSymbol = {
+ assert(hasFlag(LAZY) && referenced == NoSymbol, this)
+ referenced = sym
+ this
+ }
+
+ override def lazyAccessor: Symbol = {
+ assert(hasFlag(LAZY), this)
+ referenced
+ }
}
/** A class for module symbols */
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala
index 4cd8515900..a69d27b368 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/UnPickler.scala
@@ -205,7 +205,7 @@ abstract class UnPickler {
case _ =>
errorBadSignature("bad symbol tag: " + tag)
}
- sym.setFlag(flags)
+ sym.setFlag(flags.toLong & PickledFlags)
sym.privateWithin = privateWithin
if (readIndex != end) assert(sym hasFlag (SUPERACCESSOR | PARAMACCESSOR))
if (sym hasFlag SUPERACCESSOR) assert(readIndex != end)
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index 83cbd27812..b03c05ce3b 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -154,7 +154,7 @@ abstract class Constructors extends Transform {
if (stat.symbol.tpe.isInstanceOf[ConstantType])
assert(stat.symbol.getter(stat.symbol.owner) != NoSymbol, stat)
else {
- if (rhs != EmptyTree) {
+ if (rhs != EmptyTree && !stat.symbol.hasFlag(LAZY)) {
val rhs1 = intoConstructor(stat.symbol, rhs);
(if (canBeMoved(stat)) constrPrefixBuf else constrStatBuf) += mkAssign(
stat.symbol, rhs1)
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
new file mode 100644
index 0000000000..e4cfa6029f
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -0,0 +1,121 @@
+package scala.tools.nsc.transform;
+
+import scala.tools.nsc._
+import scala.collection.mutable.HashMap
+
+abstract class LazyVals extends Transform {
+ // inherits abstract value `global' and class `Phase' from Transform
+
+ import global._ // the global environment
+ import definitions._ // standard classes and methods
+ import typer.{typed, atOwner} // methods to type trees
+ import posAssigner.atPos // for filling in tree positions
+
+ val phaseName: String = "lazyvals"
+
+ def newTransformer(unit: CompilationUnit): Transformer =
+ new LazyValues(unit)
+
+ /** Create a new phase which applies transformer */
+ override def newPhase(prev: scala.tools.nsc.Phase): StdPhase = new Phase(prev)
+
+ /** The phase defined by this transform */
+ class Phase(prev: scala.tools.nsc.Phase) extends StdPhase(prev) {
+ def apply(unit: global.CompilationUnit): Unit =
+ newTransformer(unit).transformUnit(unit);
+ }
+
+ /**
+ * Transform local lazy accessors to check for the initialized bit.
+ */
+ class LazyValues(unit: CompilationUnit) extends Transformer {
+
+ import definitions.{Int_And, Int_Or, Int_==}
+ /** map from method symbols to the number of lazy values it defines. */
+ private val lazyVals = new HashMap[Symbol, Int] {
+ override def default(meth: Symbol) = 0
+ }
+
+ import symtab.Flags._
+ import lazyVals._
+
+ /** Perform the following transformations:
+ * - for a lazy accessor inside a method, make it check the initialization bitmap
+ * - for all methods, add enough int vars to allow one flag per lazy local value
+ */
+ override def transform(tree: Tree): Tree = {
+ val sym = tree.symbol
+ tree match {
+ case DefDef(mods, name, tparams, vparams, tpt, rhs) =>
+ val res = if (!sym.owner.isClass && sym.hasFlag(LAZY)) {
+ val idx = lazyVals(sym.enclMethod)
+ val rhs1 = mkLazyDef(sym.enclMethod, super.transform(rhs), idx)
+ lazyVals(sym.owner) = idx + 1
+ sym.resetFlag(LAZY)
+ rhs1
+ } else
+ super.transform(rhs)
+ val bmps = bitmaps(sym) map { b => ValDef(b, Literal(Constant(0))) }
+ copy.DefDef(tree, mods, name, tparams, vparams, tpt, typed(if (bmps.isEmpty) res else Block(bmps, res)))
+
+ case _ => super.transform(tree)
+ }
+ }
+
+ /** return a 'lazified' version of rhs. Rhs should conform to the
+ * following schema:
+ * {
+ * l$ = <rhs>
+ * l$
+ * }
+ * The result will be a tree of the form
+ * {
+ * if ((bitmap$n & MASK) == 0) {
+ * l$ = <rhs>
+ * bitmap$n = bimap$n | MASK
+ * }
+ * l$
+ * }
+ * where bitmap$n is an int value acting as a bitmap of initialized values. It is
+ * the 'n' is (offset / 32), the MASK is (1 << (offset % 32)).
+ */
+ private def mkLazyDef(meth: Symbol, tree: Tree, offset: Int): Tree = {
+ val bitmapSym = getBitmapFor(meth, offset)
+ val Block(List(assignment), res) = tree
+ val mask = Literal(Constant(1 << (offset % FLAGS_PER_WORD)))
+ val result =
+ If(Apply(
+ Select(
+ Apply(Select(Ident(bitmapSym), Int_And),
+ List(mask)),
+ Int_==),
+ List(Literal(Constant(0)))), Block(List(assignment, mkSetFlag(bitmapSym, mask)), Literal(Constant(()))), EmptyTree)
+ typed(Block(List(result), res))
+ }
+
+ private def mkSetFlag(bmp: Symbol, mask: Tree): Tree =
+ Assign(Ident(bmp),
+ Apply(Select(Ident(bmp), Int_Or), List(mask)))
+
+
+ final val FLAGS_PER_WORD = 32
+ val bitmaps = new HashMap[Symbol, List[Symbol]] {
+ override def default(meth: Symbol) = Nil
+ }
+
+ /** Return the symbol corresponding of the right bitmap int inside meth,
+ * given offset.
+ */
+ private def getBitmapFor(meth: Symbol, offset: Int): Symbol = {
+ val n = offset / FLAGS_PER_WORD
+ val bmps = bitmaps(meth)
+ if (bmps.length > n)
+ bmps(n)
+ else {
+ val sym = meth.newVariable(meth.pos, nme.bitmapName(n)).setInfo(IntClass.tpe)
+ bitmaps(meth) = (sym :: bmps).reverse
+ sym
+ }
+ }
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index c02691aa5a..0e515ff64f 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -10,6 +10,7 @@ import symtab._
import Flags._
import scala.collection.mutable.ListBuffer
import scala.tools.nsc.util.{Position,NoPosition}
+import scala.collection.mutable.HashMap
abstract class Mixin extends InfoTransform {
import global._
@@ -37,12 +38,13 @@ abstract class Mixin extends InfoTransform {
* (private modules, on the other hand, are implemented statically, but their
* module variable is not. all such private modules are lifted, because
* non-lifted private modules have been eliminated in ExplicitOuter)
- * - field accessors and superaccessors
+ * - field accessors and superaccessors, except for lazy value accessors which become initializer
+ * methods in the impl class (because they can have arbitrary initializers)
*/
private def isImplementedStatically(sym: Symbol) =
sym.owner.isImplClass && sym.isMethod &&
- (!sym.isModule || sym.hasFlag(PRIVATE | LIFTED)) &&
- !(sym hasFlag (ACCESSOR | SUPERACCESSOR))
+ (!sym.isModule || sym.hasFlag(PRIVATE)) &&
+ (!(sym hasFlag (ACCESSOR | SUPERACCESSOR)) || sym.hasFlag(LAZY))
/** A member of a trait is static only if it belongs only to the
* implementation class, not the interface, and it is implemented
@@ -124,6 +126,7 @@ abstract class Mixin extends InfoTransform {
* class to its interface unless they are already present. This is done
* only once per class. The mixedin flag is used to remember whether late
* members have been added to an interface.
+ * - lazy fields don't get a setter.
*
* @param clazz ...
*/
@@ -158,7 +161,7 @@ abstract class Mixin extends InfoTransform {
}
val getter = member.getter(clazz)
if (getter == NoSymbol) addMember(clazz, newGetter(member))
- if (!member.tpe.isInstanceOf[ConstantType]) {
+ if (!member.tpe.isInstanceOf[ConstantType] && !member.hasFlag(LAZY)) {
val setter = member.setter(clazz)
if (setter == NoSymbol) addMember(clazz, newSetter(member))
}
@@ -167,6 +170,9 @@ abstract class Mixin extends InfoTransform {
if (settings.debug.value) log("new defs of " + clazz + " = " + clazz.info.decls);
}
+ /** Map a lazy, mixedin field accessor to it's trait member accessor */
+ val initializer = new HashMap[Symbol, Symbol]
+
/** 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:
@@ -208,7 +214,10 @@ abstract class Mixin extends InfoTransform {
}
}
- /** Mix in members of trait mixinClass into class clazz */
+ /** Mix in members of trait mixinClass into class clazz. Also,
+ * for each lazy field in mixinClass, add a link from its mixed in member to it's
+ * initializer method inside the implclass.
+ */
def mixinTraitMembers(mixinClass: Symbol): unit = {
// For all members of a trait's interface do:
for (val member <- mixinClass.info.decls.toList) {
@@ -220,6 +229,11 @@ abstract class Mixin extends InfoTransform {
member.cloneSymbol(clazz)
setPos clazz.pos
setFlag FINAL resetFlag (DEFERRED | lateDEFERRED))
+ if (member.hasFlag(LAZY)) {
+ var init = implClass(mixinClass).info.decl(member.name)
+ assert(init != NoSymbol, "Could not find initializer for " + member.name)
+ initializer(member1) = init
+ }
if (!member.isSetter)
member.tpe match {
case MethodType(List(), ConstantType(_)) =>
@@ -229,7 +243,7 @@ abstract class Mixin extends InfoTransform {
// otherwise mixin a field as well
addMember(clazz,
clazz.newValue(member.pos, nme.getterToLocal(member.name))
- setFlag (LOCAL | PRIVATE | member.getFlag(MUTABLE))
+ setFlag (LOCAL | PRIVATE | member.getFlag(MUTABLE | LAZY))
setInfo member.tpe.resultType)
}
} else if (member hasFlag SUPERACCESSOR) { // mixin super accessors
@@ -412,6 +426,7 @@ abstract class Mixin extends InfoTransform {
* - for a non-trait class:
* - A field for every in a mixin class
* - Setters and getters for such fields
+ * - getters for mixed in lazy fields are completed
* - module variables and module creators for every module in a mixin class
* (except if module is lifted -- in this case the module variable
* is local to some function, and the creator method is static.)
@@ -422,6 +437,7 @@ abstract class Mixin extends InfoTransform {
*/
private def addNewDefs(clazz: Symbol, stats: List[Tree]): List[Tree] = {
val newDefs = new ListBuffer[Tree]
+ var offset: Int = 0
/** Attribute given tree and anchor at given position */
def attributedDef(pos: Position, tree: Tree): Tree = {
@@ -489,7 +505,92 @@ abstract class Mixin extends InfoTransform {
stat
}
+ import lazyVals._
+
+ /** return a 'lazified' version of rhs.
+ * @param clazz The class symbol
+ * @param init The tree which initializes the field ( f = <rhs> )
+ * @param fieldSym The symbol of this lazy field
+ * @param offset The offset of this field in the flags bitmap
+ *
+ * The result will be a tree of the form
+ * {
+ * if ((bitmap$n & MASK) == 0) {
+ * synhronized(this) {
+ * if ((bitmap$n & MASK) == 0) {
+ * synhronized(this) {
+ * init // l$ = <rhs>
+ * }
+ * bitmap$n = bimap$n | MASK
+ * }}}
+ * l$
+ * }
+ * where bitmap$n is an int value acting as a bitmap of initialized values. It is
+ * the 'n' is (offset / 32), the MASK is (1 << (offset % 32)).
+ */
+ def mkLazyDef(clazz: Symbol, init: Tree, fieldSym: Symbol, offset: Int): Tree = {
+
+ /** Return the bitmap field for 'offset', create one if not inheriting it already. */
+ def bitmapFor(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))
+ .setInfo(definitions.IntClass.tpe)
+ .setFlag(PROTECTED)
+ atPhase(currentRun.typerPhase) {
+ sym.attributes = AnnotationInfo(definitions.VolatileAttr.tpe, List(), List()) :: sym.attributes
+ }
+ clazz.info.decls.enter(sym)
+ addDef(clazz.pos, ValDef(sym, Literal(Constant(0))))
+ }
+ sym
+ }
+ val bitmapSym = bitmapFor(offset)
+
+ def mkSetFlag(bmp: Symbol, mask: Tree): Tree =
+ Assign(Select(This(clazz), bmp),
+ Apply(Select(Select(This(clazz), bmp), Int_Or), List(mask)))
+
+ def mkTest(mask: Tree): Tree =
+ Apply(Select(Apply(Select(Select(This(clazz), bitmapSym), Int_And), List(mask)),
+ Int_==), List(Literal(Constant(0))))
+
+ val mask = Literal(Constant(1 << (offset % FLAGS_PER_WORD)))
+ val result =
+ If(mkTest(mask),
+ gen.mkSynchronized(gen.mkAttributedThis(clazz),
+ If(mkTest(mask),
+ Block(List(gen.mkSynchronized(gen.mkAttributedThis(clazz), init),
+ mkSetFlag(bitmapSym, mask)),
+ Literal(Constant(()))),
+ EmptyTree)),
+ EmptyTree)
+ localTyper.typed(Block(List(result), Select(This(clazz), fieldSym)))
+ }
+
+ /** Complete lazy field accessors. Applies only to classes, for it's own (non inherited) lazy fields. */
+ def lazifyOwnFields(clazz: Symbol, stats: List[Tree]): List[Tree] = {
+ var offset = clazz.info.findMember(nme.ANYNAME, METHOD, LAZY, false).alternatives.filter(_.owner != clazz).length
+ val stats1 = for (stat <- stats; sym = stat.symbol) yield stat match {
+ case DefDef(mods, name, tp, vp, tpt, rhs)
+ if sym.hasFlag(LAZY) && rhs != EmptyTree && !clazz.isImplClass =>
+ val Block(List(assignment), res) = rhs
+ val rhs1 = mkLazyDef(clazz, assignment, res.symbol, offset)
+ offset += 1
+ copy.DefDef(stat, mods, name, tp, vp, tpt, rhs1)
+ case _ => stat
+ }
+ stats1
+ }
+
+
+ // the number of inherited lazy fields that are not mixed in
+ offset = (clazz.info.findMember(nme.ANYNAME, METHOD, LAZY, false)
+ .alternatives filter { f => f.owner != clazz || !f.hasFlag(MIXEDIN)}).length
// begin addNewDefs
+ var stats1 = lazifyOwnFields(clazz, stats)
+
// for all symbols `sym' in the class definition, which are mixed in:
for (val sym <- clazz.info.decls.toList) {
if (sym hasFlag MIXEDIN) {
@@ -504,7 +605,18 @@ abstract class Mixin extends InfoTransform {
addDefDef(sym, vparams => {
val accessedRef = sym.tpe match {
case MethodType(List(), ConstantType(c)) => Literal(c)
- case _ => Select(This(clazz), sym.accessed)
+ case _ =>
+ // if it is a mixed-in lazy value, complete the accessor
+ if (sym.hasFlag(LAZY) && sym.isGetter) {
+ val assign =
+ Assign(gen.mkAttributedRef(sym.accessed),
+ Apply(staticRef(initializer(sym)), gen.mkAttributedThis(clazz) :: Nil))
+
+ val rhs1 = mkLazyDef(clazz, assign, sym.accessed, offset)
+ offset += 1
+ rhs1
+ } else
+ Select(This(clazz), sym.accessed)
}
if (sym.isSetter) Assign(accessedRef, Ident(vparams.head))
else gen.mkCheckInit(accessedRef)
@@ -529,7 +641,7 @@ abstract class Mixin extends InfoTransform {
}
}
}
- var stats1 = add(stats, newDefs.toList)
+ stats1 = add(stats1, newDefs.toList)
if (!clazz.isTrait) stats1 = stats1 map completeSuperAccessor
stats1
}
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 80e5210a36..07d9c3905d 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -211,7 +211,10 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
/** Undo eta expansion for parameterless and nullaray methods */
def deEta(fun: Function): Tree = fun match {
case Function(List(), Apply(expr, List())) if treeInfo.isPureExpr(expr) =>
- expr
+ if (expr.hasSymbol && expr.symbol.hasFlag(LAZY))
+ fun
+ else
+ expr
case Function(List(), expr) if isByNameRef(expr) =>
noApply += expr
expr
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index 89f8a26546..ec88ea419b 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -106,7 +106,7 @@ trait Namers { self: Analyzer =>
sym.name.toString() + " is already defined as " +
(if (sym.hasFlag(CASE)) "case class " + sym.name else sym.toString()))
- def enterInScope(sym: Symbol): Symbol = {
+ def enterInScope[A <: Symbol](sym: A): A = {
// allow for overloaded methods
if (!(sym.isSourceMethod && sym.owner.isClass && !sym.owner.isPackageClass)) {
val prev = context.scope.lookupEntry(sym.name);
@@ -284,8 +284,9 @@ trait Namers { self: Analyzer =>
tree.symbol.moduleClass.setInfo(namerOf(tree.symbol).moduleClassTypeCompleter(tree))
finish
case ValDef(mods, name, tp, rhs) =>
- if (context.owner.isClass && (mods.flags & (PRIVATE | LOCAL)) != (PRIVATE | LOCAL)) {
- val accflags = ACCESSOR |
+ if (context.owner.isClass && (mods.flags & (PRIVATE | LOCAL)) != (PRIVATE | LOCAL)
+ || (mods.flags & LAZY) != 0) {
+ val accflags: Long = ACCESSOR |
(if ((mods.flags & MUTABLE) != 0) mods.flags & ~MUTABLE & ~PRESUPER
else mods.flags & ~PRESUPER | STABLE)
val getter = owner.newMethod(tree.pos, name).setFlag(accflags)
@@ -301,10 +302,18 @@ trait Namers { self: Analyzer =>
}
tree.symbol =
if ((mods.flags & DEFERRED) == 0) {
- val value =
- enterInScope(owner.newValue(tree.pos, nme.getterToLocal(name)))
- .setFlag(mods.flags & FieldFlags | PRIVATE | LOCAL)
+ val vsym =
+ if (!context.owner.isClass) {
+ assert((mods.flags & LAZY) != 0) // if not a field, it has to be a lazy val
+ owner.newValue(tree.pos, name + "$lzy" ).setFlag(mods.flags | MUTABLE)
+ } else {
+ owner.newValue(tree.pos, nme.getterToLocal(name))
+ .setFlag(mods.flags & FieldFlags | PRIVATE | LOCAL | (if ((mods.flags & LAZY) != 0) MUTABLE else 0))
+ }
+ val value = enterInScope(vsym)
value.setInfo(namerOf(value).typeCompleter(tree))
+ if ((mods.flags & LAZY) != 0)
+ value.setLazyAccessor(getter)
value
} else getter;
} else {
@@ -345,7 +354,7 @@ trait Namers { self: Analyzer =>
def typeCompleter(tree: Tree) = new TypeCompleter(tree) {
override def complete(sym: Symbol) {
- if (settings.debug.value) log("defining " + sym);
+ if (settings.debug.value) log("defining " + sym + Flags.flagsToString(sym.flags));
val tp = typeSig(tree)
sym.setInfo(tp)
if ((sym.isAliasType || sym.isAbstractType) && !(sym hasFlag PARAM) &&
@@ -741,7 +750,7 @@ trait Namers { self: Analyzer =>
"abstract member may not have " + Flags.flagsToString(flag2) + " modifier";
else
"illegal combination of modifiers: " +
- Flags.flagsToString(flag1) + " and " + Flags.flagsToString(flag2));
+ Flags.flagsToString(flag1) + " and " + Flags.flagsToString(flag2) + " for: " + sym + Flags.flagsToString(sym.rawflags));
if (sym.hasFlag(IMPLICIT) && !sym.isTerm)
context.error(sym.pos, "`implicit' modifier can be used only for values, variables and methods")
if (sym.hasFlag(IMPLICIT) && sym.owner.isPackageClass)
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index bad97b2d1c..162d291ddd 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -566,6 +566,11 @@ abstract class RefChecks extends InfoTransform {
stats1
}
+ /** Implements lazy value accessors:
+ * - for lazy fields inside traits, the rhs is the initializer itself
+ * - for all other lazy values z the accessor is a block of this form:
+ * { z = <rhs>; z } where z can be an identifier or a field.
+ */
def transformStat(tree: Tree, index: int): List[Tree] = tree match {
case ModuleDef(mods, name, impl) =>
val sym = tree.symbol
@@ -622,11 +627,27 @@ abstract class RefChecks extends InfoTransform {
case ValDef(_, _, _, _) =>
val tree1 = transform(tree); // important to do before forward reference check
- if (tree.symbol.isLocal && index <= currentLevel.maxindex) {
- if (settings.debug.value) Console.println(currentLevel.refsym);
- unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol);
+ val ValDef(_, _, _, rhs) = tree1
+ if (tree.symbol.hasFlag(LAZY)) {
+ assert(tree.symbol.isTerm, tree.symbol)
+ val vsym = tree.symbol
+ val lazyDefSym = vsym.lazyAccessor
+ assert(lazyDefSym != NoSymbol, vsym)
+ val lazyDef = atPos(tree.pos)(
+ DefDef(lazyDefSym, vparamss =>
+ if (tree.symbol.owner.isTrait) rhs // for traits, this is further tranformed in mixins
+ else Block(List(
+ Assign(gen.mkAttributedRef(vsym), rhs)),
+ gen.mkAttributedRef(vsym))))
+ log("Made lazy def: " + lazyDef)
+ typed(ValDef(vsym, EmptyTree)) :: typed(lazyDef) :: Nil
+ } else {
+ if (tree.symbol.isLocal && index <= currentLevel.maxindex && !tree.symbol.hasFlag(LAZY)) {
+ if (settings.debug.value) Console.println(currentLevel.refsym);
+ unit.error(currentLevel.refpos, "forward reference extends over definition of " + tree.symbol);
+ }
+ List(tree1)
}
- List(tree1)
case Import(_, _) =>
List()
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 34f09f6acf..27e358ed77 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -985,7 +985,9 @@ trait Typers { self: Analyzer =>
*/
def addGetterSetter(stat: Tree): List[Tree] = stat match {
case ValDef(mods, name, tpt, rhs)
- if (mods.flags & (PRIVATE | LOCAL)) != (PRIVATE | LOCAL) && !stat.symbol.isModuleVar =>
+ if (mods.flags & (PRIVATE | LOCAL)) != (PRIVATE | LOCAL)
+ && !stat.symbol.isModuleVar
+ && !stat.symbol.hasFlag(LAZY) =>
val vdef = copy.ValDef(stat, mods | PRIVATE | LOCAL, nme.getterToLocal(name), tpt, rhs)
val value = vdef.symbol
val getter = if ((mods hasFlag DEFERRED) || inIDE) value else value.getter(value.owner)