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.scala210
1 files changed, 113 insertions, 97 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
index f3856db552..c03094bc6a 100644
--- a/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
@@ -6,11 +6,8 @@ package scala.tools.nsc
package typechecker
import symtab.Flags._
-import scala.reflect.internal.util.StringOps.{ ojoin }
-import scala.reflect.ClassTag
+import scala.reflect.internal.util.StringOps.ojoin
import scala.reflect.internal.util.ListOfNil
-import scala.reflect.runtime.{ universe => ru }
-import scala.language.higherKinds
/** Logic related to method synthesis which involves cooperation between
* Namer and Typer.
@@ -22,17 +19,6 @@ trait MethodSynthesis {
import definitions._
import CODE._
- /** The annotations amongst those found on the original symbol which
- * should be propagated to this kind of accessor.
- */
- def deriveAnnotations(initial: List[AnnotationInfo], category: Symbol, keepClean: Boolean): List[AnnotationInfo] = {
- def annotationFilter(ann: AnnotationInfo) = ann.metaAnnotations match {
- case Nil if ann.defaultTargets.isEmpty => keepClean // no meta-annotations or default targets
- case Nil => ann.defaultTargets contains category // default targets exist for ann
- case metas => metas exists (_ matches category) // meta-annotations attached to ann
- }
- initial filter annotationFilter
- }
class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) {
def mkThis = This(clazz) setPos clazz.pos.focus
@@ -134,39 +120,41 @@ trait MethodSynthesis {
ImplicitClassWrapper(tree).createAndEnterSymbol()
}
- def enterGetterSetter(tree: ValDef) {
- val ValDef(mods, name, _, _) = tree
- if (nme.isSetterName(name))
- ValOrValWithSetterSuffixError(tree)
-
- tree.symbol = (
- if (mods.isLazy) {
+ // TODO: see if we can link symbol creation & tree derivation by sharing the Field/Getter/Setter factories
+ def enterGetterSetter(tree: ValDef): Unit = {
+ tree.symbol =
+ if (tree.mods.isLazy) {
val lazyValGetter = LazyValGetter(tree).createAndEnterSymbol()
enterLazyVal(tree, lazyValGetter)
} else {
- if (mods.isPrivateLocal)
- PrivateThisCaseClassParameterError(tree)
- val getter = Getter(tree).createAndEnterSymbol()
+ val getter = Getter(tree)
+ val getterSym = getter.createAndEnterSymbol()
+
// Create the setter if necessary.
- if (mods.isMutable)
+ if (getter.needsSetter)
Setter(tree).createAndEnterSymbol()
- // If abstract, the tree gets the getter's symbol. Otherwise, create a field.
- if (mods.isDeferred) getter setPos tree.pos
+ // If the getter's abstract the tree gets the getter's symbol,
+ // otherwise, create a field (assume the getter requires storage).
+ // 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
else enterStrictVal(tree)
}
- )
enterBeans(tree)
}
+ import AnnotationInfo.{mkFilter => annotationFilter}
+
/** 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 = deriveAnnotations(annotations, targetClass, keepClean = true)
+ val retained = annotations filter annotationFilter(targetClass, defaultRetention = true)
annotations filterNot (retained contains _) foreach (ann => issueAnnotationWarning(tree, ann, targetClass))
}
@@ -177,11 +165,11 @@ trait MethodSynthesis {
}
def addDerivedTrees(typer: Typer, stat: Tree): List[Tree] = stat match {
- case vd @ ValDef(mods, name, tpt, rhs) if !noFinishGetterSetter(vd) =>
+ 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 = (
- allValDefDerived(vd)
+ (field(vd) ::: standardAccessors(vd) ::: beanAccessors(vd))
map (acc => atPos(vd.pos.focus)(acc derive annotations))
filterNot (_ eq EmptyTree)
)
@@ -208,8 +196,8 @@ trait MethodSynthesis {
context.unit.synthetics get meth match {
case Some(mdef) =>
context.unit.synthetics -= meth
- meth setAnnotations deriveAnnotations(annotations, MethodTargetClass, keepClean = false)
- cd.symbol setAnnotations deriveAnnotations(annotations, ClassTargetClass, keepClean = true)
+ meth setAnnotations (annotations filter annotationFilter(MethodTargetClass, defaultRetention = false))
+ cd.symbol setAnnotations (annotations filter annotationFilter(ClassTargetClass, defaultRetention = true))
List(cd, mdef)
case _ =>
// Shouldn't happen, but let's give ourselves a reasonable error when it does
@@ -221,11 +209,14 @@ trait MethodSynthesis {
stat :: Nil
}
- 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 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)
@@ -234,15 +225,8 @@ trait MethodSynthesis {
BooleanBeanGetter(vd) :: setter
else Nil
}
- def allValDefDerived(vd: ValDef) = {
- 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
+ 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
@@ -260,7 +244,6 @@ trait MethodSynthesis {
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
@@ -284,8 +267,9 @@ trait MethodSynthesis {
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 mods = tree.mods
+ final def basisSym = tree.symbol
+ final def derivedMods = mods & flagsMask | flagsExtra
}
sealed trait DerivedFromClassDef extends DerivedFromMemberDef {
@@ -297,25 +281,19 @@ trait MethodSynthesis {
def tree: ValDef
final def enclClass = basisSym.enclClass
- /** Which meta-annotation is associated with this kind of entity.
- * Presently one of: field, getter, setter, beanGetter, beanSetter, param.
- */
- def category: Symbol
/* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */
final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter)
final def fieldSelection = Select(This(enclClass), basisSym)
- 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(name, tree.pos.focus, (tree.mods.flags & flagsMask) | flagsExtra)
+ def createAndEnterSymbol(): MethodSymbol = {
+ val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags)
setPrivateWithin(tree, sym)
enterInScope(sym)
sym setInfo completer(sym)
@@ -328,12 +306,35 @@ trait MethodSynthesis {
}
final def derive(initial: List[AnnotationInfo]): Tree = {
validate()
- derivedSym setAnnotations deriveAnnotations(initial, category, keepClean)
+
+ // 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.
+ //
+ // 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 => annotationFilter(GetterTargetClass, defaultRetention = false)
+ case _: Setter => annotationFilter(SetterTargetClass, defaultRetention = false)
+ case _: BeanSetter => annotationFilter(BeanSetterTargetClass, defaultRetention = false)
+ 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)
+
logDerived(derivedTree)
}
}
sealed trait DerivedGetter extends DerivedFromValDef {
- // TODO
+ // A getter must be accompanied by a setter if the ValDef is mutable.
+ def needsSetter = mods.isMutable
}
sealed trait DerivedSetter extends DerivedFromValDef {
override def isSetter = true
@@ -341,10 +342,13 @@ trait MethodSynthesis {
case (p :: Nil) :: _ => p
case _ => NoSymbol
}
- private def setterRhs = (
- if (mods.isDeferred || derivedSym.isOverloaded) EmptyTree
+
+ private 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))
- )
+ }
+
private def setterDef = DefDef(derivedSym, setterRhs)
override def derivedTree: Tree = if (setterParam == NoSymbol) EmptyTree else setterDef
}
@@ -363,8 +367,7 @@ trait MethodSynthesis {
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(mods & flagsMask | flagsExtra, name, tree)
+ def derivedTree: DefDef = factoryMeth(derivedMods, name, tree)
def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC
def flagsMask: Long = AccessFlags
def name: TermName = tree.name.toTermName
@@ -372,7 +375,6 @@ trait MethodSynthesis {
sealed abstract class BaseGetter(tree: ValDef) extends DerivedGetter {
def name = tree.name
- def category = GetterTargetClass
def flagsMask = GetterFlags
def flagsExtra = ACCESSOR.toLong | ( if (tree.mods.isMutable) 0 else STABLE )
@@ -385,8 +387,9 @@ trait MethodSynthesis {
}
}
case class Getter(tree: ValDef) extends BaseGetter(tree) {
- override def derivedSym = if (mods.isDeferred) basisSym else basisSym.getterIn(enclClass)
- private def derivedRhs = if (mods.isDeferred) EmptyTree else fieldSelection
+ override def derivedSym = if (Field.noFieldFor(tree)) basisSym else basisSym.getterIn(enclClass)
+ private def derivedRhs = if (Field.noFieldFor(tree)) tree.rhs else fieldSelection
+
private def derivedTpt = {
// 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
@@ -396,23 +399,26 @@ trait MethodSynthesis {
// starts compiling (instead of failing like it's supposed to) because the typer
// expects to be able to identify escaping locals in typedDefDef, and fails to
// spot that brand of them. In other words it's an artifact of the implementation.
- val tpt = derivedSym.tpe_*.finalResultType.widen match {
+ val getterTp = derivedSym.tpe_*.finalResultType
+ val tpt = getterTp.widen match {
// 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 mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
- case tp => TypeTree(tp)
+ 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 _ => 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
- * - 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.
- */
+ * - 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) {
@@ -432,10 +438,10 @@ trait MethodSynthesis {
override def derivedTree: DefDef = {
val ValDef(_, _, tpt0, rhs0) = tree
val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0)
- val body = (
- if (tree.symbol.owner.isTrait || hasUnitType(basisSym)) rhs1
+ 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
@@ -448,32 +454,45 @@ trait MethodSynthesis {
}
case class Setter(tree: ValDef) extends DerivedSetter {
def name = tree.setterName
- def category = SetterTargetClass
def flagsMask = SetterFlags
def flagsExtra = ACCESSOR
override def derivedSym = basisSym.setterIn(enclClass)
}
+
+ object Field {
+ // No field for these vals (either never emitted or eliminated later on):
+ // - abstract vals have no value we could store (until they become concrete, potentially)
+ // - lazy vals of type Unit
+ // - [Emitted, later removed during AddInterfaces/Mixins] concrete vals in traits can't have a field
+ // - [Emitted, later removed during Constructors] a concrete val with a statically known value (Unit / ConstantType)
+ // performs its side effect according to lazy/strict semantics, but doesn't need to store its value
+ // each access will "evaluate" the RHS (a literal) again
+ // We would like to avoid emitting unnecessary fields, but the required knowledge isn't available until after typer.
+ // The only way to avoid emitting & suppressing, is to not emit at all until we are sure to need the field, as dotty does.
+ // NOTE: do not look at `vd.symbol` when called from `enterGetterSetter` (luckily, that call-site implies `!mods.isLazy`),
+ // as the symbol info is in the process of being created then.
+ // TODO: harmonize tree & symbol creation
+ // TODO: the `def field` call-site breaks when you add `|| vd.symbol.owner.isTrait` (detected in test suite)
+ def noFieldFor(vd: ValDef) = vd.mods.isDeferred || (vd.mods.isLazy && isUnitType(vd.symbol.info))
+ }
+
case class Field(tree: ValDef) extends DerivedFromValDef {
def name = tree.localName
- 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 if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
+
+ // 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)
- )
+
}
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) {
@@ -487,7 +506,6 @@ trait MethodSynthesis {
override def derivedSym = enclClass.info decl name
}
sealed 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
@@ -501,18 +519,16 @@ trait MethodSynthesis {
// Derives a tree without attempting to use the original tree's symbol.
override def derivedTree = {
atPos(tree.pos.focus) {
- DefDef(derivedMods, name, Nil, ListOfNil, tree.tpt.duplicate,
+ DefDef(derivedMods mapAnnotations (_ => Nil), name, Nil, ListOfNil, tree.tpt.duplicate,
if (isDeferred) EmptyTree else Select(This(owner), tree.name)
)
}
}
- override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree)
+ override def createAndEnterSymbol(): MethodSymbol = enterSyntheticSym(derivedTree).asInstanceOf[MethodSymbol]
}
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
- }
+ case class BeanSetter(tree: ValDef) extends BeanAccessor("set") with DerivedSetter
// No Symbols available.
private def beanAccessorsFromNames(tree: ValDef) = {