From b005cd579899bfb1e361f836616f0a8abf74fd14 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Fri, 28 Oct 2011 23:22:38 +0000 Subject: 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. --- .../scala/reflect/internal/Definitions.scala | 10 +- src/compiler/scala/reflect/internal/Symbols.scala | 9 +- src/compiler/scala/reflect/internal/Trees.scala | 3 +- .../scala/tools/nsc/backend/icode/GenICode.scala | 5 +- .../tools/nsc/typechecker/MethodSynthesis.scala | 302 ++++++++++++++++++--- .../scala/tools/nsc/typechecker/Namers.scala | 110 ++------ .../scala/tools/nsc/typechecker/TreeCheckers.scala | 2 +- .../scala/tools/nsc/typechecker/Typers.scala | 162 +++-------- 8 files changed, 343 insertions(+), 260 deletions(-) (limited to 'src/compiler/scala') diff --git a/src/compiler/scala/reflect/internal/Definitions.scala b/src/compiler/scala/reflect/internal/Definitions.scala index 35069d2edd..f0565435ef 100644 --- a/src/compiler/scala/reflect/internal/Definitions.scala +++ b/src/compiler/scala/reflect/internal/Definitions.scala @@ -607,8 +607,8 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val uncheckedStableClass = getClass("scala.annotation.unchecked.uncheckedStable") lazy val uncheckedVarianceClass = getClass("scala.annotation.unchecked.uncheckedVariance") - lazy val BeanPropertyAttr = getClass(sn.BeanProperty) - lazy val BooleanBeanPropertyAttr = getClass(sn.BooleanBeanProperty) + lazy val BeanPropertyAttr = getClass("scala.beans.BeanProperty") + lazy val BooleanBeanPropertyAttr = getClass("scala.beans.BooleanBeanProperty") lazy val CloneableAttr = getClass("scala.cloneable") lazy val DeprecatedAttr = getClass("scala.deprecated") lazy val DeprecatedNameAttr = getClass("scala.deprecatedName") @@ -630,17 +630,13 @@ trait Definitions extends reflect.api.StandardDefinitions { lazy val GetterTargetClass = getMetaAnnotation("getter") lazy val ParamTargetClass = getMetaAnnotation("param") lazy val SetterTargetClass = getMetaAnnotation("setter") - + // TODO: module, moduleClass? package, packageObject? private def getMetaAnnotation(name: String) = getClass("scala.annotation.meta." + name) def isMetaAnnotation(sym: Symbol): Boolean = metaAnnotations(sym) || ( // Trying to allow for deprecated locations sym.isAliasType && isMetaAnnotation(sym.info.typeSymbol) ) - def hasBeanAnnotation(sym: Symbol) = ( - (sym hasAnnotation BeanPropertyAttr) - || (sym hasAnnotation BooleanBeanPropertyAttr) - ) lazy val metaAnnotations = Set( FieldTargetClass, ParamTargetClass, GetterTargetClass, SetterTargetClass, diff --git a/src/compiler/scala/reflect/internal/Symbols.scala b/src/compiler/scala/reflect/internal/Symbols.scala index cb4d2bbf94..e848f0eeda 100644 --- a/src/compiler/scala/reflect/internal/Symbols.scala +++ b/src/compiler/scala/reflect/internal/Symbols.scala @@ -1677,7 +1677,11 @@ trait Symbols extends api.Symbols { self: SymbolTable => */ final def getter(base: Symbol): Symbol = base.info.decl(getterName) filter (_.hasAccessorFlag) - def getterName = if (isSetter) nme.setterToGetter(name) else nme.getterName(name) + def getterName = ( + if (isSetter) nme.setterToGetter(name) + else if (nme.isLocalName(name)) nme.localToGetter(name) + else name + ) /** The setter of this value or getter definition, or NoSymbol if none exists */ final def setter(base: Symbol): Symbol = setter(base, false) @@ -1805,6 +1809,9 @@ trait Symbols extends api.Symbols { self: SymbolTable => else if (isRefinementClass) "refinement class" else if (isModule) "module" else if (isModuleClass) "module class" + else if (isGetter) "getter" + else if (isSetter) "setter" + else if (isVariable) "field" else sanitizedKindString /** String representation of symbol's kind, suitable for the masses. */ diff --git a/src/compiler/scala/reflect/internal/Trees.scala b/src/compiler/scala/reflect/internal/Trees.scala index c469834ac8..75b012f72e 100644 --- a/src/compiler/scala/reflect/internal/Trees.scala +++ b/src/compiler/scala/reflect/internal/Trees.scala @@ -50,6 +50,7 @@ trait Trees extends api.Trees { self: SymbolTable => flags & mask, if (hasAccessBoundary) privateWithin.toString else "" ) + def defaultFlagString = hasFlagsToString(-1L) def & (flag: Long): Modifiers = { val flags1 = flags & flag if (flags1 == flags) this @@ -79,7 +80,7 @@ trait Trees extends api.Trees { self: SymbolTable => override def mapAnnotations(f: List[Tree] => List[Tree]): Modifiers = Modifiers(flags, privateWithin, f(annotations)) setPositions positions - override def toString = "Modifiers(%s, %s, %s)".format(hasFlagsToString(-1L), annotations mkString ", ", positions) + override def toString = "Modifiers(%s, %s, %s)".format(defaultFlagString, annotations mkString ", ", positions) } def Modifiers(flags: Long, privateWithin: Name): Modifiers = Modifiers(flags, privateWithin, List()) diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala index 8676d44f34..90e387d384 100644 --- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala +++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala @@ -121,7 +121,10 @@ abstract class GenICode extends SubComponent { case Block(_, Return(_)) => () case Return(_) => () case EmptyTree => - globalError("Concrete method has no definition: " + tree) + globalError("Concrete method has no definition: " + tree + ( + if (settings.debug.value) "(found: " + m.symbol.owner.info.decls.toList.mkString(", ") + ")" + else "") + ) case _ => if (ctx1.bb.isEmpty) ctx1.bb.closeWith(RETURN(m.returnType), rhs.pos) else 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 +} diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 1f5d891d2e..9e0f70a32c 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -107,24 +107,27 @@ trait Namers extends MethodSynthesis { } } - private def owner = context.owner + protected def owner = context.owner private def contextFile = context.unit.source.file - private def isInJava = context.unit.isJava private def typeErrorHandler[T](pos: Position, alt: T): PartialFunction[Throwable, T] = { case ex: TypeError => typer.reportTypeError(pos, ex) alt } - private def hasNoAccessors(vd: ValDef) = { - val ValDef(mods, name, _, _) = vd - - !mods.isLazy && ( - !owner.isClass - || (mods.isPrivateLocal && !mods.isCaseAccessor) - || (name startsWith nme.OUTER) - || isInJava - ) - } + // PRIVATE | LOCAL are fields generated for primary constructor arguments + // @PP: ...or fields declared as private[this]. PARAMACCESSOR marks constructor arguments. + // Neither gets accessors so the code is as far as I know still correct. + def noEnterGetterSetter(vd: ValDef) = !vd.mods.isLazy && ( + !owner.isClass + || (vd.mods.isPrivateLocal && !vd.mods.isCaseAccessor) + || (vd.name startsWith nme.OUTER) + || (context.unit.isJava) + ) + def noFinishGetterSetter(vd: ValDef) = ( + vd.mods.isPrivateLocal + || vd.symbol.isModuleVar + || vd.symbol.isLazy + ) def setPrivateWithin[Sym <: Symbol](tree: Tree, sym: Sym, mods: Modifiers): Sym = { if (!sym.isPrivateLocal && mods.hasAccessBoundary) @@ -556,38 +559,12 @@ trait Namers extends MethodSynthesis { } def enterValDef(tree: ValDef) { - val ValDef(mods, name, tp, rhs) = tree - - if (hasNoAccessors(tree)) + if (noEnterGetterSetter(tree)) assignAndEnterFinishedSymbol(tree) - else { - if (mods.isPrivateLocal && !mods.isLazy) - context.error(tree.pos, "private[this] not allowed for case class parameters") - if (nme.isSetterName(name)) - context.error(tree.pos, "Names of vals or vars may not end in `_='") - - // add getter and possibly also setter - val getter = enterGetter(tree) - if (mods.isMutable) - enterSetter(tree) - - tree.symbol = ( - if (mods.isDeferred) getter setPos tree.pos // unfocus getter position, no separate value - else if (mods.isLazy) enterLazyVal(tree) setLazyAccessor getter - else enterStrictVal(tree) - ) - - if (!forMSIL) - addBeanGetterSetter(tree, getter.owner) - } - } - - def enterPackage(tree: PackageDef) { - val sym = assignSymbol(tree) - newNamer(context.make(tree, sym.moduleClass, sym.info.decls)) enterSyms tree.stats + else + enterGetterSetter(tree) } - - def enterLazyVal(tree: ValDef): TermSymbol = { + def enterLazyVal(tree: ValDef, lazyAccessor: Symbol): TermSymbol = { // 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 @@ -597,7 +574,7 @@ trait Namers extends MethodSynthesis { if (owner.isClass) createFieldSymbol(tree) else owner.newValue(tree.pos, tree.name + "$lzy") setFlag tree.mods.flags resetFlag IMPLICIT ) - enterValSymbol(tree, sym setFlag MUTABLE) + enterValSymbol(tree, sym setFlag MUTABLE setLazyAccessor lazyAccessor) } def enterStrictVal(tree: ValDef): TermSymbol = { enterValSymbol(tree, createFieldSymbol(tree)) @@ -606,7 +583,10 @@ trait Namers extends MethodSynthesis { enterInScope(sym) sym setInfo namerOf(sym).monoTypeCompleter(tree) } - + def enterPackage(tree: PackageDef) { + val sym = assignSymbol(tree) + newNamer(context.make(tree, sym.moduleClass, sym.info.decls)) enterSyms tree.stats + } def enterTypeDef(tree: TypeDef) = assignAndEnterFinishedSymbol(tree) def enterDefDef(tree: DefDef): Unit = tree match { @@ -668,51 +648,12 @@ trait Namers extends MethodSynthesis { this.context } - def enterSetter(tree: ValDef) = Setter(tree).enterAccessor() - def enterGetter(tree: ValDef) = Getter(tree).enterAccessor() def enterSyntheticSym(tree: Tree): Symbol = { enterSym(tree) context.unit.synthetics(tree.symbol) = tree tree.symbol } - private def addBeanGetterSetter(tree: ValDef, inClazz: Symbol) { - val ValDef(mods, name, tpt, _) = tree - val hasBP = mods hasAnnotationNamed tpnme.BeanPropertyAnnot - val hasBoolBP = mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot - - if (hasBP || hasBoolBP) { - 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") - else { - val flags = mods.flags & BeanPropertyFlags - val beanName = name.toString.capitalize - val getterName: Name = if (hasBoolBP) "is" + beanName else "get" + beanName - val getterMods = Modifiers(flags, mods.privateWithin, Nil) setPositions mods.positions - - // TODO: unify with the other accessor creations - // BeanGetter(getterTree).enterAccessor() - enterSyntheticSym { - atPos(tree.pos.focus) { - DefDef(getterMods, getterName, Nil, List(Nil), tpt.duplicate, - if (mods.isDeferred) EmptyTree - else Select(This(inClazz), name) - ) - } - } - - // can't use "enterSyntheticSym", because the parameter type is not yet - // known. instead, uses the same machinery as for the non-bean setter: - // create and enter the symbol here, add the tree in Typer.addGettterSetter. - if (mods.isMutable) - BeanSetter(tree).enterAccessor() - } - } - } - // --- Lazy Type Assignment -------------------------------------------------- def initializeLowerBounds(tp: Type): Type = { @@ -1004,8 +945,7 @@ trait Namers extends MethodSynthesis { if (vparam.tpt.isEmpty) { val paramtpe = pps.head.tpe vparam.symbol setInfo paramtpe - vparam.tpt defineType paramtpe - vparam.tpt setPos vparam.pos.focus + vparam.tpt defineType paramtpe setPos vparam.pos.focus } pps = pps.tail } diff --git a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala index c8e2b6fca4..b0500776fe 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TreeCheckers.scala @@ -166,7 +166,7 @@ abstract class TreeCheckers extends Analyzer { override def newTyper(context: Context): Typer = new TreeChecker(context) class TreeChecker(context0: Context) extends Typer(context0) { - override protected def typerAddSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = { + override protected def finishMethodSynthesis(templ: Template, clazz: Symbol, context: Context): Template = { // If we don't intercept this all the synthetics get added at every phase, // with predictably unfortunate results. templ diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 27753e85b8..54a3e12ce0 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -1343,7 +1343,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { val tparams1 = cdef.tparams mapConserve (typedTypeDef) val impl1 = newTyper(context.make(cdef.impl, clazz, new Scope)) .typedTemplate(cdef.impl, parentTypes(cdef.impl)) - val impl2 = typerAddSyntheticMethods(impl1, clazz, context) + val impl2 = finishMethodSynthesis(impl1, clazz, context) if ((clazz != ClassfileAnnotationClass) && (clazz isNonBottomSubClass ClassfileAnnotationClass)) restrictionWarning(cdef.pos, unit, @@ -1386,124 +1386,25 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } ) }) - val impl2 = typerAddSyntheticMethods(impl1, clazz, context) + val impl2 = finishMethodSynthesis(impl1, clazz, context) treeCopy.ModuleDef(mdef, typedMods, mdef.name, impl2) setType NoType } /** In order to override this in the TreeCheckers Typer so synthetics aren't re-added * all the time, it is exposed here the module/class typing methods go through it. + * ...but it turns out it's also the ideal spot for namer/typer coordination for + * the tricky method synthesis scenarios, so we'll make it that. */ - protected def typerAddSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = { + protected def finishMethodSynthesis(templ: Template, clazz: Symbol, context: Context): Template = { addSyntheticMethods(templ, clazz, context) } - - /** - * @param stat ... - * @return ... + /** For flatMapping a list of trees when you want the DocDefs and Annotated + * to be transparent. */ - def addGetterSetter(stat: Tree): List[Tree] = stat match { - case ValDef(mods, name, tpt, rhs) - // PRIVATE | LOCAL are fields generated for primary constructor arguments - if !mods.isPrivateLocal && !stat.symbol.isModuleVar => - val isDeferred = mods.isDeferred - val value = stat.symbol - val allAnnots = value.annotations - if (!isDeferred) - // keepClean: by default annotations go to the field, except if the field is - // generated for a class parameter (PARAMACCESSOR). - value.setAnnotations(memberAnnots(allAnnots, FieldTargetClass, keepClean = !mods.isParamAccessor)) - - val getter = if (isDeferred) value else value.getter(value.owner) - assert(getter != NoSymbol, stat) - if (getter.isOverloaded) - error(getter.pos, getter+" is defined twice") - - getter.setAnnotations(memberAnnots(allAnnots, GetterTargetClass)) - - if (value.isLazy) List(stat) - else { - val vdef = treeCopy.ValDef(stat, mods | PRIVATE | LOCAL, nme.getterToLocal(name), tpt, rhs) - val getterDef: DefDef = atPos(vdef.pos.focus) { - if (isDeferred) { - val r = DefDef(getter, EmptyTree) - r.tpt.asInstanceOf[TypeTree].setOriginal(tpt) // keep type tree of original abstract field - r - } else { - val rhs = gen.mkCheckInit(Select(This(value.owner), value)) - val r = typed { - atPos(getter.pos.focus) { - DefDef(getter, rhs) - } - }.asInstanceOf[DefDef] - r.tpt.setPos(tpt.pos.focus) - r - } - } - checkNoEscaping.privates(getter, getterDef.tpt) - def setterDef(setter: Symbol, isBean: Boolean = false): DefDef = { - setter setAnnotations memberAnnots(allAnnots, if (isBean) BeanSetterTargetClass else SetterTargetClass) - val defTree = - if ((mods hasFlag DEFERRED) || (setter hasFlag OVERLOADED)) EmptyTree - else Assign(Select(This(value.owner), value), Ident(setter.paramss.head.head)) - - - typedPos(vdef.pos.focus)(DefDef(setter, defTree)).asInstanceOf[DefDef] - } - - val gs = new ListBuffer[DefDef] - gs.append(getterDef) - if (mods.isMutable) { - val setter = getter.setter(value.owner) - gs.append(setterDef(setter)) - } - if (!forMSIL && hasBeanAnnotation(value)) { - val nameSuffix = name.toString.capitalize - val beanGetterName = - (if (value.hasAnnotation(BooleanBeanPropertyAttr)) "is" else "get") + - nameSuffix - val beanGetter = value.owner.info.decl(beanGetterName) - if (beanGetter == 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". - unit.error(stat.pos, "implementation limitation: the BeanProperty annotation cannot be used in a type alias or renamed import") - } - beanGetter.setAnnotations(memberAnnots(allAnnots, BeanGetterTargetClass)) - if (mods.isMutable && beanGetter != NoSymbol) { - val beanSetterName = "set" + nameSuffix - val beanSetter = value.owner.info.decl(beanSetterName) - // unlike for the beanGetter, the beanSetter body is generated here. see comment in Namers. - gs.append(setterDef(beanSetter, isBean = true)) - } - } - if (mods.isDeferred) gs.toList else vdef :: gs.toList - } - case dd @ DocDef(comment, defn) => - addGetterSetter(defn) map (stat => DocDef(comment, stat) setPos dd.pos) - - case Annotated(annot, defn) => - addGetterSetter(defn) map (stat => Annotated(annot, stat)) - - case _ => - List(stat) - } - - /** - * The annotations amongst `annots` that should go on a member of class - * `annotKind` (one of: field, getter, setter, beanGetter, beanSetter, param) - * If 'keepClean' is true, annotations without any meta-annotations are kept. - */ - protected def memberAnnots(annots: List[AnnotationInfo], annotKind: Symbol, keepClean: Boolean = false) = { - annots 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 annotKind) || - // `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 annotKind) - } + def rewrappingWrapperTrees(f: Tree => List[Tree]): Tree => List[Tree] = { + case dd @ DocDef(comment, defn) => f(defn) map (stat => DocDef(comment, stat) setPos dd.pos) + case Annotated(annot, defn) => f(defn) map (stat => Annotated(annot, stat)) + case tree => f(tree) } protected def enterSyms(txt: Context, trees: List[Tree]) = { @@ -1531,23 +1432,27 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { templ setSymbol clazz.newLocalDummy(templ.pos) val self1 = templ.self match { case vd @ ValDef(mods, name, tpt, EmptyTree) => - val tpt1 = - checkNoEscaping.privates( - clazz.thisSym, - treeCopy.TypeTree(tpt).setOriginal(tpt) setType vd.symbol.tpe) + val tpt1 = checkNoEscaping.privates( + clazz.thisSym, + treeCopy.TypeTree(tpt).setOriginal(tpt) setType vd.symbol.tpe + ) treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType } -// was: -// val tpt1 = checkNoEscaping.privates(clazz.thisSym, typedType(tpt)) -// treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType -// but this leads to cycles for existential self types ==> #2545 - if (self1.name != nme.WILDCARD) context.scope enter self1.symbol - val selfType = + // was: + // val tpt1 = checkNoEscaping.privates(clazz.thisSym, typedType(tpt)) + // treeCopy.ValDef(vd, mods, name, tpt1, EmptyTree) setType NoType + // but this leads to cycles for existential self types ==> #2545 + if (self1.name != nme.WILDCARD) + context.scope enter self1.symbol + + val selfType = ( if (clazz.isAnonymousClass && !phase.erasedTypes) intersectionType(clazz.info.parents, clazz.owner) - else clazz.typeOfThis + else + clazz.typeOfThis + ) // the following is necessary for templates generated later - assert(clazz.info.decls != EmptyScope) + assert(clazz.info.decls != EmptyScope, clazz) enterSyms(context.outer.make(templ, clazz, clazz.info.decls), templ.body) validateParentClasses(parents1, selfType) if (clazz.isCase) @@ -1557,10 +1462,11 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { unit.error(clazz.pos, "inner classes cannot be classfile annotations") if (!phase.erasedTypes && !clazz.info.resultType.isError) // @S: prevent crash for duplicated type members checkFinitary(clazz.info.resultType.asInstanceOf[ClassInfoType]) + val body = - if (!isPastTyper && !reporter.hasErrors) - templ.body flatMap addGetterSetter - else templ.body + if (isPastTyper || reporter.hasErrors) templ.body + else templ.body flatMap rewrappingWrapperTrees(namer.finishGetterSetter(Typer.this, _)) + val body1 = typedStats(body, templ.symbol) treeCopy.Template(templ, parents1, self1, body1) setType clazz.tpe } @@ -1775,8 +1681,7 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { if (!isPastTyper && meth.isPrimaryConstructor) { for (vparams <- ddef.vparamss; vd <- vparams) { if (vd.mods.isParamAccessor) { - val sym = vd.symbol - sym.setAnnotations(memberAnnots(sym.annotations, ParamTargetClass, keepClean = true)) + namer.validateParam(vd) } } } @@ -4461,6 +4366,9 @@ trait Typers extends Modes with Adaptations with PatMatVirtualiser { } def typedPos(pos: Position)(tree: Tree) = typed(atPos(pos)(tree)) + // TODO: see if this formulation would impose any penalty, since + // it makes for a lot less casting. + // def typedPos[T <: Tree](pos: Position)(tree: T): T = typed(atPos(pos)(tree)).asInstanceOf[T] /** Types expression tree with given prototype pt. * -- cgit v1.2.3