summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
diff options
context:
space:
mode:
authorHubert Plociniczak <hubert.plociniczak@gmail.com>2012-09-21 14:10:33 +0200
committerHubert Plociniczak <hubert.plociniczak@gmail.com>2012-10-09 21:48:56 +0200
commit981424b376e8e66253c2ec863ca1222e41d8b374 (patch)
tree32b5a2839a775531223d6ca8faf2890110542c0a /src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
parentc61c18e042d0fa41a09fea29a8dcaa2e08a40a63 (diff)
downloadscala-981424b376e8e66253c2ec863ca1222e41d8b374.tar.gz
scala-981424b376e8e66253c2ec863ca1222e41d8b374.tar.bz2
scala-981424b376e8e66253c2ec863ca1222e41d8b374.zip
Closes SI-6358. Move accessor generation for lazy vals to typers.
Until now lazy accessors were handled somehow special because their symbol was created in typers but the corresponding tree was only added in Refchecks. This irregularity caused serious problems for value classes. Also it now looks just better when lazy value is treated in a similar way as other fields. I needed to adapt reifier so that it handles the new implementation correctly. Previously it had to recreate lazy val only by removing defdef and renaming. Now we basically need to recreate lazy val from scratch. There is one minor change to cps plugin but that is still fine because lazy vals were never really part of the transformation. Some range positions needed to be fixed manually. We could do it at the creation time but that would require a lot more "if (symbol.isLazy)" conditions for MethodSyntheis and Symbol/Tree creation and would just unnecessary complicate api. If someone has a better idea, please speak up. Range positions changes were necessary because previously accessors were created at refchecks and they weren't checked by validator (even though they were wrong). This commit removes lazy val implementation restriction introduced for 2.10.0.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala63
1 files changed, 52 insertions, 11 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 91dcd90962..8e803a9a9e 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,36 @@ 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) {
+ // 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.get(rhs0) match {
+ case Some(rhs) => rhs
+ case None => 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, body.changeOwner(followModuleClass = true, basisSym -> derivedSym)))
+ // 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 +495,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)
)
}