summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project/ScalaOptionParser.scala2
-rw-r--r--src/compiler/scala/tools/ant/Scalac.scala2
-rw-r--r--src/compiler/scala/tools/nsc/Global.scala12
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala19
-rw-r--r--src/compiler/scala/tools/nsc/transform/Fields.scala246
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala310
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala561
-rw-r--r--src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala11
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala2
-rw-r--r--src/library/scala/runtime/LazyRef.scala52
-rw-r--r--src/manual/scala/man1/scalac.scala4
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala5
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala1
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala3
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Phased.scala4
-rw-r--r--test/files/neg/t6446-additional.check21
-rw-r--r--test/files/neg/t6446-missing.check19
-rw-r--r--test/files/neg/t6446-show-phases.check19
-rw-r--r--test/files/neg/t7494-no-options.check21
-rw-r--r--test/files/run/analyzerPlugins.check4
-rw-r--r--test/files/run/delambdafy_t6028.check6
-rw-r--r--test/files/run/lazy-locals-2.scala322
-rw-r--r--test/files/run/lazy_local_labels.check9
-rw-r--r--test/files/run/lazy_local_labels.scala28
-rw-r--r--test/files/run/programmatic-main.check19
-rw-r--r--test/files/run/t3569.check2
-rw-r--r--test/files/run/t3569.scala8
-rw-r--r--test/files/run/t5552.check4
-rw-r--r--test/files/run/t5552.scala10
-rw-r--r--test/files/run/t6028.check6
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala3
32 files changed, 940 insertions, 797 deletions
diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala
index 77c9d765e9..af82f8fce5 100644
--- a/project/ScalaOptionParser.scala
+++ b/project/ScalaOptionParser.scala
@@ -94,7 +94,7 @@ object ScalaOptionParser {
private def stringSettingNames = List("-Xgenerate-phase-graph", "-Xmain-class", "-Xpluginsdir", "-Xshow-class", "-Xshow-object", "-Xsource-reader", "-Ydump-classes", "-Ygen-asmp",
"-Ypresentation-log", "-Ypresentation-replay", "-Yrepl-outdir", "-d", "-dependencyfile", "-encoding", "-Xscript")
private def pathSettingNames = List("-bootclasspath", "-classpath", "-extdirs", "-javabootclasspath", "-javaextdirs", "-sourcepath", "-toolcp")
- private val phases = List("all", "parser", "namer", "packageobjects", "typer", "patmat", "superaccessors", "extmethods", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "posterasure", "lazyvals", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "delambdafy", "icode", "jvm", "terminal")
+ private val phases = List("all", "parser", "namer", "packageobjects", "typer", "patmat", "superaccessors", "extmethods", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "posterasure", "fields", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "delambdafy", "icode", "jvm", "terminal")
private val phaseSettings = List("-Xprint-icode", "-Ystop-after", "-Yskip", "-Yshow", "-Ystop-before", "-Ybrowse", "-Ylog", "-Ycheck", "-Xprint")
private def multiStringSettingNames = List("-Xmacro-settings", "-Xplugin", "-Xplugin-disable", "-Xplugin-require")
private def intSettingNames = List("-Xmax-classfile-name", "-Xelide-below", "-Ypatmat-exhaust-depth", "-Ypresentation-delay", "-Yrecursion")
diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala
index e9d1dfe4d2..511572f6f3 100644
--- a/src/compiler/scala/tools/ant/Scalac.scala
+++ b/src/compiler/scala/tools/ant/Scalac.scala
@@ -88,7 +88,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared {
object CompilerPhase extends PermissibleValue {
val values = List("namer", "typer", "pickler", "refchecks",
"uncurry", "tailcalls", "specialize", "explicitouter",
- "erasure", "lazyvals", "lambdalift", "constructors",
+ "erasure", "fields", "lambdalift", "constructors",
"flatten", "mixin", "delambdafy", "cleanup",
"jvm", "terminal")
}
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala
index af866e1a6f..32c446e16a 100644
--- a/src/compiler/scala/tools/nsc/Global.scala
+++ b/src/compiler/scala/tools/nsc/Global.scala
@@ -516,17 +516,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val runsRightAfter = Some("erasure")
} with PostErasure
- // phaseName = "lazyvals"
- object lazyVals extends {
- val global: Global.this.type = Global.this
- val runsAfter = List("erasure")
- val runsRightAfter = None
- } with LazyVals
// phaseName = "lambdalift"
object lambdaLift extends {
val global: Global.this.type = Global.this
- val runsAfter = List("lazyvals")
+ val runsAfter = List("erasure")
val runsRightAfter = None
} with LambdaLift
@@ -620,13 +614,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
pickler -> "serialize symbol tables",
refChecks -> "reference/override checking, translate nested objects",
uncurry -> "uncurry, translate function values to anonymous classes",
- fields -> "synthesize accessors and fields",
+ fields -> "synthesize accessors and fields, including bitmaps for lazy vals",
tailCalls -> "replace tail calls by jumps",
specializeTypes -> "@specialized-driven class and method specialization",
explicitOuter -> "this refs to outer pointers",
erasure -> "erase types, add interfaces for traits",
postErasure -> "clean up erased inline classes",
- lazyVals -> "allocate bitmaps, translate lazy vals into lazified defs",
lambdaLift -> "move nested functions to top level",
constructors -> "move field definitions into constructors",
mixer -> "mixin composition",
@@ -1258,7 +1251,6 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
val explicitouterPhase = phaseNamed("explicitouter")
val erasurePhase = phaseNamed("erasure")
val posterasurePhase = phaseNamed("posterasure")
- // val lazyvalsPhase = phaseNamed("lazyvals")
val lambdaliftPhase = phaseNamed("lambdalift")
// val constructorsPhase = phaseNamed("constructors")
val flattenPhase = phaseNamed("flatten")
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index 0a87e358b4..8d362f13dd 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -450,7 +450,9 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
with DelayedInitHelper
with OmittablesHelper
with GuardianOfCtorStmts
- {
+ with fields.CheckedAccessorTreeSynthesis
+ {
+ protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree)
val clazz = impl.symbol.owner // the transformed class
@@ -770,11 +772,20 @@ abstract class Constructors extends Statics with Transform with TypingTransforme
// We never eliminate delayed hooks or the constructors, so, only filter `defs`.
val prunedStats = (defs filterNot omittableStat) ::: delayedHookDefs ::: constructors
+ val statsWithInitChecks =
+ if (settings.checkInit) {
+ val addChecks = new SynthInitCheckedAccessorsIn(currentOwner)
+ prunedStats mapConserve {
+ case dd: DefDef => deriveDefDef(dd)(addChecks.wrapRhsWithInitChecks(dd.symbol))
+ case stat => stat
+ }
+ } else prunedStats
+
// Add the static initializers
- if (classInitStats.isEmpty) deriveTemplate(impl)(_ => prunedStats)
+ if (classInitStats.isEmpty) deriveTemplate(impl)(_ => statsWithInitChecks)
else {
- val staticCtor = staticConstructor(prunedStats, localTyper, impl.pos)(classInitStats)
- deriveTemplate(impl)(_ => staticCtor :: prunedStats)
+ val staticCtor = staticConstructor(statsWithInitChecks, localTyper, impl.pos)(classInitStats)
+ deriveTemplate(impl)(_ => staticCtor :: statsWithInitChecks)
}
}
} // TemplateTransformer
diff --git a/src/compiler/scala/tools/nsc/transform/Fields.scala b/src/compiler/scala/tools/nsc/transform/Fields.scala
index 6339f0002d..0b8705948c 100644
--- a/src/compiler/scala/tools/nsc/transform/Fields.scala
+++ b/src/compiler/scala/tools/nsc/transform/Fields.scala
@@ -56,8 +56,7 @@ import symtab.Flags._
*
* TODO: check init support (or drop the -Xcheck-init flag??)
*/
-abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransformers {
-
+abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransformers with AccessorSynthesis {
import global._
import definitions._
@@ -139,13 +138,12 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
class FieldMemoization(accessorOrField: Symbol, site: Symbol) {
val tp = fieldTypeOfAccessorIn(accessorOrField, site.thisType)
- // not stored, no side-effect
- val pureConstant = tp.isInstanceOf[ConstantType]
-
- // if !stored, may still have a side-effect
- // (currently not distinguished -- used to think we could drop unit-typed vals,
- // but the memory model cares about writes to unit-typed fields)
- val stored = !pureConstant // || isUnitType(tp))
+ // We can only omit strict vals of ConstantType. Lazy vals do not receive constant types (anymore).
+ // (See note at widenIfNecessary -- for example, the REPL breaks when we omit constant lazy vals)
+ // Note that a strict unit-typed val does receive a field, because we cannot omit the write to the field
+ // (well, we could emit it for non-@volatile ones, if I understand the memory model correctly,
+ // but that seems pretty edge-casey)
+ val constantTyped = tp.isInstanceOf[ConstantType]
}
private def fieldTypeForGetterIn(getter: Symbol, pre: Type): Type = getter.info.finalResultType.asSeenFrom(pre, getter.owner)
@@ -187,31 +185,29 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
private def moduleInit(module: Symbol) = {
// println(s"moduleInit for $module in ${module.ownerChain} --> ${moduleVarOf.get(module)}")
val moduleVar = moduleVarOf(module)
- gen.mkAssignAndReturn(moduleVar, gen.newModule(module, moduleVar.info))
+ def moduleVarRef = gen.mkAttributedRef(moduleVar)
+
+ // for local modules, we synchronize on the owner of the method that owns the module
+ val monitorHolder = This(moduleVar.owner.enclClass)
+ val cond = Apply(Select(moduleVarRef, Object_eq), List(CODE.NULL))
+ val init = Assign(moduleVarRef, gen.newModule(module, moduleVar.info))
+
+ Block(List(gen.mkSynchronizedCheck(monitorHolder, cond, List(init), Nil)), moduleVarRef)
}
+ // NoSymbol for lazy accessor sym with unit result type
+ def lazyVarOf(sym: Symbol) = moduleVarOf.getOrElse(sym, NoSymbol)
- private def newLazyVarSymbol(owner: Symbol, member: Symbol, tp: Type, extraFlags: Long = 0, localLazyVal: Boolean = false): TermSymbol = {
+ private def newLazyVarSymbol(owner: Symbol, member: Symbol, tp: Type, extraFlags: Long = 0): TermSymbol = {
val flags = member.flags | extraFlags
- val name = member.name.toTermName
- val pos = member.pos
-
- // If the owner is not a class, this is a lazy val from a method,
- // with no associated field. It has an accessor with $lzy appended to its name and
- // its flags are set differently. The implicit flag is reset because otherwise
- // a local implicit "lazy val x" will create an ambiguity with itself
- // via "x$lzy" as can be seen in test #3927.
- val nameSuffix =
- if (!localLazyVal) reflect.NameTransformer.LOCAL_SUFFIX_STRING
- else reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING
-
- // TODO: should end up final in bytecode
- val fieldFlags =
- if (!localLazyVal) flags & FieldFlags | PrivateLocal | MUTABLE
- else (flags & FieldFlags | ARTIFACT | MUTABLE) & ~(IMPLICIT | STABLE)
-
-// println(s"new lazy var sym in $owner for $member ${symtab.Flags.flagsToString(fieldFlags)}")
- val sym = owner.newValue(name.append(nameSuffix), pos.focus, fieldFlags | extraFlags) setInfo tp
+ val pos = member.pos
+ val name = member.name.toTermName.append(reflect.NameTransformer.LOCAL_SUFFIX_STRING)
+
+ // the underlying field for a lazy val should not be final because we write to it outside of a constructor,
+ // so, set the MUTABLE flag
+ val fieldFlags = flags & FieldFlags | PrivateLocal | MUTABLE
+
+ val sym = owner.newValue(name, pos.focus, fieldFlags | extraFlags) setInfo tp
moduleVarOf(member) = sym
sym
}
@@ -292,7 +288,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// destructively mangle accessor's name (which may cause rehashing of decls), also sets flags
// this accessor has to be implemented in a subclass -- can't be private
- if ((member hasFlag PRIVATE) && fieldMemoization.stored) member makeNotPrivate clazz
+ if ((member hasFlag PRIVATE) && !fieldMemoization.constantTyped) member makeNotPrivate clazz
// This must remain in synch with publicizeTraitMethod in Mixins, so that the
// synthesized member in a subclass and the trait member remain in synch regarding access.
@@ -304,7 +300,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// (not sure why this only problem only arose when we started setting the notPROTECTED flag)
// derive trait setter after calling makeNotPrivate (so that names are mangled consistently)
- if (accessorUnderConsideration && fieldMemoization.stored) {
+ if (accessorUnderConsideration && !fieldMemoization.constantTyped) {
synthesizeImplInSubclasses(member)
if ((member hasFlag STABLE) && !(member hasFlag LAZY))
@@ -347,19 +343,18 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// a module does not need treatment here if it's static, unless it has a matching member in a superclass
// a non-static method needs a module var
val modulesAndLazyValsNeedingExpansion =
- oldDecls.toList.filter(m =>
- (m.isModule && (!m.isStatic || m.isOverridingSymbol))
- || (m.isLazy && !(m.info.isInstanceOf[ConstantType] || isUnitType(m.info))) // no need for ASF since we're in the defining class
- )
+ oldDecls.toList.filter(m => (m.isModule && (!m.isStatic || m.isOverridingSymbol)) || m.isLazy)
+
+ val accessorSymbolSynth = checkedAccessorSymbolSynth(tp.typeSymbol)
// expand module def in class/object (if they need it -- see modulesNeedingExpansion above)
- val expandedModulesAndLazyVals =
- modulesAndLazyValsNeedingExpansion map { member =>
+ val expandedModulesAndLazyVals = (
+ modulesAndLazyValsNeedingExpansion flatMap { member =>
if (member.isLazy) {
- newLazyVarMember(member)
+ List(newLazyVarMember(member), accessorSymbolSynth.newSlowPathSymbol(member))
}
// expanding module def (top-level or nested in static module)
- else if (member.isStatic) { // implies m.isOverridingSymbol as per above filter
+ else List(if (member.isStatic) { // implies m.isOverridingSymbol as per above filter
// Need a module accessor, to implement/override a matching member in a superclass.
// Never a need for a module var if the module is static.
newMatchingModuleAccessor(clazz, member)
@@ -368,8 +363,8 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
// must reuse symbol instead of creating an accessor
member setFlag NEEDS_TREES
newModuleVarMember(member)
- }
- }
+ })
+ })
// println(s"expanded modules for $clazz: $expandedModules")
@@ -400,7 +395,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
if (nme.isTraitSetterName(member.name)) {
val getter = member.getterIn(member.owner)
val clone = cloneAccessor()
-
+
setClonedTraitSetterFlags(clazz, getter, clone)
// println(s"mixed in trait setter ${clone.defString}")
@@ -416,9 +411,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
else if (member hasFlag LAZY) {
val mixedinLazy = cloneAccessor()
val lazyVar = newLazyVarMember(mixedinLazy)
- List(lazyVar, newSuperLazy(mixedinLazy, site, lazyVar))
+ // println(s"mixing in lazy var: $lazyVar for $member")
+ List(lazyVar, accessorSymbolSynth.newSlowPathSymbol(mixedinLazy), newSuperLazy(mixedinLazy, site, lazyVar))
}
- else if (member.isGetter && fieldMemoizationIn(member, clazz).stored) {
+ else if (member.isGetter && !fieldMemoizationIn(member, clazz).constantTyped) {
// add field if needed
val field = clazz.newValue(member.localName, member.pos) setInfo fieldTypeForGetterIn(member, clazz.thisType)
@@ -432,13 +428,15 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
} else List(cloneAccessor()) // no field needed (constant-typed getter has constant as its RHS)
}
-// println(s"mixedInAccessorAndFields for $clazz: $mixedInAccessorAndFields")
+ // println(s"mixedInAccessorAndFields for $clazz: $mixedInAccessorAndFields")
// omit fields that are not memoized, retain all other members
- def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && !fieldMemoizationIn(sym, clazz).stored
+ def omittableField(sym: Symbol) = sym.isValue && !sym.isMethod && fieldMemoizationIn(sym, clazz).constantTyped
val newDecls =
- if (expandedModulesAndLazyVals.isEmpty && mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField)
+ // under -Xcheckinit we generate all kinds of bitmaps, even when there are no lazy vals
+ if (expandedModulesAndLazyVals.isEmpty && mixedInAccessorAndFields.isEmpty && !settings.checkInit)
+ oldDecls.filterNot(omittableField)
else {
// must not alter `decls` directly
val newDecls = newScope
@@ -449,10 +447,15 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
oldDecls foreach { d => if (!omittableField(d)) enter(d) }
mixedInAccessorAndFields foreach enterAll
+ // both oldDecls and mixedInAccessorAndFields (a list of lists) contribute
+ val bitmapSyms = accessorSymbolSynth.computeBitmapInfos(newDecls.toList)
+
+ bitmapSyms foreach enter
+
newDecls
}
-// println(s"new decls for $clazz: $expandedModules ++ $mixedInAccessorAndFields")
+ // println(s"new decls for $clazz: $expandedModules ++ $mixedInAccessorAndFields")
if (newDecls eq oldDecls) tp
else ClassInfoType(parents, newDecls, clazz)
@@ -468,50 +471,109 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
if (!module.isStatic) module setFlag METHOD | STABLE
}
- class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
- def mkTypedUnit(pos: Position) = localTyper.typedPos(pos)(CODE.UNIT)
- def deriveUnitDef(stat: Tree) = deriveDefDef(stat)(_ => mkTypedUnit(stat.pos))
+ class FieldsTransformer(unit: CompilationUnit) extends TypingTransformer(unit) with CheckedAccessorTreeSynthesis {
+ protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree)
- def mkAccessor(accessor: Symbol)(body: Tree) = localTyper.typedPos(accessor.pos)(DefDef(accessor, body)).asInstanceOf[DefDef]
+ def mkTypedUnit(pos: Position) = typedPos(pos)(CODE.UNIT)
+ // TODO: clean up. this method is not used
+ def deriveUnitDef(stat: Tree) = deriveDefDef(stat)(_ => mkTypedUnit(stat.pos))
- def mkField(sym: Symbol) = localTyper.typedPos(sym.pos)(ValDef(sym)).asInstanceOf[ValDef]
+ def mkAccessor(accessor: Symbol)(body: Tree) = typedPos(accessor.pos)(DefDef(accessor, body)).asInstanceOf[DefDef]
+
+ // this makes trees for mixed in fields, as well as for bitmap fields (their RHS will be EmptyTree because they are initialized implicitly)
+ // if we decide to explicitly initialize, use this RHS: if (symbol.info.typeSymbol.asClass == BooleanClass) FALSE else ZERO)
+ // could detect it's a bitmap field with something like `sym.name.startsWith(nme.BITMAP_PREFIX)` (or perhaps something more robust...)
+ def mkField(sym: Symbol, rhs: Tree = EmptyTree) = typedPos(sym.pos)(ValDef(sym, rhs)).asInstanceOf[ValDef]
+
+ /**
+ * Desugar a local `lazy val x: Int = rhs` into
+ * ```
+ * val x$lzy = new scala.runtime.LazyInt()
+ * def x(): Int =
+ * x$lzy.synchronized {
+ * if (!x$lzy.initialized) {
+ * x$lzy.initialized = true
+ * x$lzy.value = rhs
+ * }
+ * x$lzy.value
+ * }
+ * ```
+ */
+ private def mkLazyLocalDef(lazyVal: Symbol, rhs: Tree): Tree = {
+ val lazyValType = lazyVal.tpe.resultType
+ val refClass = lazyHolders.getOrElse(lazyValType.typeSymbol, LazyRefClass)
+ val refTpe = if (refClass != LazyRefClass) refClass.tpe else appliedType(refClass.typeConstructor, List(lazyValType))
+
+ val flags = (lazyVal.flags & FieldFlags | ARTIFACT | MUTABLE) & ~(IMPLICIT | STABLE) // TODO: why include MUTABLE???
+ val name = lazyVal.name.toTermName.append(nme.LAZY_LOCAL_SUFFIX_STRING)
+ val holderSym =
+ lazyVal.owner.newValue(name, lazyVal.pos, flags) setInfo refTpe
+
+ val accessor = mkAccessor(lazyVal) {
+ import CODE._
+ val initializedGetter = refTpe.member(nme.initialized)
+ val setInitialized = Apply(Select(Ident(holderSym), initializedGetter.setterIn(refClass)), TRUE :: Nil)
+ val isUnit = refClass == LazyUnitClass
+ val valueGetter = if (isUnit) NoSymbol else refTpe.member(nme.value)
+ val valueSetter = if (isUnit) NoSymbol else valueGetter.setterIn(refClass)
+ val setValue = if (isUnit) rhs else Apply(Select(Ident(holderSym), valueSetter), rhs :: Nil)
+ val getValue = if (isUnit) UNIT else Apply(Select(Ident(holderSym), valueGetter), Nil)
+ gen.mkSynchronized(Ident(holderSym),
+ Block(List(
+ If(NOT(Ident(holderSym) DOT initializedGetter),
+ Block(List(
+ setInitialized),
+ setValue),
+ EmptyTree)),
+ getValue)) // must read the value within the synchronized block since it's not volatile
+ // (there's no happens-before relation with the read of the volatile initialized field)
+ // TODO: double-checked locking
+ }
+ // do last!
+ // remove LAZY: prevent lazy expansion in mixin
+ // remove STABLE: prevent replacing accessor call of type Unit by BoxedUnit.UNIT in erasure
+ // remove ACCESSOR: prevent constructors from eliminating the method body if the lazy val is
+ // lifted into a trait (TODO: not sure about the details here)
+ lazyVal.resetFlag(LAZY | STABLE | ACCESSOR)
+ Thicket(mkField(holderSym, New(refTpe)) :: accessor :: Nil)
+ }
// synth trees for accessors/fields and trait setters when they are mixed into a class
def fieldsAndAccessors(clazz: Symbol): List[ValOrDefDef] = {
- def fieldAccess(accessor: Symbol): Option[Tree] = {
+ def fieldAccess(accessor: Symbol): List[Tree] = {
val fieldName = accessor.localName
val field = clazz.info.decl(fieldName)
// The `None` result denotes an error, but it's refchecks' job to report it (this fallback is for robustness).
// This is the result of overriding a val with a def, so that no field is found in the subclass.
- if (field.exists) Some(Select(This(clazz), field))
- else None
+ if (field.exists) List(Select(This(clazz), field))
+ else Nil
}
- def getterBody(getter: Symbol): Option[Tree] = {
+ def getterBody(getter: Symbol): List[Tree] = {
// accessor created by newMatchingModuleAccessor for a static module that does need an accessor
// (because there's a matching member in a super class)
if (getter.asTerm.referenced.isModule) {
- Some(gen.mkAttributedRef(clazz.thisType, getter.asTerm.referenced))
+ List(gen.mkAttributedRef(clazz.thisType, getter.asTerm.referenced))
} else {
val fieldMemoization = fieldMemoizationIn(getter, clazz)
- if (fieldMemoization.pureConstant) Some(gen.mkAttributedQualifier(fieldMemoization.tp)) // TODO: drop when we no longer care about producing identical bytecode
+ if (fieldMemoization.constantTyped) List(gen.mkAttributedQualifier(fieldMemoization.tp)) // TODO: drop when we no longer care about producing identical bytecode
else fieldAccess(getter)
}
}
// println(s"accessorsAndFieldsNeedingTrees for $templateSym: $accessorsAndFieldsNeedingTrees")
- def setterBody(setter: Symbol): Option[Tree] = {
+ def setterBody(setter: Symbol): List[Tree] = {
// trait setter in trait
- if (clazz.isTrait) Some(EmptyTree)
+ if (clazz.isTrait) List(EmptyTree)
// trait setter for overridden val in class
- else if (checkAndClearOverriddenTraitSetter(setter)) Some(mkTypedUnit(setter.pos))
+ else if (checkAndClearOverriddenTraitSetter(setter)) List(mkTypedUnit(setter.pos))
// trait val/var setter mixed into class
else fieldAccess(setter) map (fieldSel => Assign(fieldSel, Ident(setter.firstParam)))
}
- def moduleAccessorBody(module: Symbol): Some[Tree] = Some(
+ def moduleAccessorBody(module: Symbol): List[Tree] = List(
// added during synthFieldsAndAccessors using newModuleAccessor
// a module defined in a trait by definition can't be static (it's a member of the trait and thus gets a new instance for every outer instance)
if (clazz.isTrait) EmptyTree
@@ -519,15 +581,17 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
else moduleInit(module)
)
- def superLazy(getter: Symbol): Some[Tree] = {
+ val synthAccessorInClass = new SynthLazyAccessorsIn(clazz)
+ def superLazy(getter: Symbol): List[ValOrDefDef] = {
assert(!clazz.isTrait)
// this contortion was the only way I can get the super select to be type checked correctly.. TODO: why does SelectSuper not work?
- Some(gen.mkAssignAndReturn(moduleVarOf(getter), Apply(Select(Super(This(clazz), tpnme.EMPTY), getter.name), Nil)))
+ val rhs = Apply(Select(Super(This(clazz), tpnme.EMPTY), getter.name), Nil)
+ explodeThicket(synthAccessorInClass.expandLazyClassMember(lazyVarOf(getter), getter, rhs, Map.empty)).asInstanceOf[List[ValOrDefDef]]
}
clazz.info.decls.toList.filter(checkAndClearNeedsTrees) flatMap {
case module if module hasAllFlags (MODULE | METHOD) => moduleAccessorBody(module) map mkAccessor(module)
- case getter if getter hasAllFlags (LAZY | METHOD) => superLazy(getter) map mkAccessor(getter)
+ case getter if getter hasAllFlags (LAZY | METHOD) => superLazy(getter)
case setter if setter.isSetter => setterBody(setter) map mkAccessor(setter)
case getter if getter.hasFlag(ACCESSOR) => getterBody(getter) map mkAccessor(getter)
case field if !(field hasFlag METHOD) => Some(mkField(field)) // vals/vars and module vars (cannot have flags PACKAGE | JAVA since those never receive NEEDS_TREES)
@@ -538,7 +602,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
def rhsAtOwner(stat: ValOrDefDef, newOwner: Symbol): Tree =
atOwner(newOwner)(super.transform(stat.rhs.changeOwner(stat.symbol -> newOwner)))
- private def Thicket(trees: List[Tree]) = Block(trees, EmptyTree)
override def transform(stat: Tree): Tree = {
val clazz = currentOwner
val statSym = stat.symbol
@@ -564,37 +627,28 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
case DefDef(_, _, _, _, _, rhs) if (statSym hasFlag ACCESSOR)
&& (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym)
&& !clazz.isTrait // we've already done this for traits.. the asymmetry will be solved by the above todo
- && fieldMemoizationIn(statSym, clazz).pureConstant =>
+ && fieldMemoizationIn(statSym, clazz).constantTyped =>
deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe))
- /** Normalize ValDefs to corresponding accessors + field
- *
- * ValDef in trait --> getter DefDef
- * Lazy val receives field with a new symbol (if stored) and the ValDef's symbol is moved to a DefDef (the lazy accessor):
- * - for lazy values of type Unit and all lazy fields inside traits,
- * the rhs is the initializer itself, because we'll just "compute" the result on every access
- * ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap)
- * - 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.
- */
+ // deferred val, trait val, lazy val (local or in class)
case vd@ValDef(mods, name, tpt, rhs) if vd.symbol.hasFlag(ACCESSOR) && treeInfo.noFieldFor(vd, clazz) =>
- def notStored = {val resultType = statSym.info.resultType ; (resultType.isInstanceOf[ConstantType] || isUnitType(resultType))}
val transformedRhs = atOwner(statSym)(transform(rhs))
if (rhs == EmptyTree) mkAccessor(statSym)(EmptyTree)
- else if (clazz.isTrait || notStored) mkAccessor(statSym)(transformedRhs)
- else if (clazz.isClass) mkAccessor(statSym)(gen.mkAssignAndReturn(moduleVarOf(vd.symbol), transformedRhs))
+ else if (clazz.isTrait) mkAccessor(statSym)(transformedRhs)
+ else if (!clazz.isClass) mkLazyLocalDef(vd.symbol, transformedRhs)
else {
- // local lazy val (same story as modules: info transformer doesn't get here, so can't drive tree synthesis)
- val lazyVar = newLazyVarSymbol(currentOwner, statSym, statSym.info.resultType, extraFlags = 0, localLazyVal = true)
- val lazyValInit = gen.mkAssignAndReturn(lazyVar, transformedRhs)
- Thicket(mkField(lazyVar) :: mkAccessor(statSym)(lazyValInit) :: Nil)
+ // TODO: make `synthAccessorInClass` a field and update it in atOwner?
+ // note that `LazyAccessorTreeSynth` is pretty lightweight
+ // (it's just a bunch of methods that all take a `clazz` parameter, which is thus stored as a field)
+ val synthAccessorInClass = new SynthLazyAccessorsIn(clazz)
+ synthAccessorInClass.expandLazyClassMember(lazyVarOf(statSym), statSym, transformedRhs, nullables.getOrElse(clazz, Map.empty))
}
// drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields
case ValDef(mods, _, _, rhs) if (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym)
- && fieldMemoizationIn(statSym, clazz).pureConstant =>
- EmptyTree
+ && fieldMemoizationIn(statSym, clazz).constantTyped =>
+ EmptyThicket
case ModuleDef(_, _, impl) =>
// ??? The typer doesn't take kindly to seeing this ClassDef; we have to set NoType so it will be ignored.
@@ -616,22 +670,24 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
if (stat.isTerm) atOwner(exprOwner)(transform(stat))
else transform(stat)
+ private val nullables = perRunCaches.newMap[Symbol, Map[Symbol, List[Symbol]]]
+
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
val addedStats =
- if (!currentOwner.isClass) Nil
+ if (!currentOwner.isClass) Nil // TODO: || currentOwner.isPackageClass
else afterOwnPhase { fieldsAndAccessors(currentOwner) }
+ val inRealClass = currentOwner.isClass && !(currentOwner.isPackageClass || currentOwner.isTrait)
+ if (inRealClass)
+ nullables(currentOwner) = lazyValNullables(currentOwner, stats)
+
val newStats =
stats mapConserve (if (exprOwner != currentOwner) transformTermsAtExprOwner(exprOwner) else transform)
addedStats ::: (if (newStats eq stats) stats else {
// check whether we need to flatten thickets and drop empty ones
- if (newStats exists { case EmptyTree => true case Block(_, EmptyTree) => true case _ => false })
- newStats flatMap {
- case EmptyTree => Nil
- case Block(thicket, EmptyTree) => thicket
- case stat => stat :: Nil
- }
+ if (newStats exists mustExplodeThicket)
+ newStats flatMap explodeThicket
else newStats
})
}
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
deleted file mode 100644
index ba2f6082e0..0000000000
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ /dev/null
@@ -1,310 +0,0 @@
-package scala.tools.nsc
-package transform
-
-import scala.collection.mutable
-
-abstract class LazyVals extends Transform with TypingTransformers with ast.TreeDSL {
- // inherits abstract value `global` and class `Phase` from Transform
-
- import global._ // the global environment
- import definitions._ // standard classes and methods
- import typer.typed // methods to type trees
- import CODE._
-
- val phaseName: String = "lazyvals"
- private val FLAGS_PER_BYTE: Int = 8 // Byte
- private def bitmapKind = ByteClass
-
- def newTransformer(unit: CompilationUnit): Transformer =
- new LazyValues(unit)
-
- private def lazyUnit(sym: Symbol) = sym.tpe.resultType.typeSymbol == UnitClass
-
- object LocalLazyValFinder extends Traverser {
- var result: Boolean = _
-
- def find(t: Tree) = {result = false; traverse(t); result}
- def find(ts: List[Tree]) = {result = false; traverseTrees(ts); result}
-
- override def traverse(t: Tree) {
- if (!result)
- t match {
- case v@ValDef(_, _, _, _) if v.symbol.isLazy =>
- result = true
-
- case d@DefDef(_, _, _, _, _, _) if d.symbol.isLazy && lazyUnit(d.symbol) =>
- d.symbol.resetFlag(symtab.Flags.LAZY)
- result = true
-
- case ClassDef(_, _, _, _) | DefDef(_, _, _, _, _, _) | ModuleDef(_, _, _) =>
-
- // Avoid adding bitmaps when they are fully overshadowed by those that are added inside loops
- case LabelDef(name, _, _) if nme.isLoopHeaderLabel(name) =>
-
- case _ =>
- super.traverse(t)
- }
- }
- }
-
- /**
- * Transform local lazy accessors to check for the initialized bit.
- */
- class LazyValues(unit: CompilationUnit) extends TypingTransformer(unit) {
- /** map from method symbols to the number of lazy values it defines. */
- private val lazyVals = perRunCaches.newMap[Symbol, Int]() withDefaultValue 0
-
- import symtab.Flags._
- private def flattenThickets(stats: List[Tree]): List[Tree] = stats.flatMap(_ match {
- case b @ Block(List(d1@DefDef(_, n1, _, _, _, _)), d2@DefDef(_, n2, _, _, _, _)) if b.tpe == null && n1.endsWith(nme.LAZY_SLOW_SUFFIX) =>
- List(d1, d2)
- case stat =>
- List(stat)
- })
-
- /** Perform the following transformations:
- * - for a lazy accessor inside a method, make it check the initialization bitmap
- * - implement double checked locking of member modules for non-trait owners (trait just have the abstract accessor)
- * ```
- * // typer
- * class C { object x }
- * // fields
- * class C { var x$module; def x() = { x$module = new x; x$module }
- * // lazyvals
- * class C {
- * var x$module // module var
- * def x() = { if (x$module == null) x$lzycompute() else x$module // fast path
- * def x$lzycompute() = { synchronized { if (x$module == null) x$module = new x }; x$module } // slow path
- * }
- * ```
- * - for all methods, add enough int vars to allow one flag per lazy local value
- * - blocks in template bodies behave almost like methods. A single bitmaps section is
- * added in the first block, for all lazy values defined in such blocks.
- * - remove ACCESSOR flags: accessors in traits are not statically implemented,
- * but moved to the host class. local lazy values should be statically implemented.
- */
- override def transform(tree: Tree): Tree = {
- val sym = tree.symbol
- curTree = tree
-
- tree match {
-
- case Block(_, _) =>
- val block1 = super.transform(tree)
- val Block(stats, expr) = block1
- treeCopy.Block(block1, flattenThickets(stats), expr)
-
- case DefDef(_, _, _, _, _, rhs) => atOwner(tree.symbol) {
- val (res, slowPathDef) = if (!sym.owner.isClass && sym.isLazy) {
- val enclosingClassOrDummyOrMethod = {
- val enclMethod = sym.enclMethod
-
- if (enclMethod != NoSymbol) {
- val enclClass = sym.enclClass
- if (enclClass != NoSymbol && enclMethod == enclClass.enclMethod)
- enclClass
- else
- enclMethod
- } else
- sym.owner
- }
- debuglog(s"determined enclosing class/dummy/method for lazy val as $enclosingClassOrDummyOrMethod given symbol $sym")
- val idx = lazyVals(enclosingClassOrDummyOrMethod)
- lazyVals(enclosingClassOrDummyOrMethod) = idx + 1
- val (rhs1, sDef) = mkLazyLocalDef(enclosingClassOrDummyOrMethod, transform(rhs), idx, sym)
- sym.resetFlag((if (lazyUnit(sym)) 0 else LAZY) | ACCESSOR)
- (rhs1, sDef)
- } else if (sym.hasAllFlags(MODULE | METHOD) && !sym.owner.isTrait) {
- rhs match {
- case b @ Block((assign @ Assign(moduleRef, _)) :: Nil, expr) =>
- def cond = Apply(Select(moduleRef, Object_eq), List(Literal(Constant(null))))
- val (fastPath, slowPath) = mkDoubleCheckedLocking(sym.owner.enclClass, moduleRef.symbol, cond, transform(assign) :: Nil, Nil, transform(expr))
- (localTyper.typedPos(tree.pos)(fastPath), localTyper.typedPos(tree.pos)(slowPath))
- case rhs =>
- global.reporter.error(tree.pos, "Unexpected tree on the RHS of a module accessor: " + rhs)
- (rhs, EmptyTree)
- }
- } else {
- (transform(rhs), EmptyTree)
- }
-
- val ddef1 = deriveDefDef(tree)(_ => if (LocalLazyValFinder.find(res)) typed(addBitmapDefs(sym, res)) else res)
- if (slowPathDef != EmptyTree) {
- // The contents of this block are flattened into the enclosing statement sequence, see flattenThickets
- // This is a poor man's version of dotty's Thicket: https://github.com/lampepfl/dotty/blob/d5280358d1/src/dotty/tools/dotc/ast/Trees.scala#L707
- Block(slowPathDef, ddef1)
- } else ddef1
- }
-
- case Template(_, _, body) => atOwner(currentOwner) {
- // TODO: shady business... can this logic be encapsulated in LocalLazyValFinder?
- var added = false
- val stats = super.transformTrees(body) mapConserve {
- case stat: ValDef => typed(deriveValDef(stat)(addBitmapDefs(stat.symbol, _)))
- case stat: TermTree if !added && (LocalLazyValFinder find stat) =>
- added = true
- typed(addBitmapDefs(sym, stat))
- case stat => stat
- }
-
- val innerClassBitmaps = if (!added && currentOwner.isClass && bitmaps.contains(currentOwner)) {
- // add bitmap to inner class if necessary
- val toAdd0 = bitmaps(currentOwner).map(s => typed(ValDef(s, ZERO)))
- toAdd0.foreach(t => {
- if (currentOwner.info.decl(t.symbol.name) == NoSymbol) {
- t.symbol.setFlag(PROTECTED)
- currentOwner.info.decls.enter(t.symbol)
- }
- })
- toAdd0
- } else List()
- deriveTemplate(tree)(_ => innerClassBitmaps ++ flattenThickets(stats))
- }
-
- case ValDef(_, _, _, _) if !sym.owner.isModule && !sym.owner.isClass =>
- deriveValDef(tree) { rhs0 =>
- val rhs = transform(rhs0)
- if (LocalLazyValFinder.find(rhs)) typed(addBitmapDefs(sym, rhs)) else rhs
- }
-
- case l@LabelDef(name0, params0, ifp0@If(_, _, _)) if name0.startsWith(nme.WHILE_PREFIX) =>
- val ifp1 = super.transform(ifp0)
- val If(cond0, thenp0, elsep0) = ifp1
-
- if (LocalLazyValFinder.find(thenp0))
- deriveLabelDef(l)(_ => treeCopy.If(ifp1, cond0, typed(addBitmapDefs(sym.owner, thenp0)), elsep0))
- else
- l
-
- case l@LabelDef(name0, params0, block@Block(stats0, expr))
- if name0.startsWith(nme.WHILE_PREFIX) || name0.startsWith(nme.DO_WHILE_PREFIX) =>
- val stats1 = super.transformTrees(stats0)
- if (LocalLazyValFinder.find(stats1))
- deriveLabelDef(l)(_ => treeCopy.Block(block, typed(addBitmapDefs(sym.owner, stats1.head))::stats1.tail, expr))
- else
- l
-
- case _ => super.transform(tree)
- }
- }
-
- /** Add the bitmap definitions to the rhs of a method definition.
- * If the rhs has been tail-call transformed, insert the bitmap
- * definitions inside the top-level label definition, so that each
- * iteration has the lazy values uninitialized. Otherwise add them
- * at the very beginning of the method.
- */
- private def addBitmapDefs(methSym: Symbol, rhs: Tree): Tree = {
- def prependStats(stats: List[Tree], tree: Tree): Block = tree match {
- case Block(stats1, res) => Block(stats ::: stats1, res)
- case _ => Block(stats, tree)
- }
-
- val bmps = bitmaps(methSym) map (ValDef(_, ZERO))
-
- def isMatch(params: List[Ident]) = (params.tail corresponds methSym.tpe.params)(_.tpe == _.tpe)
-
- if (bmps.isEmpty) rhs else rhs match {
- case Block(assign, l @ LabelDef(name, params, _))
- if (name string_== "_" + methSym.name) && isMatch(params) =>
- Block(assign, deriveLabelDef(l)(rhs => typed(prependStats(bmps, rhs))))
-
- case _ => prependStats(bmps, rhs)
- }
- }
-
- def mkDoubleCheckedLocking(clazz: Symbol, lzyVal: Symbol, cond: => Tree, syncBody: List[Tree], stats: List[Tree], retVal: Tree): (Tree, Tree) = {
- val owner = lzyVal.owner
- val defSym = owner.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), lzyVal.pos, STABLE | PRIVATE)
- defSym setInfo MethodType(List(), lzyVal.tpe.resultType)
- if (owner.isClass) owner.info.decls.enter(defSym)
-
- val slowPathDef: Tree = {
- debuglog(s"crete slow compute path $defSym with owner ${defSym.owner} for lazy val $lzyVal")
- // this is a hack i don't understand for lazy vals nested in a lazy val, introduced in 3769f4d,
- // tested in pos/t3670 (add9be64). class A { val n = { lazy val b = { lazy val dd = 3; dd }; b } }
- // bitmaps has an entry bMethodSym -> List(bitmap$0), where bitmap$0.owner == bMethodSym.
- // now we set bitmap$0.owner = b$lzycomputeMethodSym.
- for (bitmap <- bitmaps(lzyVal)) bitmap.owner = defSym
-
- val rhs: Tree = gen.mkSynchronizedCheck(gen.mkAttributedThis(clazz), cond, syncBody, stats).changeOwner(currentOwner -> defSym)
-
- DefDef(defSym, addBitmapDefs(lzyVal, BLOCK(rhs, retVal)))
- }
-
-
- (If(cond, Apply(Ident(defSym), Nil), retVal), slowPathDef)
- }
-
- /** return a 'lazified' version of rhs. Rhs should conform to the
- * following schema:
- * {
- * l$ = <rhs>
- * l$
- * } or
- * <rhs> when the lazy value has type Unit (for which there is no field
- * to cache its value.
- *
- * Similarly as for normal lazy val members (see Mixin), the result will be a tree of the form
- * { if ((bitmap&n & MASK) == 0) this.l$compute()
- * else l$
- *
- * def l$compute() = { synchronized(enclosing_class_or_dummy) {
- * if ((bitmap$n & MASK) == 0) {
- * l$ = <rhs>
- * bitmap$n = bimap$n | MASK
- * }}
- * l$
- * }
- * }
- * where bitmap$n is a byte value acting as a bitmap of initialized values. It is
- * the 'n' is (offset / 8), the MASK is (1 << (offset % 8)). If the value has type
- * unit, no field is used to cache the value, so the l$compute will now look as following:
- * {
- * def l$compute() = { synchronized(enclosing_class_or_dummy) {
- * if ((bitmap$n & MASK) == 0) {
- * <rhs>;
- * bitmap$n = bimap$n | MASK
- * }}
- * ()
- * }
- * }
- */
- val bitmaps = mutable.Map[Symbol, List[Symbol]]() withDefaultValue Nil
- private def mkLazyLocalDef(methOrClass: Symbol, tree: Tree, offset: Int, lazyVal: Symbol): (Tree, Tree) = {
- /** Return the symbol corresponding of the right bitmap int inside meth,
- * given offset.
- */
- val bitmapSym = {
- val n = offset / FLAGS_PER_BYTE
- val bmps = bitmaps(methOrClass)
- if (bmps.length > n)
- bmps(n)
- else {
- val sym = methOrClass.newVariable(nme.newBitmapName(nme.BITMAP_NORMAL, n), methOrClass.pos).setInfo(ByteTpe)
- enteringTyper {
- sym addAnnotation VolatileAttr
- }
-
- bitmaps(methOrClass) = (sym :: bmps).reverse
- sym
- }
- }
-
- val mask = LIT(1 << (offset % FLAGS_PER_BYTE))
- val bitmapRef = if (methOrClass.isClass) Select(This(methOrClass), bitmapSym) else Ident(bitmapSym)
-
- debuglog(s"create complete lazy def in $methOrClass for $lazyVal")
-
- val (stmt, res) = tree match {
- case Block(List(assignment), res) if !lazyUnit(lazyVal) => (assignment, res)
- case rhs => (rhs, UNIT)
- }
- val block = Block(List(stmt, bitmapRef === (bitmapRef GEN_| (mask, bitmapKind))), UNIT)
-
- def cond = (bitmapRef GEN_& (mask, bitmapKind)) GEN_== (ZERO, bitmapKind)
- val lazyDefs = mkDoubleCheckedLocking(methOrClass.enclClass, lazyVal, cond, List(block), Nil, res)
- (atPos(tree.pos)(localTyper.typed {lazyDefs._1 }), atPos(tree.pos)(localTyper.typed {lazyDefs._2 }))
- }
- }
-}
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index b3558d6281..190755ff53 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -11,33 +11,34 @@ import Flags._
import scala.annotation.tailrec
import scala.collection.mutable
-// TODO: move lazy vals bitmap creation to lazy vals phase now that lazy vals are mixed in during fields
-trait InitBitmaps extends Transform with ast.TreeDSL {
+trait AccessorSynthesis extends Transform with ast.TreeDSL {
import global._
import definitions._
import CODE._
- /** Map a field symbol to a unique integer denoting its position in the class layout.
- * For each class, fields defined by the class come after inherited fields.
- * Mixed-in fields count as fields defined by the class itself.
- *
- * DO NOT USE THIS MAP DIRECTLY, go through convenience methods
- */
- private val _fieldOffset = perRunCaches.newMap[Symbol, Int]()
+ val EmptyThicket = EmptyTree
+ def Thicket(trees: List[Tree]) = if (trees.isEmpty) EmptyTree else Block(trees, EmptyTree)
+ def mustExplodeThicket(tree: Tree): Boolean =
+ tree match {
+ case EmptyTree => true
+ case Block(_, EmptyTree) => true
+ case _ => false
+ }
+ def explodeThicket(tree: Tree): List[Tree] = tree match {
+ case EmptyTree => Nil
+ case Block(thicket, EmptyTree) => thicket
+ case stat => stat :: Nil
+ }
- private val bitmapKindForCategory = perRunCaches.newMap[Name, ClassSymbol]()
- trait InitializationTransformation {
+ trait AccessorTreeSynthesis {
protected def typedPos(pos: Position)(tree: Tree): Tree
- def accessorInitialization(clazz: Symbol, templStats: List[Tree]): AccessorInitialization =
- if (settings.checkInit) new CheckInitAccessorInitialization(clazz, templStats)
- else new LazyAccessorInitialization(clazz, templStats)
-
- class AccessorInitialization(protected val clazz: Symbol){
- private val _newDefs = mutable.ListBuffer[Tree]()
+ // used while we still need to synthesize some accessors in mixins: paramaccessors and presupers
+ class UncheckedAccessorSynth(protected val clazz: Symbol){
+ protected val _newDefs = mutable.ListBuffer[Tree]()
- def deriveStatsWithInitChecks(stats: List[Tree]): List[Tree] = stats
+ def newDefs = _newDefs.toList
/** Add tree at given position as new definition */
protected def addDef(tree: ValOrDefDef): Unit = _newDefs += typedPos(position(tree.symbol))(tree)
@@ -93,196 +94,208 @@ trait InitBitmaps extends Transform with ast.TreeDSL {
Select(This(clazz), accessor.accessed)
}
+ }
+ case class BitmapInfo(symbol: Symbol, mask: Literal) {
+ def storageClass: ClassSymbol = symbol.info.typeSymbol.asClass
+ }
- protected class LazyAccessorInitialization(clazz: Symbol, templStats: List[Tree]) extends AccessorInitialization(clazz) {
- private def bitmapNumber(sym: Symbol): Int = _fieldOffset(sym) / flagsPerBitmap(sym)
- private def offsetInBitmap(sym: Symbol) = _fieldOffset(sym) % flagsPerBitmap(sym)
- protected def hasBitmap(sym: Symbol) = _fieldOffset isDefinedAt sym
- private def flagsPerBitmap(field: Symbol): Int =
- bitmapKind(field) match {
- case BooleanClass => 1
- case ByteClass => 8
- case IntClass => 32
- case LongClass => 64
- }
+ // TODO: better way to communicate from info transform to tree transfor?
+ private[this] val _bitmapInfo = perRunCaches.newMap[Symbol, BitmapInfo]
+ private[this] val _slowPathFor = perRunCaches.newMap[Symbol, Symbol]()
- protected def bitmapKind(field: Symbol): ClassSymbol = bitmapKindForCategory(bitmapCategory(field))
- protected def bitmapType(field: Symbol): Type = bitmapKind(field).tpe
- protected def bitmapName(field: Symbol): TermName = nme.newBitmapName(bitmapCategory(field), bitmapNumber(field)).toTermName
+ def checkedAccessorSymbolSynth(clz: Symbol) =
+ if (settings.checkInit) new CheckInitAccessorSymbolSynth { val clazz = clz }
+ else new CheckedAccessorSymbolSynth { val clazz = clz }
- protected def isTransientField(field: Symbol) = field.accessedOrSelf hasAnnotation TransientAttr
-// protected def isFieldWithTransientBitmap(field: Symbol) = isTransientField(field) && isFieldWithBitmap(field)
+ // base trait, with enough functionality for lazy vals -- CheckInitAccessorSymbolSynth adds logic for -Xcheckinit
+ trait CheckedAccessorSymbolSynth {
+ protected val clazz: Symbol
+ protected def defaultPos = clazz.pos.focus
+ protected def isTrait = clazz.isTrait
+ protected def hasTransientAnnot(field: Symbol) = field.accessedOrSelf hasAnnotation TransientAttr
- /** Examines the symbol and returns a name indicating what brand of
- * bitmap it requires. The possibilities are the BITMAP_* vals
- * defined in StdNames. If it needs no bitmap, nme.NO_NAME.
- *
- * bitmaps for checkinit fields are not inherited
- */
- protected def bitmapCategory(field: Symbol): Name = {
- def isFieldWithBitmap(field: Symbol) = {
- field.info // ensure that nested objects are transformed
- // For checkinit consider normal value getters
- // but for lazy values only take into account lazy getters
- field.isLazy && field.isMethod && !field.isDeferred
- }
+ def needsBitmap(sym: Symbol): Boolean = !(isTrait || sym.isDeferred) && sym.isMethod && sym.isLazy && !sym.isSpecialized
- import nme._
- if (isFieldWithBitmap(field))
- if (isTransientField(field)) BITMAP_TRANSIENT else BITMAP_NORMAL
- else NO_NAME
- }
+ /** Examines the symbol and returns a name indicating what brand of
+ * bitmap it requires. The possibilities are the BITMAP_* vals
+ * defined in StdNames. If it needs no bitmap, nme.NO_NAME.
+ *
+ * bitmaps for checkinit fields are not inherited
+ */
+ protected def bitmapCategory(sym: Symbol): Name = {
+ // ensure that nested objects are transformed TODO: still needed?
+ sym.initialize
- /** Fill the map from fields to offset numbers. And from bitmap category to the type used for the bitmap field (its "kind").
- *
- * Instead of field symbols, the map keeps their getter symbols. This makes code generation easier later.
- */
- def bitmapsFor(decls: List[Symbol]): List[Symbol] = {
-// val bitmaps = mutable.ListBuffer.empty[Symbol]
- def fold(fields: List[Symbol], category: Name) = {
- var idx = 0
- fields foreach { f =>
- _fieldOffset(f) = idx
-// bitmaps += newBitmapFor(idx, f)
-
- idx += 1
- }
+ import nme._
- if (idx == 0) ()
- else if (idx == 1) bitmapKindForCategory(category) = BooleanClass
- else if (idx < 9) bitmapKindForCategory(category) = ByteClass
- else if (idx < 33) bitmapKindForCategory(category) = IntClass
- else bitmapKindForCategory(category) = LongClass
- }
- decls groupBy bitmapCategory foreach {
- case (nme.NO_NAME, _) => ()
- case (category, fields) => fold(fields, category)
- }
-// bitmaps.toList
- Nil
- }
+ if (needsBitmap(sym) && sym.isLazy)
+ if (hasTransientAnnot(sym)) BITMAP_TRANSIENT else BITMAP_NORMAL
+ else NO_NAME
+ }
- /*
- * Return the bitmap field for 'offset'. Depending on the hierarchy it is possible to reuse
- * the bitmap of its parents. If that does not exist yet we create one.
- */
- def bitmapFor(field: Symbol): Symbol = {
- val sym = clazz.info.decl(bitmapName(field))
+ def bitmapFor(sym: Symbol): BitmapInfo = _bitmapInfo(sym)
+ protected def hasBitmap(sym: Symbol): Boolean = _bitmapInfo isDefinedAt sym
- assert(!sym.isOverloaded, sym)
- sym orElse newBitmapFor(field)
- }
+ /** Fill the map from fields to bitmap infos.
+ *
+ * Instead of field symbols, the map keeps their getter symbols. This makes code generation easier later.
+ */
+ def computeBitmapInfos(decls: List[Symbol]): List[Symbol] = {
+ def doCategory(fields: List[Symbol], category: Name) = {
+ val nbFields = fields.length // we know it's > 0
+ val (bitmapClass, bitmapCapacity) =
+ if (nbFields == 1) (BooleanClass, 1)
+ else if (nbFields <= 8) (ByteClass, 8)
+ else if (nbFields <= 32) (IntClass, 32)
+ else (LongClass, 64)
+
+ // 0-based index of highest bit, divided by bits per bitmap
+ // note that this is only ever > 0 when bitmapClass == LongClass
+ val maxBitmapNumber = (nbFields - 1) / bitmapCapacity
+
+ // transient fields get their own category
+ val isTransientCategory = fields.head hasAnnotation TransientAttr
+
+ val bitmapSyms =
+ (0 to maxBitmapNumber).toArray map { bitmapNumber =>
+ val bitmapSym = (
+ clazz.newVariable(nme.newBitmapName(category, bitmapNumber).toTermName, defaultPos)
+ setInfo bitmapClass.tpe
+ setFlag PrivateLocal | NEEDS_TREES
+ )
- private def newBitmapFor(field: Symbol): Symbol = {
- val bitmapSym =
- clazz.newVariable(bitmapName(field), clazz.pos) setInfo bitmapType(field) setFlag PrivateLocal setPos clazz.pos
+ bitmapSym addAnnotation VolatileAttr
- bitmapSym addAnnotation VolatileAttr
- if (isTransientField(field)) bitmapSym addAnnotation TransientAttr
+ if (isTransientCategory) bitmapSym addAnnotation TransientAttr
- clazz.info.decls.enter(bitmapSym)
- addDef(ValDef(bitmapSym, if (bitmapSym.info == BooleanClass) FALSE else ZERO))
+ bitmapSym
+ }
- bitmapSym
- }
+ fields.zipWithIndex foreach { case (f, idx) =>
+ val bitmapIdx = idx / bitmapCapacity
+ val offsetInBitmap = idx % bitmapCapacity
+ val mask =
+ if (bitmapClass == LongClass) Constant(1L << offsetInBitmap)
+ else Constant(1 << offsetInBitmap)
- protected def isUnitGetter(sym: Symbol) = sym.tpe.resultType.typeSymbol == UnitClass
+ _bitmapInfo(f) = BitmapInfo(bitmapSyms(bitmapIdx), Literal(mask))
+ }
- // overridden in CheckInitAccessorInitialization
- def rhsWithOtherInitCheck(sym: Symbol)(rhs: Tree): Tree = rhs
+ bitmapSyms
+ }
- /** Complete lazy field accessors. Applies only to classes,
- * for its own (non inherited) lazy fields.
- */
- def rhsWithInitCheck(sym: Symbol)(rhs: Tree): Tree = {
- if (!clazz.isTrait && sym.isLazy && rhs != EmptyTree) {
- rhs match {
- case rhs if isUnitGetter(sym) =>
- mkLazyMemberDef(sym, List(rhs), UNIT)
+ decls groupBy bitmapCategory flatMap {
+ case (category, fields) if category != nme.NO_NAME && fields.nonEmpty => doCategory(fields, category)
+ case _ => Nil
+ } toList
+ }
- case Block(stats, res) =>
- mkLazyMemberDef(sym, stats, Select(This(clazz), res.symbol))
+ def slowPathFor(lzyVal: Symbol): Symbol = _slowPathFor(lzyVal)
- case rhs => rhs
- }
- }
- else rhsWithOtherInitCheck(sym)(rhs)
- }
+ def newSlowPathSymbol(lzyVal: Symbol): Symbol = {
+ val pos = if (lzyVal.pos != NoPosition) lzyVal.pos else defaultPos // TODO: is the else branch ever taken?
+ val sym = clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), pos, PRIVATE) setInfo MethodType(Nil, lzyVal.tpe.resultType)
+ _slowPathFor(lzyVal) = sym
+ sym
+ }
- override def deriveStatsWithInitChecks(stats: List[Tree]): List[Tree] = {
- bitmapsFor(clazz.info.decls.toList)
+ }
-// foreach { bitmapSym =>
-// clazz.info.decls.enter(bitmapSym)
-// addDef(ValDef(bitmapSym, if (bitmapSym.info == BooleanClass) FALSE else ZERO))
-// }
+ trait CheckInitAccessorSymbolSynth extends CheckedAccessorSymbolSynth {
+ /** Does this field require an initialized bit?
+ * Note: fields of classes inheriting DelayedInit are not checked.
+ * This is because they are neither initialized in the constructor
+ * nor do they have a setter (not if they are vals anyway). The usual
+ * logic for setting bitmaps does therefore not work for such fields.
+ * That's why they are excluded.
+ * Note: The `checkinit` option does not check if transient fields are initialized.
+ */
+ protected def needsInitFlag(sym: Symbol): Boolean =
+ sym.isGetter &&
+ !( sym.isInitializedToDefault
+ || isConstantType(sym.info.finalResultType) // SI-4742
+ || sym.hasFlag(PARAMACCESSOR | SPECIALIZED | LAZY)
+ || sym.accessed.hasFlag(PRESUPER)
+ || sym.isOuterAccessor
+ || (sym.owner isSubClass DelayedInitClass)
+ || (sym.accessed hasAnnotation TransientAttr))
+
+ /** Examines the symbol and returns a name indicating what brand of
+ * bitmap it requires. The possibilities are the BITMAP_* vals
+ * defined in StdNames. If it needs no bitmap, nme.NO_NAME.
+ *
+ * bitmaps for checkinit fields are not inherited
+ */
+ override protected def bitmapCategory(sym: Symbol): Name = {
+ import nme._
- stats mapConserve {
- case dd: DefDef => deriveDefDef(dd)(rhsWithInitCheck(dd.symbol))
- case stat => stat
- }
+ super.bitmapCategory(sym) match {
+ case NO_NAME if needsInitFlag(sym) && !sym.isDeferred =>
+ if (hasTransientAnnot(sym)) BITMAP_CHECKINIT_TRANSIENT else BITMAP_CHECKINIT
+ case category => category
}
+ }
+
+ override def needsBitmap(sym: Symbol): Boolean = super.needsBitmap(sym) || !(isTrait || sym.isDeferred) && needsInitFlag(sym)
+ }
- private def maskForOffset(sym: Symbol, kind: ClassSymbol): Constant =
- if (kind == LongClass) Constant(1L << offsetInBitmap(sym))
- else Constant(1 << offsetInBitmap(sym))
+ // synthesize trees based on info gathered during info transform
+ // (which are known to have been run because the tree transform runs afterOwnPhase)
+ // since we can't easily share all info via symbols and flags, we have two maps above
+ // (they are persisted even between phases because the -Xcheckinit logic runs during constructors)
+ // TODO: can we use attachments instead of _bitmapInfo and _slowPathFor?
+ trait CheckedAccessorTreeSynthesis extends AccessorTreeSynthesis {
+ // note: we deal in getters here, not field symbols
+ trait SynthCheckedAccessorsTreesInClass extends CheckedAccessorSymbolSynth {
+ def isUnitGetter(sym: Symbol) = sym.tpe.resultType.typeSymbol == UnitClass
+ def thisRef = gen.mkAttributedThis(clazz)
/** Return an (untyped) tree of the form 'clazz.this.bitmapSym & mask (==|!=) 0', the
* precise comparison operator depending on the value of 'equalToZero'.
*/
- protected def mkTest(field: Symbol, equalToZero: Boolean): Tree = {
- val bitmapSym = bitmapFor(field)
- val bitmapTree = This(clazz) DOT bitmapSym
- val bitmapClass = bitmapSym.info.typeSymbol.asClass
+ def mkTest(field: Symbol, equalToZero: Boolean = true): Tree = {
+ val bitmap = bitmapFor(field)
+ val bitmapTree = thisRef DOT bitmap.symbol
- if (bitmapClass == BooleanClass) {
+ if (bitmap.storageClass == BooleanClass) {
if (equalToZero) NOT(bitmapTree) else bitmapTree
} else {
- val mask = Literal(maskForOffset(field, bitmapClass))
- val lhs = bitmapTree GEN_&(mask, bitmapClass)
- if (equalToZero) lhs GEN_==(ZERO, bitmapClass)
- else lhs GEN_!=(ZERO, bitmapClass)
+ val lhs = bitmapTree GEN_&(bitmap.mask, bitmap.storageClass)
+ if (equalToZero) lhs GEN_==(ZERO, bitmap.storageClass)
+ else lhs GEN_!=(ZERO, bitmap.storageClass)
}
}
-
- /* Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */
+ /** Return an (untyped) tree of the form 'Clazz.this.bmp = Clazz.this.bmp | mask'. */
def mkSetFlag(valSym: Symbol): Tree = {
- val bitmapSym = bitmapFor(valSym)
- val bitmapClass = bitmapSym.info.typeSymbol.asClass
- def x = This(clazz) DOT bitmapSym
+ val bitmap = bitmapFor(valSym)
+ def x = thisRef DOT bitmap.symbol
- // NOTE: bitwise or (`|`) on two bytes yields and Int (TODO: why was this not a problem when this ran during mixins?)
Assign(x,
- if (bitmapClass == BooleanClass) TRUE
- else Apply(Select(x, getMember(bitmapClass, nme.OR)), List(Literal(maskForOffset(valSym, bitmapClass))))
+ if (bitmap.storageClass == BooleanClass) TRUE
+ else {
+ val or = Apply(Select(x, getMember(bitmap.storageClass, nme.OR)), List(bitmap.mask))
+ // NOTE: bitwise or (`|`) on two bytes yields and Int (TODO: why was this not a problem when this ran during mixins?)
+ // TODO: need this to make it type check -- is there another way??
+ if (bitmap.storageClass != LongClass) Apply(Select(or, newTermName("to" + bitmap.storageClass.name)), Nil)
+ else or
+ }
)
-
}
+ }
-
- /** return a 'lazified' version of rhs. It uses double-checked locking to ensure
- * initialization is performed at most once. For performance reasons the double-checked
- * locking is split into two parts, the first (fast) path checks the bitmap without
- * synchronizing, and if that fails it initializes the lazy val within the
- * synchronization block (slow path). This way the inliner should optimize
- * the fast path because the method body is small enough.
- * Private fields used only in this initializer are subsequently set to null.
- *
- * @param lzyVal The symbol of this lazy field
- * @param init The tree which initializes the field ( f = <rhs> )
- * @param offset The offset of this field in the flags bitmap
+ class SynthLazyAccessorsIn(protected val clazz: Symbol) extends SynthCheckedAccessorsTreesInClass {
+ /**
+ * The compute method (slow path) looks like:
*
- * The result will be a tree of the form `if ((bitmap&n & MASK) == 0) this.l$compute() else l$`
- * A synthetic compute method will also be added to the current class, of the following shape:
* ```
* def l$compute() = {
* synchronized(this) {
@@ -306,134 +319,106 @@ trait InitBitmaps extends Transform with ast.TreeDSL {
*
* If the class contains only a single lazy val then the bitmap is
* represented as a Boolean and the condition checking is a simple bool test.
+ *
+ * Private fields used only in this initializer are subsequently set to null.
+ *
+ * For performance reasons the double-checked locking is split into two parts,
+ * the first (fast) path checks the bitmap without synchronizing, and if that
+ * fails it initializes the lazy val within the synchronization block (slow path).
+ *
+ * This way the inliner should optimize the fast path because the method body is small enough.
*/
- private def mkLazyMemberDef(lzyVal: Symbol, init: List[Tree], retVal: Tree): Tree = {
- def thisRef = gen.mkAttributedThis(clazz)
- def cond = mkTest(lzyVal, equalToZero = true)
+ def expandLazyClassMember(lazyVar: Symbol, lazyAccessor: Symbol, transformedRhs: Tree, nullables: Map[Symbol, List[Symbol]]): Tree = {
+ // use cast so that specialization can turn null.asInstanceOf[T] into null.asInstanceOf[Long]
+ def nullify(sym: Symbol) =
+ Select(thisRef, sym.accessedOrSelf) === gen.mkAsInstanceOf(NULL, sym.info.resultType)
- val slowPathDef = {
- def nullify(sym: Symbol) = Select(This(clazz), sym.accessedOrSelf) === LIT(null)
- val nulls = lazyValNullables.getOrElse(lzyVal, Nil) map nullify
+ val nulls = nullables.getOrElse(lazyAccessor, Nil) map nullify
- if (nulls.nonEmpty)
- log("nulling fields inside " + lzyVal + ": " + nulls)
+ if (nulls.nonEmpty)
+ log("nulling fields inside " + lazyAccessor + ": " + nulls)
- val pos = if (lzyVal.pos != NoPosition) lzyVal.pos else clazz.pos // TODO: is the else branch ever taken?
- val slowPathSym =
- clazz.newMethod(nme.newLazyValSlowComputeName(lzyVal.name.toTermName), pos, PRIVATE) setInfoAndEnter MethodType(Nil, lzyVal.tpe.resultType)
+ val slowPathSym = slowPathFor(lazyAccessor)
+ val rhsAtSlowDef = transformedRhs.changeOwner(lazyAccessor -> slowPathSym)
- val statsToSynch = init ::: List(mkSetFlag(lzyVal), UNIT)
- val synchedRhs = gen.mkSynchronizedCheck(thisRef, cond, statsToSynch, nulls)
+ val isUnit = isUnitGetter(lazyAccessor)
+ val selectVar = if (isUnit) UNIT else Select(thisRef, lazyVar)
+ val storeRes = if (isUnit) rhsAtSlowDef else Assign(selectVar, rhsAtSlowDef)
- // TODO: this code used to run after classes were flattened -- do we have all the needed owner changes?
- DefDef(slowPathSym, Block(List(synchedRhs.changeOwner(lzyVal -> slowPathSym)), retVal))
- }
+ val synchedStats = storeRes :: mkSetFlag(lazyAccessor) :: Nil
+ val slowPathRhs =
+ Block(List(gen.mkSynchronizedCheck(thisRef, mkTest(lazyAccessor), synchedStats, nulls)), selectVar)
- addDef(slowPathDef)
+ // The lazy accessor delegates to the compute method if needed, otherwise just accesses the var (it was initialized previously)
+ // `if ((bitmap&n & MASK) == 0) this.l$compute() else l$`
+ val accessorRhs = If(mkTest(lazyAccessor), Apply(Select(thisRef, slowPathSym), Nil), selectVar)
- typedPos(init.head.pos)(If(cond, Apply(Select(thisRef, slowPathDef.symbol), Nil), retVal))
+ afterOwnPhase { // so that we can assign to vals
+ Thicket(List((DefDef(slowPathSym, slowPathRhs)), DefDef(lazyAccessor, accessorRhs)) map typedPos(lazyAccessor.pos.focus))
+ }
}
+ }
- /** Map lazy values to the fields they should null after initialization. */
- private lazy val lazyValNullables: Map[Symbol, List[Symbol]] =
- // if there are no lazy fields, take the fast path and save a traversal of the whole AST
- if (!clazz.info.decls.exists(_.isLazy)) Map()
- else {
- // A map of single-use fields to the lazy value that uses them during initialization.
- // Each field has to be private and defined in the enclosing class, and there must
- // be exactly one lazy value using it.
- //
- // Such fields will be nulled after the initializer has memoized the lazy value.
- val singleUseFields: Map[Symbol, List[Symbol]] = {
- val usedIn = mutable.HashMap[Symbol, List[Symbol]]() withDefaultValue Nil
-
- object SingleUseTraverser extends Traverser {
- override def traverse(tree: Tree) {
- tree match {
- case Assign(lhs, rhs) => traverse(rhs) // assignments don't count
- case _ =>
- if (tree.hasSymbolField && tree.symbol != NoSymbol) {
- val sym = tree.symbol
- if ((sym.hasAccessorFlag || (sym.isTerm && !sym.isMethod))
- && sym.isPrivate
- && !(currentOwner.isGetter && currentOwner.accessed == sym) // getter
- && !definitions.isPrimitiveValueClass(sym.tpe.resultType.typeSymbol)
- && sym.owner == clazz
- && !sym.isLazy
- && !tree.isDef) {
- debuglog("added use in: " + currentOwner + " -- " + tree)
- usedIn(sym) ::= currentOwner
- }
- }
- super.traverse(tree)
- }
+ /** Map lazy values to the fields they should null after initialization. */
+ // TODO: fix
+ def lazyValNullables(clazz: Symbol, templStats: List[Tree]): Map[Symbol, List[Symbol]] = {
+ // if there are no lazy fields, take the fast path and save a traversal of the whole AST
+ if (!clazz.info.decls.exists(_.isLazy)) Map()
+ else {
+ // A map of single-use fields to the lazy value that uses them during initialization.
+ // Each field has to be private and defined in the enclosing class, and there must
+ // be exactly one lazy value using it.
+ //
+ // Such fields will be nulled after the initializer has memoized the lazy value.
+ val singleUseFields: Map[Symbol, List[Symbol]] = {
+ val usedIn = mutable.HashMap[Symbol, List[Symbol]]() withDefaultValue Nil
+
+ object SingleUseTraverser extends Traverser {
+ override def traverse(tree: Tree) {
+ tree match {
+ // assignment targets don't count as a dereference -- only check the rhs
+ case Assign(_, rhs) => traverse(rhs)
+ case tree: RefTree if tree.symbol != NoSymbol =>
+ val sym = tree.symbol
+ // println(s"$sym in ${sym.owner} from $currentOwner ($tree)")
+ if ((sym.hasAccessorFlag || (sym.isTerm && !sym.isMethod)) && sym.isPrivate && !sym.isLazy // non-lazy private field or its accessor
+ && !definitions.isPrimitiveValueClass(sym.tpe.resultType.typeSymbol) // primitives don't hang on to significant amounts of heap
+ && sym.owner == currentOwner.enclClass && !(currentOwner.isGetter && currentOwner.accessed == sym)) {
+
+ // println("added use in: " + currentOwner + " -- " + tree)
+ usedIn(sym) ::= currentOwner
+ }
+ super.traverse(tree)
+ case _ => super.traverse(tree)
}
}
- templStats foreach SingleUseTraverser.apply
- debuglog("usedIn: " + usedIn)
- usedIn filter {
- case (_, member :: Nil) => member.isValue && member.isLazy
- case _ => false
- } toMap
}
+ templStats foreach SingleUseTraverser.apply
+ // println("usedIn: " + usedIn)
- val map = mutable.Map[Symbol, Set[Symbol]]() withDefaultValue Set()
- // check what fields can be nulled for
- for ((field, users) <- singleUseFields; lazyFld <- users if !lazyFld.accessed.hasAnnotation(TransientAttr))
- map(lazyFld) += field
+ // only consider usages from non-transient lazy vals (SI-9365)
+ val singlyUsedIn = usedIn filter { case (_, member :: Nil) => member.isLazy && !member.accessed.hasAnnotation(TransientAttr) case _ => false } toMap
- map.mapValues(_.toList sortBy (_.id)).toMap
+ // println("singlyUsedIn: " + singlyUsedIn)
+ singlyUsedIn
}
- }
-
-
- protected class CheckInitAccessorInitialization(clazz: Symbol, templStats: List[Tree]) extends LazyAccessorInitialization(clazz, templStats) {
- /** Does this field require an initialized bit?
- * Note: fields of classes inheriting DelayedInit are not checked.
- * This is because they are neither initialized in the constructor
- * nor do they have a setter (not if they are vals anyway). The usual
- * logic for setting bitmaps does therefore not work for such fields.
- * That's why they are excluded.
- * Note: The `checkinit` option does not check if transient fields are initialized.
- */
- private def needsInitFlag(sym: Symbol): Boolean = (
- sym.isGetter
- && !sym.isInitializedToDefault
- && !isConstantType(sym.info.finalResultType) // SI-4742
- && !sym.hasFlag(PARAMACCESSOR | SPECIALIZED | LAZY)
- && !sym.accessed.hasFlag(PRESUPER)
- && !sym.isOuterAccessor
- && !(sym.owner isSubClass DelayedInitClass)
- && !(sym.accessed hasAnnotation TransientAttr)
- )
-
- private def needsInitFlagAndHasBitmap(sym: Symbol) = hasBitmap(sym) && needsInitFlag(sym)
-
-// override protected def isFieldWithTransientBitmap(field: Symbol) =
-// super.isFieldWithTransientBitmap(field) || needsInitFlag(field) && !field.isDeferred && isTransientField(field)
- /** Examines the symbol and returns a name indicating what brand of
- * bitmap it requires. The possibilities are the BITMAP_* vals
- * defined in StdNames. If it needs no bitmap, nme.NO_NAME.
- *
- * bitmaps for checkinit fields are not inherited
- */
- override protected def bitmapCategory(field: Symbol): Name = {
- import nme._
+ val map = mutable.Map[Symbol, Set[Symbol]]() withDefaultValue Set()
+ // invert the map to see which fields can be nulled for each non-transient lazy val
+ for ((field, users) <- singleUseFields; lazyFld <- users) map(lazyFld) += field
- super.bitmapCategory(field) match {
- case NO_NAME if needsInitFlag(field) && !field.isDeferred =>
- if (isTransientField(field)) BITMAP_CHECKINIT_TRANSIENT else BITMAP_CHECKINIT
- case category => category
- }
+ map.mapValues(_.toList sortBy (_.id)).toMap
}
+ }
+
- object addInitBitsTransformer extends Transformer {
+ class SynthInitCheckedAccessorsIn(protected val clazz: Symbol) extends SynthCheckedAccessorsTreesInClass with CheckInitAccessorSymbolSynth {
+ private object addInitBitsTransformer extends Transformer {
private def checkedGetter(lhs: Tree)(pos: Position) = {
- val sym = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter)
- if (needsInitFlagAndHasBitmap(sym)) {
- debuglog("adding checked getter for: " + sym + " " + lhs.symbol.flagString)
- List(typedPos(pos)(mkSetFlag(sym)))
+ val getter = clazz.info decl lhs.symbol.getterName suchThat (_.isGetter)
+ if (hasBitmap(getter) && needsInitFlag(getter)) {
+ debuglog("adding checked getter for: " + getter + " " + lhs.symbol.flagString)
+ List(typedPos(pos)(mkSetFlag(getter)))
}
else Nil
}
@@ -453,23 +438,21 @@ trait InitBitmaps extends Transform with ast.TreeDSL {
}
/** Make getters check the initialized bit, and the class constructor & setters are changed to set the initialized bits. */
- override def rhsWithOtherInitCheck(sym: Symbol)(rhs: Tree): Tree = {
+ def wrapRhsWithInitChecks(sym: Symbol)(rhs: Tree): Tree = {
// Add statements to the body of a constructor to set the 'init' bit for each field initialized in the constructor
if (sym.isConstructor) addInitBitsTransformer transform rhs
- else if (clazz.hasFlag(TRAIT) || rhs == EmptyTree) rhs
- else if (needsInitFlag(sym)) { // implies sym.isGetter
- mkCheckedAccessor(if (isUnitGetter(sym)) UNIT else rhs, rhs.pos, sym)
- }
+ else if (isTrait || rhs == EmptyTree) rhs
+ else if (needsInitFlag(sym)) // getter
+ mkCheckedAccessorRhs(if (isUnitGetter(sym)) UNIT else rhs, rhs.pos, sym)
else if (sym.isSetter) {
val getter = sym.getterIn(clazz)
- if (needsInitFlagAndHasBitmap(getter))
- Block(List(rhs, typedPos(rhs.pos.focus)(mkSetFlag(getter))), UNIT)
+ if (needsInitFlag(getter)) Block(List(rhs, typedPos(rhs.pos.focus)(mkSetFlag(getter))), UNIT)
else rhs
}
else rhs
}
- def mkCheckedAccessor(retVal: Tree, pos: Position, getter: Symbol): Tree = {
+ private def mkCheckedAccessorRhs(retVal: Tree, pos: Position, getter: Symbol): Tree = {
val msg = s"Uninitialized field: ${clazz.sourceFile}: ${pos.line}"
val result =
IF(mkTest(getter, equalToZero = false)).
@@ -478,23 +461,11 @@ trait InitBitmaps extends Transform with ast.TreeDSL {
typedPos(pos)(BLOCK(result, retVal))
}
-
- // TODO: need to run this on all accessor bodies (after fields refactoring, this is only run on accessors mixed in during mixins, which is only PRESUPER | PARAMACCESSOR)
- override def getterBody(getter: Symbol) = {
- if (!needsInitFlag(getter)) super.getterBody(getter)
- else mkCheckedAccessor(super.getterBody(getter), getter.pos, getter)
- }
-
- override def setterBody(setter: Symbol, getter: Symbol) = {
- if (!needsInitFlag(getter)) super.setterBody(setter, getter)
- else Block(List(super.setterBody(setter, getter)), mkSetFlag(getter))
- }
}
}
}
-
-abstract class Mixin extends InfoTransform with ast.TreeDSL with InitBitmaps {
+abstract class Mixin extends InfoTransform with ast.TreeDSL with AccessorSynthesis {
import global._
import definitions._
import CODE._
@@ -830,7 +801,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL with InitBitmaps {
protected def newTransformer(unit: CompilationUnit): Transformer =
new MixinTransformer(unit)
- class MixinTransformer(unit : CompilationUnit) extends Transformer with InitializationTransformation {
+ class MixinTransformer(unit : CompilationUnit) extends Transformer with AccessorTreeSynthesis {
/** The typer */
private var localTyper: erasure.Typer = _
protected def typedPos(pos: Position)(tree: Tree): Tree = localTyper.typedPos(pos)(tree)
@@ -879,10 +850,8 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL with InitBitmaps {
* @param clazz The class to which definitions are added
*/
private def addNewDefs(clazz: Symbol, stats: List[Tree]): List[Tree] = {
- val accessorInit = accessorInitialization(clazz, stats)
- import accessorInit._
-
- val statsWithInitChecks = deriveStatsWithInitChecks(stats)
+ val accessorSynth = new UncheckedAccessorSynth(clazz)
+ import accessorSynth._
// for all symbols `sym` in the class definition, which are mixed in by mixinTraitMembers
for (sym <- clazz.info.decls ; if sym hasFlag MIXEDIN) {
@@ -904,10 +873,10 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL with InitBitmaps {
}
}
- val addedStatsWithInitBitsAndChecks = implementWithNewDefs(statsWithInitChecks)
+ val implementedAccessors = implementWithNewDefs(stats)
if (clazz.isTrait)
- addedStatsWithInitBitsAndChecks filter {
+ implementedAccessors filter {
case vd: ValDef => assert(vd.symbol.hasFlag(PRESUPER | PARAMACCESSOR), s"unexpected valdef $vd in trait $clazz"); false
case _ => true
}
@@ -929,7 +898,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL with InitBitmaps {
stat
}
- addedStatsWithInitBitsAndChecks map completeSuperAccessor
+ implementedAccessors map completeSuperAccessor
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
index 87c14eb3a1..c171050bbd 100644
--- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
+++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -723,7 +723,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
} else if (!sClass.isTrait && m.isMethod && m.hasFlag(LAZY)) {
forwardToOverload(m)
- } else if (m.isValue && !m.isMethod && !m.hasFlag(LAZY)) { // concrete value definition
+ } else if (m.isValue && !m.isMethod) { // concrete value definition
def mkAccessor(field: Symbol, name: Name) = {
val newFlags = (SPECIALIZED | m.getterIn(clazz).flags) & ~(LOCAL | CASEACCESSOR | PARAMACCESSOR)
// we rely on the super class to initialize param accessors
@@ -744,7 +744,14 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
enterMember(specVal)
// create accessors
- if (nme.isLocalName(m.name)) {
+ if (m.isLazy) {
+ // no getters needed (we'll specialize the compute method and accessor separately), can stay private
+ // m.setFlag(PRIVATE) -- TODO: figure out how to leave the non-specialized lazy var private
+ // (the implementation needs it to be visible while duplicating and retypechecking,
+ // but it really could be private in bytecode)
+ specVal.setFlag(PRIVATE)
+ }
+ else if (nme.isLocalName(m.name)) {
val specGetter = mkAccessor(specVal, specVal.getterName) setInfo MethodType(Nil, specVal.info)
val origGetter = overrideIn(sClass, m.getterIn(clazz))
info(origGetter) = Forward(specGetter)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index fe7a4d5fef..3d2b689e4c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -982,7 +982,7 @@ trait Namers extends MethodSynthesis {
)
dropIllegalStarTypes(
if (shouldWiden) tpe.widen
- else if (sym.isFinal) tpe // "final val" allowed to retain constant type
+ else if (sym.isFinal && !sym.isLazy) tpe // "final val" allowed to retain constant type
else tpe.deconst
)
}
diff --git a/src/library/scala/runtime/LazyRef.scala b/src/library/scala/runtime/LazyRef.scala
new file mode 100644
index 0000000000..3cd2d2d06b
--- /dev/null
+++ b/src/library/scala/runtime/LazyRef.scala
@@ -0,0 +1,52 @@
+package scala.runtime
+
+/* Classes used as holders for local lazy vals */
+
+class LazyRef[T] {
+ var value: T = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyBoolean {
+ var value: Boolean = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyByte {
+ var value: Byte = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyChar {
+ var value: Char = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyShort {
+ var value: Short = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyInt {
+ var value: Int = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyLong {
+ var value: Long = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyFloat {
+ var value: Float = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyDouble {
+ var value: Double = _
+ @volatile var initialized: Boolean = false
+}
+
+class LazyUnit {
+ @volatile var initialized: Boolean = false
+}
diff --git a/src/manual/scala/man1/scalac.scala b/src/manual/scala/man1/scalac.scala
index 6ffcccea25..79c175e0f0 100644
--- a/src/manual/scala/man1/scalac.scala
+++ b/src/manual/scala/man1/scalac.scala
@@ -379,8 +379,8 @@ object scalac extends Command {
MItalic("posterasure"),
"clean up erased inline classes"),
Definition(
- MItalic("lazyvals"),
- "allocate bitmaps, translate lazy vals into lazified defs"),
+ MItalic("fields"),
+ "synthesize accessors and fields, including bitmaps for lazy vals"),
Definition(
MItalic("lambdalift"),
"move nested functions to top level"),
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index d0539dfd42..89d1b0637a 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -81,7 +81,7 @@ trait Definitions extends api.StandardDefinitions {
}
}
- private[Definitions] def classesMap[T](f: Name => T) = symbolsMap(ScalaValueClassesNoUnit, f)
+ private[Definitions] def classesMap[T](f: Name => T): Map[Symbol, T] = symbolsMap(ScalaValueClassesNoUnit, f)
private def symbolsMap[T](syms: List[Symbol], f: Name => T): Map[Symbol, T] = mapFrom(syms)(x => f(x.name))
private def symbolsMapFilt[T](syms: List[Symbol], p: Name => Boolean, f: Name => T) = symbolsMap(syms filter (x => p(x.name)), f)
@@ -93,6 +93,9 @@ trait Definitions extends api.StandardDefinitions {
lazy val boxedClass = classesMap(x => getClassByName(boxedName(x)))
lazy val refClass = classesMap(x => getRequiredClass("scala.runtime." + x + "Ref"))
lazy val volatileRefClass = classesMap(x => getRequiredClass("scala.runtime.Volatile" + x + "Ref"))
+ lazy val lazyHolders = symbolsMap(ScalaValueClasses, x => getClassIfDefined("scala.runtime.Lazy" + x))
+ lazy val LazyRefClass = getClassIfDefined("scala.runtime.LazyRef")
+ lazy val LazyUnitClass = getClassIfDefined("scala.runtime.LazyUnit")
lazy val allRefClasses: Set[Symbol] = {
refClass.values.toSet ++ volatileRefClass.values.toSet ++ Set(VolatileObjectRefClass, ObjectRefClass)
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 925018d3a6..c8e4d3d1d5 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -704,6 +704,7 @@ trait StdNames {
val immutable: NameType = "immutable"
val implicitly: NameType = "implicitly"
val in: NameType = "in"
+ val initialized : NameType = "initialized"
val internal: NameType = "internal"
val inlinedEquals: NameType = "inlinedEquals"
val isArray: NameType = "isArray"
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 10ae68cdd1..ac025e50ae 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -1682,7 +1682,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
*
* - packageobjects (follows namer)
* - superaccessors (follows typer)
- * - lazyvals (follows erasure)
+ * - lambdaLift (follows erasure)
* - null
*/
private def unsafeTypeParamPhase = {
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index f55b33959a..d56cecc965 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -432,6 +432,9 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.boxedClass
definitions.refClass
definitions.volatileRefClass
+ definitions.lazyHolders
+ definitions.LazyRefClass
+ definitions.LazyUnitClass
definitions.allRefClasses
definitions.UnitClass
definitions.ByteClass
diff --git a/src/repl/scala/tools/nsc/interpreter/Phased.scala b/src/repl/scala/tools/nsc/interpreter/Phased.scala
index d1d422ce3e..dd327a13d4 100644
--- a/src/repl/scala/tools/nsc/interpreter/Phased.scala
+++ b/src/repl/scala/tools/nsc/interpreter/Phased.scala
@@ -88,7 +88,7 @@ trait Phased {
lazy val all = List(
Parser, Namer, Packageobjects, Typer, Superaccessors, Pickler, Refchecks,
- Uncurry, Tailcalls, Specialize, Explicitouter, Erasure, Lazyvals, Lambdalift,
+ Uncurry, Tailcalls, Specialize, Explicitouter, Erasure, Fields, Lambdalift,
Constructors, Flatten, Mixin, Cleanup, Delambdafy, Jvm, Terminal
)
lazy val nameMap = all.map(x => x.name -> x).toMap withDefaultValue NoPhaseName
@@ -114,12 +114,12 @@ trait Phased {
case object Pickler extends PhaseName
case object Refchecks extends PhaseName
case object Uncurry extends PhaseName
+ case object Fields extends PhaseName
case object Tailcalls extends PhaseName
case object Specialize extends PhaseName
case object Explicitouter extends PhaseName
case object Erasure extends PhaseName
case object PostErasure extends PhaseName
- case object Lazyvals extends PhaseName
case object Lambdalift extends PhaseName
case object Constructors extends PhaseName
case object Flatten extends PhaseName
diff --git a/test/files/neg/t6446-additional.check b/test/files/neg/t6446-additional.check
index 45db63317c..23df978cd9 100644
--- a/test/files/neg/t6446-additional.check
+++ b/test/files/neg/t6446-additional.check
@@ -10,19 +10,18 @@ superaccessors 6 add super accessors in traits and nested classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
uncurry 10 uncurry, translate function values to anonymous classes
- fields 11 synthesize accessors and fields
+ fields 11 synthesize accessors and fields, including bitmaps for la...
tailcalls 12 replace tail calls by jumps
specialize 13 @specialized-driven class and method specialization
explicitouter 14 this refs to outer pointers
erasure 15 erase types, add interfaces for traits
posterasure 16 clean up erased inline classes
- lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
- lambdalift 18 move nested functions to top level
- constructors 19 move field definitions into constructors
- flatten 20 eliminate inner classes
- mixin 21 mixin composition
- cleanup 22 platform-specific cleanups, generate reflective calls
- delambdafy 23 remove lambdas
- jvm 24 generate JVM bytecode
- ploogin 25 A sample phase that does so many things it's kind of hard...
- terminal 26 the last phase during a compilation run
+ lambdalift 17 move nested functions to top level
+ constructors 18 move field definitions into constructors
+ flatten 19 eliminate inner classes
+ mixin 20 mixin composition
+ cleanup 21 platform-specific cleanups, generate reflective calls
+ delambdafy 22 remove lambdas
+ jvm 23 generate JVM bytecode
+ ploogin 24 A sample phase that does so many things it's kind of hard...
+ terminal 25 the last phase during a compilation run
diff --git a/test/files/neg/t6446-missing.check b/test/files/neg/t6446-missing.check
index 04523d18e6..c0a8fea140 100644
--- a/test/files/neg/t6446-missing.check
+++ b/test/files/neg/t6446-missing.check
@@ -11,18 +11,17 @@ superaccessors 6 add super accessors in traits and nested classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
uncurry 10 uncurry, translate function values to anonymous classes
- fields 11 synthesize accessors and fields
+ fields 11 synthesize accessors and fields, including bitmaps for la...
tailcalls 12 replace tail calls by jumps
specialize 13 @specialized-driven class and method specialization
explicitouter 14 this refs to outer pointers
erasure 15 erase types, add interfaces for traits
posterasure 16 clean up erased inline classes
- lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
- lambdalift 18 move nested functions to top level
- constructors 19 move field definitions into constructors
- flatten 20 eliminate inner classes
- mixin 21 mixin composition
- cleanup 22 platform-specific cleanups, generate reflective calls
- delambdafy 23 remove lambdas
- jvm 24 generate JVM bytecode
- terminal 25 the last phase during a compilation run
+ lambdalift 17 move nested functions to top level
+ constructors 18 move field definitions into constructors
+ flatten 19 eliminate inner classes
+ mixin 20 mixin composition
+ cleanup 21 platform-specific cleanups, generate reflective calls
+ delambdafy 22 remove lambdas
+ jvm 23 generate JVM bytecode
+ terminal 24 the last phase during a compilation run
diff --git a/test/files/neg/t6446-show-phases.check b/test/files/neg/t6446-show-phases.check
index 03f8273c17..cf8595db5d 100644
--- a/test/files/neg/t6446-show-phases.check
+++ b/test/files/neg/t6446-show-phases.check
@@ -10,18 +10,17 @@ superaccessors 6 add super accessors in traits and nested classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
uncurry 10 uncurry, translate function values to anonymous classes
- fields 11 synthesize accessors and fields
+ fields 11 synthesize accessors and fields, including bitmaps for la...
tailcalls 12 replace tail calls by jumps
specialize 13 @specialized-driven class and method specialization
explicitouter 14 this refs to outer pointers
erasure 15 erase types, add interfaces for traits
posterasure 16 clean up erased inline classes
- lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
- lambdalift 18 move nested functions to top level
- constructors 19 move field definitions into constructors
- flatten 20 eliminate inner classes
- mixin 21 mixin composition
- cleanup 22 platform-specific cleanups, generate reflective calls
- delambdafy 23 remove lambdas
- jvm 24 generate JVM bytecode
- terminal 25 the last phase during a compilation run
+ lambdalift 17 move nested functions to top level
+ constructors 18 move field definitions into constructors
+ flatten 19 eliminate inner classes
+ mixin 20 mixin composition
+ cleanup 21 platform-specific cleanups, generate reflective calls
+ delambdafy 22 remove lambdas
+ jvm 23 generate JVM bytecode
+ terminal 24 the last phase during a compilation run
diff --git a/test/files/neg/t7494-no-options.check b/test/files/neg/t7494-no-options.check
index bb143e8644..138d2fe9a3 100644
--- a/test/files/neg/t7494-no-options.check
+++ b/test/files/neg/t7494-no-options.check
@@ -11,19 +11,18 @@ superaccessors 6 add super accessors in traits and nested classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
uncurry 10 uncurry, translate function values to anonymous classes
- fields 11 synthesize accessors and fields
+ fields 11 synthesize accessors and fields, including bitmaps for la...
tailcalls 12 replace tail calls by jumps
specialize 13 @specialized-driven class and method specialization
explicitouter 14 this refs to outer pointers
erasure 15 erase types, add interfaces for traits
posterasure 16 clean up erased inline classes
- lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
- lambdalift 18 move nested functions to top level
- constructors 19 move field definitions into constructors
- flatten 20 eliminate inner classes
- mixin 21 mixin composition
- cleanup 22 platform-specific cleanups, generate reflective calls
- delambdafy 23 remove lambdas
- jvm 24 generate JVM bytecode
- ploogin 25 A sample phase that does so many things it's kind of hard...
- terminal 26 the last phase during a compilation run
+ lambdalift 17 move nested functions to top level
+ constructors 18 move field definitions into constructors
+ flatten 19 eliminate inner classes
+ mixin 20 mixin composition
+ cleanup 21 platform-specific cleanups, generate reflective calls
+ delambdafy 22 remove lambdas
+ jvm 23 generate JVM bytecode
+ ploogin 24 A sample phase that does so many things it's kind of hard...
+ terminal 25 the last phase during a compilation run
diff --git a/test/files/run/analyzerPlugins.check b/test/files/run/analyzerPlugins.check
index 9643079b83..64b68db242 100644
--- a/test/files/run/analyzerPlugins.check
+++ b/test/files/run/analyzerPlugins.check
@@ -39,7 +39,7 @@ pluginsPt(?, Trees$TypeBoundsTree) [2]
pluginsPt(?, Trees$TypeDef) [1]
pluginsPt(?, Trees$TypeTree) [32]
pluginsPt(?, Trees$Typed) [1]
-pluginsPt(?, Trees$ValDef) [21]
+pluginsPt(?, Trees$ValDef) [13]
pluginsPt(Any, Trees$Literal) [2]
pluginsPt(Any, Trees$Typed) [1]
pluginsPt(Array[Any], Trees$ArrayValue) [1]
@@ -109,7 +109,7 @@ pluginsTyped(<notype>, Trees$ClassDef) [2]
pluginsTyped(<notype>, Trees$DefDef) [14]
pluginsTyped(<notype>, Trees$PackageDef) [1]
pluginsTyped(<notype>, Trees$TypeDef) [1]
-pluginsTyped(<notype>, Trees$ValDef) [21]
+pluginsTyped(<notype>, Trees$ValDef) [13]
pluginsTyped(=> Boolean @testAnn, Trees$Select) [1]
pluginsTyped(=> Double, Trees$Select) [1]
pluginsTyped(=> Int, Trees$Select) [5]
diff --git a/test/files/run/delambdafy_t6028.check b/test/files/run/delambdafy_t6028.check
index 8b0ae7e9b9..b188928c09 100644
--- a/test/files/run/delambdafy_t6028.check
+++ b/test/files/run/delambdafy_t6028.check
@@ -42,7 +42,7 @@ package <empty> {
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
};
- final <stable> private[this] def MethodLocalObject$lzycompute$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
+ final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
T.this.synchronized({
if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1);
@@ -50,10 +50,6 @@ package <empty> {
});
MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]()
};
- final <stable> private[this] def MethodLocalObject$1(barParam$1: String, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
- T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1)
- else
- MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]();
final <artifact> private[this] def $anonfun$tryy$1(tryyParam$1: String, tryyLocal$1: runtime.ObjectRef): Unit = try {
tryyLocal$1.elem = tryyParam$1
} finally ()
diff --git a/test/files/run/lazy-locals-2.scala b/test/files/run/lazy-locals-2.scala
new file mode 100644
index 0000000000..cb905d3bef
--- /dev/null
+++ b/test/files/run/lazy-locals-2.scala
@@ -0,0 +1,322 @@
+object Logs {
+ val logBuf = new collection.mutable.StringBuilder()
+ def log(m: Any): Unit = { if (logBuf.nonEmpty) logBuf.append(":"); logBuf.append(m) }
+ def checkLog(expected: String): Unit = {
+ val res = logBuf.toString
+ assert(res == expected, s"expected:\n$expected\nfound:\n$res")
+ logBuf.clear()
+ }
+}
+
+import Logs._
+
+class C {
+ def getInt : Int = { log("getInt"); 1 }
+ def getString: String = { log("getString"); "s" }
+ def getUnit : Unit = { log("getUnit") }
+
+ lazy val t1 = getInt
+ lazy val t2 = getString
+ lazy val t3 = getUnit
+ checkLog("")
+
+ def m1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ def m2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ def m3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("")
+
+
+ val vl1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ val vl2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ val vl3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("getInt:getString:getUnit:():()")
+
+
+ var vr1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ var vr2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ var vr3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("getInt:getString:getUnit:():()")
+
+
+ lazy val lvl1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ lazy val lvl2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ lazy val lvl3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("")
+
+
+ {
+ lazy val t1 = getInt
+ lazy val t2 = getString
+ lazy val t3 = getUnit
+
+ log(t1 + t1)
+ log(t2 + t2)
+ log(t3); log(t3)
+ }
+ checkLog("getInt:2:getString:ss:getUnit:():()")
+
+
+ def run(): Unit = {
+ log(t1); log(t1);
+ log(t2); log(t2);
+ log(t3); log(t3);
+ checkLog("getInt:1:1:getString:s:s:getUnit:():()")
+
+ log(m1); log(m1)
+ log(m2); log(m2)
+ log(m3); log(m3)
+ checkLog("getInt:2:getInt:2:getString:ss:getString:ss:getUnit:():():():getUnit:():():()")
+
+ log(vl1); log(vl1)
+ log(vl2); log(vl2)
+ log(vl3); log(vl3)
+ checkLog("2:2:ss:ss:():()")
+
+ log(vr1); log(vr1); vr1 = 393; log(vr1)
+ log(vr2); log(vr2); vr2 = "h"; log(vr2)
+ log(vr3); log(vr3); vr3 = () ; log(vr3)
+ checkLog("2:2:393:ss:ss:h:():():()")
+
+ log(lvl1); log(lvl1)
+ log(lvl2); log(lvl2)
+ log(lvl3); log(lvl3)
+ checkLog("getInt:2:2:getString:ss:ss:getUnit:():():():()")
+ }
+}
+
+trait T {
+ def getInt : Int = { log("getInt"); 1 }
+ def getString: String = { log("getString"); "s" }
+ def getUnit : Unit = { log("getUnit") }
+
+ lazy val t1 = getInt
+ lazy val t2 = getString
+ lazy val t3 = getUnit
+ checkLog("")
+
+ def m1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ def m2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ def m3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("")
+
+
+ val vl1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ val vl2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ val vl3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("getInt:getString:getUnit:():()")
+
+
+ var vr1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ var vr2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ var vr3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("getInt:getString:getUnit:():()")
+
+
+ lazy val lvl1 = {
+ lazy val t1 = getInt
+ t1 + t1
+ }
+ lazy val lvl2 = {
+ lazy val t1 = getString
+ t1 + t1
+ }
+ lazy val lvl3 = {
+ lazy val t1 = getUnit
+ log(t1); log(t1)
+ }
+ checkLog("")
+
+
+ {
+ lazy val t1 = getInt
+ lazy val t2 = getString
+ lazy val t3 = getUnit
+
+ log(t1 + t1)
+ log(t2 + t2)
+ log(t3); log(t3)
+ }
+ checkLog("getInt:2:getString:ss:getUnit:():()")
+
+
+ def run(): Unit = {
+ log(t1); log(t1);
+ log(t2); log(t2);
+ log(t3); log(t3);
+ checkLog("getInt:1:1:getString:s:s:getUnit:():()")
+
+ log(m1); log(m1)
+ log(m2); log(m2)
+ log(m3); log(m3)
+ checkLog("getInt:2:getInt:2:getString:ss:getString:ss:getUnit:():():():getUnit:():():()")
+
+ log(vl1); log(vl1)
+ log(vl2); log(vl2)
+ log(vl3); log(vl3)
+ checkLog("2:2:ss:ss:():()")
+
+ log(vr1); log(vr1); vr1 = 393; log(vr1)
+ log(vr2); log(vr2); vr2 = "h"; log(vr2)
+ log(vr3); log(vr3); vr3 = () ; log(vr3)
+ checkLog("2:2:393:ss:ss:h:():():()")
+
+ log(lvl1); log(lvl1)
+ log(lvl2); log(lvl2)
+ log(lvl3); log(lvl3)
+ checkLog("getInt:2:2:getString:ss:ss:getUnit:():():():()")
+ }
+}
+
+class D extends T
+
+class D1 extends T {
+ override lazy val t1 = { log("o-t1"); -1 }
+ checkLog("")
+
+ override def m1 = { log("o-m1"); -2 }
+ override val m2 = { log("o-m2"); "n" }
+ override lazy val m3 = { log("o-m3") }
+ checkLog("o-m2")
+
+ override val vl1 = { log("o-vl1"); -3 }
+ checkLog("o-vl1")
+
+ override lazy val lvl1 = { log("o-lvl1"); -4 }
+ checkLog("")
+
+ override def run(): Unit = {
+ log(t1); log(t1)
+ checkLog("o-t1:-1:-1")
+
+ log(m1); log(m1)
+ log(m2); log(m2)
+ log(m3); log(m3)
+ checkLog("o-m1:-2:o-m1:-2:n:n:o-m3:():()")
+
+ log(vl1); log(vl1)
+ checkLog("-3:-3")
+
+ log(lvl1); log(lvl1)
+ checkLog("o-lvl1:-4:-4")
+ }
+}
+
+class E {
+ object T { log("init T"); override def toString = "T" }
+ def m = { object T { log("init T"); val x = 1 }; T.x }
+ checkLog("")
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ val c = new C
+ c.run()
+
+ val lzyComputeMethods = c.getClass.getDeclaredMethods.filter(_.getName contains "lzycompute").map(_.getName).toList.sorted
+ val expComputeMethods = List("lvl1$lzycompute", "lvl2$lzycompute", "lvl3$lzycompute", "t1$lzycompute", "t2$lzycompute", "t3$lzycompute")
+ assert(
+ lzyComputeMethods == expComputeMethods,
+ s"wrong lzycompute methods. expected:\n$expComputeMethods\nfound:\n$lzyComputeMethods")
+
+ val fields = c.getClass.getDeclaredFields.toList.sortBy(_.getName).map(_.toString)
+ val expFields = List(
+ "private volatile byte C.bitmap$0",
+ "private int C.lvl1",
+ "private java.lang.String C.lvl2",
+ "private scala.runtime.BoxedUnit C.lvl3",
+ "private int C.t1",
+ "private java.lang.String C.t2",
+ "private scala.runtime.BoxedUnit C.t3",
+ "private final int C.vl1",
+ "private final java.lang.String C.vl2",
+ "private final scala.runtime.BoxedUnit C.vl3",
+ "private int C.vr1",
+ "private java.lang.String C.vr2",
+ "private scala.runtime.BoxedUnit C.vr3")
+ assert(
+ fields == expFields,
+ s"wrong fields. expected:\n$expFields\nfound:\n$fields")
+
+
+ val d = new D
+ d.run()
+
+ val dFields = d.getClass.getDeclaredFields.toList.sortBy(_.getName).map(_.toString)
+ assert(
+ dFields == expFields.map(_.replaceAll(" C.", " D.")),
+ s"wrong fields. expected:\n$expFields\nfound:\n$fields")
+
+
+ val d1 = new D1
+ d1.run()
+
+ val e = new E
+ log(e.T); log(e.T)
+ checkLog("init T:T:T")
+ log(e.m); log(e.m)
+ checkLog("init T:1:init T:1")
+ }
+}
diff --git a/test/files/run/lazy_local_labels.check b/test/files/run/lazy_local_labels.check
new file mode 100644
index 0000000000..e42c8fb8ce
--- /dev/null
+++ b/test/files/run/lazy_local_labels.check
@@ -0,0 +1,9 @@
+HI
+HI
+HI
+HI
+HI
+HI
+HI
+HI
+HI
diff --git a/test/files/run/lazy_local_labels.scala b/test/files/run/lazy_local_labels.scala
new file mode 100644
index 0000000000..f4a1cdf689
--- /dev/null
+++ b/test/files/run/lazy_local_labels.scala
@@ -0,0 +1,28 @@
+// should print HI nine times to indicate the lazy val has been re-initialized on every iteration
+object Test extends App {
+ def fooDo: Unit = {
+ var i = 3
+ do {
+ lazy val x = { println("HI"); 1 }
+ i -= x
+ } while(i > 0)
+ }
+
+ def fooWhile: Unit = {
+ var i = 3
+ while(i > 0) {
+ lazy val x = { println("HI"); 1 }
+ i -= x
+ }
+ }
+
+ @annotation.tailrec def fooTail(i: Int): Unit = {
+ lazy val x = { println("HI"); 1 }
+ if (i > 0) fooTail(i - x)
+ }
+
+
+ fooWhile
+ fooDo
+ fooTail(3)
+}
diff --git a/test/files/run/programmatic-main.check b/test/files/run/programmatic-main.check
index 03f8273c17..cf8595db5d 100644
--- a/test/files/run/programmatic-main.check
+++ b/test/files/run/programmatic-main.check
@@ -10,18 +10,17 @@ superaccessors 6 add super accessors in traits and nested classes
pickler 8 serialize symbol tables
refchecks 9 reference/override checking, translate nested objects
uncurry 10 uncurry, translate function values to anonymous classes
- fields 11 synthesize accessors and fields
+ fields 11 synthesize accessors and fields, including bitmaps for la...
tailcalls 12 replace tail calls by jumps
specialize 13 @specialized-driven class and method specialization
explicitouter 14 this refs to outer pointers
erasure 15 erase types, add interfaces for traits
posterasure 16 clean up erased inline classes
- lazyvals 17 allocate bitmaps, translate lazy vals into lazified defs
- lambdalift 18 move nested functions to top level
- constructors 19 move field definitions into constructors
- flatten 20 eliminate inner classes
- mixin 21 mixin composition
- cleanup 22 platform-specific cleanups, generate reflective calls
- delambdafy 23 remove lambdas
- jvm 24 generate JVM bytecode
- terminal 25 the last phase during a compilation run
+ lambdalift 17 move nested functions to top level
+ constructors 18 move field definitions into constructors
+ flatten 19 eliminate inner classes
+ mixin 20 mixin composition
+ cleanup 21 platform-specific cleanups, generate reflective calls
+ delambdafy 22 remove lambdas
+ jvm 23 generate JVM bytecode
+ terminal 24 the last phase during a compilation run
diff --git a/test/files/run/t3569.check b/test/files/run/t3569.check
index a9fb5ff32e..e0e1d6c405 100644
--- a/test/files/run/t3569.check
+++ b/test/files/run/t3569.check
@@ -2,6 +2,8 @@
private final int Test$X.val1
private final int Test$X.val2
private final int Test$X.val3
+private int Test$X.const1
+private int Test$X.const2
private int Test$X.lval1
private int Test$X.lval2
private int Test$X.lval3
diff --git a/test/files/run/t3569.scala b/test/files/run/t3569.scala
index eb3b424439..7da4de9e95 100644
--- a/test/files/run/t3569.scala
+++ b/test/files/run/t3569.scala
@@ -4,7 +4,13 @@ object Test {
lazy val lv = scala.util.Random.nextInt()
- class X(final var x: Int) {
+ trait T { final lazy val const1 = 1 } // no fields
+
+ class X(final var x: Int) extends T {
+ // a lazy val does not receive a constant type, for backwards compat (e.g. for the repl)
+ // besides, since you explicitly wanted something lazy, we'll give you something lazy! (a field and a bitmap)
+ final lazy val const2 = 2
+
final var var1: Int = 0
final private var var2: Int = 0
final private[this] var var3: Int = 0
diff --git a/test/files/run/t5552.check b/test/files/run/t5552.check
index a19a60840e..73ad9cf824 100644
--- a/test/files/run/t5552.check
+++ b/test/files/run/t5552.check
@@ -1,2 +1,6 @@
+lazy: 3
(3,3)
+(3,3)
+lazy: 3.0
+(3.0,3.0)
(3.0,3.0)
diff --git a/test/files/run/t5552.scala b/test/files/run/t5552.scala
index afb8a1f0be..5b717f9e13 100644
--- a/test/files/run/t5552.scala
+++ b/test/files/run/t5552.scala
@@ -1,10 +1,14 @@
class C[@specialized(Int) A](a:A) {
- lazy val b = (a, a)
+ lazy val b = {println(s"lazy: $a"); (a, a)} // there should only be two instances of "lazy" in the output
def c = b
}
object Test {
def main(args:Array[String]) {
- println(new C(3).c)
- println(new C(3.0).c)
+ val cInt = new C(3)
+ println(cInt.c)
+ println(cInt.c)
+ val cFloat = new C(3.0)
+ println(cFloat.c)
+ println(cFloat.c)
}
}
diff --git a/test/files/run/t6028.check b/test/files/run/t6028.check
index 532d177300..c2e3ca58d8 100644
--- a/test/files/run/t6028.check
+++ b/test/files/run/t6028.check
@@ -54,7 +54,7 @@ package <empty> {
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer;
<synthetic> <stable> <artifact> def $outer(): T = MethodLocalObject$2.this.$outer
};
- final <stable> private[this] def MethodLocalObject$lzycompute$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
+ final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = {
T.this.synchronized({
if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
MethodLocalObject$module$1.elem = new T#MethodLocalObject$2.type(T.this, barParam$1);
@@ -62,10 +62,6 @@ package <empty> {
});
MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]()
};
- final <stable> private[this] def MethodLocalObject$1(barParam$1: Int, MethodLocalObject$module$1: runtime.VolatileObjectRef): T#MethodLocalObject$2.type = if (MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]().eq(null))
- T.this.MethodLocalObject$lzycompute$1(barParam$1, MethodLocalObject$module$1)
- else
- MethodLocalObject$module$1.elem.$asInstanceOf[T#MethodLocalObject$2.type]();
@SerialVersionUID(value = 0) final <synthetic> class $anonfun$tryy$1 extends scala.runtime.AbstractFunction0$mcV$sp with Serializable {
def <init>($outer: T, tryyParam$1: Int, tryyLocal$1: runtime.IntRef): <$anon: Function0> = {
$anonfun$tryy$1.super.<init>();
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
index 56da0e2493..eae5385147 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ScalaInlineInfoTest.scala
@@ -105,7 +105,6 @@ class ScalaInlineInfoTest extends BytecodeTesting {
("x4$(LT;)I", MethodInlineInfo(true ,false,false)),
("x5()I", MethodInlineInfo(true, false,false)),
("x5$(LT;)I", MethodInlineInfo(true ,false,false)),
- ("L$lzycompute$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)),
("L$1(Lscala/runtime/VolatileObjectRef;)LT$L$2$;", MethodInlineInfo(true, false,false)),
("nest$1()I", MethodInlineInfo(true, false,false)),
("$init$(LT;)V", MethodInlineInfo(true,false,false))),
@@ -118,7 +117,6 @@ class ScalaInlineInfoTest extends BytecodeTesting {
val infoC = inlineInfo(c)
val expectC = InlineInfo(false, None, Map(
"O()LT$O$;" -> MethodInlineInfo(true ,false,false),
- "O$lzycompute()LT$O$;" -> MethodInlineInfo(true, false,false),
"f6()I" -> MethodInlineInfo(false,false,false),
"x1()I" -> MethodInlineInfo(false,false,false),
"T$_setter_$x1_$eq(I)V" -> MethodInlineInfo(false,false,false),
@@ -181,7 +179,6 @@ class ScalaInlineInfoTest extends BytecodeTesting {
val infoC = inlineInfo(c)
val expected = Map(
"<init>()V" -> MethodInlineInfo(false,false,false),
- "O$lzycompute()LC$O$;" -> MethodInlineInfo(true,false,false),
"O()LC$O$;" -> MethodInlineInfo(true,false,false))
assert(infoC.methodInfos == expected, mapDiff(infoC.methodInfos, expected))
assertSameMethods(c, expected.keySet)