package scala.tools.nsc
package typechecker
import symtab.Flags._
trait Unapplies extends ast.TreeDSL
{
self: Analyzer =>
import global._
import definitions._
import CODE.{ CASE => _, _ }
import treeInfo.{ isRepeatedParamType, isByNameParamType }
def unapplyTypeList(ufn: Symbol, ufntpe: Type) = {
assert(ufn.isMethod)
ufn.name match {
case nme.unapply => unapplyTypeListFromReturnType(ufntpe)
case nme.unapplySeq => unapplyTypeListFromReturnTypeSeq(ufntpe)
case _ => throw new TypeError(ufn+" is not an unapply or unapplySeq")
}
}
def unapplyTypeListFromReturnType(tp1: Type): List[Type] = {
val tp = unapplyUnwrap(tp1)
tp.typeSymbol match {
case BooleanClass => Nil
case OptionClass | SomeClass =>
val prod = tp.typeArgs.head
getProductArgs(prod) match {
case Some(xs) if xs.size > 1 => xs
case _ => List(prod)
}
case _ =>
throw new TypeError("result type "+tp+" of unapply not in {Boolean, Option[_], Some[_]}")
}
}
def unapplyTypeListFromReturnTypeSeq(tp1: Type): List[Type] = {
val tp = unapplyUnwrap(tp1)
tp.typeSymbol match {
case OptionClass | SomeClass =>
val ts = unapplyTypeListFromReturnType(tp1)
val last1 = (ts.last baseType SeqClass) match {
case TypeRef(pre, SeqClass, args) => typeRef(pre, RepeatedParamClass, args)
case _ => throw new TypeError("last not seq")
}
ts.init :+ last1
case _ =>
throw new TypeError("result type "+tp+" of unapply not in {Option[_], Some[_]}")
}
}
def unapplyReturnTypeExpected(argsLength: Int) = argsLength match {
case 0 => BooleanClass.tpe
case 1 => optionType(WildcardType)
case n => optionType(productType((List fill n)(WildcardType)))
}
def unapplyMember(tp: Type): Symbol = (tp member nme.unapply) match {
case NoSymbol => tp member nme.unapplySeq
case unapp => unapp
}
def unapplyParameterType(extractor: Symbol) = extractor.tpe.params match {
case p :: Nil => p.tpe.typeSymbol
case _ => NoSymbol
}
def copyUntyped[T <: Tree](tree: T): T =
returning[T](tree.duplicate)(UnTyper traverse _)
def copyUntypedInvariant(td: TypeDef): TypeDef = {
val copy = treeCopy.TypeDef(td, td.mods &~ (COVARIANT | CONTRAVARIANT), td.name, td.tparams, td.rhs)
returning[TypeDef](copy.duplicate)(UnTyper traverse _)
}
private def toIdent(x: DefTree) = Ident(x.name) setPos x.pos.focus
private def classType(cdef: ClassDef, tparams: List[TypeDef]): Tree = {
val tycon = REF(cdef.symbol)
if (tparams.isEmpty) tycon else AppliedTypeTree(tycon, tparams map toIdent)
}
private def constrParamss(cdef: ClassDef): List[List[ValDef]] = {
val DefDef(_, _, _, vparamss, _, _) = treeInfo firstConstructor cdef.impl.body
vparamss map (_ map copyUntyped[ValDef])
}
private def caseClassUnapplyReturnValue(param: Name, caseclazz: Symbol) = {
def caseFieldAccessorValue(selector: Symbol): Tree = Ident(param) DOT selector
caseclazz.caseFieldAccessors match {
case Nil => TRUE
case xs => SOME(xs map caseFieldAccessorValue: _*)
}
}
def caseModuleDef(cdef: ClassDef): ModuleDef = {
def primaries = constrParamss(cdef).head take MaxFunctionArity map (_.tpt)
def inheritFromFun = !cdef.mods.hasAbstractFlag && cdef.tparams.isEmpty && constrParamss(cdef).length == 1
def createFun = gen.scalaFunctionConstr(primaries, toIdent(cdef), abstractFun = true)
def parents = if (inheritFromFun) List(createFun) else Nil
def toString = DefDef(
Modifiers(OVERRIDE | FINAL),
nme.toString_,
Nil,
List(Nil),
TypeTree(),
Literal(Constant(cdef.name.decode)))
companionModuleDef(cdef, parents, List(toString))
}
def companionModuleDef(cdef: ClassDef, parents: List[Tree] = Nil, body: List[Tree] = Nil): ModuleDef = atPos(cdef.pos.focus) {
val allParents = parents :+ gen.scalaScalaObjectConstr
ModuleDef(
Modifiers(cdef.mods.flags & AccessFlags | SYNTHETIC, cdef.mods.privateWithin),
cdef.name.toTermName,
Template(allParents, emptyValDef, NoMods, Nil, List(Nil), body, cdef.impl.pos.focus))
}
private val caseMods = Modifiers(SYNTHETIC | CASE)
def caseModuleApplyMeth(cdef: ClassDef): DefDef = {
val tparams = cdef.tparams map copyUntypedInvariant
val cparamss = constrParamss(cdef)
atPos(cdef.pos.focus)(
DefDef(caseMods, nme.apply, tparams, cparamss, classType(cdef, tparams),
New(classType(cdef, tparams), cparamss map (_ map gen.paramToArg)))
)
}
def caseModuleUnapplyMeth(cdef: ClassDef): DefDef = {
val tparams = cdef.tparams map copyUntypedInvariant
val paramName = newTermName("x$0")
val method = constrParamss(cdef) match {
case xs :: _ if xs.nonEmpty && isRepeatedParamType(xs.last.tpt) => nme.unapplySeq
case _ => nme.unapply
}
val cparams = List(ValDef(Modifiers(PARAM | SYNTHETIC), paramName, classType(cdef, tparams), EmptyTree))
val ifNull = if (constrParamss(cdef).head.isEmpty) FALSE else REF(NoneModule)
val body = nullSafe({ case Ident(x) => caseClassUnapplyReturnValue(x, cdef.symbol) }, ifNull)(Ident(paramName))
atPos(cdef.pos.focus)(
DefDef(caseMods, method, tparams, List(cparams), TypeTree(), body)
)
}
def caseClassCopyMeth(cdef: ClassDef): Option[DefDef] = {
def isDisallowed(vd: ValDef) = isRepeatedParamType(vd.tpt) || isByNameParamType(vd.tpt)
val cparamss = constrParamss(cdef)
val flat = cparamss flatten
if (flat.isEmpty || cdef.symbol.hasAbstractFlag || (flat exists isDisallowed)) None
else {
val tparams = cdef.tparams map copyUntypedInvariant
def paramWithDefault(vd: ValDef) =
treeCopy.ValDef(vd, vd.mods | DEFAULTPARAM, vd.name, atPos(vd.pos.focus)(TypeTree() setOriginal vd.tpt), toIdent(vd))
val paramss = cparamss map (_ map paramWithDefault)
val classTpe = classType(cdef, tparams)
Some(atPos(cdef.pos.focus)(
DefDef(Modifiers(SYNTHETIC), nme.copy, tparams, paramss, classTpe,
New(classTpe, paramss map (_ map toIdent)))
))
}
}
}