package dotty.tools.dotc
package transform
import core._
import Names._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Phases.NeedsCompanions
import dotty.tools.dotc.transform.TreeTransforms._
import ast.Trees._
import Flags._
import Types._
import Constants.Constant
import Contexts.Context
import Symbols._
import SymDenotations._
import Decorators._
import dotty.tools.dotc.core.Annotations.ConcreteAnnotation
import dotty.tools.dotc.core.Denotations.SingleDenotation
import scala.collection.mutable
import DenotTransformers._
import typer.Checking
import NameOps._
import NameKinds.{AvoidClashName, OuterSelectName}
import StdNames._
/** The first tree transform
* - ensures there are companion objects for all classes except module classes
* - eliminates some kinds of trees: Imports, NamedArgs
* - stubs out native methods
* - eliminates self tree in Template and self symbol in ClassInfo
* - collapses all type trees to trees of class TypeTree
* - converts idempotent expressions with constant types
* - drops branches of ifs using the rules
* if (true) A else B --> A
* if (false) A else B --> B
*/
class FirstTransform extends MiniPhaseTransform with InfoTransformer with AnnotationTransformer { thisTransformer =>
import ast.tpd._
override def phaseName = "firstTransform"
private var addCompanionPhases: List[NeedsCompanions] = _
def needsCompanion(cls: ClassSymbol)(implicit ctx: Context) =
addCompanionPhases.exists(_.isCompanionNeeded(cls))
override def prepareForUnit(tree: tpd.Tree)(implicit ctx: Context): TreeTransform = {
addCompanionPhases = ctx.phasePlan.flatMap(_ collect { case p: NeedsCompanions => p })
this
}
/** eliminate self symbol in ClassInfo */
override def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = tp match {
case tp @ ClassInfo(_, _, _, _, self: Symbol) =>
tp.derivedClassInfo(selfInfo = self.info)
case _ =>
tp
}
/*
tp match {
//create companions for value classes that are not from currently compiled source file
case tp@ClassInfo(_, cls, _, decls, _)
if (ValueClasses.isDerivedValueClass(cls)) &&
!sym.isDefinedInCurrentRun && sym.scalacLinkedClass == NoSymbol =>
val newDecls = decls.cloneScope
val (modul, mcMethod, symMethod) = newCompanion(sym.name.toTermName, sym)
modul.entered
mcMethod.entered
newDecls.enter(symMethod)
tp.derivedClassInfo(decls = newDecls)
case _ => tp
}
}
*/
override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
tree match {
case Select(qual, name) if !name.is(OuterSelectName) && tree.symbol.exists =>
assert(qual.tpe derivesFrom tree.symbol.owner, i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe} in $tree")
case _: TypeTree =>
case _: Import | _: NamedArg | _: TypTree =>
assert(false, i"illegal tree: $tree")
case _ =>
}
}
/** Reorder statements so that module classes always come after their companion classes, add missing companion classes */
private def reorderAndComplete(stats: List[Tree])(implicit ctx: Context): List[Tree] = {
val moduleClassDefs, singleClassDefs = mutable.Map[Name, Tree]()
def reorder(stats: List[Tree]): List[Tree] = stats match {
case (stat: TypeDef) :: stats1 if stat.symbol.isClass =>
if (stat.symbol is Flags.Module) {
moduleClassDefs += (stat.name -> stat)
singleClassDefs -= stat.name.stripModuleClassSuffix
val stats1r = reorder(stats1)
if (moduleClassDefs contains stat.name) stat :: stats1r else stats1r
} else {
def stats1r = reorder(stats1)
val normalized = moduleClassDefs remove stat.name.moduleClassName match {
case Some(mcdef) =>
mcdef :: stats1r
case None =>
singleClassDefs += (stat.name -> stat)
stats1r
}
stat :: normalized
}
case stat :: stats1 => stat :: reorder(stats1)
case Nil => Nil
}
def registerCompanion(name: TermName, forClass: Symbol): TermSymbol = {
val (modul, mcCompanion, classCompanion) = newCompanion(name, forClass)
if (ctx.owner.isClass) modul.enteredAfter(thisTransformer)
mcCompanion.enteredAfter(thisTransformer)
classCompanion.enteredAfter(thisTransformer)
modul
}
def addMissingCompanions(stats: List[Tree]): List[Tree] = stats map {
case stat: TypeDef if (singleClassDefs contains stat.name) && needsCompanion(stat.symbol.asClass) =>
val objName = stat.name.toTermName
val nameClash = stats.exists {
case other: MemberDef =>
other.name == objName && other.symbol.info.isParameterless
case _ =>
false
}
val uniqueName = if (nameClash) AvoidClashName(objName) else objName
Thicket(stat :: ModuleDef(registerCompanion(uniqueName, stat.symbol), Nil).trees)
case stat => stat
}
addMissingCompanions(reorder(stats))
}
private def newCompanion(name: TermName, forClass: Symbol)(implicit ctx: Context) = {
val modul = ctx.newCompleteModuleSymbol(forClass.owner, name, Synthetic, Synthetic,
defn.ObjectType :: Nil, Scopes.newScope, assocFile = forClass.asClass.assocFile)
val mc = modul.moduleClass
val mcComp = ctx.synthesizeCompanionMethod(nme.COMPANION_CLASS_METHOD, forClass, mc)
val classComp = ctx.synthesizeCompanionMethod(nme.COMPANION_MODULE_METHOD, mc, forClass)
(modul, mcComp, classComp)
}
/** elimiate self in Template */
override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo): Tree = {
cpy.Template(impl)(self = EmptyValDef)
}
override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) = {
if (ddef.symbol.hasAnnotation(defn.NativeAnnot)) {
ddef.symbol.resetFlag(Deferred)
DefDef(ddef.symbol.asTerm,
_ => ref(defn.Sys_errorR).withPos(ddef.pos)
.appliedTo(Literal(Constant("native method stub"))))
} else ddef
}
override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] =
ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next)))
override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match {
case tree: Import => EmptyTree
case tree: NamedArg => transform(tree.arg)
case tree => if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos) else tree
}
override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) =
if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos)
else constToLiteral(tree)
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
if (tree.isType) TypeTree(tree.tpe).withPos(tree.pos)
else constToLiteral(tree)
override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo) =
constToLiteral(tree)
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) =
constToLiteral(tree)
override def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo) =
constToLiteral(tree)
override def transformBlock(tree: Block)(implicit ctx: Context, info: TransformerInfo) =
constToLiteral(tree)
override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) =
tree.cond match {
case Literal(Constant(c: Boolean)) => if (c) tree.thenp else tree.elsep
case _ => tree
}
// invariants: all modules have companion objects
// all types are TypeTrees
// all this types are explicit
}