summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2009-06-18 17:19:55 +0000
committerIulian Dragos <jaguarul@gmail.com>2009-06-18 17:19:55 +0000
commit3ee6b3653f8c25d7d6b19b9f5d4af7fa082146a8 (patch)
treee97b8c0dd8d61e82f825f528f98842f777621f7a /src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
parentbe8e3c69114da5bc3020d5363b338b1c83aa22ef (diff)
downloadscala-3ee6b3653f8c25d7d6b19b9f5d4af7fa082146a8.tar.gz
scala-3ee6b3653f8c25d7d6b19b9f5d4af7fa082146a8.tar.bz2
scala-3ee6b3653f8c25d7d6b19b9f5d4af7fa082146a8.zip
Specialization landed in trunk.
Diffstat (limited to 'src/compiler/scala/tools/nsc/typechecker/Duplicators.scala')
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Duplicators.scala242
1 files changed, 242 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
new file mode 100644
index 0000000000..b20b7c97bd
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/typechecker/Duplicators.scala
@@ -0,0 +1,242 @@
+package scala.tools.nsc.typechecker
+
+import scala.tools.nsc.symtab.Flags
+
+import scala.collection.{mutable, immutable}
+
+/** Duplicate trees and re-type check them, taking care to replace
+ * and create fresh symbols for new local definitions.
+ */
+abstract class Duplicators extends Analyzer {
+ import global._
+
+ def retyped(context: Context, tree: Tree): Tree = {
+ resetClassOwners
+ (new BodyDuplicator(context)).typed(tree)
+ }
+
+ /** Retype the given tree in the given context. Use this method when retyping
+ * a method in a different class. The typer will replace references to the this of
+ * the old class with the new class, and map symbols through the given 'env'. The
+ * environment is a map from type skolems to concrete types (see SpecializedTypes).
+ */
+ 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)
+
+ /** Return the special typer for duplicate method bodies. */
+ 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]
+
+ /** A typer that creates new symbols for all definitions in the given tree
+ * and updates references to them while re-typechecking. All types in the
+ * tree, except for TypeTrees, are erased prior to type checking. TypeTrees
+ * are fixed by substituting invalid symbols for the new ones.
+ */
+ 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)
+// assert(sym1 ne NoSymbol, tpe)
+ 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 _ =>
+ super.mapOver(tpe)
+ }
+ }
+
+ /** Fix the given type by replacing invalid symbols with the new ones. */
+ def fixType(tpe: Type): Type = {
+ val tpe1 = envSubstitution(tpe)
+ log("tpe1: " + tpe1)
+ (new FixInvalidSyms)(tpe1)
+ }
+
+ /** Return the new symbol corresponding to `sym'. */
+ 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
+ // breakIf(true, this, ldef, context)
+ val newsym = ldef.symbol.cloneSymbol(context.owner)
+ newsym.setInfo(fixType(ldef.symbol.info))
+ ldef.symbol = newsym
+ log("newsym: " + newsym + " info: " + newsym.info)
+
+ case DefDef(_, _, _, _, _, rhs) =>
+ // invalidate parameters
+ invalidate(tree.asInstanceOf[DefDef].tparams)
+ invalidate(List.flatten(tree.asInstanceOf[DefDef].vparamss))
+ 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 <- List.flatten(ddef.vparamss)) {
+ invalidate(vdef)
+ vdef.tpe = null
+ }
+ ddef.symbol = NoSymbol
+ enterSym(context, ddef)
+ log("remapping this of " + oldClassOwner + " to " + newClassOwner)
+ typed(ddef)
+ }
+
+ /** Special typer method allowing for re-type checking trees. It expects a typed tree.
+ * Returns a typed tree that has fresh symbols for all definitions in the original tree.
+ *
+ * Each definition tree is visited and its symbol added to the invalidSyms map (except LabelDefs),
+ * then cleared (forcing the namer to create fresh symbols).
+ * All invalid symbols found in trees are cleared (except for LabelDefs), forcing the
+ * typechecker to look for fresh ones in the context.
+ *
+ * Type trees are typed by substituting old symbols for new ones (@see fixType).
+ *
+ * LabelDefs are not typable from trees alone, unless they have the type ()Unit. Therefore,
+ * their symbols are recreated ad-hoc and their types are fixed inline, instead of letting the
+ * namer/typer handle them, or Idents that refer to them.
+ */
+ override def typed(tree: Tree, mode: Int, pt: Type): Tree = {
+ log("typing " + tree)
+ if (tree.hasSymbol && tree.symbol != NoSymbol
+ && !tree.symbol.isLabel // labels cannot be retyped by the type checker as LabelDef has no ValDef/return type trees
+ && invalidSyms.isDefinedAt(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)) =>
+// log("invalidating classdef " + tree.tpe)
+ 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) =>
+// log("vdef fixing tpe: " + tree.tpe + " with sym: " + tree.tpe.typeSymbol + " and " + invalidSyms)
+ 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) =>
+ log("selection on this, no type ascription required")
+ super.typed(atPos(tree.pos)(Select(This(newClassOwner), sel)), mode, pt)
+
+ case This(_) if (oldClassOwner ne null) && (tree.symbol == oldClassOwner) =>
+// val tree1 = Typed(This(newClassOwner), TypeTree(fixType(tree.tpe.widen)))
+ val tree1 = This(newClassOwner)
+ log("mapped " + tree + " to " + tree1)
+ super.typed(atPos(tree.pos)(tree1), mode, pt)
+
+ case Super(qual, mix) if (oldClassOwner ne null) && (tree.symbol == oldClassOwner) =>
+ val tree1 = Super(qual, mix)
+ log("changed " + tree + " to " + tree1)
+ super.typed(atPos(tree.pos)(tree1))
+
+ case _ =>
+ tree.tpe = null
+ super.typed(tree, mode, pt)
+ }
+ }
+ }
+}
+