summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-10-28 23:22:38 +0000
committerPaul Phillips <paulp@improving.org>2011-10-28 23:22:38 +0000
commitb005cd579899bfb1e361f836616f0a8abf74fd14 (patch)
treed53c8366949a6a10f7f195228a0b9e94fcbfd12e /src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
parent61117024744d756fb95b77f9672c4d9a5405f75b (diff)
downloadscala-b005cd579899bfb1e361f836616f0a8abf74fd14.tar.gz
scala-b005cd579899bfb1e361f836616f0a8abf74fd14.tar.bz2
scala-b005cd579899bfb1e361f836616f0a8abf74fd14.zip
Overhaul of getter/setter synthesis.
It represents a lot of work because the mutation flies fast and furious and you can't even blink at these things without upsetting them. They're a little hardier now, or at least we stand a better chance of changing them. Open season on review.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala302
1 files changed, 265 insertions, 37 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 882b43bf2b..0d93d982d2 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -6,6 +6,7 @@ package scala.tools.nsc
package typechecker
import symtab.Flags._
+import scala.collection.{ mutable, immutable }
object listutil {
def mexists[T](xss: List[List[T]])(p: T => Boolean) =
@@ -32,75 +33,302 @@ trait MethodSynthesis {
import global._
import definitions._
+ /** There are two key methods in here.
+ *
+ * 1) enterGetterSetter is called from Namer with a ValDef which
+ * may need accessors. Some setup is performed. In general this
+ * creates symbols and enters them into the scope of the owner.
+ *
+ * 2) finishGetterSetter is called from Typer when a Template is typed.
+ * It completes the job, returning a list of trees with their symbols
+ * set to those created in enterGetterSetter. Those trees then become
+ * part of the typed template.
+ */
trait MethodSynth {
self: Namer =>
- private def createAccessorSymbol(tree: ValDef, name: Name, mask: Long): TermSymbol = (
- context.owner.newMethod(tree.pos.focus, name) setFlag tree.mods.flags & mask
- )
+ def enterGetterSetter(tree: ValDef) {
+ val ValDef(mods, name, _, _) = tree
+ if (nme.isSetterName(name))
+ context.error(tree.pos, "Names of vals or vars may not end in `_='")
+
+ val getter = Getter(tree).createAndEnterSymbol()
+
+ tree.symbol = (
+ if (mods.isLazy) enterLazyVal(tree, getter)
+ else {
+ if (mods.isPrivateLocal)
+ context.error(tree.pos, "private[this] not allowed for case class parameters")
+ // Create the setter if necessary.
+ if (mods.isMutable)
+ Setter(tree).createAndEnterSymbol()
+
+ // If abstract, the tree gets the getter's symbol. Otherwise, create a field.
+ if (mods.isDeferred) getter setPos tree.pos
+ else enterStrictVal(tree)
+ }
+ )
+
+ enterBeans(tree)
+ }
+ def finishGetterSetter(typer: Typer, stat: Tree): List[Tree] = stat match {
+ 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.annotations
+ val trees = (
+ allValDefDerived(vd)
+ map (acc => atPos(vd.pos.focus)(acc derive annotations))
+ filterNot (_ eq EmptyTree)
+ )
+ log(trees.mkString("Accessor trees:\n ", "\n ", "\n"))
+ if (vd.symbol.isLazy) List(stat)
+ else trees
+ case _ =>
+ List(stat)
+ }
- // TODO
- object Derived {
- def apply(tree: Tree): Derived = tree match {
- case vd @ ValDef(mods, name, _, _) =>
- if (mods hasAnnotationNamed tpnme.BeanPropertyAnnot)
- BeanGetter(vd)
- else if (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot)
- BooleanBeanGetter(vd)
- else
- NoDerived
- case _ => NoDerived
+ def standardAccessors(vd: ValDef): List[DerivedFromValDef] = (
+ if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd))
+ else List(Getter(vd))
+ )
+ def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
+ if (forMSIL) Nil
+ else if (vd.symbol hasAnnotation BeanPropertyAttr) {
+ if (vd.mods.isMutable) List(BeanGetter(vd), BeanSetter(vd))
+ else List(BeanGetter(vd))
}
+ else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr)
+ List(BooleanBeanGetter(vd))
+ else Nil
}
- object NoDerived extends Derived {
- def name = nme.NO_NAME
- def flagsMask = 0L
- def flagsExtra = 0L
- def completer(sym: Symbol) = NoType
+ def allValDefDerived(vd: ValDef) = {
+ val field = if (vd.mods.isDeferred) Nil else List(Field(vd))
+ field ::: standardAccessors(vd) ::: beanAccessors(vd)
}
+
trait Derived {
def name: TermName
def flagsMask: Long
def flagsExtra: Long
def completer(sym: Symbol): Type
}
- trait DerivedAccessor extends Derived {
+ trait DerivedFromValDef extends Derived {
+ /** The declaration from which we are deriving.
+ */
def tree: ValDef
- def isSetter: Boolean
- def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter)
- def enterAccessor(): Symbol = {
- val sym = createAccessorSymbol(tree, name, flagsMask) setFlag flagsExtra
+
+ /** Which meta-annotation is associated with this kind of entity.
+ * Presently one of: field, getter, setter, beanGetter, beanSetter, param.
+ */
+ def category: Symbol
+
+ // Final methods to make the rest easier to reason about.
+ final def mods = tree.mods
+ final def basisSym = tree.symbol
+ final def enclClass = basisSym.enclClass
+
+ final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter)
+ final def fieldSelection = Select(This(enclClass), basisSym)
+ final def derivedFlags: Long = basisSym.flags & flagsMask | flagsExtra
+ final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil)
+
+ def derivedSym: Symbol = tree.symbol
+ def derivedTree: Tree = EmptyTree
+
+ def isSetter = false
+ def isDeferred = mods.isDeferred
+ def keepClean = false // whether annotations whose definitions are not meta-annotated should be kept.
+ def validate() { }
+ def createAndEnterSymbol(): Symbol = {
+ val sym = (
+ owner.newMethod(tree.pos.focus, name)
+ setFlag tree.mods.flags & flagsMask
+ setFlag flagsExtra
+ )
setPrivateWithin(tree, sym)
enterInScope(sym)
sym setInfo completer(sym)
}
+ /** The annotations amongst those found on the original symbol which
+ * should be propagated to this kind of accessor.
+ */
+ private def deriveAnnotations(initial: List[AnnotationInfo]): List[AnnotationInfo] = {
+ initial filter { ann =>
+ // There are no meta-annotation arguments attached to `ann`
+ if (ann.metaAnnotations.isEmpty) {
+ // A meta-annotation matching `annotKind` exists on `ann`'s definition.
+ (ann.defaultTargets contains category) ||
+ // `ann`'s definition has no meta-annotations, and `keepClean` is true.
+ (ann.defaultTargets.isEmpty && keepClean)
+ }
+ // There are meta-annotation arguments, and one of them matches `annotKind`
+ else ann.metaAnnotations exists (_ matches category)
+ }
+ }
+ private def logDerived(result: Tree): Tree = {
+ val id = List(mods.defaultFlagString, basisSym.accurateKindString, basisSym.getterName) filterNot (_ == "") mkString " "
+ log("[+derived] " + id + " (" + derivedSym + ")\n " + result)
+ result
+ }
+ final def derive(initial: List[AnnotationInfo]): Tree = {
+ validate()
+ derivedSym setAnnotations deriveAnnotations(initial)
+ logDerived(derivedTree)
+ }
}
- case class Getter(tree: ValDef) extends DerivedAccessor {
+ trait DerivedGetter extends DerivedFromValDef {
+ // TODO
+ }
+ trait DerivedSetter extends DerivedFromValDef {
+ override def isSetter = true
+ private def setterParam = derivedSym.paramss match {
+ case (p :: Nil) :: _ => p
+ case _ => NoSymbol
+ }
+ private def setterRhs = (
+ if (mods.isDeferred || derivedSym.isOverloaded) EmptyTree
+ else Assign(fieldSelection, Ident(setterParam))
+ )
+ private def setterDef = DefDef(derivedSym, setterRhs)
+ override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef
+ }
+ case class Getter(tree: ValDef) extends DerivedGetter {
def name = tree.name
- def isSetter = false
+ 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)
+ context.error(derivedSym.pos, derivedSym+" is defined twice")
+
+ super.validate()
+ }
+ // keep type tree of original abstract field
+ private def fixTypeTree(dd: DefDef): DefDef = {
+ dd.tpt match {
+ case tt: TypeTree if dd.rhs == EmptyTree =>
+ tt setOriginal tree.tpt
+ case tpt =>
+ tpt setPos tree.tpt.pos.focus
+ }
+ dd
+ }
+ override def derivedTree: DefDef = {
+ fixTypeTree {
+ DefDef(derivedSym,
+ if (mods.isDeferred) EmptyTree
+ else gen.mkCheckInit(fieldSelection)
+ )
+ }
+ }
}
- case class Setter(tree: ValDef) extends DerivedAccessor {
+ case class Setter(tree: ValDef) extends DerivedSetter {
def name = nme.getterToSetter(tree.name)
- def isSetter = true
+ def category = SetterTargetClass
def flagsMask = SetterFlags
def flagsExtra = ACCESSOR
+
+ override def derivedSym = basisSym.setter(enclClass)
}
- case class Field(tree: ValDef) extends DerivedAccessor {
+ case class Field(tree: ValDef) extends DerivedFromValDef {
def name = nme.getterToLocal(tree.name)
- def isSetter = false
+ def category = FieldTargetClass
def flagsMask = FieldFlags
def flagsExtra = PrivateLocal
+ // By default annotations go to the field, except if the field is
+ // generated for a class parameter (PARAMACCESSOR).
+ override def keepClean = !mods.isParamAccessor
+ override def derivedTree = (
+ if (mods.isDeferred) EmptyTree
+ else treeCopy.ValDef(tree, mods | flagsExtra, name, tree.tpt, tree.rhs)
+ )
+ }
+ case class Param(tree: ValDef) extends DerivedFromValDef {
+ def name = tree.name
+ def category = ParamTargetClass
+ def flagsMask = -1L
+ def flagsExtra = 0L
+ override def keepClean = true
+ override def derivedTree = EmptyTree
+ }
+ def validateParam(tree: ValDef) {
+ Param(tree).derive(tree.symbol.annotations)
}
- sealed abstract class BeanAccessor(bean: String, val isSetter: Boolean) extends DerivedAccessor {
- def name = bean + tree.name.toString.capitalize
- def flagsMask = BeanPropertyFlags
+ sealed abstract class BeanAccessor(bean: String) extends DerivedFromValDef {
+ def name = bean + tree.name.toString.capitalize
+ def flagsMask = BeanPropertyFlags
def flagsExtra = 0
+ override def derivedSym = enclClass.info decl name
+ }
+ trait AnyBeanGetter extends BeanAccessor with DerivedGetter {
+ def category = BeanGetterTargetClass
+ override def validate() {
+ if (derivedSym == NoSymbol) {
+ // the namer decides whether to generate these symbols or not. at that point, we don't
+ // have symbolic information yet, so we only look for annotations named "BeanProperty".
+ context.error(tree.pos,
+ "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import")
+ }
+ super.validate()
+ }
+ }
+ trait NoSymbolBeanGetter extends AnyBeanGetter {
+ // Derives a tree without attempting to use the original tree's symbol.
+ override def derivedTree = {
+ atPos(tree.pos.focus) {
+ DefDef(derivedMods, name, Nil, List(Nil), tree.tpt.duplicate,
+ if (isDeferred) EmptyTree else Select(This(owner), tree.name)
+ )
+ }
+ }
+ override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree)
+ }
+ case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { }
+ case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { }
+ case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter {
+ def category = BeanSetterTargetClass
+ }
+
+ // No Symbols available.
+ private def beanAccessorsFromNames(tree: ValDef) = {
+ val ValDef(mods, name, tpt, _) = tree
+ val hasBP = mods hasAnnotationNamed tpnme.BeanPropertyAnnot
+ val hasBoolBP = mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot
+
+ if (hasBP || hasBoolBP) {
+ val getter = (
+ if (hasBP) new BeanGetter(tree) with NoSymbolBeanGetter
+ else new BooleanBeanGetter(tree) with NoSymbolBeanGetter
+ )
+ getter :: {
+ if (mods.isMutable) List(BeanSetter(tree)) else Nil
+ }
+ }
+ else Nil
+ }
+
+ protected def enterBeans(tree: ValDef) {
+ if (forMSIL)
+ return
+
+ val ValDef(mods, name, _, _) = tree
+ val beans = beanAccessorsFromNames(tree)
+ if (beans.nonEmpty) {
+ if (!name(0).isLetter)
+ context.error(tree.pos, "`BeanProperty' annotation can be applied only to fields that start with a letter")
+ else if (mods.isPrivate) // avoids name clashes with private fields in traits
+ context.error(tree.pos, "`BeanProperty' annotation can be applied only to non-private fields")
+
+ // Create and enter the symbols here, add the trees in finishGetterSetter.
+ beans foreach (_.createAndEnterSymbol())
+ }
}
- case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is", false)
- case class BeanGetter(tree: ValDef) extends BeanAccessor("get", false)
- case class BeanSetter(tree: ValDef) extends BeanAccessor("set", true)
}
-} \ No newline at end of file
+}