Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline logic related to accessor derivation in MethodSynthesis & Namers #4709

Merged
merged 3 commits into from Sep 8, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/compiler/scala/reflect/reify/phases/Reshape.scala
Expand Up @@ -325,8 +325,7 @@ trait Reshape {
if (reifyDebug) println(s"reconstructed lazy val is $vdef1")
vdef1::Nil
case ddef: DefDef if ddef.symbol.isLazy =>
def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty
if (hasUnitType(ddef.symbol)) {
if (isUnitType(ddef.symbol.info)) {
// since lazy values of type Unit don't have val's
// we need to create them from scratch
toPreTyperLazyVal(ddef) :: Nil
Expand Down
Expand Up @@ -1105,7 +1105,7 @@ trait ContextErrors {
def GetterDefinedTwiceError(getter: Symbol) =
issueSymbolTypeError(getter, getter+" is defined twice")

def ValOrValWithSetterSuffixError(tree: Tree) =
def ValOrVarWithSetterSuffixError(tree: Tree) =
issueNormalTypeError(tree, "Names of vals or vars may not end in `_='")

def PrivateThisCaseClassParameterError(tree: Tree) =
Expand Down
137 changes: 79 additions & 58 deletions src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala
Expand Up @@ -134,28 +134,28 @@ 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)
}
Expand All @@ -177,11 +177,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)
)
Expand Down Expand Up @@ -221,11 +221,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)
Expand All @@ -234,15 +237,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
Expand All @@ -260,7 +256,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
Expand All @@ -284,8 +279,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 {
Expand All @@ -305,7 +301,6 @@ trait MethodSynthesis {
/* 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
Expand All @@ -314,8 +309,8 @@ trait MethodSynthesis {
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)
Expand All @@ -333,18 +328,22 @@ trait MethodSynthesis {
}
}
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
private def setterParam = derivedSym.paramss match {
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
}
Expand All @@ -363,8 +362,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
Expand All @@ -385,8 +383,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
Expand All @@ -400,19 +399,21 @@ 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 mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
case _ if isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
case tp => TypeTree(tp)
}
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) {
Expand All @@ -432,10 +433,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
Expand All @@ -454,6 +455,24 @@ trait MethodSynthesis {

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 does not tollerate including `|| vd.symbol.owner.isTrait` --> tests break
def noFieldFor(vd: ValDef) = vd.mods.isDeferred || (vd.mods.isLazy && isUnitType(vd.symbol.info)) // || vd.symbol.owner.isTrait))
}

case class Field(tree: ValDef) extends DerivedFromValDef {
def name = tree.localName
def category = FieldTargetClass
Expand All @@ -462,11 +481,13 @@ trait MethodSynthesis {
// 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
Expand Down Expand Up @@ -501,12 +522,12 @@ 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 { }
Expand Down
35 changes: 16 additions & 19 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Expand Up @@ -115,21 +115,14 @@ trait Namers extends MethodSynthesis {
TypeSigError(tree, ex)
alt
}
// 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)
|| isEnumConstant(vd)
)

def noFinishGetterSetter(vd: ValDef) = (
(vd.mods.isPrivateLocal && !vd.mods.isLazy) // all lazy vals need accessors, even private[this]
|| vd.symbol.isModuleVar
|| isEnumConstant(vd))
// All lazy vals need accessors, including those owned by terms (e.g., in method) or private[this] in a class
def deriveAccessors(vd: ValDef) = vd.mods.isLazy || (owner.isClass && deriveAccessorsInClass(vd))

private def deriveAccessorsInClass(vd: ValDef) =
!vd.mods.isPrivateLocal && // note, private[this] lazy vals do get accessors -- see outer disjunction of deriveAccessors
!(vd.name startsWith nme.OUTER) && // outer accessors are added later, in explicitouter
!isEnumConstant(vd) // enums can only occur in classes, so only check here

/** Determines whether this field holds an enum constant.
* To qualify, the following conditions must be met:
Expand Down Expand Up @@ -655,11 +648,15 @@ trait Namers extends MethodSynthesis {
}
}

def enterValDef(tree: ValDef) {
if (noEnterGetterSetter(tree))
assignAndEnterFinishedSymbol(tree)
else
enterGetterSetter(tree)
def enterValDef(tree: ValDef): Unit = {
val isScala = !context.unit.isJava
if (isScala) {
if (nme.isSetterName(tree.name)) ValOrVarWithSetterSuffixError(tree)
if (tree.mods.isPrivateLocal && tree.mods.isCaseAccessor) PrivateThisCaseClassParameterError(tree)
}

if (isScala && deriveAccessors(tree)) enterGetterSetter(tree)
else assignAndEnterFinishedSymbol(tree)

if (isEnumConstant(tree))
tree.symbol setInfo ConstantType(Constant(tree.symbol))
Expand Down
2 changes: 2 additions & 0 deletions src/reflect/scala/reflect/internal/Definitions.scala
Expand Up @@ -233,6 +233,8 @@ trait Definitions extends api.StandardDefinitions {
|| tp =:= AnyRefTpe
)

def isUnitType(tp: Type) = tp.typeSymbol == UnitClass && tp.annotations.isEmpty

def hasMultipleNonImplicitParamLists(member: Symbol): Boolean = hasMultipleNonImplicitParamLists(member.info)
def hasMultipleNonImplicitParamLists(info: Type): Boolean = info match {
case PolyType(_, restpe) => hasMultipleNonImplicitParamLists(restpe)
Expand Down