From b4b91dcb58b5804f7ec1513e8f4d1e4534fd8018 Mon Sep 17 00:00:00 2001 From: Iulian Dragos Date: Wed, 27 Jun 2007 14:26:47 +0000 Subject: Merged lazy values branch to trunk. --- src/compiler/scala/tools/nsc/Global.scala | 6 + src/compiler/scala/tools/nsc/ast/TreeGen.scala | 4 + .../scala/tools/nsc/ast/TreePrinters.scala | 2 +- .../scala/tools/nsc/ast/parser/Parsers.scala | 52 ++++--- .../scala/tools/nsc/ast/parser/Scanners.scala | 1 + .../scala/tools/nsc/ast/parser/Tokens.scala | 33 ++--- .../scala/tools/nsc/symtab/Definitions.scala | 7 + src/compiler/scala/tools/nsc/symtab/Flags.scala | 26 ++-- src/compiler/scala/tools/nsc/symtab/StdNames.scala | 4 + src/compiler/scala/tools/nsc/symtab/Symbols.scala | 14 ++ .../tools/nsc/symtab/classfile/UnPickler.scala | 2 +- .../scala/tools/nsc/transform/Constructors.scala | 2 +- .../scala/tools/nsc/transform/LazyVals.scala | 121 ++++++++++++++++ src/compiler/scala/tools/nsc/transform/Mixin.scala | 128 +++++++++++++++-- .../scala/tools/nsc/transform/UnCurry.scala | 5 +- .../scala/tools/nsc/typechecker/Namers.scala | 25 ++-- .../scala/tools/nsc/typechecker/RefChecks.scala | 29 +++- .../scala/tools/nsc/typechecker/Typers.scala | 4 +- test/files/neg/lazyvals.check | 25 ++++ test/files/neg/lazyvals.scala | 26 ++++ test/files/run/bug603.scala | 4 +- test/files/run/lazy-exprs.check | 10 ++ test/files/run/lazy-exprs.scala | 84 +++++++++++ test/files/run/lazy-locals.check | 82 +++++++++++ test/files/run/lazy-locals.scala | 143 +++++++++++++++++++ test/files/run/lazy-traits.check | 154 +++++++++++++++++++++ test/files/run/lazy-traits.scala | 133 ++++++++++++++++++ 27 files changed, 1051 insertions(+), 75 deletions(-) create mode 100644 src/compiler/scala/tools/nsc/transform/LazyVals.scala create mode 100644 test/files/neg/lazyvals.check create mode 100644 test/files/neg/lazyvals.scala create mode 100644 test/files/run/lazy-exprs.check create mode 100644 test/files/run/lazy-exprs.scala create mode 100644 test/files/run/lazy-locals.check create mode 100644 test/files/run/lazy-locals.scala create mode 100644 test/files/run/lazy-traits.check create mode 100644 test/files/run/lazy-traits.scala 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 ) "" else if (flag == TRANS_FLAG ) "" else if (flag == 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("") val ERRORtype = newTypeName("") val LOCALCHILD = newTypeName("") 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$ = + * l$ + * } + * The result will be a tree of the form + * { + * if ((bitmap$n & MASK) == 0) { + * l$ = + * 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 = ) + * @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$ = + * } + * 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 = ; 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) diff --git a/test/files/neg/lazyvals.check b/test/files/neg/lazyvals.check new file mode 100644 index 0000000000..c4daf9d842 --- /dev/null +++ b/test/files/neg/lazyvals.check @@ -0,0 +1,25 @@ +lazyvals.scala:6: error: lazy values may not be abstract + lazy val t: Int + ^ +lazyvals.scala:9: error: lazy not allowed here. Only vals can be lazy + lazy var p: Int = 100 + ^ +lazyvals.scala:12: error: lazy not allowed here. Only vals can be lazy + lazy def q: Double = 0.0 + ^ +lazyvals.scala:15: error: '=' expected but ';' found. + lazy val t; + ^ +lazyvals.scala:20: error: lazy not allowed here. Only vals can be lazy + lazy trait T {} + ^ +lazyvals.scala:21: error: lazy not allowed here. Only vals can be lazy + lazy class C {} + ^ +lazyvals.scala:22: error: lazy not allowed here. Only vals can be lazy + lazy object O {} + ^ +lazyvals.scala:25: error: lazy modifier not allowed here. Use call-by-name parameters instead + class A(lazy val obj: Object) {} + ^ +8 errors found diff --git a/test/files/neg/lazyvals.scala b/test/files/neg/lazyvals.scala new file mode 100644 index 0000000000..8514d42c13 --- /dev/null +++ b/test/files/neg/lazyvals.scala @@ -0,0 +1,26 @@ + +/** Test which should fail compilation */ +class Lazy { + + // no abstract lazy values + lazy val t: Int + + // no lazy var + lazy var p: Int = 100 + + // no lazy defs + lazy def q: Double = 0.0 + + def foo { + lazy val t; + () + } + + // no trait/class/object can be lazy + lazy trait T {} + lazy class C {} + lazy object O {} + + // no lazy modifiers in class parameters + class A(lazy val obj: Object) {} +} diff --git a/test/files/run/bug603.scala b/test/files/run/bug603.scala index c1e0aace12..361cef1f41 100644 --- a/test/files/run/bug603.scala +++ b/test/files/run/bug603.scala @@ -1,4 +1,4 @@ -object lazy { +object forceDelay { class Susp[+A](lazyValue: => A) extends Function0[A] { private var func: () => Any = () => lazyValue private var value: Any = null @@ -21,7 +21,7 @@ object lazy { } object Test { - import lazy._ + import forceDelay._ def main(args: Array[String]) = { val s: Susp[Int] = delay { Console.println("evaluating..."); 3 } diff --git a/test/files/run/lazy-exprs.check b/test/files/run/lazy-exprs.check new file mode 100644 index 0000000000..2aaa0be3f6 --- /dev/null +++ b/test/files/run/lazy-exprs.check @@ -0,0 +1,10 @@ +forced +lazy val in scrutinee: ok +forced +lazy val in case: ok +lazy val in case: forced +ok +lazy val in if condition: forced +ok +lazy val in pattern: forced LazyField +ok diff --git a/test/files/run/lazy-exprs.scala b/test/files/run/lazy-exprs.scala new file mode 100644 index 0000000000..307915bcac --- /dev/null +++ b/test/files/run/lazy-exprs.scala @@ -0,0 +1,84 @@ +object TestExpressions { + + def patmatchScrut { + lazy val z1: Option[String] = { println("forced "); Some("lazy z1") } + + val res = z1 match { + case Some(msg) => msg + case None => "failed" + } + print("lazy val in scrutinee: ") + if (res == "lazy z1") + println("ok") + else + println("failed") + } + + def patmatchCase { + val t: Option[String] = Some("test") + val res = t match { + case Some(msg) => + lazy val z1 = { println("forced "); "lazy z1" } + z1 + + case None => "failed" + } + print("lazy val in case: ") + if (res == "lazy z1") + println("ok") + else + println("failed") + } + + + def patmatchPat { + lazy val Z1 = { println("forced "); "lazy Z1" } + print("lazy val in case: ") + val t: Option[String] = Some("lazy Z1") + t match { + case Some(Z1) => + println("ok") + + case None => + println("failed") + } + } + + def ifcond { + lazy val z1 = { println("forced "); "lazy z1" } + print("lazy val in if condition: ") + if (z1 == "lazy z1") + println("ok") + else + println("failed") + } + + + lazy val LazyField = { println("forced LazyField"); "LazyField" } + + def testPatMatchField { + print("lazy val in pattern: ") + val t: Option[String] = Some("LazyField") + t match { + case Some(LazyField) => + println("ok") + + case None => + println("failed") + } + } + + def test { + patmatchScrut + patmatchCase + patmatchPat + ifcond + testPatMatchField + } +} + + +object Test extends Application { + + TestExpressions.test +} diff --git a/test/files/run/lazy-locals.check b/test/files/run/lazy-locals.check new file mode 100644 index 0000000000..7f65cd3db2 --- /dev/null +++ b/test/files/run/lazy-locals.check @@ -0,0 +1,82 @@ +forced lazy val q +q = 10 +forced lazy val t +p = 21 +85 +forced lazy val t31 +forced lazy val t30 +forced lazy val t29 +forced lazy val t28 +forced lazy val t27 +forced lazy val t26 +forced lazy val t25 +forced lazy val t24 +forced lazy val t23 +forced lazy val t22 +forced lazy val t21 +forced lazy val t20 +forced lazy val t19 +forced lazy val t18 +forced lazy val t17 +forced lazy val t16 +forced lazy val t15 +forced lazy val t14 +forced lazy val t13 +forced lazy val t12 +forced lazy val t11 +forced lazy val t10 +forced lazy val t09 +forced lazy val t08 +forced lazy val t07 +forced lazy val t06 +forced lazy val t05 +forced lazy val t04 +forced lazy val t03 +forced lazy val t02 +forced lazy val t01 +forced lazy val t00 +Sum is: 496 +forced lazy val t32 +forced lazy val t31 +forced lazy val t30 +forced lazy val t29 +forced lazy val t28 +forced lazy val t27 +forced lazy val t26 +forced lazy val t25 +forced lazy val t24 +forced lazy val t23 +forced lazy val t22 +forced lazy val t21 +forced lazy val t20 +forced lazy val t19 +forced lazy val t18 +forced lazy val t17 +forced lazy val t16 +forced lazy val t15 +forced lazy val t14 +forced lazy val t13 +forced lazy val t12 +forced lazy val t11 +forced lazy val t10 +forced lazy val t09 +forced lazy val t08 +forced lazy val t07 +forced lazy val t06 +forced lazy val t05 +forced lazy val t04 +forced lazy val t03 +forced lazy val t02 +forced lazy val t01 +forced lazy val t00 +Sum is: 528 +forced lazy val t at n = 0 +42 +forced lazy val t at n = 0 +forced lazy val t at n = 1 +forced lazy val t at n = 2 +forced lazy val t at n = 3 +forced lazy val t at n = 4 +forced lazy val t at n = 5 +1764 +First 5 elements of ones: List(1, 1, 1, 1, 1) diff --git a/test/files/run/lazy-locals.scala b/test/files/run/lazy-locals.scala new file mode 100644 index 0000000000..937b5a80c0 --- /dev/null +++ b/test/files/run/lazy-locals.scala @@ -0,0 +1,143 @@ + +object Test extends Application { + + lazy val w = 10 + + /** normal test */ + def testLazy = { + lazy val t = { Console.println("forced lazy val t"); 42 } + lazy val p = t / 2 + lazy val q = { println("forced lazy val q"); 10} + println("q = " + q) + println("p = " + p) + 1 + t + t + } + + /** test 32 lazy vals, which fills one bitmap int. */ + def testLazy32 = { + lazy val t00 = { Console.println("forced lazy val t00"); 0 } + lazy val t01 = { Console.println("forced lazy val t01"); 1 } + lazy val t02 = { Console.println("forced lazy val t02"); 2 } + lazy val t03 = { Console.println("forced lazy val t03"); 3 } + lazy val t04 = { Console.println("forced lazy val t04"); 4 } + lazy val t05 = { Console.println("forced lazy val t05"); 5 } + lazy val t06 = { Console.println("forced lazy val t06"); 6 } + lazy val t07 = { Console.println("forced lazy val t07"); 7 } + lazy val t08 = { Console.println("forced lazy val t08"); 8 } + lazy val t09 = { Console.println("forced lazy val t09"); 9 } + lazy val t10 = { Console.println("forced lazy val t10"); 10 } + lazy val t11 = { Console.println("forced lazy val t11"); 11 } + lazy val t12 = { Console.println("forced lazy val t12"); 12 } + lazy val t13 = { Console.println("forced lazy val t13"); 13 } + lazy val t14 = { Console.println("forced lazy val t14"); 14 } + lazy val t15 = { Console.println("forced lazy val t15"); 15 } + lazy val t16 = { Console.println("forced lazy val t16"); 16 } + lazy val t17 = { Console.println("forced lazy val t17"); 17 } + lazy val t18 = { Console.println("forced lazy val t18"); 18 } + lazy val t19 = { Console.println("forced lazy val t19"); 19 } + lazy val t20 = { Console.println("forced lazy val t20"); 20 } + lazy val t21 = { Console.println("forced lazy val t21"); 21 } + lazy val t22 = { Console.println("forced lazy val t22"); 22 } + lazy val t23 = { Console.println("forced lazy val t23"); 23 } + lazy val t24 = { Console.println("forced lazy val t24"); 24 } + lazy val t25 = { Console.println("forced lazy val t25"); 25 } + lazy val t26 = { Console.println("forced lazy val t26"); 26 } + lazy val t27 = { Console.println("forced lazy val t27"); 27 } + lazy val t28 = { Console.println("forced lazy val t28"); 28 } + lazy val t29 = { Console.println("forced lazy val t29"); 29 } + lazy val t30 = { Console.println("forced lazy val t30"); 30 } + lazy val t31 = { Console.println("forced lazy val t31"); 31 } + + val sum = t31 + t30 + t29 + t28 + t27 + t26 + t25 + t24 + t23 + + t22 + t21 + t20 + t19 + t18 + t17 + t16 + t15 + t14 + + t13 + t12 + t11 + t10 + t09 + t08 + t07 + t06 + t05 + + t04 + t03 + t02 + t01 + t00; + println("Sum is: " + sum); + } + + /** test 32 lazy vals, which needs two bitmap ints. */ + def testLazy33 = { + lazy val t00 = { Console.println("forced lazy val t00"); 0 } + lazy val t01 = { Console.println("forced lazy val t01"); 1 } + lazy val t02 = { Console.println("forced lazy val t02"); 2 } + lazy val t03 = { Console.println("forced lazy val t03"); 3 } + lazy val t04 = { Console.println("forced lazy val t04"); 4 } + lazy val t05 = { Console.println("forced lazy val t05"); 5 } + lazy val t06 = { Console.println("forced lazy val t06"); 6 } + lazy val t07 = { Console.println("forced lazy val t07"); 7 } + lazy val t08 = { Console.println("forced lazy val t08"); 8 } + lazy val t09 = { Console.println("forced lazy val t09"); 9 } + lazy val t10 = { Console.println("forced lazy val t10"); 10 } + lazy val t11 = { Console.println("forced lazy val t11"); 11 } + lazy val t12 = { Console.println("forced lazy val t12"); 12 } + lazy val t13 = { Console.println("forced lazy val t13"); 13 } + lazy val t14 = { Console.println("forced lazy val t14"); 14 } + lazy val t15 = { Console.println("forced lazy val t15"); 15 } + lazy val t16 = { Console.println("forced lazy val t16"); 16 } + lazy val t17 = { Console.println("forced lazy val t17"); 17 } + lazy val t18 = { Console.println("forced lazy val t18"); 18 } + lazy val t19 = { Console.println("forced lazy val t19"); 19 } + lazy val t20 = { Console.println("forced lazy val t20"); 20 } + lazy val t21 = { Console.println("forced lazy val t21"); 21 } + lazy val t22 = { Console.println("forced lazy val t22"); 22 } + lazy val t23 = { Console.println("forced lazy val t23"); 23 } + lazy val t24 = { Console.println("forced lazy val t24"); 24 } + lazy val t25 = { Console.println("forced lazy val t25"); 25 } + lazy val t26 = { Console.println("forced lazy val t26"); 26 } + lazy val t27 = { Console.println("forced lazy val t27"); 27 } + lazy val t28 = { Console.println("forced lazy val t28"); 28 } + lazy val t29 = { Console.println("forced lazy val t29"); 29 } + lazy val t30 = { Console.println("forced lazy val t30"); 30 } + lazy val t31 = { Console.println("forced lazy val t31"); 31 } + lazy val t32 = { Console.println("forced lazy val t32"); 32 } + + val sum = t32 + t31 + t30 + t29 + t28 + t27 + t26 + t25 + t24 + t23 + + t22 + t21 + t20 + t19 + t18 + t17 + t16 + t15 + t14 + + t13 + t12 + t11 + t10 + t09 + t08 + t07 + t06 + t05 + + t04 + t03 + t02 + t01 + t00; + println("Sum is: " + sum); + } + + + /** test recursive method with lazy vals and a single forced */ + def testLazyRec(n: Int): Int = { + lazy val t = { println("forced lazy val t at n = " + n); 42 } + if (n > 0) + testLazyRec(n - 1) + else + t + } + + /** test recursive method with lazy vals and a all vals forced */ + def testLazyRecMany(n: Int): Int = { + lazy val t = { println("forced lazy val t at n = " + n); 42 } + if (n > 0) { + testLazyRecMany(n - 1); + t*t + } else + t + } + + def testRecVal { + lazy val twos: List[Int] = 2 :: twos + lazy val ones: Stream[Int] = Stream.cons(1, ones) + + println("First 5 elements of ones: " + ones.take(5).toList) + } + + // should compile without error + def testMutualRecVal { + lazy val odd: Int = 1 + even + lazy val even: Int = 1 + odd + + () + } + + println(testLazy) + testLazy32 + testLazy33 + println(testLazyRec(5)) + println(testLazyRecMany(5)) + testRecVal + +} diff --git a/test/files/run/lazy-traits.check b/test/files/run/lazy-traits.check new file mode 100644 index 0000000000..dcc8228ac1 --- /dev/null +++ b/test/files/run/lazy-traits.check @@ -0,0 +1,154 @@ +Cls test: + +z1 = lazy z1 +z1 = lazy z1 +z1 = lazy z1 +Cls2 test: + + +z1 = lazy z1 z2 = lazy z2 +z1 = lazy z1 z2 = lazy z2 +z1 = lazy z1 z2 = lazy z2 +Cls with B test: + + + +z1 = lazy z1 zb1 = lazy zb1 zc1 = lazy zc1 +z1 = lazy z1 zb1 = lazy zb1 zc1 = lazy zc1 +z1 = lazy z1 zb1 = lazy zb1 zc1 = lazy zc1 +OverflownLazyFields with A test: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +zc00 = lazy zc00 +zc01 = lazy zc01 +zc02 = lazy zc02 +zc03 = lazy zc03 +zc04 = lazy zc04 +zc05 = lazy zc05 +zc06 = lazy zc06 +zc07 = lazy zc07 +zc08 = lazy zc08 +zc09 = lazy zc09 +zc10 = lazy zc10 +zc11 = lazy zc11 +zc12 = lazy zc12 +zc13 = lazy zc13 +zc14 = lazy zc14 +zc15 = lazy zc15 +zc16 = lazy zc16 +zc17 = lazy zc17 +zc18 = lazy zc18 +zc19 = lazy zc19 +zc20 = lazy zc20 +zc21 = lazy zc21 +zc22 = lazy zc22 +zc23 = lazy zc23 +zc24 = lazy zc24 +zc25 = lazy zc25 +zc26 = lazy zc26 +zc27 = lazy zc27 +zc28 = lazy zc28 +zc29 = lazy zc29 +zc30 = lazy zc30 +zc31 = lazy zc31 +z1 = lazy z1 + +zc00 = lazy zc00 +zc01 = lazy zc01 +zc02 = lazy zc02 +zc03 = lazy zc03 +zc04 = lazy zc04 +zc05 = lazy zc05 +zc06 = lazy zc06 +zc07 = lazy zc07 +zc08 = lazy zc08 +zc09 = lazy zc09 +zc10 = lazy zc10 +zc11 = lazy zc11 +zc12 = lazy zc12 +zc13 = lazy zc13 +zc14 = lazy zc14 +zc15 = lazy zc15 +zc16 = lazy zc16 +zc17 = lazy zc17 +zc18 = lazy zc18 +zc19 = lazy zc19 +zc20 = lazy zc20 +zc21 = lazy zc21 +zc22 = lazy zc22 +zc23 = lazy zc23 +zc24 = lazy zc24 +zc25 = lazy zc25 +zc26 = lazy zc26 +zc27 = lazy zc27 +zc28 = lazy zc28 +zc29 = lazy zc29 +zc30 = lazy zc30 +zc31 = lazy zc31 +z1 = lazy z1 + +zc00 = lazy zc00 +zc01 = lazy zc01 +zc02 = lazy zc02 +zc03 = lazy zc03 +zc04 = lazy zc04 +zc05 = lazy zc05 +zc06 = lazy zc06 +zc07 = lazy zc07 +zc08 = lazy zc08 +zc09 = lazy zc09 +zc10 = lazy zc10 +zc11 = lazy zc11 +zc12 = lazy zc12 +zc13 = lazy zc13 +zc14 = lazy zc14 +zc15 = lazy zc15 +zc16 = lazy zc16 +zc17 = lazy zc17 +zc18 = lazy zc18 +zc19 = lazy zc19 +zc20 = lazy zc20 +zc21 = lazy zc21 +zc22 = lazy zc22 +zc23 = lazy zc23 +zc24 = lazy zc24 +zc25 = lazy zc25 +zc26 = lazy zc26 +zc27 = lazy zc27 +zc28 = lazy zc28 +zc29 = lazy zc29 +zc30 = lazy zc30 +zc31 = lazy zc31 +z1 = lazy z1 diff --git a/test/files/run/lazy-traits.scala b/test/files/run/lazy-traits.scala new file mode 100644 index 0000000000..f91da7ed1a --- /dev/null +++ b/test/files/run/lazy-traits.scala @@ -0,0 +1,133 @@ +trait A { + lazy val z1 = { + println("") + "lazy z1" + } +} + +/** Simple class which mixes in one lazy val. */ +class Cls extends AnyRef with A { + override def toString = + "z1 = " + z1 +} + +/** Own lazy val + one mixed in. */ +class Cls2 extends AnyRef with A { + lazy val z2 = { + println("") + "lazy z2" + } + + override def toString = + "z1 = " + z1 + " z2 = " + z2 +} + +trait B extends A { + lazy val zb1 = { + println("") + "lazy zb1" + } +} + +class ClsB extends Object with B { + lazy val zc1 = { + println("") + "lazy zc1" + } + override def toString = + "z1 = " + z1 + " zb1 = " + zb1 + " zc1 = " + zc1 +} + +/** Class with 32 lazy fields mixes in one more. */ +class OverflownLazyFields extends Object with A { + lazy val zc00 = { println(""); "lazy zc00" } + lazy val zc01 = { println(""); "lazy zc01" } + lazy val zc02 = { println(""); "lazy zc02" } + lazy val zc03 = { println(""); "lazy zc03" } + lazy val zc04 = { println(""); "lazy zc04" } + lazy val zc05 = { println(""); "lazy zc05" } + lazy val zc06 = { println(""); "lazy zc06" } + lazy val zc07 = { println(""); "lazy zc07" } + lazy val zc08 = { println(""); "lazy zc08" } + lazy val zc09 = { println(""); "lazy zc09" } + lazy val zc10 = { println(""); "lazy zc10" } + lazy val zc11 = { println(""); "lazy zc11" } + lazy val zc12 = { println(""); "lazy zc12" } + lazy val zc13 = { println(""); "lazy zc13" } + lazy val zc14 = { println(""); "lazy zc14" } + lazy val zc15 = { println(""); "lazy zc15" } + lazy val zc16 = { println(""); "lazy zc16" } + lazy val zc17 = { println(""); "lazy zc17" } + lazy val zc18 = { println(""); "lazy zc18" } + lazy val zc19 = { println(""); "lazy zc19" } + lazy val zc20 = { println(""); "lazy zc20" } + lazy val zc21 = { println(""); "lazy zc21" } + lazy val zc22 = { println(""); "lazy zc22" } + lazy val zc23 = { println(""); "lazy zc23" } + lazy val zc24 = { println(""); "lazy zc24" } + lazy val zc25 = { println(""); "lazy zc25" } + lazy val zc26 = { println(""); "lazy zc26" } + lazy val zc27 = { println(""); "lazy zc27" } + lazy val zc28 = { println(""); "lazy zc28" } + lazy val zc29 = { println(""); "lazy zc29" } + lazy val zc30 = { println(""); "lazy zc30" } + lazy val zc31 = { println(""); "lazy zc31" } + + override def toString = + "\nzc00 = " + zc00 + + "\nzc01 = " + zc01 + + "\nzc02 = " + zc02 + + "\nzc03 = " + zc03 + + "\nzc04 = " + zc04 + + "\nzc05 = " + zc05 + + "\nzc06 = " + zc06 + + "\nzc07 = " + zc07 + + "\nzc08 = " + zc08 + + "\nzc09 = " + zc09 + + "\nzc10 = " + zc10 + + "\nzc11 = " + zc11 + + "\nzc12 = " + zc12 + + "\nzc13 = " + zc13 + + "\nzc14 = " + zc14 + + "\nzc15 = " + zc15 + + "\nzc16 = " + zc16 + + "\nzc17 = " + zc17 + + "\nzc18 = " + zc18 + + "\nzc19 = " + zc19 + + "\nzc20 = " + zc20 + + "\nzc21 = " + zc21 + + "\nzc22 = " + zc22 + + "\nzc23 = " + zc23 + + "\nzc24 = " + zc24 + + "\nzc25 = " + zc25 + + "\nzc26 = " + zc26 + + "\nzc27 = " + zc27 + + "\nzc28 = " + zc28 + + "\nzc29 = " + zc29 + + "\nzc30 = " + zc30 + + "\nzc31 = " + zc31 + + "\nz1 = " + z1 +} + +trait PrivateLazy { + private lazy val str = "z1" +} + +/** Test successful compilation. */ +class InheritPrivateLazy extends AnyRef with PrivateLazy {} + + +object Test extends Application { + + def test(name: String, v: A) { + println(name + " test:") + println(v) + println(v) + println(v) + } + + test("Cls", new Cls) + test("Cls2", new Cls2) + test("Cls with B", new ClsB) + test("OverflownLazyFields with A", new OverflownLazyFields) +} -- cgit v1.2.3