/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.visit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import polyglot.ast.Call;
import polyglot.ast.Expr;
import polyglot.ast.Field;
import polyglot.ast.FieldAssign;
import polyglot.ast.Formal;
import polyglot.ast.Local;
import polyglot.ast.LocalDecl;
import polyglot.ast.New;
import polyglot.ast.Node;
import polyglot.ast.Special;
import polyglot.ast.Term;
import polyglot.ast.TypeNode;
import polyglot.ast.VarDecl;
import polyglot.types.ClassType;
import polyglot.types.CodeDef;
import polyglot.types.CodeInstance;
import polyglot.types.LocalDef;
import polyglot.types.LocalInstance;
import polyglot.types.Ref;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.types.VarDef;
import polyglot.types.VarInstance;
import x10.ast.AssignPropertyCall;
import x10.ast.Async;
import x10.ast.AtEach;
import x10.ast.AtStmt;
import x10.ast.AtStmt_c;
import x10.ast.Closure;
import x10.ast.ClosureCall;
import x10.ast.DepParameterExpr;
import x10.ast.SettableAssign;
import x10.ast.TypeParamNode;
import x10.ast.X10ConstructorCall;
import x10.ast.X10Formal;
import x10.constraint.XFailure;
import x10.constraint.XTerm;
import x10.constraint.XVar;
import x10.extension.X10Ext_c;
import x10.types.AsyncDef;
import x10.types.AtDef;
import x10.types.ClosureDef;
import x10.types.ClosureDef_c;
import x10.types.ClosureInstance;
import x10.types.ClosureType;
import x10.types.ConstrainedType;
import x10.types.EnvironmentCapture;
import x10.types.FunctionType;
import x10.types.ParameterType;
import x10.types.ThisDef;
import x10.types.X10ClassType;
import x10.types.X10ConstructorInstance;
import x10.types.X10FieldInstance;
import x10.types.X10LocalDef;
import x10.types.X10LocalInstance;
import x10.types.MethodInstance;
import x10.types.X10MemberDef;
import x10.types.X10ParsedClassType;
import x10.types.constraints.CConstraint;
import x10.types.constraints.CLocal;
import x10.types.constraints.ConstraintManager;
import polyglot.types.TypeSystem;
import polyglot.visit.ContextVisitor;
import polyglot.util.CollectionUtil;
import polyglot.util.InternalCompilerError;
import x10.util.CollectionFactory;
/**
* A {@link NodeTransformer} that transforms the types stored
* in the visited AST nodes. Subclasses should override the
* various transform* methods with TypeObject arguments.
*/
public class TypeTransformer extends NodeTransformer {
protected Type transformType(Type type) {
Type nt = transformTypeRecursively(type);
//if (nt != null && nt.toString().contains("!!!")) { // validation
// throw new InternalCompilerError("Type was not fully transformed: "+nt, nt.position());
//}
return nt;
}
protected CConstraint transformConstraint(CConstraint c) {
if (c == null)
return null;
VarDef currentLocal = this.visitor().context().varWhoseTypeIsBeingElaborated();
List<XVar> oldvars = new ArrayList<XVar>();
List<XVar> newvars = new ArrayList<XVar>();
for (XVar v : c.vars()) {
if (v instanceof CLocal) {
CLocal l = (CLocal) v;
X10LocalDef ld = l.name();
X10LocalDef newld = vars.get(ld);
if (ld == currentLocal) { // we are in the declaration for this variable
assert (newld == null);
Type rt = Types.get(ld.type());
TypeSystem ts = rt.typeSystem();
newld = copyLocalDef(ld);
mapLocal(ld, newld); // have to do this first, else get infinite recursion
mapLocal(newld, newld); // just in case some client substitutes first
Type newrt = transformType(rt);
newld.setType(Types.ref(newrt));
}
if (newld == null || newld == ld) continue;
oldvars.add(v);
//if (!l.s.endsWith("!!!")) l.s+="!!!"; // validation
//newvars.add(ConstraintManager.getConstraintSystem().makeLocal(newld, newld.name().toString())); // validation
newvars.add(ConstraintManager.getConstraintSystem().makeLocal(newld, v.toString()));
}
}
try {
CConstraint newC = c.substitute(newvars.toArray(new XTerm[0]), oldvars.toArray(new XVar[0]));
if (newC != c && (!newC.entails(c) || !c.entails(newC))) {
c = newC;
}
} catch (XFailure e) {
throw new InternalCompilerError("Unexpected failure while transforming constraint: "+c);
}
return c;
}
protected <T> Ref<T> transformRef(Ref<T> ref) {
return ref;
}
protected ParameterType transformParameterType(ParameterType pt) {
return pt;
}
protected X10LocalInstance transformLocalInstance(X10LocalInstance li) {
return li;
}
protected X10FieldInstance transformFieldInstance(X10FieldInstance fi) {
return fi;
}
protected MethodInstance transformMethodInstance(MethodInstance mi) {
return mi;
}
protected X10ConstructorInstance transformConstructorInstance(X10ConstructorInstance ci) {
return ci;
}
protected ClosureInstance transformClosureInstance(ClosureInstance ci) {
return ci;
}
protected final CodeInstance<?> transformCodeInstance(CodeInstance<?> ci) {
if (ci instanceof MethodInstance) {
return transformMethodInstance((MethodInstance) ci);
} else if (ci instanceof X10ConstructorInstance) {
return transformConstructorInstance((X10ConstructorInstance) ci);
} else if (ci instanceof ClosureInstance) {
return transformClosureInstance((ClosureInstance) ci);
}
return ci;
}
protected final List<Type> transformTypeList(List<Type> l) {
if (l == null)
return null;
List<Type> res = l;
List<Type> acc = new ArrayList<Type>();
for (Type t : l) {
Type xt = transformType(t);
acc.add(xt);
if (xt != t) {
res = acc;
}
}
return res;
}
private Type transformTypeRecursively(Type t) {
if (t instanceof ConstrainedType) {
ConstrainedType ct = (ConstrainedType) t;
Type bt = Types.get(ct.baseType());
Type newbt = transformTypeRecursively(bt);
CConstraint constraint = ct.getRealXClause();
CConstraint newConstraint = transformConstraint(constraint);
if (newbt != bt || newConstraint != constraint) {
t = Types.xclause(newbt, newConstraint);
}
} else if (t instanceof ClosureType) { // order matters!
ClosureType ft = (ClosureType) t; // TODO
X10ClassType ct = (X10ClassType) ft.outer();
FunctionType fi = ft.functionInterface();
CodeInstance<?> cm = ft.methodContainer();
X10ClassType nct = (X10ClassType) transformType(ct);
FunctionType nfi = (FunctionType) transformTypeRecursively(fi);
CodeInstance<?> ncm = transformCodeInstance(cm);
if (nct != ct || nfi != fi || ncm != cm) {
List<Ref<? extends Type>> fts = new ArrayList<Ref<? extends Type>>();
List<ParameterType> tps = Collections.<ParameterType>emptyList();
for (Type a : nfi.argumentTypes()) {
fts.add(Types.ref(a));
}
List<LocalDef> nfns = new ArrayList<LocalDef>();
for (LocalInstance li : nfi.formalNames()) {
nfns.add(li.def());
}
ThisDef td = ncm.def() instanceof X10MemberDef ? ((X10MemberDef) ncm.def()).thisDef() : ct.x10Def().thisDef();
Type ot = null; // FIXME
TypeSystem ts = t.typeSystem();
ClosureDef cd = ts.closureDef(ft.position(), Types.ref(nct), Types.ref(ncm), Types.ref(nfi.returnType()), fts, td, nfns, Types.ref(nfi.guard()), Types.ref(ot));
t = ts.closureType(cd);
}
} else if (t instanceof FunctionType) { // order matters!
FunctionType ft = (FunctionType) t;
Type rt = ft.returnType();
List<Type> tas = ft.typeParameters();
assert (tas.isEmpty());
List<LocalInstance> fns = ft.formalNames();
List<Type> ats = ft.argumentTypes();
CConstraint g = ft.guard();
Type nrt = transformType(rt);
List<LocalDef> nfns = new ArrayList<LocalDef>();
boolean changedFN = false;
for (LocalInstance li : fns) {
X10LocalDef ld = (X10LocalDef) li.def();
X10LocalDef newld = vars.get(ld);
if (newld == null) {
Type lt = Types.get(ld.type());
Type newlt = transformType(lt);
if (newlt != rt) {
newld = copyLocalDef(ld);
newld.setType(Types.ref(newlt));
} else {
newld = ld; // unchanged
}
mapLocal(ld, newld);
}
if (newld != ld) changedFN = true;
nfns.add(newld);
}
List<Type> nats = transformTypeList(ats);
CConstraint ng = transformConstraint(g);
if (nrt != rt || changedFN || nats != ats || ng != g) {
List<Ref<? extends Type>> fts = new ArrayList<Ref<? extends Type>>();
List<ParameterType> tps = Collections.<ParameterType>emptyList();
for (Type a : nats) {
fts.add(Types.ref(a));
}
TypeSystem ts = t.typeSystem();
t = ts.functionType(ft.position(), Types.ref(nrt), tps, fts, nfns, Types.ref(ng));
}
} else if (t instanceof X10ParsedClassType) { // order matters!
X10ParsedClassType qt = (X10ParsedClassType) t;
List<Type> tas = qt.typeArguments();
List<Type> ntas = transformTypeList(tas);
if (ntas != tas) {
qt = qt.typeArguments(ntas);
}
X10ClassType ct = (X10ClassType) qt.outer();
X10ClassType nct = ct == null ? null : (X10ClassType) transformType(ct);
if (ct != null && !qt.isInnerClass()) {
nct = nct.typeArguments(null);
TypeSystem ts = ct.typeSystem();
if (ts.typeEquals(nct, ct, ts.emptyContext())) {
nct = ct;
}
}
if (nct != ct) {
qt = qt.container(nct);
}
t = qt;
}
// TODO: annotations
return t;
}
@Override
public Node transform(Node n, Node old, ContextVisitor v) {
n = super.transform(n, old, v);
if (n instanceof Term) {
X10Ext_c ext = (X10Ext_c) n.ext();
Set<LocalDef> initVals = ext.initVals;
if (initVals != null) {
ext = (X10Ext_c) ext.copy();
ext.initVals = CollectionFactory.newHashSet();
for (LocalDef ld : initVals) {
ext.initVals.add(getLocal((X10LocalDef) ld));
}
n = n.ext(ext);
}
}
return n;
}
@Override
protected TypeParamNode transform(TypeParamNode pn, TypeParamNode old) {
ParameterType type = pn.type();
ParameterType pt = transformParameterType(type);
if (!pt.name().equals(type.name()))
pn = pn.name(visitor().nodeFactory().Id(pn.position(), pt.name()));
pn = pn.type(pt);
return pn;
}
@Override
protected TypeNode transform(TypeNode tn, TypeNode old) {
Ref<? extends Type> tr = transformRef(tn.typeRef());
Type t = Types.get(tr);
Type rt = transformType(t);
if (t != rt) {
tr = remapRef(tr);
((Ref<Type>)tr).update(rt);
}
if (tn.typeRef() != tr) {
tn = tn.typeRef(tr);
}
return tn;
}
@Override
protected Expr transformExpr(Expr e, Expr old) {
Type rt = transformType(e.type());
if (e.type() != rt) {
e = e.type(rt);
}
return super.transformExpr(e, old);
}
@Override
protected Local transform(Local l, Local old) {
X10LocalInstance li = (X10LocalInstance) l.localInstance();
X10LocalDef ld = getLocal(li.x10Def());
if (li.x10Def() != ld) {
X10LocalInstance newli = transformLocalInstance(((X10LocalInstance) ld.asInstance()));
li = li.lval() ? (X10LocalInstance)newli.lval(true) : newli;
}
if (l.localInstance() != li) {
return l.localInstance(li);
}
return l;
}
@Override
protected Field transform(Field f, Field old) {
X10FieldInstance fi = transformFieldInstance((X10FieldInstance) f.fieldInstance());
if (f.fieldInstance() != fi) {
return f.fieldInstance(fi);
}
return f;
}
@Override
protected Call transform(Call c, Call old) {
MethodInstance mi = transformMethodInstance((MethodInstance) c.methodInstance());
if (c.methodInstance() != mi) {
return c.methodInstance(mi);
}
return c;
}
@Override
protected New transform(New w, New old) {
X10ConstructorInstance ci = transformConstructorInstance((X10ConstructorInstance) w.constructorInstance());
if (w.constructorInstance() != ci) {
return w.constructorInstance(ci);
}
return w;
}
@Override
protected ClosureCall transform(ClosureCall c, ClosureCall old) {
MethodInstance ci = transformMethodInstance(c.closureInstance());
if (c.closureInstance() != ci) {
return c.closureInstance(ci);
}
return c;
}
@Override
protected SettableAssign transform(SettableAssign a, SettableAssign old) {
MethodInstance mi = transformMethodInstance(a.methodInstance());
MethodInstance ami = transformMethodInstance(a.applyMethodInstance());
if (a.methodInstance() != mi || a.applyMethodInstance() != ami) {
return a.methodInstance(mi).applyMethodInstance(ami);
}
return a;
}
@Override
protected FieldAssign transform(FieldAssign f, FieldAssign old) {
X10FieldInstance fi = transformFieldInstance((X10FieldInstance) f.fieldInstance());
if (f.fieldInstance() != fi) {
return f.fieldInstance(fi);
}
return f;
}
@Override
protected Closure transform(Closure d, Closure old) {
ClosureDef cd = d.closureDef();
boolean sigChanged = d.returnType() != old.returnType();
List<Formal> params = d.formals();
List<Formal> oldParams = old.formals();
for (int i = 0; i < params.size(); i++) {
sigChanged |= params.get(i) != oldParams.get(i);
}
sigChanged |= d.guard() != old.guard();
if (sigChanged) {
DepParameterExpr g = d.guard();
List<Ref<? extends Type>> argTypes = new ArrayList<Ref<? extends Type>>();
List<LocalDef> formalNames = new ArrayList<LocalDef>();
for (int i = 0; i < params.size(); i++) {
Formal p = params.get(i);
argTypes.add(p.type().typeRef());
formalNames.add(p.localDef());
}
TypeSystem xts = visitor().typeSystem();
ClosureDef icd = (ClosureDef) cd.copy();
icd.setReturnType(d.returnType().typeRef());
icd.setFormalTypes(argTypes);
icd.setFormalNames(formalNames);
icd.setGuard(g == null ? null : g.valueConstraint());
cd = icd;
}
List<VarInstance<? extends VarDef>> ce = cd.capturedEnvironment();
List<VarInstance<? extends VarDef>> ice = transformCapturedEnvironment(ce);
if (ce != ice) {
if (cd == d.closureDef()) {
cd = (ClosureDef) cd.copy();
}
cd.setCapturedEnvironment(ice);
}
return d.closureDef(cd);
}
@Override
protected Special transform(Special s, Special old) {
return s;
}
@Override
protected X10ConstructorCall transform(X10ConstructorCall c, X10ConstructorCall old) {
X10ConstructorInstance ci = transformConstructorInstance(c.constructorInstance());
if (c.constructorInstance() != ci) {
return c.constructorInstance(ci);
}
return c;
}
@Override
protected AssignPropertyCall transform(AssignPropertyCall p, AssignPropertyCall old) {
List<X10FieldInstance> ps = p.properties();
List<X10FieldInstance> acc = new ArrayList<X10FieldInstance>();
for (X10FieldInstance fi : p.properties()) {
X10FieldInstance xfi = transformFieldInstance(fi);
acc.add(xfi);
if (xfi != fi) {
ps = acc;
}
}
if (p.properties() != ps) {
return p.properties(ps);
}
return p;
}
@Override
protected AtStmt transform(AtStmt d, AtStmt old) {
AtDef ad = d.atDef();
List<VarInstance<? extends VarDef>> ce = transformCapturedEnvironment(ad.capturedEnvironment());
if (ad.capturedEnvironment() != ce) {
AtDef iad = (AtDef) ad.copy();
iad.setCapturedEnvironment(ce);
return d.atDef(iad);
}
return d;
}
@Override
protected AtEach transform(AtEach a, AtEach old) {
AtDef ad = a.atDef();
List<VarInstance<? extends VarDef>> ce = transformCapturedEnvironment(ad.capturedEnvironment());
if (ad.capturedEnvironment() != ce) {
AtDef iad = (AtDef) ad.copy();
iad.setCapturedEnvironment(ce);
return a.atDef(iad);
}
return a;
}
@Override
protected Async transform(Async a, Async old) {
AsyncDef ad = a.asyncDef();
List<VarInstance<? extends VarDef>> ce = transformCapturedEnvironment(ad.capturedEnvironment());
if (ad.capturedEnvironment() != ce) {
AsyncDef iad = (AsyncDef) ad.copy();
iad.setCapturedEnvironment(ce);
return a.asyncDef(iad);
}
return a;
}
private List<VarInstance<? extends VarDef>> transformCapturedEnvironment(List<VarInstance<? extends VarDef>> ce) {
List<VarInstance<? extends VarDef>> ice = new ArrayList<VarInstance<? extends VarDef>>();
List<VarInstance<? extends VarDef>> res = ce;
for (VarInstance<?> vi : ce) {
if (vi instanceof X10LocalInstance) {
X10LocalInstance li = (X10LocalInstance) vi;
X10LocalDef ld = getLocal(li.x10Def());
if (li.x10Def() != ld) {
li = transformLocalInstance(((X10LocalInstance) ld.asInstance()));
if (vi.lval()) {
li = (X10LocalInstance)li.lval(true);
}
res = ice;
}
ClosureDef_c.addCapturedVariable(ice, li);
} else if (vi instanceof X10FieldInstance) {
X10FieldInstance fi = transformFieldInstance((X10FieldInstance) vi);
if (fi != vi) {
res = ice;
}
ClosureDef_c.addCapturedVariable(ice, fi);
} else {
ClosureDef_c.addCapturedVariable(ice, vi);
}
}
return res;
}
@Override
protected LocalDecl transform(LocalDecl d, LocalDecl old) {
return (LocalDecl) transformVarDecl(d, old);
}
@Override
protected X10Formal transform(X10Formal f, X10Formal old) {
return (X10Formal) transformVarDecl(f, old);
}
private VarDecl transformVarDecl(VarDecl d, VarDecl old) {
boolean sigChanged = d.type() != old.type(); // conservative compare detects changes in substructure
// There may already be a localdef mapping for this variable. This happens when variable references
// are encountered before the declaration, e.g., in return types and in the variable initializer type
// (the self binding). If that happens, use the existing mapping, but validate.
// We use a mapping of a localdef to itself to indicate that a reference was encountered before the
// declaration, but processing the reference did not change the local def (which means that this
// method cannot change the local def either).
TypeSystem xts = visitor().typeSystem();
X10LocalDef ld = (X10LocalDef) d.localDef();
X10LocalDef mld = vars.get(ld);
if (mld != null) {
if (sigChanged) {
// validate the type
if (mld == ld || !xts.typeEquals(d.type().type(), Types.get(mld.type()), visitor().context())) {
throw new InternalCompilerError("Inconsistent local mapping for "+d.name().id(), d.position());
}
// adjust the return type node's type reference to match that of the stored localdef
d = d.type(d.type().typeRef(mld.type()));
}
// now use the new mapping
return d.localDef(mld);
}
if (sigChanged) {
X10LocalDef ild = copyLocalDef(ld);
ild.setType(d.type().typeRef());
mapLocal(ld, ild);
return d.localDef(ild);
} else {
mapLocal(ld, ld); // mark this localdef as having been processed
}
return d;
}
protected static final X10LocalDef copyLocalDef(X10LocalDef ld) {
TypeSystem xts = ld.typeSystem();
X10LocalDef res = xts.localDef(ld.position(), ld.flags(), Types.ref(Types.get(ld.type())), ld.name());
if (ld.isAsyncInit()) res.setAsyncInit(); // Cannot use copy() because it's not a deep clone
return res;
}
private static final class IdentityRefKey {
private Ref<?> v;
public IdentityRefKey(Ref<?> v) { this.v = v; }
public int hashCode() { return System.identityHashCode(v); }
public boolean equals(Object o) {
return o instanceof IdentityRefKey && ((IdentityRefKey)o).v == this.v;
}
}
private final Map<IdentityRefKey, Ref<?>> refs = CollectionFactory.newHashMap();
@SuppressWarnings("unchecked")
protected <T> Ref<T> remapRef(Ref<T> ref) {
if (ref == null) return null;
IdentityRefKey key = new IdentityRefKey(ref);
Ref<T> remappedRef = (Ref<T>) refs.get(key);
if (remappedRef == null) {
refs.put(key, remappedRef = Types.ref(ref.get()));
}
return remappedRef;
}
protected final Map<X10LocalDef, X10LocalDef> vars = CollectionFactory.newHashMap();
protected void mapLocal(X10LocalDef def, X10LocalDef newDef) {
vars.put(def, newDef);
}
protected X10LocalDef getLocal(X10LocalDef def) {
X10LocalDef remappedDef = vars.get(def);
return remappedDef != null ? remappedDef : def;
}
}