diff options
author | Iulian Dragos <jaguarul@gmail.com> | 2007-06-27 14:26:47 +0000 |
---|---|---|
committer | Iulian Dragos <jaguarul@gmail.com> | 2007-06-27 14:26:47 +0000 |
commit | b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018 (patch) | |
tree | 758a2817dd728101980c7ffc3f206d484ad77ba0 /src | |
parent | a1ec75c2642396a83854510ba7e88d8a302a67fd (diff) | |
download | scala-b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018.tar.gz scala-b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018.tar.bz2 scala-b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018.zip |
Merged lazy values branch to trunk.
Diffstat (limited to 'src')
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) |