summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan@lightbend.com>2016-07-08 17:24:23 -0700
committerAdriaan Moors <adriaan@lightbend.com>2016-08-11 10:59:17 -0700
commitfdc94676928cd9177acfcca8eb7d669e1f4eac48 (patch)
treec99a021f8c0fc6e691bc63f5c8cc635a38380e78 /src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
parent3540ffc1fb81eef75aeff41a0ba9142b1cce8a53 (diff)
downloadscala-fdc94676928cd9177acfcca8eb7d669e1f4eac48.tar.gz
scala-fdc94676928cd9177acfcca8eb7d669e1f4eac48.tar.bz2
scala-fdc94676928cd9177acfcca8eb7d669e1f4eac48.zip
Drive accessor synthesis from info transformer
Derive/filter/propagate annotations in info transformer, don't rely on having type checked the derived trees in order to see the annotations. Use synthetics mechanism for bean accessors -- the others will soon follow. Propagate inferred tpt from valdef to accessors by setting type in right spot of synthetic tree during the info completer. No need to add trees in derivedTrees, and get rid of some overfactoring in method synthesis, now that we have joined symbol and tree creation. Preserve symbol order because tests are sensitive to it. Drop warning on potentially discarded annotations, I don't think this warrants a warning. Motivated by breaking the scala-js compiler, which relied on annotations appearing when trees are type checked. Now that ordering constraint is gone in the new encoding, we may as well finally fix annotation assignment.
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())
- }
}
+
}
}