/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package typechecker
import scala.reflect.NameTransformer
import symtab.Flags._
import scala.reflect.internal.util.StringOps.ojoin
import scala.reflect.internal.util.ListOfNil
/** Logic related to method synthesis which involves cooperation between
* Namer and Typer.
*/
trait MethodSynthesis {
self: Analyzer =>
import global._
import definitions._
import CODE._
class ClassMethodSynthesis(val clazz: Symbol, localTyper: Typer) {
def mkThis = This(clazz) setPos clazz.pos.focus
def mkThisSelect(sym: Symbol) = atPos(clazz.pos.focus)(
if (clazz.isClass) Select(This(clazz), sym) else Ident(sym)
)
private def isOverride(name: TermName) =
clazzMember(name).alternatives exists (sym => !sym.isDeferred && (sym.owner != clazz))
def newMethodFlags(name: TermName) = {
val overrideFlag = if (isOverride(name)) OVERRIDE else 0L
overrideFlag | SYNTHETIC
}
def newMethodFlags(method: Symbol) = {
val overrideFlag = if (isOverride(method.name.toTermName)) OVERRIDE else 0L
(method.flags | overrideFlag | SYNTHETIC) & ~DEFERRED
}
private def finishMethod(method: Symbol, f: Symbol => Tree): Tree =
localTyper typed (
if (method.isLazy) ValDef(method, f(method))
else DefDef(method, f(method))
)
private def createInternal(name: Name, f: Symbol => Tree, info: Type): Tree = {
val name1 = name.toTermName
val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1))
finishMethod(m setInfoAndEnter info, f)
}
private def createInternal(name: Name, f: Symbol => Tree, infoFn: Symbol => Type): Tree = {
val name1 = name.toTermName
val m = clazz.newMethod(name1, clazz.pos.focus, newMethodFlags(name1))
finishMethod(m setInfoAndEnter infoFn(m), f)
}
private def cloneInternal(original: Symbol, f: Symbol => Tree, name: Name): Tree = {
val m = original.cloneSymbol(clazz, newMethodFlags(original), name) setPos clazz.pos.focus
finishMethod(clazz.info.decls enter m, f)
}
def clazzMember(name: Name) = clazz.info nonPrivateMember name
def typeInClazz(sym: Symbol) = clazz.thisType memberType sym
def deriveMethod(original: Symbol, nameFn: Name => Name)(f: Symbol => Tree): Tree =
cloneInternal(original, f, nameFn(original.name))
def createMethod(name: Name, paramTypes: List[Type], returnType: Type)(f: Symbol => Tree): Tree =
createInternal(name, f, (m: Symbol) => MethodType(m newSyntheticValueParams paramTypes, returnType))
def createMethod(name: Name, returnType: Type)(f: Symbol => Tree): Tree =
createInternal(name, f, NullaryMethodType(returnType))
def createMethod(original: Symbol)(f: Symbol => Tree): Tree =
createInternal(original.name, f, original.info)
def forwardMethod(original: Symbol, newMethod: Symbol)(transformArgs: List[Tree] => List[Tree]): Tree =
createMethod(original)(m => gen.mkMethodCall(newMethod, transformArgs(m.paramss.head map Ident)))
def createSwitchMethod(name: Name, range: Seq[Int], returnType: Type)(f: Int => Tree) = {
createMethod(name, List(IntTpe), returnType) { m =>
val arg0 = Ident(m.firstParam)
val default = DEFAULT ==> Throw(IndexOutOfBoundsExceptionClass.tpe_*, fn(arg0, nme.toString_))
val cases = range.map(num => CASE(LIT(num)) ==> f(num)).toList :+ default
Match(arg0, cases)
}
}
// def foo() = constant
def constantMethod(name: Name, value: Any): Tree = {
val constant = Constant(value)
createMethod(name, Nil, constant.tpe)(_ => Literal(constant))
}
// def foo = constant
def constantNullary(name: Name, value: Any): Tree = {
val constant = Constant(value)
createMethod(name, constant.tpe)(_ => Literal(constant))
}
}
/** There are two key methods in here.
*
* 1) Enter methods such as enterGetterSetter are called
* from Namer with a tree which may generate further trees such as accessors or
* implicit wrappers. Some setup is performed. In general this creates symbols
* and enters them into the scope of the owner.
*
* 2) addDerivedTrees 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 the enter methods. Those trees then become
* part of the typed template.
*/
trait MethodSynth {
self: Namer =>
import NamerErrorGen._
import treeInfo.noFieldFor
// populate synthetics for this unit with trees that will later be added by the typer
// we get here when entering the symbol for the valdef, so its rhs has not yet been type checked
def enterGetterSetter(tree: ValDef): Unit = {
val fieldSym =
if (noFieldFor(tree, owner)) NoSymbol
else owner.newValue(tree.name append NameTransformer.LOCAL_SUFFIX_STRING, tree.pos, tree.mods.flags & FieldFlags | PrivateLocal)
val getter = Getter(tree)
val getterSym = getter.createSym
// 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
//
// SI-10009 the tree's modifiers can be temporarily out of sync with the new symbol's flags.
// typedValDef corrects this later on.
tree.symbol = fieldSym orElse (getterSym setPos tree.pos)
val namer = namerOf(tree.symbol)
// the valdef gets the accessor symbol for a lazy val (too much going on in its RHS)
// the fields phase creates the field symbol
if (!tree.mods.isLazy) {
// if there's a field symbol, the getter is considered a synthetic that must be added later
// if there's no field symbol, the ValDef tree receives the getter symbol and thus is not a synthetic
if (fieldSym != NoSymbol) {
context.unit.synthetics(getterSym) = getter.derivedTree(getterSym)
getterSym setInfo new namer.AccessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = false)
} else getterSym setInfo new namer.ValTypeCompleter(tree)
enterInScope(getterSym)
if (getter.needsSetter) {
val setter = Setter(tree)
val setterSym = setter.createSym
context.unit.synthetics(setterSym) = setter.derivedTree(setterSym)
setterSym setInfo new namer.AccessorTypeCompleter(tree, tree.tpt.isEmpty, isBean = false, isSetter = true)
enterInScope(setterSym)
}
// TODO: delay emitting the field to the fields phase (except for private[this] vals, which only get a field and no accessors)
if (fieldSym != NoSymbol) {
fieldSym setInfo new namer.ValTypeCompleter(tree)
enterInScope(fieldSym)
}
} else {
getterSym setInfo new namer.ValTypeCompleter(tree)
enterInScope(getterSym)
}
deriveBeanAccessors(tree, namer)
}
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 = new namer.AccessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = false)
enterInScope(deriveBeanAccessor(if (hasBeanProperty) "get" else "is") setInfo getterCompleter)
if (tree.mods.isMutable) {
val setterCompleter = new namer.AccessorTypeCompleter(tree, missingTpt, isBean = true, isSetter = true)
enterInScope(deriveBeanAccessor("set") setInfo setterCompleter)
}
}
}
def enterImplicitWrapper(classDef: ClassDef): Unit = {
val methDef = factoryMeth(classDef.mods & AccessFlags | METHOD | IMPLICIT | SYNTHETIC, classDef.name.toTermName, classDef)
val methSym = enterInScope(assignMemberSymbol(methDef))
context.unit.synthetics(methSym) = methDef
methSym setInfo implicitFactoryMethodCompleter(methDef, classDef.symbol)
}
trait DerivedAccessor {
def tree: ValDef
def derivedName: TermName
def derivedFlags: Long
def derivedTree(sym: Symbol): Tree
def derivedPos = tree.pos.focus
def createSym = createMethod(tree, derivedName, derivedPos, derivedFlags)
}
case class Getter(tree: ValDef) extends DerivedAccessor {
def derivedName = tree.name
def derivedFlags = tree.mods.flags & GetterFlags | ACCESSOR.toLong | ( if (needsSetter) 0 else STABLE )
def needsSetter = tree.mods.isMutable // implies !lazy
override def derivedTree(derivedSym: Symbol) = {
val missingTpt = tree.tpt.isEmpty
val tpt = if (missingTpt) TypeTree() else tree.tpt.duplicate
val rhs =
if (noFieldFor(tree, owner)) tree.rhs // context.unit.transformed.getOrElse(tree.rhs, tree.rhs)
else Select(This(tree.symbol.enclClass), tree.symbol)
newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = Nil, tpt = tpt)
}
// derivedSym setPos tree.pos
// // 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
// tpt.setPos(tree.tpt.pos)
// tree.tpt.setPos(tree.tpt.pos.focus)
}
case class Setter(tree: ValDef) extends DerivedAccessor {
def derivedName = tree.setterName
def derivedFlags = tree.mods.flags & SetterFlags | ACCESSOR
def derivedTree(derivedSym: Symbol) = {
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 missingTpt = tree.tpt.isEmpty
val tptToPatch = if (missingTpt) TypeTree() else tree.tpt.duplicate
val vparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), setterParam, tptToPatch, EmptyTree))
val tpt = TypeTree(UnitTpe)
val rhs =
if (noFieldFor(tree, owner)) EmptyTree
else Assign(Select(This(tree.symbol.enclClass), tree.symbol), Ident(setterParam))
newDefDef(derivedSym, rhs)(tparams = Nil, vparamss = List(vparams), tpt = tpt)
}
}
}
}