/*
* 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.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import polyglot.ast.Block;
import polyglot.ast.ClassBody;
import polyglot.ast.ClassDecl;
import polyglot.ast.Id;
import polyglot.ast.Id_c;
import polyglot.ast.Node;
import polyglot.ast.NodeFactory;
import polyglot.frontend.Job;
import polyglot.types.ConstructorDef;
import polyglot.types.ContainerType;
import polyglot.types.FieldDef;
import polyglot.types.InitializerDef;
import polyglot.types.LocalDef;
import polyglot.types.MemberDef;
import polyglot.types.MethodDef;
import polyglot.types.Name;
import polyglot.types.ProcedureDef;
import polyglot.types.Ref;
import polyglot.types.SemanticException;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.TypeSystem_c;
import polyglot.types.Types;
import polyglot.types.VarDef;
import polyglot.types.VarInstance;
import polyglot.util.Pair;
import polyglot.visit.ContextVisitor;
import polyglot.visit.NodeVisitor;
import x10.ast.Closure_c;
import x10.ast.PropertyDecl;
import x10.ast.TypeParamNode;
import x10.ast.TypeParamNode_c;
import x10.ast.X10ClassDecl_c;
import x10.ast.X10ConstructorDecl;
import x10.ast.X10ConstructorDecl_c;
import x10.ast.X10MethodDecl;
import x10.ast.X10MethodDecl_c;
import x10.types.ClosureDef;
import x10.types.ParameterType;
import x10.types.TypeParamSubst;
import x10.types.X10ClassDef;
import x10.types.X10ConstructorDef;
import x10.types.X10LocalDef;
import x10.types.constraints.CConstraint;
import x10.types.constraints.TypeConstraint;
import x10.util.CollectionFactory;
/**
* This visitor alpha-renames type parameters that have the same name as another
* type parameter in outer scope.
* WARNING this class modifies type objects (X10ClassDef etc) in place without copying them
*
* Needs to be a ContextVistitor so we can use TypeSubst to change the types (even though full power of TypeSubst is not needed)
*
* @author Dave Cunningham (originally: unknown, probably Igor)
*/
public class TypeParamAlphaRenamer extends ContextVisitor {
TypeParamAlphaRenamer parent;
// each name maps to the original and the transformed type (aliases if not being transformed)
private Map<Name, Pair<ParameterType,ParameterType>> map;
// This guy keeps internal state that is generated at AST leaves and must be visible at other leaves
// Construct it in the root visitor (parent == null) and alias it in all children, updating the
// substitution before each transform.
TypeParamSubstTransformer tpst;
protected TypeParamAlphaRenamer(Map<Name, Pair<ParameterType,ParameterType>> map, TypeParamAlphaRenamer parent) {
super(parent.job, parent.ts, parent.nf);
//this.job = parent.job;
//this.ts = parent.ts;
//this.nf = parent.nf;
this.map = map;
this.parent = parent;
this.context = parent.context;
this.tpst = parent.tpst;
}
public TypeParamAlphaRenamer(Job job, TypeSystem ts, NodeFactory nf) {
super(job,ts,nf);
//this.job = job;
//this.ts = ts;
//this.nf = nf;
this.map = new HashMap<Name, Pair<ParameterType,ParameterType>>();
this.parent = null;
this.context = ts.emptyContext();
tpst = new TypeParamSubstTransformer(null);
}
// checks inner scopes first
// returns null if the type does not exist, otherwise returns the pair
private Pair<ParameterType,ParameterType> lookupMap(Name name) {
for (TypeParamAlphaRenamer t = this; t!= null ; t = t.parent) {
if (t.map.containsKey(name)) return t.map.get(name);
}
return null;
}
protected TypeParamSubst buildSubst() {
// squash maps into a single map
Map<Name, Pair<ParameterType,ParameterType>> squashed = new HashMap<Name, Pair<ParameterType,ParameterType>>();
for (TypeParamAlphaRenamer t = this; t!= null ; t = t.parent) {
for (Entry<Name, Pair<ParameterType,ParameterType>> e : t.map.entrySet()) {
// let inner scopes overwrite outer scopes
if (!squashed.containsKey(e.getKey()))
squashed.put(e.getKey(), e.getValue());
}
}
// build a TypeParamSubst out of it
List<ParameterType> from = new ArrayList<ParameterType>();
List<ParameterType> to = new ArrayList<ParameterType>();
for (Entry<Name, Pair<ParameterType,ParameterType>> e : squashed.entrySet()) {
ParameterType from_ = e.getValue().fst();
ParameterType to_ = e.getValue().snd();
if (from_ != to_) {
from.add(from_);
to.add(to_);
}
}
assert from.size() == to.size() : from.size()+" != "+to.size();
if (from.size() == 0) return null;
return new TypeParamSubst(ts, to, from, true);
}
@Override
public NodeVisitor enterCall(Node parent, Node n) {
List<ParameterType> bound_params = null;
boolean static_scope = false;
if (n instanceof X10ClassDecl_c) {
X10ClassDecl_c n2 = (X10ClassDecl_c) n;
bound_params = n2.classDef().typeParameters();
static_scope = n2.flags().flags().isStatic();
} else if (n instanceof X10MethodDecl_c) {
X10MethodDecl_c n2 = (X10MethodDecl_c) n;
bound_params = n2.methodDef().typeParameters();
static_scope = n2.flags().flags().isStatic();
}
// constructors do not introduce additional type parameters
if (bound_params == null) return this;
Map<Name, Pair<ParameterType,ParameterType>> new_scope = new HashMap<Name,Pair<ParameterType,ParameterType>>();
for (ParameterType p : bound_params) {
ParameterType map_to = p;
Pair<ParameterType,ParameterType> outer = lookupMap(p.name());
if (outer!=null && !static_scope) {
// this name clashes, rewrite it
map_to = new ParameterType(ts, p.position(), p.position().markCompilerGenerated(), Name.makeFresh(p.name()), p.def());
}
new_scope.put(p.name(), new Pair<ParameterType,ParameterType>(p, map_to));
}
return new TypeParamAlphaRenamer(new_scope, this);
}
@Override
public Node leaveCall(Node parent, Node old, Node n, NodeVisitor v) {
TypeParamAlphaRenamer v2 = (TypeParamAlphaRenamer)v;
TypeParamSubst subst = v2.buildSubst();
if (subst == null) return n;
// First, apply the subst
tpst.subst(subst);
n = tpst.transform(n, old, v2);
// Now, fix the defs which apparently are not changed by the type substitution
if (n instanceof X10ClassDecl_c) {
X10ClassDecl_c n2 = (X10ClassDecl_c) n;
X10ClassDef def = (X10ClassDef)n2.classDef();
for (FieldDef fd : def.fields()) {
adjustFieldDef(fd, subst);
}
for (MethodDef md : def.methods()) {
adjustMethodDef(md, subst);
}
for (ConstructorDef cd : def.constructors()) {
adjustConstructorDef(cd, subst);
}
def.superType(subst.reinstantiate(def.superType()));
def.setInterfaces(subst.reinstantiate(def.interfaces()));
// [DC] these guys were updated already
List<TypeParamNode> new_type_param_nodes = n2.typeParameters();
// [DC] whack the class def with new type parameter types
for (int i = 0; i < new_type_param_nodes.size(); i++) {
ParameterType new_param = new_type_param_nodes.get(i).type();
def.replaceTypeParameter(i, new_param, def.variances().get(i));
}
def.setSubst(subst); // [DC] unsure how well this works
}
if (n instanceof X10MethodDecl_c) {
X10MethodDecl_c n2 = (X10MethodDecl_c) n;
MethodDef def = n2.methodDef();
adjustMethodDef(def, subst);
// [DC] these guys were updated already
List<TypeParamNode> new_type_param_nodes = n2.typeParameters();
// [DC] whack the method def with new type parameter types
List<ParameterType> tps = new ArrayList<ParameterType>();
for (int i = 0; i < def.typeParameters().size(); i++) {
ParameterType new_param = new_type_param_nodes.get(i).type();
tps.add(new_param);
}
def.setTypeParameters(tps);
}
if (n instanceof X10ConstructorDecl_c) {
X10ConstructorDecl_c n2 = (X10ConstructorDecl_c)n;
X10ConstructorDef def = n2.constructorDef();
adjustConstructorDef(def, subst);
}
/*if (n instanceof Closure_c) {
Closure_c n2 = (Closure_c)n;
ClosureDef def = n2.closureDef();
List<VarInstance<? extends VarDef>> new_env = new ArrayList<VarInstance<? extends VarDef>>();
for (VarInstance<? extends VarDef> env_vi : def.capturedEnvironment()) {
new_env.add(subst.reinstantiate(env_vi));
}
def.setCapturedEnvironment(new_env);
}*/
return n;
}
private static void adjustFieldDef(FieldDef fd, TypeParamSubst subst) {
Type t = Types.get(fd.type());
((Ref<Type>) fd.type()).update(subst.reinstantiate(t));
InitializerDef id = fd.initializer();
adjustMemberDef(id, subst);
}
private static void adjustConstructorDef(ConstructorDef cd, TypeParamSubst subst) {
adjustMemberDef(cd, subst);
adjustProcedureDef(cd, subst);
}
private static void adjustMethodDef(MethodDef md, TypeParamSubst subst) {
adjustMemberDef(md, subst);
adjustProcedureDef(md, subst);
}
private static void adjustProcedureDef(ProcedureDef pd, TypeParamSubst subst) {
Type r = Types.get(pd.returnType());
((Ref<Type>) pd.returnType()).update(subst.reinstantiate(r));
List<Ref<? extends Type>> ft = pd.formalTypes();
List<LocalDef> fn = pd.formalNames();
for (Ref<? extends Type> f : ft) {
((Ref<Type>) f).update(subst.reinstantiate(Types.get(f)));
}
for (LocalDef f : fn) {
((Ref<Type>) f.type()).update(subst.reinstantiate(Types.get(f.type())));
}
Type o = Types.get(pd.offerType());
if (o != null) {
((Ref<Type>) pd.offerType()).update(subst.reinstantiate(o));
}
CConstraint g = Types.get(pd.guard());
if (g != null) {
pd.guard().update(subst.reinstantiate(g));
}
TypeConstraint q = Types.get(pd.typeGuard());
if (q != null) {
pd.typeGuard().update(subst.reinstantiate(q));
}
}
private static void adjustMemberDef(MemberDef md, TypeParamSubst subst) {
if (md == null) return;
ContainerType c = Types.get(md.container());
((Ref<ContainerType>) md.container()).update(subst.reinstantiate(c));
}
}