diff options
Diffstat (limited to 'src/compiler/scala')
3 files changed, 349 insertions, 453 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()) - } } + } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 98dca1089c..99c1b6991e 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -300,7 +300,7 @@ trait Namers extends MethodSynthesis { def assignSymbol(tree: Tree): Symbol = logAssignSymbol(tree, tree match { case PackageDef(pid, _) => createPackageSymbol(tree.pos, pid) - case Import(_, _) => createImportSymbol(tree) + case imp: Import => createImportSymbol(imp) case mdef: MemberDef => createMemberSymbol(mdef, mdef.name, -1L) case _ => abort("Unexpected tree: " + tree) }) @@ -319,6 +319,12 @@ trait Namers extends MethodSynthesis { sym } + def createMethod(accessQual: MemberDef, name: TermName, pos: Position, flags: Long): MethodSymbol = { + val sym = owner.newMethod(name, pos, flags) + setPrivateWithin(accessQual, sym) + sym + } + private def logAssignSymbol(tree: Tree, sym: Symbol): Symbol = { if (isPastTyper) sym.name.toTermName match { case nme.IMPORT | nme.OUTER | nme.ANON_CLASS_NAME | nme.ANON_FUN_NAME | nme.CONSTRUCTOR => () @@ -355,11 +361,9 @@ trait Namers extends MethodSynthesis { else owner.newValue(name.toTermName, pos, flags) } } - def createFieldSymbol(tree: ValDef): TermSymbol = - owner.newValue(tree.localName, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal) - def createImportSymbol(tree: Tree) = - NoSymbol.newImport(tree.pos) setInfo completerOf(tree) + def createImportSymbol(tree: Import) = + NoSymbol.newImport(tree.pos) setInfo (namerOf(tree.symbol) importTypeCompleter tree) /** All PackageClassInfoTypes come from here. */ def createPackageSymbol(pos: Position, pid: RefTree): Symbol = { @@ -632,7 +636,7 @@ trait Namers extends MethodSynthesis { } } - def completerOf(tree: Tree): TypeCompleter = { + def completerOf(tree: MemberDef): TypeCompleter = { val mono = namerOf(tree.symbol) monoTypeCompleter tree val tparams = treeInfo.typeParameters(tree) if (tparams.isEmpty) mono @@ -666,25 +670,6 @@ trait Namers extends MethodSynthesis { } } - 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 - // a local implicit "lazy val x" will create an ambiguity with itself - // via "x$lzy" as can be seen in test #3927. - val sym = ( - if (owner.isClass) createFieldSymbol(tree) - else owner.newValue(tree.name append nme.LAZY_LOCAL, tree.pos, (tree.mods.flags | ARTIFACT) & ~IMPLICIT) - ) - enterValSymbol(tree, sym setFlag MUTABLE setLazyAccessor lazyAccessor) - } - def enterStrictVal(tree: ValDef): TermSymbol = { - enterValSymbol(tree, createFieldSymbol(tree)) - } - def enterValSymbol(tree: ValDef, sym: TermSymbol): TermSymbol = { - 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 @@ -771,7 +756,7 @@ trait Namers extends MethodSynthesis { NoSymbol } - def monoTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => + def monoTypeCompleter(tree: MemberDef) = mkTypeCompleter(tree) { sym => // this early test is there to avoid infinite baseTypes when // adding setters and getters --> bug798 // It is a def in an attempt to provide some insulation against @@ -780,8 +765,9 @@ trait Namers extends MethodSynthesis { // on these flag checks so it can't hurt. def needsCycleCheck = sym.isNonClassType && !sym.isParameter && !sym.isExistential - // logDefinition(sym) { - val tp = typeSig(tree) + val annotations = annotSig(tree.mods.annotations) + + val tp = typeSig(tree, annotations) findCyclicalLowerBound(tp) andAlso { sym => if (needsCycleCheck) { @@ -792,42 +778,140 @@ trait Namers extends MethodSynthesis { sym.initialize } } - sym setInfo { - if (sym.isJavaDefined) RestrictJavaArraysMap(tp) - else tp - } + + sym.setInfo(if (!sym.isJavaDefined) tp else RestrictJavaArraysMap(tp)) + if (needsCycleCheck) { log(s"Needs cycle check: ${sym.debugLocationString}") if (!typer.checkNonCyclic(tree.pos, tp)) sym setInfo ErrorType } - //} validate(sym) } - def moduleClassTypeCompleter(tree: ModuleDef) = { - mkTypeCompleter(tree) { sym => - val moduleSymbol = tree.symbol - assert(moduleSymbol.moduleClass == sym, moduleSymbol.moduleClass) - moduleSymbol.info // sets moduleClass info as a side effect. - } + def moduleClassTypeCompleter(tree: ModuleDef) = mkTypeCompleter(tree) { sym => + val moduleSymbol = tree.symbol + assert(moduleSymbol.moduleClass == sym, moduleSymbol.moduleClass) + moduleSymbol.info // sets moduleClass info as a side effect. + } + + + def importTypeCompleter(imp: Import) = mkTypeCompleter(imp) { sym => + sym setInfo importSig(imp) + } + + import AnnotationInfo.{mkFilter => annotationFilter} + + def valTypeCompleter(tree: ValDef) = mkTypeCompleter(tree) { sym => + val annots = + if (tree.mods.annotations.isEmpty) Nil + else annotSig(tree.mods.annotations) filter annotationFilter(FieldTargetClass, !tree.mods.isParamAccessor) + + sym setInfo typeSig(tree, annots) + + validate(sym) } /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ def accessorTypeCompleter(tree: ValDef, isSetter: Boolean) = mkTypeCompleter(tree) { sym => + // println(s"triaging for ${sym.debugFlagString} $sym from $valAnnots to $annots") + // typeSig calls valDefSig (because tree: ValDef) // sym is an accessor, while tree is the field (which may have the same symbol as the getter, or maybe it's the field) - val sig = accessorSigFromFieldTp(sym, isSetter, typeSig(tree)) + // TODO: can we make this work? typeSig is called on same tree (valdef) to complete info for field and all its accessors + // reuse work done in valTypeCompleter if we already computed the type signature of the val + // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait) +// val valSig = +// if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info +// else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + + val valSig = typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + + val sig = accessorSigFromFieldTp(sym, isSetter, valSig) + + val mods = tree.mods + if (mods.annotations.nonEmpty) { + val annotSigs = annotSig(mods.annotations) + + // neg/t3403: check that we didn't get a sneaky type alias/renamed import that we couldn't detect because we only look at names during synthesis + // (TODO: can we look at symbols earlier?) + if (!((mods hasAnnotationNamed tpnme.BeanPropertyAnnot) || (mods hasAnnotationNamed tpnme.BooleanBeanPropertyAnnot)) + && annotSigs.exists(ann => (ann.matches(BeanPropertyAttr)) || ann.matches(BooleanBeanPropertyAttr))) + BeanPropertyAnnotationLimitationError(tree) + + sym setAnnotations (annotSigs filter filterAccessorAnnotations(isSetter)) + } sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym) validate(sym) } - private def accessorSigFromFieldTp(sym: global.Symbol, isSetter: Boolean, tp: global.Type): global.Type with Product with Serializable = { - if (isSetter) MethodType(List(sym.newSyntheticValueParam(tp)), UnitTpe) else NullaryMethodType(tp) + /* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */ + def beanAccessorTypeCompleter(tree: ValDef, missingTpt: Boolean, isSetter: Boolean) = mkTypeCompleter(tree) { sym => + context.unit.synthetics get sym match { + case Some(ddef: DefDef) => + // sym is an accessor, while tree is the field (for traits it's actually the getter, and we're completing the setter) + // reuse work done in valTypeCompleter if we already computed the type signature of the val + // (assuming the field and accessor symbols are distinct -- i.e., we're not in a trait) + val valSig = + if ((sym ne tree.symbol) && tree.symbol.isInitialized) tree.symbol.info + else typeSig(tree, Nil) // don't set annotations for the valdef -- we just want to compute the type sig + + // patch up the accessor's tree if the valdef's tpt was not known back when the tree was synthesized + if (missingTpt) { // can't look at tree.tpt here because it may have been completed by now + if (!isSetter) ddef.tpt setType valSig + else if (ddef.vparamss.nonEmpty && ddef.vparamss.head.nonEmpty) ddef.vparamss.head.head.tpt setType valSig + else throw new TypeError(tree.pos, s"Internal error: could not complete parameter/return type for $ddef from $sym") + } + + val annots = + if (tree.mods.annotations.isEmpty) Nil + else annotSig(tree.mods.annotations) filter filterBeanAccessorAnnotations(isSetter) + + val sig = typeSig(ddef, annots) + + sym setInfo pluginsTypeSigAccessor(sig, typer, tree, sym) + + validate(sym) + + case _ => + throw new TypeError(tree.pos, s"Internal error: no synthetic tree found for bean accessor $sym") + } + } + + + // 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 targeting 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) + private def filterAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean = + if (isSetter || !owner.isTrait) + annotationFilter(if (isSetter) SetterTargetClass else GetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(GetterTargetClass, defaultRetention = true)(ann)) + + private def filterBeanAccessorAnnotations(isSetter: Boolean): AnnotationInfo => Boolean = + if (isSetter || !owner.isTrait) + annotationFilter(if (isSetter) BeanSetterTargetClass else BeanGetterTargetClass, defaultRetention = false) + else (ann => + annotationFilter(FieldTargetClass, defaultRetention = true)(ann) || + annotationFilter(BeanGetterTargetClass, defaultRetention = true)(ann)) + + + private def accessorSigFromFieldTp(sym: Symbol, isSetter: Boolean, tp: Type): Type = + if (isSetter) MethodType(List(sym.newSyntheticValueParam(tp)), UnitTpe) + else NullaryMethodType(tp) + def selfTypeCompleter(tree: Tree) = mkTypeCompleter(tree) { sym => val selftpe = typer.typedType(tree).tpe sym setInfo { @@ -1539,67 +1623,52 @@ trait Namers extends MethodSynthesis { * is then assigned to the corresponding symbol (typeSig itself does not need to assign * the type to the symbol, but it can if necessary). */ - def typeSig(tree: Tree): Type = { - // log("typeSig " + tree) - /* For definitions, transform Annotation trees to AnnotationInfos, assign - * them to the sym's annotations. Type annotations: see Typer.typedAnnotated - * We have to parse definition annotations here (not in the typer when traversing - * the MemberDef tree): the typer looks at annotations of certain symbols; if - * they were added only in typer, depending on the compilation order, they may - * or may not be visible. - */ - def annotate(annotated: Symbol) = { - // typeSig might be called multiple times, e.g. on a ValDef: val, getter, setter - // parse the annotations only once. - if (!annotated.isInitialized) tree match { - case defn: MemberDef => - val ainfos = defn.mods.annotations filterNot (_ eq null) map { ann => - val ctx = typer.context - val annCtx = ctx.makeNonSilent(ann) - // need to be lazy, #1782. beforeTyper to allow inferView in annotation args, SI-5892. - AnnotationInfo lazily { - enteringTyper(newTyper(annCtx) typedAnnotation ann) - } - } - if (ainfos.nonEmpty) { - annotated setAnnotations ainfos - if (annotated.isTypeSkolem) - annotated.deSkolemize setAnnotations ainfos - } - case _ => + def typeSig(tree: Tree, annotSigs: List[AnnotationInfo]): Type = { + if (annotSigs.nonEmpty) annotate(tree.symbol, annotSigs) + + try tree match { + case member: MemberDef => createNamer(tree).memberSig(member) + case imp: Import => importSig(imp) + } catch typeErrorHandler(tree, ErrorType) + } + + /* For definitions, transform Annotation trees to AnnotationInfos, assign + * them to the sym's annotations. Type annotations: see Typer.typedAnnotated + * We have to parse definition annotations here (not in the typer when traversing + * the MemberDef tree): the typer looks at annotations of certain symbols; if + * they were added only in typer, depending on the compilation order, they may + * or may not be visible. + */ + def annotSig(annotations: List[Tree]): List[AnnotationInfo] = + annotations filterNot (_ eq null) map { ann => + val ctx = typer.context + // need to be lazy, #1782. enteringTyper to allow inferView in annotation args, SI-5892. + AnnotationInfo lazily { + enteringTyper { + newTyper(ctx.makeNonSilent(ann)) typedAnnotation ann + } } } - val sym: Symbol = tree.symbol + private def annotate(sym: Symbol, annotSigs: List[AnnotationInfo]): Unit = { + sym setAnnotations annotSigs // TODO: meta-annotations to indicate where module annotations should go (module vs moduleClass) - annotate(sym) - if (sym.isModule) annotate(sym.moduleClass) - - def getSig = tree match { - case cdef: ClassDef => - createNamer(tree).classSig(cdef) - - case mdef: ModuleDef => - createNamer(tree).moduleSig(mdef) - - case ddef: DefDef => - createNamer(tree).methodSig(ddef) - - case vdef: ValDef => - createNamer(tree).valDefSig(vdef) - - case tdef: TypeDef => - createNamer(tree).typeDefSig(tdef) //@M! + if (sym.isModule) sym.moduleClass setAnnotations annotSigs + else if (sym.isTypeSkolem) sym.deSkolemize setAnnotations annotSigs + } - case imp: Import => - importSig(imp) + // TODO OPT: move to method on MemberDef? + private def memberSig(member: MemberDef) = + member match { + case ddef: DefDef => methodSig(ddef) + case vdef: ValDef => valDefSig(vdef) + case tdef: TypeDef => typeDefSig(tdef) + case cdef: ClassDef => classSig(cdef) + case mdef: ModuleDef => moduleSig(mdef) + // skip PackageDef } - try getSig - catch typeErrorHandler(tree, ErrorType) - } - def includeParent(tpe: Type, parent: Symbol): Type = tpe match { case PolyType(tparams, restpe) => PolyType(tparams, includeParent(restpe, parent)) diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 1fe8438b56..f42f5bf75d 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -2221,7 +2221,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper if (!isPastTyper && meth.isPrimaryConstructor) { for (vparams <- ddef.vparamss; vd <- vparams) { if (vd.mods.isParamAccessor) { - namer.validateParam(vd) + vd.symbol setAnnotations (vd.symbol.annotations filter AnnotationInfo.mkFilter(ParamTargetClass, defaultRetention = true)) } } } |