summaryrefslogtreecommitdiff
path: root/sources/scalac/transformer/LambdaLift.java
diff options
context:
space:
mode:
Diffstat (limited to 'sources/scalac/transformer/LambdaLift.java')
-rw-r--r--sources/scalac/transformer/LambdaLift.java508
1 files changed, 508 insertions, 0 deletions
diff --git a/sources/scalac/transformer/LambdaLift.java b/sources/scalac/transformer/LambdaLift.java
new file mode 100644
index 0000000000..56cc92e6de
--- /dev/null
+++ b/sources/scalac/transformer/LambdaLift.java
@@ -0,0 +1,508 @@
+/* ____ ____ ____ ____ ______ *\
+** / __// __ \/ __// __ \/ ____/ SOcos COmpiles Scala **
+** __\_ \/ /_/ / /__/ /_/ /\_ \ (c) 2002, LAMP/EPFL **
+** /_____/\____/\___/\____/____/ **
+** **
+** $Id$
+\* */
+
+package scalac.transformer;
+
+import java.io.*;
+import java.util.HashMap;
+import scalac.*;
+import scalac.util.*;
+import scalac.ast.*;
+import scalac.symtab.*;
+import Tree.*;
+
+/** A lambda lifting transformer
+ *
+ * @author Martin Odersky
+ * @version 1.0
+ *
+ * What it does:
+ * Lift every class and function that's contained in another function
+ * out to be a member to the next enclosing class.
+ * Pass free variables and type variables as parameters to class constructors.
+ * Variables and values that are free in some function or class
+ * are given new unique names.
+ * Free mutable variables are converted to reference cells.
+ * All types are updated so that proxies are passed for additional free
+ * type variables.
+ */
+public class LambdaLift extends OwnerTransformer
+ implements Modifiers, Kinds {
+
+ final Global global;
+ final Definitions definitions;
+ final FreeVars free;
+ final LambdaLiftPhase descr;
+
+ public LambdaLift(Global global, LambdaLiftPhase descr) {
+ super(global, descr);
+ this.global = global;
+ this.definitions = global.definitions;
+ this.free = new FreeVars(global, descr);
+ this.descr = descr;
+ }
+
+ public void apply(Unit unit) {
+ global.log(unit.source.toString());
+ free.initialize(unit);
+ unit.body = transformTemplateStats(unit.body, currentOwner);
+ }
+
+ /** If `sym' is a class, return its primary constructor;
+ * otherwise current symbol itself
+ */
+ static Symbol asFunction(Symbol sym) {
+ return sym.kind == CLASS ? sym.constructor() : sym;
+ }
+
+ /** `asFunction' applied to the next enclosing function or class owner.
+ */
+ static Symbol enclFun(Symbol owner) {
+ Symbol sym = owner;
+ while (!asFunction(sym).isMethod()) sym = sym.owner();
+ return asFunction(sym);
+ }
+
+ /** Return SymSet from a hashmap.
+ */
+ private static SymSet get(HashMap f, Symbol sym) {
+ SymSet ss = (SymSet) f.get(sym);
+ return ss == null ? SymSet.EMPTY : ss;
+ }
+
+ /** Compute free variables map `fvs'.
+ * Also assign unique names to all
+ * value/variable/let symbols that are free in some function or class, and to
+ * all class/function symbols that are owned by some function.
+ */
+ static class FreeVars extends OwnerTransformer {
+
+ private Unit unit;
+
+ public FreeVars(Global global, PhaseDescriptor descr) {
+ super(global, descr);
+ }
+
+ /** A hashtable storing free variables of functions and class constructors.
+ */
+ private HashMap/*<Symbol,SymSet>*/ fvs;
+
+ /** A hashtable storing free type variables of functions and class constructors.
+ */
+ private HashMap/*<Symbol,SymSet>*/ ftvs;
+
+ /** A hashtable storing calls between functions and class constructors
+ */
+ private HashMap/*<Symbol,SymSet>*/ called;
+
+ /** The set of symbols that need to be renamed.
+ */
+ private SymSet renamable;
+
+ /** A flag to indicate whether new free variables have been found
+ */
+ private boolean changedFreeVars;
+
+ /** A flag to indicate whether we are in propagation phase
+ * (used in assertion).
+ */
+ private boolean propagatePhase;
+
+ /** Insert `sym' into the set of free variables of `owner'
+ */
+ private void putFree(Symbol owner, Symbol sym) {
+ assert owner.isLocal();
+ HashMap f = sym.isType() ? ftvs : fvs;
+ SymSet ss = get(f, owner);
+ if (!ss.contains(sym)) {
+ f.put(owner, ss.incl(sym));
+ changedFreeVars = true;
+ if (global.debug) global.log(sym + " is free in " + owner);
+ }
+ }
+
+ /** Insert `to' into the set of functions called by `from'
+ */
+ private void putCall(Symbol from, Symbol to) {
+ SymSet ss = get(called, from);
+ if (!ss.contains(to)) {
+ called.put(from, ss.incl(to));
+ }
+ }
+
+ /** Mark symbol `sym' as being free in `owner', unless `sym'
+ * is defined in `owner' or there is a class between `owner's owner
+ * and the owner of `sym'.
+ * Return `true' if there is no class between `owner' and
+ * the owner of sym.
+ */
+ private boolean markFree(Symbol sym, Symbol owner) {
+ //if (global.debug) global.log("mark " + sym + " free in " + owner);//DEBUG
+ if (owner.kind == NONE) {
+ assert propagatePhase : sym + " in " + sym.owner();
+ return false;
+ } else if (sym.owner() == owner) {
+ return true;
+ } else if (markFree(sym, owner.owner())) {
+ Symbol fowner = asFunction(owner);
+ if (fowner.isMethod()) {
+ putFree(fowner, sym);
+ renamable = renamable.incl(sym);
+ if (sym.isVariable()) sym.flags |= CAPTURED;
+ }
+ return owner.kind != CLASS;
+ } else {
+ return false;
+ }
+ }
+
+ /** Assign unique name to symbol.
+ * If symbol is a class assign same name to its primary constructor.
+ */
+ private void makeUnique(Symbol sym) {
+ Name newname = global.freshNameCreator.newName(sym.name);
+ sym.name = newname;
+ if (sym.kind == CLASS)
+ sym.constructor().name = newname.toConstrName();
+ }
+
+ private Type.Map traverseTypeMap = new Type.Map() {
+ public Type apply(Type tp) {
+ switch (tp) {
+ case TypeRef(ThisType(_), Symbol sym, Type[] targs):
+ if (sym.isLocal() && sym.kind == TYPE)
+ markFree(sym, currentOwner);
+ }
+ return map(tp);
+ }
+ };
+
+ public Tree transform(Tree tree) {
+ global.debugPrinter.print("free ").print(tree).println().end();//debug
+ traverseTypeMap.apply(tree.type.widen());
+ Symbol sym = tree.symbol();
+ switch(tree) {
+ case ClassDef(_, _, _, _, _, _):
+ case DefDef(_, _, _, _, _, _):
+ if (sym.isLocal()) {
+ renamable = renamable.incl(sym);
+ }
+ return super.transform(tree);
+
+ case Ident(Name name):
+ assert sym.owner() != null : sym + " " + name;//debug
+ if (sym.isLocal()) {
+ if (sym.isMethod()) {
+ Symbol f = enclFun(currentOwner);
+ if (f.name.length() > 0) // it is not a template function {
+ putCall(f, sym);
+ } else if (sym.kind == VAL || sym.kind == TYPE) {
+ markFree(sym, currentOwner);
+ }
+ }
+ return tree;
+
+ default:
+ return super.transform(tree);
+ }
+ }
+
+ /** Propagate free fariables from all called functions.
+ */
+ void propagateFvs(HashMap fvs) {
+ Object[] fs = called.keySet().toArray();
+ for (int i = 0; i < fs.length; i++) {
+ Symbol f = (Symbol) fs[i];
+ Symbol[] calledFromF = get(called, f).toArray();
+ for (int j = 0; j < calledFromF.length; j++) {
+ //System.out.println(f + " calls " + calledFromF[j]);//DEBUG
+ Symbol[] newFvs = get(fvs, calledFromF[j]).toArray();
+ for (int k = 0; k < newFvs.length; k++) {
+ markFree(newFvs[k], f);
+ }
+ }
+ }
+ }
+
+ /** This method re-enters all free variables into their free variable sets
+ * This is necessary because the names of these variables (and therefore their
+ * `isLess' order have changed.
+ */
+ void restoreFvs(HashMap fvs) {
+ Object[] fs = fvs.keySet().toArray();
+ for (int i = 0; i < fs.length; i++) {
+ Symbol f = (Symbol) fs[i];
+ Symbol[] elems = get(fvs, f).toArray();
+ SymSet elems1 = SymSet.EMPTY;
+ for (int j = 0; j < elems.length; j++)
+ elems1 = elems1.incl(elems[j]);
+ fvs.put(f, elems1);
+ }
+ }
+
+ /** Compute a mapping from symbols to their free variables
+ * in hashtable `fvs'. Also rename all variables that need it.
+ */
+ public void initialize(Unit unit) {
+ this.unit = unit;
+ fvs = new HashMap();
+ ftvs = new HashMap();
+ called = new HashMap();
+ renamable = SymSet.EMPTY;
+ apply(unit);
+
+ propagatePhase = true;
+ do {
+ changedFreeVars = false;
+ propagateFvs(fvs);
+ propagateFvs(ftvs);
+ } while (changedFreeVars);
+
+ Symbol[] ss = renamable.toArray();
+ for (int i = 0; i < ss.length; i++) {
+ makeUnique(ss[i]);
+ }
+
+ restoreFvs(fvs);
+ restoreFvs(ftvs);
+ }
+ }
+
+ private TreeList liftedDefs;
+
+ /** Transform template and add lifted definitions to it.
+ */
+ public Tree[] transformTemplateStats(Tree[] stats, Symbol tsym) {
+ TreeList prevLiftedDefs = liftedDefs;
+ liftedDefs = new TreeList();
+ TreeList stats1 = new TreeList(super.transformTemplateStats(stats, tsym));
+ stats1.append(liftedDefs);
+ liftedDefs = prevLiftedDefs;
+ return stats1.toArray();
+ }
+
+ public Tree transform(Tree tree) {
+ global.debugPrinter.print("lifting ").print(tree).println().end();//debug
+ tree.type = descr.transform(tree.type, currentOwner);
+ switch (tree) {
+ case ClassDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Template impl):
+ Symbol sym = tree.symbol();
+ if (sym.isLocal()) {
+ Symbol[] newtparams = ftvsParams(sym.constructor());
+ Symbol[] newparams = fvsParams(sym.constructor());
+ liftSymbol(sym, newtparams, newparams);
+ Tree tree1 = copy.ClassDef(
+ tree, mods, sym.name,
+ addTypeParams(transform(tparams, sym), newtparams),
+ new ValDef[][]{addParams(transform(vparams, sym)[0], newparams)},
+ transform(tpe),
+ transform(impl, sym));
+ liftedDefs.append(tree1);
+ return Tree.Empty;
+ } else {
+ return copy.ClassDef(
+ tree, mods, sym.name,
+ transform(tparams, sym), transform(vparams, sym), transform(tpe),
+ transform(impl, sym));
+ }
+
+ case DefDef(int mods, Name name, TypeDef[] tparams, ValDef[][] vparams, Tree tpe, Tree rhs):
+ Symbol sym = tree.symbol();
+ if (sym.isLocal()) {
+ Symbol[] newtparams = ftvsParams(sym);
+ Symbol[] newparams = fvsParams(sym);
+ liftSymbol(sym, newtparams, newparams);
+ Tree tree1 = copy.DefDef(
+ tree, mods, sym.name,
+ addTypeParams(transform(tparams, sym), newtparams),
+ new ValDef[][]{addParams(transform(vparams, sym)[0], newparams)},
+ transform(tpe),
+ transform(rhs, sym));
+ liftedDefs.append(tree1);
+ return Tree.Empty;
+ } else {
+ return copy.DefDef(
+ tree, mods, sym.name,
+ transform(tparams, sym), transform(vparams, sym), transform(tpe),
+ transform(rhs, sym));
+ }
+
+ case ValDef(int mods, Name name, Tree tpe, Tree rhs):
+ Symbol sym = tree.symbol();
+ Name name1 = sym.name;
+ Tree tpe1 = transform(tpe);
+ Tree rhs1 = transform(rhs, sym);
+ if ((sym.flags & CAPTURED) != 0) {
+ assert sym.isLocal();
+ Type unboxedType = sym.typeAt(descr.nextPhase);
+ Type boxedType = descr.refType(unboxedType);
+ tpe1 = gen.mkType(tpe.pos, boxedType);
+ rhs1 = gen.New(
+ rhs.pos,
+ definitions.SCALA_TYPE,
+ boxedType.symbol(),
+ new Type[]{unboxedType},
+ new Tree[]{rhs1});
+ }
+ return copy.ValDef(tree, mods, name1, tpe1, rhs1);
+
+ case Apply(Tree fn, Tree[] args):
+ Symbol fsym = TreeInfo.methSymbol(fn);
+ Tree fn1 = transform(fn);
+ switch (fn1) {
+ case TypeApply(Tree fn2, Tree[] targs):
+ fn1 = copy.TypeApply(
+ fn1, fn2, addFreeArgs(tree.pos, get(free.ftvs, fsym), targs));
+ break;
+ default:
+ Tree[] targs = addFreeArgs(
+ tree.pos, get(free.ftvs, fsym), Tree.EMPTY_ARRAY);
+ if (targs.length > 0) fn1 = gen.TypeApply(fn1, targs);
+ }
+ Tree[] args1 = transform(args);
+ return copy.Apply(
+ tree, fn1, addFreeArgs(tree.pos, get(free.fvs, fsym), args1));
+
+ case Ident(Name name):
+ Symbol sym = tree.symbol();
+ Name name1 = sym.name;
+ if (sym.isLocal() &&
+ (sym.kind == TYPE || (sym.kind == VAL && !sym.isMethod()))) {
+ sym = descr.proxy(sym, currentOwner);
+ }
+ Tree tree1 = copy.Ident(tree, name1).setSymbol(sym).setType(
+ sym.typeAt(descr.nextPhase));
+ if ((sym.flags & CAPTURED) != 0) return gen.Select(tree1, Names.elem);
+ else return tree1;
+
+ default:
+ return super.transform(tree);
+ }
+ }
+
+ Symbol[] ftvsParams(Symbol owner) {
+ Symbol[] freevars = get(free.ftvs, owner).toArray();
+ Symbol[] params = new Symbol[freevars.length];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = freevars[i].cloneSymbol();
+ params[i].pos = owner.pos;
+ params[i].flags = PARAM | SYNTHETIC;
+ params[i].setOwner(owner);
+ params[i].setInfo(freevars[i].type());
+ }
+ return params;
+ }
+
+ Symbol[] fvsParams(Symbol owner) {
+ Symbol[] freevars = get(free.fvs, owner).toArray();
+ Symbol[] params = new Symbol[freevars.length];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = freevars[i].cloneSymbol();
+ params[i].pos = owner.pos;
+ params[i].flags &= CAPTURED;
+ params[i].flags |= PARAM | SYNTHETIC;
+ params[i].setOwner(owner);
+ params[i].setInfo(freevars[i].type());
+ }
+ return params;
+ }
+
+ /** change symbol so that
+ * owner = currentClass
+ * newparams are added
+ * enter symbol in scope of currentClass
+ */
+ void liftSymbol(Symbol sym, Symbol[] newtparams, Symbol[] newparams) {
+ Symbol enclClass = sym.owner().enclClass();
+ sym.setOwner(enclClass);
+ enclClass.members().enter(sym);
+ if (sym.isMethod()) {
+ if (newtparams.length != 0 || newparams.length != 0) {
+ sym.updateInfo(
+ addParams(
+ addTypeParams(sym.infoAt(descr.nextPhase), newtparams),
+ newparams));
+ if (global.debug)
+ global.log(sym + " has now type " + sym.typeAt(descr.nextPhase));
+ }
+ } else if (sym.kind == CLASS) {
+ liftSymbol(sym.constructor(), newtparams, newparams);
+ } else {
+ throw new ApplicationError();
+ }
+ }
+
+ Type addTypeParams(Type tp, Symbol[] newtparams) {
+ if (newtparams.length == 0) return tp;
+ switch (tp) {
+ case MethodType(_, _):
+ return Type.PolyType(newtparams, tp);
+ case PolyType(Symbol[] tparams, Type restpe):
+ Symbol[] tparams1 = new Symbol[tparams.length + newtparams.length];
+ System.arraycopy(tparams, 0, tparams1, 0, tparams.length);
+ System.arraycopy(newtparams, 0, tparams1, tparams.length, newtparams.length);
+ return Type.PolyType(tparams1, restpe);
+ default:
+ throw new ApplicationError("illegal type: " + tp);
+ }
+ }
+
+ Type addParams(Type tp, Symbol[] newparams) {
+ if (newparams.length == 0) return tp;
+ switch (tp) {
+ case MethodType(Symbol[] params, Type restpe):
+ Symbol[] params1 = new Symbol[params.length + newparams.length];
+ System.arraycopy(params, 0, params1, 0, params.length);
+ System.arraycopy(newparams, 0, params1, params.length, newparams.length);
+ return Type.MethodType(params1, restpe);
+ case PolyType(Symbol[] tparams, Type restpe):
+ return Type.PolyType(tparams, addParams(restpe, newparams));
+ default:
+ throw new ApplicationError("illegal type: " + tp);
+ }
+ }
+
+ TypeDef[] addTypeParams(TypeDef[] tparams, Symbol[] newtparams) {
+ if (newtparams.length == 0) return tparams;
+ TypeDef[] tparams1 = new TypeDef[tparams.length + newtparams.length];
+ System.arraycopy(tparams, 0, tparams1, 0, tparams.length);
+ for (int i = 0; i < newtparams.length; i++) {
+ tparams1[tparams.length + i] = (Tree.TypeDef)gen.TypeDef(newtparams[i]);
+ }
+ return tparams1;
+ }
+
+ ValDef[] addParams(ValDef[] params, Symbol[] newparams) {
+ if (newparams.length == 0) return params;
+ ValDef[] params1 = new ValDef[params.length + newparams.length];
+ System.arraycopy(params, 0, params1, 0, params.length);
+ for (int i = 0; i < newparams.length; i++) {
+ params1[params.length + i] = gen.Param(newparams[i]);
+ }
+ return params1;
+ }
+
+ /** For all variables or type variables in `fvs',
+ * append proxies to argument array `args'.
+ */
+ Tree[] addFreeArgs(int pos, SymSet fvs, Tree[] args) {
+ if (fvs != SymSet.EMPTY) {
+ Symbol[] fparams = fvs.toArray();
+ Tree[] args1 = new Tree[args.length + fparams.length];
+ System.arraycopy(args, 0, args1, 0, args.length);
+ for (int i = 0; i < fparams.length; i++) {
+ Symbol farg = descr.proxy(fparams[i], currentOwner);
+ args1[args.length + i] =
+ gen.Ident(pos, farg).setType(farg.typeAt(descr.nextPhase));
+ }
+ return args1;
+ } else {
+ return args;
+ }
+ }
+}