/* NSC -- new Scala compiler
* Copyright 2005-2012 LAMP/EPFL
* @author Paul Phillips
*/
package scala.tools.nsc
package typechecker
/** A generic means of breaking down types into their subcomponents.
* Types are decomposed top down, and recognizable substructure is
* dispatched via self-apparently named methods. Those methods can
* be overridden for custom behavior, but only the abstract methods
* require implementations, each of which must create some unknown
* "Node" type from its inputs.
*
* - wrapProduct create Node from a product of Nodes
* - wrapSequence create Node from a sequence of Nodes
* - wrapAtom create Node from an arbitrary value
*
* This is a work in progress.
*/
trait DestructureTypes {
val global: Global
import global._
import definitions.{ NothingClass, AnyClass }
trait DestructureType[Node] extends (Type => Node) {
def withLabel(node: Node, label: String): Node
def withType(node: Node, typeName: String): Node
def wrapEmpty: Node
def wrapPoly(in: Node, out: Node): Node
def wrapMono(in: Node, out: Node): Node
def wrapProduct(nodes: List[Node]): Node
def wrapSequence(nodes: List[Node]): Node
def wrapAtom[U](value: U): Node
private implicit def liftToTerm(name: String): TermName = newTermName(name)
private val openSymbols = collection.mutable.Set[Symbol]()
private def nodeList[T](elems: List[T], mkNode: T => Node): Node =
if (elems.isEmpty) wrapEmpty else list(elems map mkNode)
private def scopeMemberList(elems: List[Symbol]): Node = nodeList(elems, wrapAtom)
private def typeList(elems: List[Type]): Node = nodeList(elems, this)
private def symbolList(elems: List[Symbol]): Node = nodeList(elems, wrapSymbolInfo)
private def treeList(elems: List[Tree]): Node = nodeList(elems, wrapTree)
private def annotationList(annots: List[AnnotationInfo]): Node = nodeList(annots, annotation)
private def assocsNode(ann: AnnotationInfo): Node = {
val (names, args) = ann.assocs.toIndexedSeq.unzip
if (names.isEmpty) wrapEmpty
else node("assocs", nodeList(names.indices.toList, (i: Int) => atom(names(i).toString, args(i))))
}
private def typeTypeName(tp: Type) = tp match {
case mt @ MethodType(_, _) if mt.isImplicit => "ImplicitMethodType"
case TypeRef(_, sym, _) => typeRefType(sym)
case _ => tp.kind
}
def wrapTree(tree: Tree): Node = withType(
tree match {
case x: NameTree => atom(x.name.toString, x)
case _ => wrapAtom(tree)
},
tree.printingPrefix
)
def wrapSymbol(label: String, sym: Symbol): Node = {
if (sym eq NoSymbol) wrapEmpty
else atom(label, sym)
}
def wrapInfo(sym: Symbol) = sym.info match {
case TypeBounds(lo, hi) => typeBounds(lo, hi)
case PolyType(tparams, restpe) => polyFunction(tparams, restpe)
case _ => wrapEmpty
}
def wrapSymbolInfo(sym: Symbol): Node = {
if ((sym eq NoSymbol) || openSymbols(sym)) wrapEmpty
else {
openSymbols += sym
try product(symbolType(sym), wrapAtom(sym.defString))
finally openSymbols -= sym
}
}
def list(nodes: List[Node]): Node = wrapSequence(nodes)
def product(tp: Type, nodes: Node*): Node = product(typeTypeName(tp), nodes: _*)
def product(typeName: String, nodes: Node*): Node = (
nodes.toList filterNot (_ == wrapEmpty) match {
case Nil => wrapEmpty
case xs => withType(wrapProduct(xs), typeName)
}
)
def atom[U](label: String, value: U): Node = node(label, wrapAtom(value))
def constant(label: String, const: Constant): Node = atom(label, const)
def scope(decls: Scope): Node = node("decls", scopeMemberList(decls.toList))
def const[T](named: (String, T)): Node = constant(named._1, Constant(named._2))
def resultType(restpe: Type): Node = this("resultType", restpe)
def typeParams(tps: List[Symbol]): Node = node("typeParams", symbolList(tps))
def valueParams(params: List[Symbol]): Node = node("params", symbolList(params))
def typeArgs(tps: List[Type]): Node = node("args", typeList(tps))
def parentList(tps: List[Type]): Node = node("parents", typeList(tps))
def polyFunction(tparams: List[Symbol], restpe: Type): Node = wrapPoly(typeParams(tparams), resultType(restpe))
def monoFunction(params: List[Symbol], restpe: Type): Node = wrapMono(valueParams(params), resultType(restpe))
def nullaryFunction(restpe: Type): Node = wrapMono(wrapEmpty, this(restpe))
def prefix(pre: Type): Node = pre match {
case NoPrefix => wrapEmpty
case _ => this("pre", pre)
}
def typeBounds(lo0: Type, hi0: Type): Node = {
val lo = if ((lo0 eq WildcardType) || (lo0.typeSymbol eq NothingClass)) wrapEmpty else this("lo", lo0)
val hi = if ((hi0 eq WildcardType) || (hi0.typeSymbol eq AnyClass)) wrapEmpty else this("hi", hi0)
product("TypeBounds", lo, hi)
}
def annotation(ann: AnnotationInfo): Node = product(
"AnnotationInfo",
this("atp", ann.atp),
node("args", treeList(ann.args)),
assocsNode(ann)
)
def typeConstraint(constr: TypeConstraint): Node = product(
"TypeConstraint",
node("lo", typeList(constr.loBounds)),
node("hi", typeList(constr.hiBounds)),
this("inst", constr.inst)
)
def annotatedType(annotations: List[AnnotationInfo], underlying: Type) = product(
"AnnotatedType",
node("annotations", annotationList(annotations)),
this("underlying", underlying)
)
/** This imposes additional structure beyond that which is visible in
* the case class hierarchy. In particular, (too) many different constructs
* are encoded in TypeRefs; here they are partitioned somewhat before
* being dispatched.
*
* For example, a typical type parameter is encoded as TypeRef(NoPrefix, sym, Nil)
* with its upper and lower bounds stored in the info of the symbol. Viewing the
* TypeRef naively we are treated to both too much information (useless prefix, usually
* empty args) and too little (bounds hidden behind indirection.) So drop the prefix
* and promote the bounds.
*/
def typeRef(tp: TypeRef) = {
val TypeRef(pre, sym, args) = tp
// Filtered down to elements with "interesting" content
product(
tp,
if (sym.isDefinedInPackage) wrapEmpty else prefix(pre),
wrapSymbolInfo(sym),
typeArgs(args),
if (tp ne tp.normalize) this("normalize", tp.normalize) else wrapEmpty
)
}
def symbolType(sym: Symbol) = (
if (sym.isRefinementClass) "Refinement"
else if (sym.isAliasType) "Alias"
else if (sym.isTypeSkolem) "TypeSkolem"
else if (sym.isTypeParameter) "TypeParam"
else if (sym.isAbstractType) "AbstractType"
else if (sym.isType) "TypeSymbol"
else "TermSymbol"
)
def typeRefType(sym: Symbol) = (
if (sym.isRefinementClass) "RefinementTypeRef"
else if (sym.isAliasType) "AliasTypeRef"
else if (sym.isTypeSkolem) "SkolemTypeRef"
else if (sym.isTypeParameter) "TypeParamTypeRef"
else if (sym.isAbstractType) "AbstractTypeRef"
else "TypeRef"
) + ( if (sym.isFBounded) "(F-Bounded)" else "" )
def node(label: String, node: Node): Node = withLabel(node, label)
def apply(label: String, tp: Type): Node = withLabel(this(tp), label)
def apply(tp: Type): Node = tp match {
case AntiPolyType(pre, targs) => product(tp, prefix(pre), typeArgs(targs))
case ClassInfoType(parents, decls, clazz) => product(tp, parentList(parents), scope(decls), wrapAtom(clazz))
case ConstantType(const) => product(tp, constant("value", const))
case DeBruijnIndex(level, index, args) => product(tp, const("level" -> level), const("index" -> index), typeArgs(args))
case OverloadedType(pre, alts) => product(tp, prefix(pre), node("alts", typeList(alts map pre.memberType)))
case RefinedType(parents, decls) => product(tp, parentList(parents), scope(decls))
case SingleType(pre, sym) => product(tp, prefix(pre), wrapAtom(sym))
case SuperType(thistp, supertp) => product(tp, this("this", thistp), this("super", supertp))
case ThisType(clazz) => product(tp, wrapAtom(clazz))
case TypeVar(inst, constr) => product(tp, this("inst", inst), typeConstraint(constr))
case AnnotatedType(annotations, underlying, _) => annotatedType(annotations, underlying)
case ExistentialType(tparams, underlying) => polyFunction(tparams, underlying)
case PolyType(tparams, restpe) => polyFunction(tparams, restpe)
case MethodType(params, restpe) => monoFunction(params, restpe)
case NullaryMethodType(restpe) => nullaryFunction(restpe)
case TypeBounds(lo, hi) => typeBounds(lo, hi)
case tr @ TypeRef(pre, sym, args) => typeRef(tr)
case _ => wrapAtom(tp) // XXX see what this is
}
}
}