package dotty.tools.dotc
package transform
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
import dotty.tools.dotc.ast.{Trees, tpd, untpd}
import scala.collection.{ mutable, immutable }
import ValueClasses._
import scala.annotation.tailrec
import core._
import typer.ErrorReporting._
import typer.Checking
import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._, DenotTransformers._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Scopes._, Denotations._
import util.Positions._
import Decorators._
import config.Printers.typr
import Symbols._, TypeUtils._
/** A macro transform that runs immediately after typer and that performs the following functions:
*
* (1) Add super accessors and protected accessors (@see SuperAccessors)
*
* (2) Convert parameter fields that have the same name as a corresponding
* public parameter field in a superclass to a forwarder to the superclass
* field (corresponding = super class field is initialized with subclass field)
* (@see ForwardParamAccessors)
*
* (3) Add synthetic methods (@see SyntheticMethods)
*
* (4) Check that `New` nodes can be instantiated, and that annotations are valid
*
* (5) Convert all trees representing types to TypeTrees.
*
* (6) Check the bounds of AppliedTypeTrees
*
* (7) Insert `.package` for selections of package object members
*
* (8) Replaces self references by name with `this`
*
* (9) Adds SourceFile annotations to all top-level classes and objects
*
* (10) Adds Child annotations to all sealed classes
*
* (11) Minimizes `call` fields of `Inline` nodes to just point to the toplevel
* class from which code was inlined.
*
* The reason for making this a macro transform is that some functions (in particular
* super and protected accessors and instantiation checks) are naturally top-down and
* don't lend themselves to the bottom-up approach of a mini phase. The other two functions
* (forwarding param accessors and synthetic methods) only apply to templates and fit
* mini-phase or subfunction of a macro phase equally well. But taken by themselves
* they do not warrant their own group of miniphases before pickling.
*/
class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTransformer =>
import tpd._
/** the following two members override abstract members in Transform */
override def phaseName: String = "posttyper"
override def transformPhase(implicit ctx: Context) = thisTransformer.next
protected def newTransformer(implicit ctx: Context): Transformer =
new PostTyperTransformer
val superAcc = new SuperAccessors(thisTransformer)
val paramFwd = new ParamForwarding(thisTransformer)
val synthMth = new SyntheticMethods(thisTransformer)
private def newPart(tree: Tree): Option[New] = methPart(tree) match {
case Select(nu: New, _) => Some(nu)
case _ => None
}
private def checkValidJavaAnnotation(annot: Tree)(implicit ctx: Context): Unit = {
// TODO fill in
}
/** If the type of `tree` is a TermRefWithSignature with an underdefined
* signature, narrow the type by re-computing the signature (which should
* be fully-defined by now).
*/
private def fixSignature[T <: Tree](tree: T)(implicit ctx: Context): T = tree.tpe match {
case tpe: TermRefWithSignature if tpe.signature.isUnderDefined =>
typr.println(i"fixing $tree with type ${tree.tpe.widen.toString} with sig ${tpe.signature} to ${tpe.widen.signature}")
tree.withType(TermRef.withSig(tpe.prefix, tpe.name, tpe.widen.signature)).asInstanceOf[T]
case _ => tree
}
class PostTyperTransformer extends Transformer {
private var inJavaAnnot: Boolean = false
private var parentNews: Set[New] = Set()
private def transformAnnot(annot: Tree)(implicit ctx: Context): Tree = {
val saved = inJavaAnnot
inJavaAnnot = annot.symbol is JavaDefined
if (inJavaAnnot) checkValidJavaAnnotation(annot)
try transform(annot)
finally inJavaAnnot = saved
}
private def transformAnnot(annot: Annotation)(implicit ctx: Context): Annotation =
annot.derivedAnnotation(transformAnnot(annot.tree))
private def registerChild(sym: Symbol, tp: Type)(implicit ctx: Context) = {
val cls = tp.classSymbol
if (cls.is(Sealed)) cls.addAnnotation(Annotation.makeChild(sym))
}
private def transformMemberDef(tree: MemberDef)(implicit ctx: Context): Unit = {
val sym = tree.symbol
if (sym.is(CaseVal, butNot = Method | Module)) registerChild(sym, sym.info)
sym.transformAnnotations(transformAnnot)
}
private def transformSelect(tree: Select, targs: List[Tree])(implicit ctx: Context): Tree = {
val qual = tree.qualifier
qual.symbol.moduleClass.denot match {
case pkg: PackageClassDenotation if !tree.symbol.maybeOwner.is(Package) =>
transformSelect(cpy.Select(tree)(qual select pkg.packageObj.symbol, tree.name), targs)
case _ =>
val tree1 = super.transform(tree)
constToLiteral(tree1) match {
case _: Literal => tree1
case _ => superAcc.transformSelect(tree1, targs)
}
}
}
private def normalizeTypeArgs(tree: TypeApply)(implicit ctx: Context): TypeApply = tree.tpe match {
case pt: PolyType => // wait for more arguments coming
tree
case _ =>
def decompose(tree: TypeApply): (Tree, List[Tree]) = tree.fun match {
case fun: TypeApply =>
val (tycon, args) = decompose(fun)
(tycon, args ++ tree.args)
case _ =>
(tree.fun, tree.args)
}
def reorderArgs(pnames: List[Name], namedArgs: List[NamedArg], otherArgs: List[Tree]): List[Tree] = pnames match {
case pname :: pnames1 =>
namedArgs.partition(_.name == pname) match {
case (NamedArg(_, arg) :: _, namedArgs1) =>
arg :: reorderArgs(pnames1, namedArgs1, otherArgs)
case _ =>
val otherArg :: otherArgs1 = otherArgs
otherArg :: reorderArgs(pnames1, namedArgs, otherArgs1)
}
case nil =>
assert(namedArgs.isEmpty && otherArgs.isEmpty)
Nil
}
val (tycon, args) = decompose(tree)
tycon.tpe.widen match {
case tp: PolyType =>
val (namedArgs, otherArgs) = args.partition(isNamedArg)
val args1 = reorderArgs(tp.paramNames, namedArgs.asInstanceOf[List[NamedArg]], otherArgs)
TypeApply(tycon, args1).withPos(tree.pos).withType(tree.tpe)
case _ =>
tree
}
}
override def transform(tree: Tree)(implicit ctx: Context): Tree =
try tree match {
case tree: Ident if !tree.isType =>
tree.tpe match {
case tpe: ThisType => This(tpe.cls).withPos(tree.pos)
case _ => paramFwd.adaptRef(fixSignature(tree))
}
case tree @ Select(qual, name) =>
if (name.isTypeName) {
Checking.checkRealizable(qual.tpe, qual.pos.focus)
super.transform(tree)
}
else
transformSelect(paramFwd.adaptRef(fixSignature(tree)), Nil)
case tree: Super =>
if (ctx.owner.enclosingMethod.isInlineMethod)
ctx.error(em"super not allowed in inline ${ctx.owner}", tree.pos)
super.transform(tree)
case tree: TypeApply =>
val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
fn match {
case sel: Select =>
val args1 = transform(args)
val sel1 = transformSelect(sel, args1)
if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree1)(sel1, args1)
case _ =>
super.transform(tree1)
}
case tree @ Assign(sel: Select, _) =>
superAcc.transformAssign(super.transform(tree))
case Inlined(call, bindings, expansion) =>
// Leave only a call trace consisting of
// - a reference to the top-level class from which the call was inlined,
// - the call's position
// in the call field of an Inlined node.
// The trace has enough info to completely reconstruct positions.
// The minimization is done for two reasons:
// 1. To save space (calls might contain large inline arguments, which would otherwise
// be duplicated
// 2. To enable correct pickling (calls can share symbols with the inlined code, which
// would trigger an assertion when pickling).
val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion))
case tree: Template =>
val saved = parentNews
parentNews ++= tree.parents.flatMap(newPart)
try {
val templ1 = paramFwd.forwardParamAccessors(tree)
synthMth.addSyntheticMethods(
superAcc.wrapTemplate(templ1)(
super.transform(_).asInstanceOf[Template]))
}
finally parentNews = saved
case tree: DefDef =>
transformMemberDef(tree)
superAcc.wrapDefDef(tree)(super.transform(tree).asInstanceOf[DefDef])
case tree: TypeDef =>
transformMemberDef(tree)
val sym = tree.symbol
if (sym.isClass) {
// Add SourceFile annotation to top-level classes
if (sym.owner.is(Package) &&
ctx.compilationUnit.source.exists &&
sym != defn.SourceFileAnnot)
sym.addAnnotation(Annotation.makeSourceFile(ctx.compilationUnit.source.file.path))
// Add Child annotation to sealed parents unless current class is anonymous
if (!sym.isAnonymousClass) // ignore anonymous class
sym.asClass.classInfo.classParents.foreach { parent =>
val sym2 = if (sym.is(Module)) sym.sourceModule else sym
registerChild(sym2, parent)
}
tree
}
super.transform(tree)
case tree: MemberDef =>
transformMemberDef(tree)
super.transform(tree)
case tree: New if !inJavaAnnot && !parentNews.contains(tree) =>
Checking.checkInstantiable(tree.tpe, tree.pos)
super.transform(tree)
case tree @ Annotated(annotated, annot) =>
cpy.Annotated(tree)(transform(annotated), transformAnnot(annot))
case tree: AppliedTypeTree =>
Checking.checkAppliedType(tree)
super.transform(tree)
case SingletonTypeTree(ref) =>
Checking.checkRealizable(ref.tpe, ref.pos.focus)
super.transform(tree)
case tree: TypeTree =>
tree.withType(
tree.tpe match {
case AnnotatedType(tpe, annot) => AnnotatedType(tpe, transformAnnot(annot))
case tpe => tpe
}
)
case Import(expr, selectors) =>
val exprTpe = expr.tpe
def checkIdent(ident: untpd.Ident): Unit = {
val name = ident.name.asTermName.encode
if (name != nme.WILDCARD && !exprTpe.member(name).exists && !exprTpe.member(name.toTypeName).exists)
ctx.error(s"${ident.name} is not a member of ${expr.show}", ident.pos)
}
selectors.foreach {
case ident: untpd.Ident => checkIdent(ident)
case Thicket((ident: untpd.Ident) :: _) => checkIdent(ident)
case _ =>
}
super.transform(tree)
case tree =>
super.transform(tree)
}
catch {
case ex : AssertionError =>
println(i"error while transforming $tree")
throw ex
}
}
}