package scala.tools.nsc
package typechecker
import scala.tools.nsc.symtab.Flags
import scala.collection.{ mutable, immutable }
abstract class Duplicators extends Analyzer {
import global._
def retyped(context: Context, tree: Tree): Tree = {
resetClassOwners
(new BodyDuplicator(context)).typed(tree)
}
def retyped(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol, env: collection.Map[Symbol, Type]): Tree = {
if (oldThis ne newThis) {
oldClassOwner = oldThis
newClassOwner = newThis
} else resetClassOwners
envSubstitution = new SubstSkolemsTypeMap(env.keysIterator.toList, env.valuesIterator.toList)
log("retyped with env: " + env)
(new BodyDuplicator(context)).typed(tree)
}
def retypedMethod(context: Context, tree: Tree, oldThis: Symbol, newThis: Symbol): Tree =
(new BodyDuplicator(context)).retypedMethod(tree.asInstanceOf[DefDef], oldThis, newThis)
override def newTyper(context: Context): Typer =
new BodyDuplicator(context)
private def resetClassOwners() {
oldClassOwner = null
newClassOwner = null
}
private var oldClassOwner: Symbol = _
private var newClassOwner: Symbol = _
private var envSubstitution: SubstTypeMap = _
private class SubstSkolemsTypeMap(from: List[Symbol], to: List[Type]) extends SubstTypeMap(from, to) {
protected override def matches(sym1: Symbol, sym2: Symbol) =
if (sym2.isTypeSkolem) sym2.deSkolemize eq sym1
else sym1 eq sym2
}
private val invalidSyms: mutable.Map[Symbol, Tree] = mutable.HashMap.empty[Symbol, Tree]
class BodyDuplicator(context: Context) extends Typer(context: Context) {
class FixInvalidSyms extends TypeMap {
def apply(tpe: Type): Type = {
mapOver(tpe)
}
override def mapOver(tpe: Type): Type = tpe match {
case TypeRef(NoPrefix, sym, args) if sym.isTypeParameterOrSkolem =>
val sym1 = context.scope.lookup(sym.name)
if ((sym1 ne NoSymbol) && (sym1 ne sym)) {
log("fixing " + sym + " -> " + sym1)
typeRef(NoPrefix, sym1, mapOverArgs(args, sym1.typeParams))
} else super.mapOver(tpe)
case TypeRef(pre, sym, args) =>
val newsym = updateSym(sym)
if (newsym ne sym) {
log("fixing " + sym + " -> " + newsym)
typeRef(mapOver(pre), newsym, mapOverArgs(args, newsym.typeParams))
} else
super.mapOver(tpe)
case SingleType(pre, sym) =>
val sym1 = updateSym(sym)
if (sym1 ne sym) {
log("fixing " + sym + " -> " + sym1)
singleType(mapOver(pre), sym1)
} else
super.mapOver(tpe)
case ThisType(sym) =>
val sym1 = updateSym(sym)
if (sym1 ne sym) {
log("fixing " + sym + " -> " + sym1)
ThisType(sym1)
} else
super.mapOver(tpe)
case _ =>
super.mapOver(tpe)
}
}
def fixType(tpe: Type): Type = {
val tpe1 = envSubstitution(tpe)
val tpe2: Type = (new FixInvalidSyms)(tpe1)
val tpe3 = if (newClassOwner ne null) {
tpe2.asSeenFrom(newClassOwner.thisType, oldClassOwner)
} else tpe2
tpe3
}
private def updateSym(sym: Symbol): Symbol =
if (invalidSyms.isDefinedAt(sym))
invalidSyms(sym).symbol
else
sym
private def invalidate(tree: Tree) {
if (tree.isDef && tree.symbol != NoSymbol) {
log("invalid " + tree.symbol)
invalidSyms(tree.symbol) = tree
tree match {
case ldef @ LabelDef(name, params, rhs) =>
log("LabelDef " + name + " sym.info: " + ldef.symbol.info)
invalidSyms(ldef.symbol) = ldef
val newsym = ldef.symbol.cloneSymbol(context.owner)
newsym.setInfo(fixType(ldef.symbol.info))
ldef.symbol = newsym
log("newsym: " + newsym + " info: " + newsym.info)
case DefDef(_, name, tparams, vparamss, _, rhs) =>
invalidate(tparams ::: vparamss.flatten)
tree.symbol = NoSymbol
case _ =>
tree.symbol = NoSymbol
}
}
}
private def invalidate(stats: List[Tree]) {
stats foreach invalidate
}
def retypedMethod(ddef: DefDef, oldThis: Symbol, newThis: Symbol): Tree = {
oldClassOwner = oldThis
newClassOwner = newThis
invalidate(ddef.tparams)
for (vdef <- ddef.vparamss.flatten) {
invalidate(vdef)
vdef.tpe = null
}
ddef.symbol = NoSymbol
enterSym(context, ddef)
log("remapping this of " + oldClassOwner + " to " + newClassOwner)
typed(ddef)
}
private def inspectTpe(tpe: Type) = {
tpe match {
case MethodType(_, res) =>
res + ", " + res.bounds.hi + ", " + (res.bounds.hi match {
case TypeRef(_, _, args) if (args.length > 0) => args(0) + ", " + args(0).bounds.hi
case _ => "non-tref: " + res.bounds.hi.getClass
})
case _ =>
}
}
override def typed(tree: Tree, mode: Int, pt: Type): Tree = {
if (settings.debug.value) log("typing " + tree + ": " + tree.tpe)
if (tree.hasSymbol && tree.symbol != NoSymbol
&& !tree.symbol.isLabel
&& invalidSyms.isDefinedAt(tree.symbol)) {
if (settings.debug.value) log("removed symbol " + tree.symbol)
tree.symbol = NoSymbol
}
tree match {
case ttree @ TypeTree() =>
log("fixing tpe: " + tree.tpe + " with sym: " + tree.tpe.typeSymbol)
ttree.tpe = fixType(ttree.tpe)
ttree
case Block(stats, res) =>
log("invalidating block")
invalidate(stats)
invalidate(res)
tree.tpe = null
super.typed(tree, mode, pt)
case ClassDef(_, _, _, tmpl @ Template(parents, _, stats)) =>
tmpl.symbol = tree.symbol.newLocalDummy(tree.pos)
invalidate(stats)
tree.tpe = null
super.typed(tree, mode, pt)
case ddef @ DefDef(_, _, _, _, tpt, rhs) =>
ddef.tpt.tpe = fixType(ddef.tpt.tpe)
ddef.tpe = null
super.typed(ddef, mode, pt)
case vdef @ ValDef(_, _, tpt, rhs) =>
vdef.tpt.tpe = fixType(vdef.tpt.tpe)
vdef.tpe = null
super.typed(vdef, mode, pt)
case ldef @ LabelDef(name, params, rhs) =>
ldef.tpe = null
val params1 = params map { p => Ident(updateSym(p.symbol)) }
super.typed(treeCopy.LabelDef(tree, name, params1, rhs), mode, pt)
case Bind(name, _) =>
invalidate(tree)
tree.tpe = null
super.typed(tree, mode, pt)
case Ident(_) if tree.symbol.isLabel =>
log("Ident to labeldef " + tree + " switched to ")
tree.symbol = updateSym(tree.symbol)
tree.tpe = null
super.typed(tree, mode, pt)
case Select(th @ This(_), sel) if (oldClassOwner ne null) && (th.symbol == oldClassOwner) =>
val t = super.typed(atPos(tree.pos)(Select(This(newClassOwner), tree.symbol.name)), mode, pt)
t
case This(_) if (oldClassOwner ne null) && (tree.symbol == oldClassOwner) =>
val tree1 = This(newClassOwner)
if (settings.debug.value) log("mapped " + tree + " to " + tree1)
super.typed(atPos(tree.pos)(tree1), mode, pt)
case This(_) =>
tree.symbol = updateSym(tree.symbol)
tree.tpe = null
val tree1 = super.typed(tree, mode, pt)
tree1
case Match(scrut, cases) =>
val scrut1 = typed(scrut, EXPRmode | BYVALmode, WildcardType)
val scrutTpe = scrut1.tpe.widen
val cases1 = if (scrutTpe.isFinalType) cases filter {
case CaseDef(Bind(_, pat @ Typed(_, tpt)), EmptyTree, body) =>
scrutTpe.matchesPattern(fixType(tpt.tpe))
case CaseDef(Typed(_, tpt), EmptyTree, body) =>
scrutTpe.matchesPattern(fixType(tpt.tpe))
case _ => true
} else cases
super.typed(atPos(tree.pos)(Match(scrut, cases1)), mode, pt)
case EmptyTree =>
tree
case _ =>
if (tree.hasSymbol && tree.symbol != NoSymbol && (tree.symbol.owner == definitions.AnyClass)) {
tree.symbol = NoSymbol
}
tree.tpe = null
super.typed(tree, mode, pt)
}
}
}
}