diff options
author | Paul Phillips <paulp@improving.org> | 2011-10-28 23:22:38 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2011-10-28 23:22:38 +0000 |
commit | b005cd579899bfb1e361f836616f0a8abf74fd14 (patch) | |
tree | d53c8366949a6a10f7f195228a0b9e94fcbfd12e /src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala | |
parent | 61117024744d756fb95b77f9672c4d9a5405f75b (diff) | |
download | scala-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.scala | 302 |
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 +} |