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.scala541
1 files changed, 184 insertions, 357 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index 0f79bb60ed..c036a2a9b8 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -116,78 +116,99 @@ trait MethodSynthesis {
import NamerErrorGen._
- def enterImplicitWrapper(tree: ClassDef) {
- ImplicitClassWrapper(tree).createAndEnterSymbol()
+ def enterImplicitWrapper(tree: ClassDef): Unit = {
+ enterSyntheticSym(ImplicitClassWrapper(tree).derivedTree)
}
- // TODO: see if we can link symbol creation & tree derivation by sharing the Field/Getter/Setter factories
- // maybe we can at least reuse some variant of standardAccessors?
+ // trees are later created by addDerivedTrees (common logic is encapsulated in field/standardAccessors/beanAccessors)
def enterGetterSetter(tree: ValDef): Unit = {
- tree.symbol =
- if (tree.mods.isLazy) {
- val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol()
- enterLazyVal(tree, lazyValGetter)
- } else {
- val getter = Getter(tree)
- val getterSym = getter.createAndEnterSymbol()
-
- // Create the setter if necessary.
- if (getter.needsSetter) Setter(tree).createAndEnterSymbol()
-
- // If the getter's abstract, the tree gets the getter's symbol,
- // otherwise, create a field (we have to assume the getter requires storage for now).
- // NOTE: we cannot look at symbol info, since we're in the process of deriving them
- // (luckily, they only matter for lazy vals, which we've ruled out in this else branch,
- // and `doNotDeriveField` will skip them if `!mods.isLazy`)
- if (Field.noFieldFor(tree)) getterSym setPos tree.pos // TODO: why do setPos? `createAndEnterSymbol` already gave `getterSym` the position `tree.pos.focus`
- else enterStrictVal(tree)
- }
+ val getter = Getter(tree)
+ val getterSym = getter.createSym
+ val setterSym = if (getter.needsSetter) Setter(tree).createSym else NoSymbol
- enterBeans(tree)
- }
+ // a lazy field is linked to its lazy accessor (TODO: can we do the same for field -> getter -> setter)
+ val fieldSym = if (Field.noFieldFor(tree)) NoSymbol else Field(tree).createSym(getterSym)
- import AnnotationInfo.{mkFilter => annotationFilter}
+ // only one symbol can have `tree.pos`, the others must focus their position
+ // normally the field gets the range position, but if there is none, give it to the getter
+ tree.symbol = fieldSym orElse (getterSym setPos tree.pos)
+
+ val namer = if (fieldSym != NoSymbol) namerOf(fieldSym) else namerOf(getterSym)
+
+ // There's no reliable way to detect all kinds of setters from flags or name!!!
+ // A BeanSetter's name does not end in `_=` -- it does begin with "set", but so could the getter
+ // for a regular Scala field... TODO: can we add a flag to distinguish getter/setter accessors?
+ val getterCompleter = namer.accessorTypeCompleter(tree, isSetter = false)
+ val setterCompleter = namer.accessorTypeCompleter(tree, isSetter = true)
- /** This is called for those ValDefs which addDerivedTrees ignores, but
- * which might have a warnable annotation situation.
- */
- private def warnForDroppedAnnotations(tree: Tree) {
- val annotations = tree.symbol.initialize.annotations
- val targetClass = defaultAnnotationTarget(tree)
- val retained = annotations filter annotationFilter(targetClass, defaultRetention = true)
+ getterSym setInfo getterCompleter
+ setterSym andAlso (_ setInfo setterCompleter)
+ fieldSym andAlso (_ setInfo namer.valTypeCompleter(tree))
- annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass))
+ enterInScope(getterSym)
+ setterSym andAlso (enterInScope(_))
+ fieldSym andAlso (enterInScope(_))
+
+ deriveBeanAccessors(tree, namer)
}
- private def issueAnnotationWarning(tree: Tree, ann: AnnotationInfo, defaultTarget: Symbol) {
- global.reporter.warning(ann.pos,
- s"no valid targets for annotation on ${tree.symbol} - it is discarded unused. " +
- s"You may specify targets with meta-annotations, e.g. @($ann @${defaultTarget.name})")
+
+ private def deriveBeanAccessors(tree: ValDef, namer: Namer): Unit = {
+ // TODO: can we look at the annotations symbols? (name-based introduced in 8cc477f8b6, see neg/t3403)
+ val hasBeanProperty = tree.mods hasAnnotationNamed tpnme.BeanPropertyAnnot
+ val hasBoolBP = tree.mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot
+
+ if (hasBeanProperty || hasBoolBP) {
+ if (!tree.name.charAt(0).isLetter) BeanPropertyAnnotationFieldWithoutLetterError(tree)
+ // avoids name clashes with private fields in traits
+ else if (tree.mods.isPrivate) BeanPropertyAnnotationPrivateFieldError(tree)
+
+ val derivedPos = tree.pos.focus
+ val missingTpt = tree.tpt.isEmpty
+
+ def deriveBeanAccessor(prefix: String): Symbol = {
+ val isSetter = prefix == "set"
+ val name = newTermName(prefix + tree.name.toString.capitalize)
+ val setterParam = nme.syntheticParamName(1)
+
+ // note: tree.tpt may be EmptyTree, which will be a problem when use as the tpt of a parameter
+ // the completer will patch this up (we can't do this now without completing the field)
+ val tptToPatch = if (missingTpt) TypeTree() else tree.tpt.duplicate
+
+ val (vparams, tpt) =
+ if (isSetter) (List(ValDef(Modifiers(PARAM | SYNTHETIC), setterParam, tptToPatch, EmptyTree)), TypeTree(UnitTpe))
+ else (Nil, tptToPatch)
+
+ val rhs =
+ if (tree.mods.isDeferred) EmptyTree
+ else if (isSetter) Apply(Ident(tree.name.setterName), List(Ident(setterParam)))
+ else Select(This(owner), tree.name)
+
+ val sym = createMethod(tree, name, derivedPos, tree.mods.flags & BeanPropertyFlags)
+ context.unit.synthetics(sym) = newDefDef(sym, rhs)(tparams = Nil, vparamss = List(vparams), tpt = tpt)
+ sym
+ }
+
+ val getterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = false)
+ enterInScope(deriveBeanAccessor(if (hasBeanProperty) "get" else "is") setInfo getterCompleter)
+
+ if (tree.mods.isMutable) {
+ val setterCompleter = namer.beanAccessorTypeCompleter(tree, missingTpt, isSetter = true)
+ enterInScope(deriveBeanAccessor("set") setInfo setterCompleter)
+ }
+ }
}
+
+ import AnnotationInfo.{mkFilter => annotationFilter}
def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match {
case vd @ ValDef(mods, name, tpt, rhs) if deriveAccessors(vd) && !vd.symbol.isModuleVar =>
- // If we don't save the annotations, they seem to wander off.
- val annotations = stat.symbol.initialize.annotations
- val trees = (
- (field(vd) ::: standardAccessors(vd) ::: beanAccessors(vd))
- map (acc => atPos(vd.pos.focus)(acc derive annotations))
- filterNot (_ eq EmptyTree)
- )
- // Verify each annotation landed safely somewhere, else warn.
- // Filtering when isParamAccessor is a necessary simplification
- // because there's a bunch of unwritten annotation code involving
- // the propagation of annotations - constructor parameter annotations
- // may need to make their way to parameters of the constructor as
- // well as fields of the class, etc.
- if (!mods.isParamAccessor) annotations foreach (ann =>
- if (!trees.exists(_.symbol hasAnnotation ann.symbol))
- issueAnnotationWarning(vd, ann, GetterTargetClass)
- )
-
- trees
- case vd: ValDef =>
- warnForDroppedAnnotations(vd)
- vd :: Nil
+ stat.symbol.initialize // needed!
+
+ val getter = Getter(vd)
+ getter.validate()
+ val accessors = getter :: (if (getter.needsSetter) Setter(vd) :: Nil else Nil)
+ (Field(vd) :: accessors).map(_.derivedTree).filter(_ ne EmptyTree)
+
case cd @ ClassDef(mods, _, _, _) if mods.isImplicit =>
val annotations = stat.symbol.initialize.annotations
// TODO: need to shuffle annotations between wrapper and class.
@@ -209,194 +230,86 @@ trait MethodSynthesis {
stat :: Nil
}
- def standardAccessors(vd: ValDef): List[DerivedFromValDef] =
- if (vd.mods.isLazy) List(LazyValGetter(vd))
- else {
- val getter = Getter(vd)
- if (getter.needsSetter) List(getter, Setter(vd))
- else List(getter)
- }
-
- def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
- val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil
- if (vd.symbol hasAnnotation BeanPropertyAttr)
- BeanGetter(vd) :: setter
- else if (vd.symbol hasAnnotation BooleanBeanPropertyAttr)
- BooleanBeanGetter(vd) :: setter
- else Nil
- }
- def field(vd: ValDef): List[Field] = if (Field.noFieldFor(vd)) Nil else List(Field(vd))
-
- /** 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.
- * So it's important that creating an instance of Derived does not have a side effect,
- * or if it has a side effect, control that it is done only once.
- */
sealed trait Derived {
-
- /** The tree from which we are deriving a synthetic member. Typically, that's
- * given as an argument of the instance. */
- def tree: Tree
-
- /** The name of the method */
- def name: TermName
-
- /** The flags that are retained from the original symbol */
- def flagsMask: Long
-
- /** The flags that the derived symbol has in addition to those retained from
- * the original symbol*/
- def flagsExtra: Long
-
- /** type completer for the synthetic member.
- */
- def completer(sym: Symbol): Type
-
/** The derived symbol. It is assumed that this symbol already exists and has been
- * entered in the parent scope when derivedSym is called */
+ * entered in the parent scope when derivedSym is called
+ */
def derivedSym: Symbol
/** The definition tree of the derived symbol. */
def derivedTree: Tree
}
- sealed trait DerivedFromMemberDef extends Derived {
- def tree: MemberDef
- def enclClass: Symbol
-
- // Final methods to make the rest easier to reason about.
- final def mods = tree.mods
- final def basisSym = tree.symbol
- final def derivedMods = mods & flagsMask | flagsExtra
- }
-
- sealed trait DerivedFromClassDef extends DerivedFromMemberDef {
- def tree: ClassDef
- final def enclClass = basisSym.owner.enclClass
- }
-
- sealed trait DerivedFromValDef extends DerivedFromMemberDef {
- def tree: ValDef
- final def enclClass = basisSym.enclClass
-
-
- // There's no reliable way to detect all kinds of setters from flags or name!!!
- // A BeanSetter's name does not end in `_=` -- it does begin with "set", but so could the getter
- // for a regular Scala field... TODO: can we add a flag to distinguish getter/setter accessors?
- final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, this.isInstanceOf[DerivedSetter])
- final def fieldSelection = Select(This(enclClass), basisSym)
-
- def derivedSym: Symbol = tree.symbol
- def derivedTree: Tree = EmptyTree
-
- def isDeferred = mods.isDeferred
- def validate() { }
- def createAndEnterSymbol(): MethodSymbol = {
- val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags)
- setPrivateWithin(tree, sym)
- enterInScope(sym)
- sym setInfo completer(sym)
- }
- private def logDerived(result: Tree): Tree = {
- debuglog("[+derived] " + ojoin(mods.flagString, basisSym.accurateKindString, basisSym.getterName.decode)
- + " (" + derivedSym + ")\n " + result)
+ /** A synthetic method which performs the implicit conversion implied by
+ * the declaration of an implicit class.
+ */
+ case class ImplicitClassWrapper(tree: ClassDef) extends Derived {
+ def derivedSym = {
+ val enclClass = tree.symbol.owner.enclClass
+ // Only methods will do! Don't want to pick up any stray
+ // companion objects of the same name.
+ val result = enclClass.info decl derivedName filter (x => x.isMethod && x.isSynthetic)
+ if (result == NoSymbol || result.isOverloaded)
+ context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $derivedName in $enclClass / ${enclClass.info.decls}")
result
}
- final def derive(initial: List[AnnotationInfo]): Tree = {
- validate()
-
- // see scala.annotation.meta's package class for more info
- // Annotations on ValDefs can be targeted towards the following: field, getter, setter, beanGetter, beanSetter, param.
- // The defaults are:
- // - (`val`-, `var`- or plain) constructor parameter annotations end up on the parameter, not on any other entity.
- // - val/var member annotations solely end up on the underlying field, except in traits (@since 2.12),
- // where there is no field, and the getter thus holds annotations targetting both getter & field.
- // As soon as there is a field/getter (in subclasses mixing in the trait), we triage the annotations.
- //
- // TODO: these defaults can be surprising for annotations not meant for accessors/fields -- should we revisit?
- // (In order to have `@foo val X` result in the X getter being annotated with `@foo`, foo needs to be meta-annotated with @getter)
- val annotFilter: AnnotationInfo => Boolean = this match {
- case _: Param => annotationFilter(ParamTargetClass, defaultRetention = true)
- // By default annotations go to the field, except if the field is generated for a class parameter (PARAMACCESSOR).
- case _: Field => annotationFilter(FieldTargetClass, defaultRetention = !mods.isParamAccessor)
- case _: BaseGetter if owner.isTrait => annotationFilter(List(FieldTargetClass, GetterTargetClass), defaultRetention = true)
- case _: BaseGetter => annotationFilter(GetterTargetClass, defaultRetention = false)
- case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false)
- case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false)
- // TODO do bean getters need special treatment to collect field-targeting annotations in traits?
- case _: AnyBeanGetter => annotationFilter(BeanGetterTargetClass, defaultRetention = false)
- }
-
- // The annotations amongst those found on the original symbol which
- // should be propagated to this kind of accessor.
- derivedSym setAnnotations (initial filter annotFilter)
+ def derivedTree = factoryMeth(derivedMods, derivedName, tree)
- if (derivedSym.isSetter && owner.isTrait && !isDeferred)
- derivedSym addAnnotation TraitSetterAnnotationClass
-
- logDerived(derivedTree)
- }
+ def derivedName = tree.name.toTermName
+ def derivedMods = tree.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC
}
- sealed trait DerivedGetter extends DerivedFromValDef {
- def needsSetter = mods.isMutable
+ trait DerivedAccessor extends Derived {
+ def tree: ValDef
+ def derivedName: TermName
+ def derivedFlags: Long
+
+ def derivedPos = tree.pos.focus
+ def createSym = createMethod(tree, derivedName, derivedPos, derivedFlags)
}
- sealed trait DerivedSetter extends DerivedFromValDef {
- protected def setterParam = derivedSym.paramss match {
- case (p :: Nil) :: _ => p
- case _ => NoSymbol
- }
- protected def setterRhs = {
- assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for $basisSym in $enclClass")
- if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree
- else Assign(fieldSelection, Ident(setterParam))
- }
+ case class Getter(tree: ValDef) extends DerivedAccessor {
+ def derivedName = tree.name
- private def setterDef = DefDef(derivedSym, setterRhs)
- override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef
- }
+ def derivedSym =
+ if (tree.mods.isLazy) tree.symbol.lazyAccessor
+ else if (Field.noFieldFor(tree)) tree.symbol
+ else tree.symbol.getterIn(tree.symbol.enclClass)
- /** A synthetic method which performs the implicit conversion implied by
- * the declaration of an implicit class.
- */
- case class ImplicitClassWrapper(tree: ClassDef) extends DerivedFromClassDef {
- def completer(sym: Symbol): Type = ??? // not needed
- def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree)
- def derivedSym: Symbol = {
- // Only methods will do! Don't want to pick up any stray
- // companion objects of the same name.
- val result = enclClass.info decl name filter (x => x.isMethod && x.isSynthetic)
- if (result == NoSymbol || result.isOverloaded)
- context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $name in $enclClass / ${enclClass.info.decls}")
- result
- }
- def derivedTree: DefDef = factoryMeth(derivedMods, name, tree)
- def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC
- def flagsMask: Long = AccessFlags
- def name: TermName = tree.name.toTermName
- }
+ def derivedFlags = tree.mods.flags & GetterFlags | ACCESSOR.toLong | ( if (needsSetter) 0 else STABLE )
- sealed abstract class BaseGetter(tree: ValDef) extends DerivedGetter {
- def name = tree.name
- def flagsMask = GetterFlags
- def flagsExtra = ACCESSOR.toLong | ( if (tree.mods.isMutable) 0 else STABLE )
+ def needsSetter = tree.mods.isMutable // implies !lazy
- override def validate() {
- assert(derivedSym != NoSymbol, tree)
- if (derivedSym.isOverloaded)
- GetterDefinedTwiceError(derivedSym)
+ override def derivedTree =
+ if (tree.mods.isLazy) deriveLazyAccessor
+ else newDefDef(derivedSym, if (Field.noFieldFor(tree)) tree.rhs else Select(This(tree.symbol.enclClass), tree.symbol))(tpt = derivedTpt)
+
+ /** Implements lazy value accessors:
+ * - 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.
+ */
+ private def deriveLazyAccessor: DefDef = {
+ val ValDef(_, _, tpt0, rhs0) = tree
+ val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0)
+ val body =
+ if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor
+ else gen.mkAssignAndReturn(tree.symbol, rhs1)
- super.validate()
+ derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possibly still have NoPosition
+ val ddefRes = DefDef(derivedSym, new ChangeOwnerTraverser(tree.symbol, 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 Getter(tree: ValDef) extends BaseGetter(tree) {
- override def derivedSym = if (Field.noFieldFor(tree)) basisSym else basisSym.getterIn(enclClass)
- private def derivedRhs = if (Field.noFieldFor(tree)) tree.rhs else fieldSelection
// TODO: more principled approach -- this is a bit bizarre
private def derivedTpt = {
@@ -413,61 +326,35 @@ trait MethodSynthesis {
// Range position errors ensue if we don't duplicate this in some
// circumstances (at least: concrete vals with existential types.)
case _: ExistentialType => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus)
- case _ if isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
+ case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
case _ => TypeTree(getterTp)
}
tpt setPos tree.tpt.pos.focus
}
- override def derivedTree: DefDef = newDefDef(derivedSym, derivedRhs)(tpt = derivedTpt)
- }
- /** Implements lazy value accessors:
- * - 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.
- */
- 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)
- }
+ def validate() = {
+ assert(derivedSym != NoSymbol, tree)
+ if (derivedSym.isOverloaded)
+ GetterDefinedTwiceError(derivedSym)
}
- // 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 = context.unit.transformed.getOrElse(rhs0, rhs0)
- val body =
- if (tree.symbol.owner.isTrait || Field.noFieldFor(tree)) rhs1 // TODO move tree.symbol.owner.isTrait into noFieldFor
- else gen.mkAssignAndReturn(basisSym, rhs1)
-
- derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possibly still have NoPosition
- val ddefRes = 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 = tree.setterName
- def flagsMask = SetterFlags
- def flagsExtra = ACCESSOR
- // TODO: double check logic behind need for name expansion in context of new fields phase
- override def derivedSym = basisSym.setterIn(enclClass)
+ case class Setter(tree: ValDef) extends DerivedAccessor {
+ def derivedName = tree.setterName
+ def derivedSym = tree.symbol.setterIn(tree.symbol.enclClass)
+ def derivedFlags = tree.mods.flags & SetterFlags | ACCESSOR
+ def derivedTree =
+ derivedSym.paramss match {
+ case (setterParam :: Nil) :: _ =>
+ // assert(!derivedSym.isOverloaded, s"Unexpected overloaded setter $derivedSym for ${tree.symbol} in ${tree.symbol.enclClass}")
+ val rhs =
+ if (Field.noFieldFor(tree) || derivedSym.isOverloaded) EmptyTree
+ else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam))
+
+ DefDef(derivedSym, rhs)
+ case _ => EmptyTree
+ }
}
object Field {
@@ -495,102 +382,42 @@ trait MethodSynthesis {
private def traitFieldFor(vd: ValDef): Boolean = vd.mods.hasFlag(PRESUPER | LAZY)
}
- case class Field(tree: ValDef) extends DerivedFromValDef {
- def name = tree.localName
- def flagsMask = FieldFlags
- def flagsExtra = PrivateLocal
+ case class Field(tree: ValDef) extends Derived {
+ private val isLazy = tree.mods.isLazy
- // TODO: override def createAndEnterSymbol (currently never called on Field)
- // and do `enterStrictVal(tree)`, so that enterGetterSetter and addDerivedTrees can share some logic...
+ // 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.
+ private val localLazyVal = isLazy && !owner.isClass
+ private val nameSuffix =
+ if (!localLazyVal) reflect.NameTransformer.LOCAL_SUFFIX_STRING
+ else reflect.NameTransformer.LAZY_LOCAL_SUFFIX_STRING
- // handle lazy val first for now (we emit a Field even though we probably shouldn't...)
- override def derivedTree =
- if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
- else if (Field.noFieldFor(tree)) EmptyTree
- else copyValDef(tree)(mods = mods | flagsExtra, name = this.name)
+ def derivedName = tree.name.append(nameSuffix)
- }
- case class Param(tree: ValDef) extends DerivedFromValDef {
- def name = tree.name
- def flagsMask = -1L
- def flagsExtra = 0L
- override def derivedTree = EmptyTree
- }
- def validateParam(tree: ValDef) {
- Param(tree).derive(tree.symbol.annotations)
- }
-
- sealed abstract class BeanAccessor(bean: String) extends DerivedFromValDef {
- val name = newTermName(bean + tree.name.toString.capitalize)
- def flagsMask = BeanPropertyFlags
- def flagsExtra = 0
- override def derivedSym = enclClass.info decl name
- }
- sealed trait AnyBeanGetter extends BeanAccessor with DerivedGetter {
- 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".
- BeanPropertyAnnotationLimitationError(tree)
- }
- super.validate()
+ def createSym(getter: MethodSymbol) = {
+ val sym = owner.newValue(derivedName, tree.pos, derivedMods.flags)
+ if (isLazy) sym setLazyAccessor getter
+ sym
}
- }
- // This trait is mixed into BooleanBeanGetter and BeanGetter by beanAccessorsFromNames, but not by beanAccessors
- 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 mapAnnotations (_ => Nil), name, Nil, ListOfNil, tree.tpt.duplicate,
- if (isDeferred) EmptyTree else Select(This(owner), tree.name)
- )
- }
- }
- override def createAndEnterSymbol(): MethodSymbol = enterSyntheticSym(derivedTree).asInstanceOf[MethodSymbol]
- }
+ def derivedSym = tree.symbol
- // NoSymbolBeanGetter synthesizes the getter's RHS (which defers to the regular setter)
- // (not sure why, but there is one use site of the BeanGetters where NoSymbolBeanGetter is not mixed in)
- // TODO: clean this up...
- case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter
- case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter
+ def derivedMods =
+ if (!localLazyVal) tree.mods & FieldFlags | PrivateLocal | (if (isLazy) MUTABLE else 0)
+ else (tree.mods | ARTIFACT | MUTABLE) & ~IMPLICIT
- // the bean setter's RHS delegates to the setter
- case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter {
- override protected def setterRhs = Apply(Ident(tree.name.setterName), List(Ident(setterParam)))
- }
+ // TODO: why is this different from the symbol!?
+ private def derivedModsForTree = tree.mods | PrivateLocal
- // No Symbols available.
- private def beanAccessorsFromNames(tree: ValDef) = {
- val ValDef(mods, _, _, _) = 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
- }
+ def derivedTree =
+ if (Field.noFieldFor(tree)) EmptyTree
+ else if (isLazy) copyValDef(tree)(mods = derivedModsForTree, name = derivedName, rhs = EmptyTree).setPos(tree.pos.focus)
+ else copyValDef(tree)(mods = derivedModsForTree, name = derivedName)
- protected def enterBeans(tree: ValDef) {
- val ValDef(mods, name, _, _) = tree
- val beans = beanAccessorsFromNames(tree)
- if (beans.nonEmpty) {
- if (!name.charAt(0).isLetter)
- BeanPropertyAnnotationFieldWithoutLetterError(tree)
- else if (mods.isPrivate) // avoids name clashes with private fields in traits
- BeanPropertyAnnotationPrivateFieldError(tree)
-
- // Create and enter the symbols here, add the trees in finishGetterSetter.
- beans foreach (_.createAndEnterSymbol())
- }
}
+
}
}