package dotty.tools
package dotc
package ast
import core._
import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import CheckTrees._, Denotations._
object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Modifiers(sym: Symbol)(implicit ctx: Context): Modifiers = Modifiers(
sym.flags & ModifierFlags,
if (sym.privateWithin.exists) sym.privateWithin.asType.name else tpnme.EMPTY,
sym.annotations map (_.tree))
def Ident(tp: NamedType)(implicit ctx: Context): Ident =
underlyingIfRepeated(untpd.Ident(tp.name) withType tp).checked
def Select(qualifier: Tree, name: Name)(implicit ctx: Context): Select =
Select(qualifier, NamedType(qualifier.tpe, name))
def Select(pre: Tree, tp: NamedType)(implicit ctx: Context): Select =
untpd.Select(pre, tp.name).withType(tp).checked
def SelectWithSig(qualifier: Tree, name: Name, sig: Signature)(implicit ctx: Context) =
untpd.SelectWithSig(qualifier, name, sig)
.withType(TermRef.withSig(qualifier.tpe, name.asTermName, sig))
def This(cls: ClassSymbol)(implicit ctx: Context): This =
untpd.This(cls.name).withType(cls.thisType).checked
def Super(qual: Tree, mix: TypeName)(implicit ctx: Context): Super = {
val owntype =
if (mix.isEmpty) ctx.typeComparer.glb(qual.tpe.parents)
else {
val mixParents = qual.tpe.parents filter (_.name == mix)
check(mixParents.length == 1)
mixParents.head
}
untpd.Super(qual, mix).withType(SuperType(qual.tpe, owntype)).checked
}
def Apply(fn: Tree, args: List[Tree])(implicit ctx: Context): Apply = {
val owntype = fn.tpe.widen match {
case fntpe @ MethodType(pnames, ptypes) =>
check(sameLength(ptypes, args), s"${fn.show}: ${fntpe.show} to ${args.map(_.show).mkString(", ")}")
fntpe.instantiate(args map (_.tpe))
case t =>
check(false, s"fn = $fn, args = $args, tp = $t")
ErrorType
}
untpd.Apply(fn, args).withType(owntype).checked
}
def TypeApply(fn: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = {
val owntype = fn.tpe.widen match {
case fntpe @ PolyType(pnames) =>
check(sameLength(pnames, args))
fntpe.instantiate(args map (_.tpe))
case _ =>
check(false)
ErrorType
}
untpd.TypeApply(fn, args).withType(owntype).checked
}
def Literal(const: Constant)(implicit ctx: Context): Literal =
untpd.Literal(const).withType(const.tpe).checked
def unitLiteral(implicit ctx: Context): Literal =
Literal(Constant(()))
def New(tpt: Tree)(implicit ctx: Context): New =
untpd.New(tpt).withType(tpt.tpe).checked
def New(tp: Type)(implicit ctx: Context): New = New(TypeTree(tp))
def Pair(left: Tree, right: Tree)(implicit ctx: Context): Pair =
untpd.Pair(left, right).withType(defn.PairType.appliedTo(left.tpe, right.tpe)).checked
def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed =
untpd.Typed(expr, tpt).withType(tpt.tpe).checked
def NamedArg(name: Name, arg: Tree)(implicit ctx: Context) =
untpd.NamedArg(name, arg).withType(arg.tpe).checked
def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign =
untpd.Assign(lhs, rhs).withType(defn.UnitType).checked
def Block(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block =
untpd.Block(stats, expr).withType(blockType(stats, expr.tpe)).checked
def blockType(stats: List[Tree], exprType: Type)(implicit ctx: Context): Type = {
lazy val locals = localSyms(stats).toSet
def widen(tp: Type): Type = tp match {
case tp: TermRef if locals contains tp.symbol =>
widen(tp.info)
case _ => tp
}
widen(exprType)
}
def maybeBlock(stats: List[Tree], expr: Tree)(implicit ctx: Context): Tree =
if (stats.isEmpty) expr else Block(stats, expr)
def If(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If =
untpd.If(cond, thenp, elsep).withType(thenp.tpe | elsep.tpe).checked
def Closure(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = {
val ownType = if (tpt.isEmpty) meth.tpe.widen.toFunctionType else tpt.tpe
untpd.Closure(env, meth, tpt).withType(ownType).checked
}
/** A function def
*
* vparams => expr
*
* gets expanded to
*
* { def $anonfun(vparams) = expr; Closure($anonfun) }
*
* where the closure's type is the target type of the expression (FunctionN, unless
* otherwise specified).
*/
def Closure(meth: TermSymbol, bodyFn: List[Tree] => Tree, targetType: Type = NoType)(implicit ctx: Context): Block = {
val rhsFn: List[List[Tree]] => Tree = { case args :: Nil => bodyFn(args) }
val targetTpt = if (targetType.exists) TypeTree(targetType) else EmptyTree
Block(
DefDef(meth, rhsFn) :: Nil,
Closure(Nil, Ident(TermRef.withSym(NoPrefix, meth)), targetTpt))
}
def Match(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match =
untpd.Match(selector, cases).withType(ctx.typeComparer.lub(cases.tpes)).checked
def CaseDef(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef =
untpd.CaseDef(pat, guard, body).withType(body.tpe).checked
def Return(expr: Tree, from: Tree)(implicit ctx: Context): Return =
untpd.Return(expr, from).withType(defn.NothingType).checked
def Try(block: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try =
untpd.Try(block, handler, finalizer).withType(block.tpe | handler.tpe).checked
def Throw(expr: Tree)(implicit ctx: Context): Throw =
untpd.Throw(expr).withType(defn.NothingType).checked
def SeqLiteral(elems: List[Tree])(implicit ctx: Context): SeqLiteral =
untpd.SeqLiteral(elems)
.withType(defn.SeqClass.typeConstructor.appliedTo(ctx.typeComparer.lub(elems.tpes)))
.checked
def SeqLiteral(tpe: Type, elems: List[Tree])(implicit ctx: Context): SeqLiteral = {
val untpdSeqLit =
if (tpe derivesFrom defn.SeqClass) untpd.SeqLiteral(elems)
else untpd.JavaSeqLiteral(elems)
untpdSeqLit.withType(tpe).checked
}
def JavaSeqLiteral(elems: List[Tree])(implicit ctx: Context): SeqLiteral =
new untpd.JavaSeqLiteral(elems)
.withType(defn.ArrayClass.typeConstructor.appliedTo(ctx.typeComparer.lub(elems.tpes)))
.checked
def TypeTree(original: Tree)(implicit ctx: Context): TypeTree =
TypeTree(original.tpe, original)
def TypeTree(tp: Type, original: Tree = EmptyTree)(implicit ctx: Context): TypeTree =
untpd.TypeTree(original).withType(tp).checked
def SingletonTypeTree(ref: Tree)(implicit ctx: Context): SingletonTypeTree =
untpd.SingletonTypeTree(ref).withType(ref.tpe).checked
def SelectFromTypeTree(qualifier: Tree, name: Name)(implicit ctx: Context): SelectFromTypeTree =
SelectFromTypeTree(qualifier, NamedType(qualifier.tpe, name))
def SelectFromTypeTree(qualifier: Tree, tp: NamedType)(implicit ctx: Context): SelectFromTypeTree =
untpd.SelectFromTypeTree(qualifier, tp.name).withType(tp).checked
def AndTypeTree(left: Tree, right: Tree)(implicit ctx: Context): AndTypeTree =
untpd.AndTypeTree(left, right).withType(left.tpe & right.tpe).checked
def OrTypeTree(left: Tree, right: Tree)(implicit ctx: Context): OrTypeTree =
untpd.OrTypeTree(left, right).withType(left.tpe | right.tpe).checked
def RefinedTypeTree(tpt: Tree, refinements: List[Tree])(implicit ctx: Context): RefinedTypeTree = {
def refineType(tp: Type, refinement: Symbol): Type =
RefinedType(tp, refinement.name, refinement.info)
untpd.RefinedTypeTree(tpt, refinements)
.withType((tpt.tpe /: (refinements map (_.symbol)))(refineType)).checked
}
def refineType(tp: Type, refinement: Symbol)(implicit ctx: Context): Type =
RefinedType(tp, refinement.name, refinement.info)
def AppliedTypeTree(tpt: Tree, args: List[Tree])(implicit ctx: Context): AppliedTypeTree =
untpd.AppliedTypeTree(tpt, args).withType(tpt.tpe.appliedTo(args map (_.tpe))).checked
def TypeBoundsTree(lo: Tree, hi: Tree)(implicit ctx: Context): TypeBoundsTree =
untpd.TypeBoundsTree(lo, hi).withType(TypeBounds(lo.tpe, hi.tpe)).checked
def Bind(sym: TermSymbol, body: Tree)(implicit ctx: Context): Bind =
untpd.Bind(sym.name, body).withType(refType(sym)).checked
def Alternative(trees: List[Tree])(implicit ctx: Context): Alternative =
untpd.Alternative(trees).withType(ctx.typeComparer.lub(trees map (_.tpe))).checked
def UnApply(fun: Tree, args: List[Tree])(implicit ctx: Context): UnApply = {
val owntype = fun.tpe.widen match {
case MethodType(_, paramType :: Nil) => paramType
case _ => check(false); ErrorType
}
untpd.UnApply(fun, args).withType(owntype).checked
}
def ValDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): ValDef =
untpd.ValDef(Modifiers(sym), sym.name, TypeTree(sym.info), rhs).withType(refType(sym)).checked
def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef =
DefDef(sym, Function.const(rhs) _)
def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = {
val (tparams, mtp) = sym.info match {
case tp: PolyType =>
val tparams = ctx.newTypeParams(sym, tp.paramNames, EmptyFlags, tp.instantiateBounds)
(tparams, tp.instantiate(tparams map (_.typeConstructor)))
case tp => (Nil, tp)
}
def valueParamss(tp: Type): (List[List[TermSymbol]], Type) = tp match {
case tp @ MethodType(paramNames, paramTypes) =>
def valueParam(name: TermName, info: Type): TermSymbol =
ctx.newSymbol(sym, name, TermParam, info)
val params = (paramNames, paramTypes).zipped.map(valueParam)
val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.symRef)))
(params :: paramss, rtp)
case tp => (Nil, tp)
}
val (vparamss, rtp) = valueParamss(mtp)
val argss = vparamss map (_ map (vparam => Ident(vparam.symRef)))
untpd.DefDef(
Modifiers(sym), sym.name, tparams map TypeDef,
vparamss map (_ map (ValDef(_))), TypeTree(rtp), rhsFn(argss))
.withType(refType(sym)).checked
}
def TypeDef(sym: TypeSymbol)(implicit ctx: Context): TypeDef =
untpd.TypeDef(Modifiers(sym), sym.name, TypeTree(sym.info))
.withType(refType(sym)).checked
def ClassDef(cls: ClassSymbol, constr: DefDef, body: List[Tree])(implicit ctx: Context): TypeDef = {
val parents = cls.info.parents map (TypeTree(_))
val selfType =
if (cls.classInfo.selfInfo ne NoType) ValDef(ctx.newSelfSym(cls))
else EmptyValDef
def isOwnTypeParam(stat: Tree) =
(stat.symbol is TypeParam) && stat.symbol.owner == cls
val bodyTypeParams = body filter isOwnTypeParam map (_.symbol)
val newTypeParams =
for (tparam <- cls.typeParams if !(bodyTypeParams contains tparam))
yield TypeDef(tparam)
val findLocalDummy = new FindLocalDummyAccumulator(cls)
val localDummy = ((NoSymbol: Symbol) /: body)(findLocalDummy)
.orElse(ctx.newLocalDummy(cls))
val impl = untpd.Template(constr, parents, selfType, newTypeParams ++ body)
.withType(refType(localDummy)).checked
untpd.TypeDef(Modifiers(cls), cls.name, impl)
.withType(refType(cls)).checked
}
def Import(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import =
untpd.Import(expr, selectors).withType(refType(ctx.newImportSymbol(SharedTree(expr)))).checked
def PackageDef(pid: RefTree, stats: List[Tree])(implicit ctx: Context): PackageDef =
untpd.PackageDef(pid, stats).withType(refType(pid.symbol)).checked
def Annotated(annot: Tree, arg: Tree)(implicit ctx: Context): Annotated =
untpd.Annotated(annot, arg).withType(AnnotatedType(Annotation(annot), arg.tpe)).checked
def SharedTree(tree: Tree)(implicit ctx: Context): SharedTree =
Trees.SharedTree(tree).withType(tree.tpe)
// ------ Making references ------------------------------------------------------
def refType(sym: Symbol)(implicit ctx: Context): NamedType = NamedType.withSym(sym.owner.thisType, sym)
/** A tree representing the same reference as the given type */
def ref(tp: NamedType)(implicit ctx: Context): NameTree =
if (tp.symbol.isStatic) Ident(tp)
else tp.prefix match {
case pre: TermRef => Select(ref(pre), tp)
case pre => SelectFromTypeTree(TypeTree(pre), tp)
} // no checks necessary
def ref(sym: Symbol)(implicit ctx: Context): tpd.NameTree =
ref(NamedType(sym.owner.thisType, sym.name).withDenot(sym))
// ----- Converting to releated trees -----------------------------------------------
def underlyingIfRepeated(id: Ident)(implicit ctx: Context): Ident =
if (id.isType) id else id withType id.tpe.underlyingIfRepeated
def seqToRepeated(tree: Tree)(implicit ctx: Context): Tree =
Typed(tree, TypeTree(tree.tpe.translateParameterized(defn.SeqClass, defn.RepeatedParamClass)))
// ------ Creating typed equivalents of trees that exist only in untyped form -------
/** new C(args) */
def New(tp: Type, args: List[Tree])(implicit ctx: Context): Apply = {
val targs = tp.typeArgs
Apply(
Select(
New(tp withoutArgs targs),
TermRef.withSym(tp.normalizedPrefix, tp.typeSymbol.primaryConstructor.asTerm))
.appliedToTypes(targs),
args)
}
/** An object def
*
* object obs extends parents { decls }
*
* gets expanded to
*
* <module> lazy val obj = new obj$
* <module> class obj$ extends parents { this: obj.type => decls }
*
* (The following no longer applies:
* What's interesting here is that the block is well typed
* (because class obj$ is hoistable), but the type of the `obj` val is
* not expressible. What needs to happen in general when
* inferring the type of a val from its RHS, is: if the type contains
* a class that has the val itself as owner, then that class
* is remapped to have the val's owner as owner. Remapping could be
* done by cloning the class with the new owner and substituting
* everywhere in the tree. We know that remapping is safe
* because the only way a local class can appear in the RHS of a val is
* by being hoisted outside of a block, and the necessary checks are
* done at this point already.
*
* On the other hand, for method result type inference, if the type of
* the RHS of a method contains a class owned by the method, this would be
* an error.)
*/
def ModuleDef(sym: TermSymbol, body: List[Tree])(implicit ctx: Context): tpd.Thicket = {
val modcls = sym.moduleClass.asClass
val constr = DefDef(modcls.primaryConstructor.asTerm, EmptyTree)
val clsdef = ClassDef(modcls, constr, body)
val valdef = ValDef(sym, New(modcls.typeConstructor))
Thicket(valdef, clsdef)
}
private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] {
def apply(sym: Symbol, tree: Tree) =
if (sym.exists) sym
else if (tree.isDef) {
val owner = tree.symbol.owner
if (owner.isLocalDummy && owner.owner == cls) owner
else if (owner == cls) foldOver(sym, tree)
else sym
} else foldOver(sym, tree)
}
override val cpy = new TypedTreeCopier
class TypedTreeCopier extends TreeCopier {
def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] =
copied.withTypeUnchecked(tree.tpe)
}
implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal {
def isValue(implicit ctx: Context): Boolean =
tree.isTerm && tree.tpe.widen.isValueType
def isValueOrPattern(implicit ctx: Context) =
tree.isValue || tree.isPattern
def isValueType: Boolean =
tree.isType && tree.tpe.isValueType
def isInstantiation: Boolean = tree match {
case Apply(Select(New(_), nme.CONSTRUCTOR), _) => true
case _ => false
}
def checked(implicit ctx: Context): ThisTree = {
if (ctx.settings.YcheckTypedTrees.value) checkType(tree)
tree
}
def shallowFold[T](z: T)(op: (T, tpd.Tree) => T) =
new ShallowFolder(op).apply(z, tree)
def deepFold[T](z: T)(op: (T, tpd.Tree) => T) =
new DeepFolder(op).apply(z, tree)
def subst(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): ThisTree =
new TreeMapper(typeMap = new ctx.SubstSymMap(from, to)).apply(tree)
def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree =
new TreeMapper(ownerMap = (sym => if (sym == from) to else sym)).apply(tree)
def appliedToTypes(targs: List[Type])(implicit ctx: Context): Tree =
if (targs.isEmpty) tree else TypeApply(tree, targs map (TypeTree(_)))
}
implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {
def tpes: List[Type] = xs map (_.tpe)
}
class TreeMapper(val typeMap: TypeMap = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity)(implicit ctx: Context) extends TreeTransformer {
override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = super.transform {
tree.withType(typeMap(tree.tpe)) match {
case bind: tpd.Bind =>
val sym = bind.symbol
val newOwner = ownerMap(sym.owner)
val newInfo = typeMap(sym.info)
if ((newOwner ne sym.owner) || (newInfo ne sym.info))
bind.withType(tpd.refType(sym.copy(owner = newOwner, info = newInfo)))
else
bind
case tree1 =>
tree1
}
}
override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = {
val locals = localSyms(trees)
val mapped = ctx.mapSymbols(locals, typeMap, ownerMap)
if (locals eq mapped) super.transform(trees)
else withSubstitution(locals, mapped).transform(trees)
}
def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree]
def apply(annot: Annotation): Annotation = {
val tree1 = apply(annot.tree)
if (tree1 eq annot.tree) annot else ConcreteAnnotation(tree1)
}
/** The current tree map composed with a substitution [from -> to] */
def withSubstitution(from: List[Symbol], to: List[Symbol]) =
new TreeMapper(
typeMap andThen ((tp: Type) => tp.substSym(from, to)),
ownerMap andThen (from zip to).toMap)
}
// ensure that constructors are fully applied?
// ensure that normal methods are fully applied?
def localSyms(stats: List[tpd.Tree])(implicit ctx: Context): List[Symbol] =
for (stat <- stats if stat.isDef) yield stat.symbol
}