diff options
author | Iulian Dragos <jaguarul@gmail.com> | 2009-06-18 17:19:55 +0000 |
---|---|---|
committer | Iulian Dragos <jaguarul@gmail.com> | 2009-06-18 17:19:55 +0000 |
commit | 3ee6b3653f8c25d7d6b19b9f5d4af7fa082146a8 (patch) | |
tree | e97b8c0dd8d61e82f825f528f98842f777621f7a /src/compiler/scala/tools/nsc/typechecker/Duplicators.scala | |
parent | be8e3c69114da5bc3020d5363b338b1c83aa22ef (diff) | |
download | scala-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.scala | 242 |
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) + } + } + } +} + |