summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala72
1 files changed, 61 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 6448c28e21..e8a2c9f43c 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -198,13 +198,14 @@ trait MethodSynthesis {
if (nme.isSetterName(name))
ValOrValWithSetterSuffixError(tree)
- val getter = Getter(tree).createAndEnterSymbol()
-
tree.symbol = (
- if (mods.isLazy) enterLazyVal(tree, getter)
- else {
+ if (mods.isLazy) {
+ val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol()
+ enterLazyVal(tree, lazyValGetter)
+ } else {
if (mods.isPrivateLocal)
PrivateThisCaseClassParameterError(tree)
+ val getter = Getter(tree).createAndEnterSymbol()
// Create the setter if necessary.
if (mods.isMutable)
Setter(tree).createAndEnterSymbol()
@@ -219,7 +220,7 @@ trait MethodSynthesis {
}
def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match {
- case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) && !vd.symbol.isLazy =>
+ case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) =>
// If we don't save the annotations, they seem to wander off.
val annotations = stat.symbol.initialize.annotations
( allValDefDerived(vd)
@@ -247,6 +248,7 @@ trait MethodSynthesis {
def standardAccessors(vd: ValDef): List[DerivedFromValDef] = (
if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd))
+ else if (vd.mods.isLazy) List(LazyValGetter(vd))
else List(Getter(vd))
)
def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
@@ -259,10 +261,15 @@ trait MethodSynthesis {
else Nil
}
def allValDefDerived(vd: ValDef) = {
- val field = if (vd.mods.isDeferred) Nil else List(Field(vd))
+ val field = if (vd.mods.isDeferred || (vd.mods.isLazy && hasUnitType(vd.symbol))) Nil
+ else List(Field(vd))
field ::: standardAccessors(vd) ::: beanAccessors(vd)
}
+ // Take into account annotations so that we keep annotated unit lazy val
+ // to get better error message already from the cps plugin itself
+ def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty
+
/** This trait assembles what's needed for synthesizing derived methods.
* Important: Typically, instances of this trait are created TWICE for each derived
* symbol; once form Namers in an enter method, and once from Typers in addDerivedTrees.
@@ -388,16 +395,12 @@ trait MethodSynthesis {
def name: TermName = tree.name.toTermName
}
- case class Getter(tree: ValDef) extends DerivedGetter {
+ abstract class BaseGetter(tree: ValDef) extends DerivedGetter {
def name = tree.name
def category = GetterTargetClass
def flagsMask = GetterFlags
def flagsExtra = ACCESSOR | ( if (tree.mods.isMutable) 0 else STABLE )
- override def derivedSym = (
- if (mods.isDeferred) basisSym
- else basisSym.getter(enclClass)
- )
override def validate() {
assert(derivedSym != NoSymbol, tree)
if (derivedSym.isOverloaded)
@@ -405,6 +408,13 @@ trait MethodSynthesis {
super.validate()
}
+ }
+ case class Getter(tree: ValDef) extends BaseGetter(tree) {
+ override def derivedSym = (
+ if (mods.isDeferred) basisSym
+ else basisSym.getter(enclClass)
+ )
+
override def derivedTree: DefDef = {
// For existentials, don't specify a type for the getter, even one derived
// from the symbol! This leads to incompatible existentials for the field and
@@ -437,6 +447,45 @@ trait MethodSynthesis {
}
}
}
+ /** Implements lazy value accessors:
+ * - for lazy values of type Unit and all lazy fields inside traits,
+ * the rhs is the initializer itself
+ * - for all other lazy values z the accessor is a block of this form:
+ * { z = <rhs>; z } where z can be an identifier or a field.
+ */
+ case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) {
+ class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol)
+ extends ChangeOwnerTraverser(oldowner, newowner) {
+
+ override def traverse(tree: Tree) {
+ tree match {
+ case _: DefTree => change(tree.symbol.moduleClass)
+ case _ =>
+ }
+ super.traverse(tree)
+ }
+ }
+
+ // todo: in future this should be enabled but now other phases still depend on the flag for various reasons
+ //override def flagsMask = (super.flagsMask & ~LAZY)
+ override def derivedSym = basisSym.lazyAccessor
+ override def derivedTree: DefDef = {
+ val ValDef(_, _, tpt0, rhs0) = tree
+ val rhs1 = transformed.getOrElse(rhs0, rhs0)
+ val body = (
+ if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1
+ else gen.mkAssignAndReturn(basisSym, rhs1)
+ )
+ derivedSym.setPos(tree.pos) // cannot set it at createAndEnterSymbol because basisSym can possible stil have NoPosition
+ val ddefRes = atPos(tree.pos)(DefDef(derivedSym, new ChangeOwnerAndModuleClassTraverser(basisSym, derivedSym)(body)))
+ // ValDef will have its position focused whereas DefDef will have original correct rangepos
+ // ideally positions would be correct at the creation time but lazy vals are really a special case
+ // here so for the sake of keeping api clean we fix positions manually in LazyValGetter
+ ddefRes.tpt.setPos(tpt0.pos)
+ tpt0.setPos(tpt0.pos.focus)
+ ddefRes
+ }
+ }
case class Setter(tree: ValDef) extends DerivedSetter {
def name = nme.getterToSetter(tree.name)
def category = SetterTargetClass
@@ -455,6 +504,7 @@ trait MethodSynthesis {
override def keepClean = !mods.isParamAccessor
override def derivedTree = (
if (mods.isDeferred) EmptyTree
+ else if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
else copyValDef(tree)(mods = mods | flagsExtra, name = this.name)
)
}