/*
* 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.types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import polyglot.types.ContainerType;
import polyglot.types.LazyRef;
import polyglot.types.Ref;
import polyglot.types.Type;
import polyglot.types.Types;
import polyglot.types.TypeSystem;
import polyglot.util.Pair;
import polyglot.util.Transformation;
import polyglot.util.TransformingList;
import x10.constraint.XFailure;
import x10.constraint.XVar;
import x10.constraint.XTerm;
import x10.types.constraints.ConstraintManager;
import x10.types.constraints.CConstraint;
import x10.types.constraints.CConstraint;
import x10.types.constraints.SubtypeConstraint;
import x10.types.constraints.SubtypeConstraint;
import x10.types.constraints.TypeConstraint;
/**
* Implements a type substitution [ActualT1,..., ActualTn/FormalX1,..., FormalXn].
* A substitution is lazy by default (creating a reinstantiated wrapper object for
* constructor and method instances), but can also be eager (replacing the types
* right away). Eager substitutions cannot be used until after typechecking, as
* that may introduce dependency cycles.
* @author nystrom
*/
public class TypeParamSubst {
public static final TypeParamSubst IDENTITY = new TypeParamSubst();
private final List<? extends Type> typeArguments;
private final List<ParameterType> typeParameters;
private final TypeSystem ts;
private final boolean identityInstantiation;
private final boolean eager;
public TypeParamSubst(TypeSystem ts, List<? extends Type> tas, List<ParameterType> tps) {
this(ts, tas, tps, false);
}
public TypeParamSubst(TypeSystem ts, List<? extends Type> tas, List<ParameterType> tps, boolean eager) {
this.identityInstantiation = isIdentityInstantiation(tas, tps);
this.eager = eager;
tas = tas == null ? tps : tas;
if (!(tps == null ? tas == null : tas.size() == tps.size())) {
assert (tps == null ? tas == null : tas.size() == tps.size());
}
this.ts = ts;
this.typeArguments = tas == null ? Collections.<Type>emptyList() : tas;
this.typeParameters = tps == null ? Collections.<ParameterType>emptyList() : tps;
}
private TypeParamSubst() {
this(null, null, null, false);
}
public ArrayList<Type> copyTypeArguments() {
return new ArrayList<Type>(typeArguments);
}
public ArrayList<ParameterType> copyTypeParameters() {
return new ArrayList<ParameterType>(typeParameters);
}
public static boolean isSameParameter(ParameterType pt1, ParameterType pt2) {
return pt1 == pt2 ||
(Types.get(pt1.def()) == Types.get(pt2.def()) && pt1.name().equals(pt2.name()));
}
private Type reinstantiateType(Type t, boolean forceTypeArguments) {
if (t instanceof ParameterType) { // always eager
ParameterType pt = (ParameterType) t;
for (int i = 0; i < typeParameters.size(); i++) {
ParameterType pt2 = typeParameters.get(i);
if (i < typeArguments.size()) {
if (isSameParameter(pt, pt2)) {
return typeArguments.get(i);
}
}
}
return pt;
}
if (t instanceof ConstrainedType) { // always eager
ConstrainedType ct = (ConstrainedType) t;
ct = ct.baseType(reinstantiate(ct.baseType()));
ct = ct.constraint(reinstantiate(ct.constraint()));
return ct;
}
if (t instanceof MacroType) {
MacroType mt = (MacroType) t;
if (eager) {
mt = mt.definedType(reinstantiate(mt.definedType()));
mt = (MacroType) mt.formalNames(reinstantiate(mt.formalNames()));
mt = (MacroType) mt.formalTypes(reinstantiate(mt.formalTypes()));
mt = (MacroType) mt.typeParameters(reinstantiate(mt.typeParameters()));
mt = mt.guard(reinstantiate(mt.guard()));
}
return new ReinstantiatedMacroType(this, ts, mt.position(), Types.ref(mt.def()), mt);
}
// if (t instanceof ClosureType) {
// ClosureType ct = (ClosureType) t;
// ct = ct.closureInstance(reinstantiate(ct.applyMethod()));
// return ct;
// }
if (t instanceof X10ClassType) { // always eager
X10ClassType ct = (X10ClassType) t;
if (!canReferToParams(ct))
return ct;
List<Type> typeArgs = ct.typeArguments();
List<ParameterType> tParams = ct.x10Def().typeParameters();
if (typeArgs == null && forceTypeArguments && !tParams.isEmpty()) {
typeArgs = new ArrayList<Type>(tParams);
}
// [DC] surely the following is an error in the compiler -- any pass that changes
// the def should also update the types to avoid such inconsistencies
if (typeArgs != null && typeArgs.size() < tParams.size()) {
typeArgs = new ArrayList<Type>(typeArgs);
// The def changed since the type was created; params were added
for (int i = typeArgs.size(); i < tParams.size(); i++) {
typeArgs.add(tParams.get(i));
}
}
ct = ct.typeArguments(reinstantiate(typeArgs));
if (ct.isMember()) {
ct = ct.container((ContainerType) reinstantiateType(ct.container(), ct.isInnerClass()));
}
return ct;
}
return t;
}
private static boolean canReferToParams(X10ClassType t) {
// FIXME: to fix XTENLANG-2055, this should be false for null typeargs,
// but constructor and method def containers are not instantiated.
// TODO: (t.typeArguments() != null && t.typeArguments().size() != 0)
if (t.typeArguments() == null || t.typeArguments().size() != 0) {
return true;
}
if (t.isMember())
return canReferToParams((X10ClassType) t.outer());
if (! t.isTopLevel())
return true;
return false;
}
public boolean isIdentityInstantiation() {
return identityInstantiation;
}
public boolean isEager() {
return eager;
}
private static boolean isIdentityInstantiation(List<? extends Type> tas, List<ParameterType> tps) {
if (tas == null) return true;
int n = tps.size();
if (n != tas.size()) return false;
for (int i = 0; i < n; i++) {
ParameterType pt = tps.get(i);
Type at = tas.get(i);
if (at instanceof ParameterType) {
ParameterType apt = (ParameterType) at;
if (! isSameParameter(pt, apt))
return false;
}
else {
return false;
}
}
return true;
}
//private Map<Object,Object> cache = CollectionFactory.newHashMap();
//@SuppressWarnings("unchecked") // Casting to a generic type parameter
public <T> T reinstantiate(T t) {
if (t == null)
return null;
//Object o = cache.get(t);
//if (o != null && false)
// return (T) o;
T x = reinstantiateUncached(t);
//cache.put(t, x);
return x;
}
@SuppressWarnings("unchecked") // Casting to a generic type parameter
private <T> T reinstantiateUncached(T t) {
if (isIdentityInstantiation()) {
return t;
}
if (t instanceof Ref<?>) return (T) reinstantiateRef((Ref<?>) t);
if (t instanceof Type) return (T) reinstantiateType((Type) t, true);
if (t instanceof X10FieldInstance) return (T) reinstantiateFI((X10FieldInstance) t);
if (t instanceof MethodInstance) return (T) reinstantiateMI((MethodInstance) t);
if (t instanceof X10ConstructorInstance) return (T) reinstantiateCI((X10ConstructorInstance) t);
if (t instanceof ClosureInstance) return (T) reinstantiateClosure((ClosureInstance) t);
if (t instanceof CConstraint) return (T) reinstantiateConstraint((CConstraint) t);
if (t instanceof XTerm) return (T) reinstantiateTerm((XTerm) t);
if (t instanceof TypeConstraint) return (T) reinstantiateTypeConstraint((TypeConstraint) t);
if (t instanceof X10LocalInstance) return (T) reinstantiateLI((X10LocalInstance) t);
//if (t instanceof X10LocalDef) return (T) reinstantiateLD((X10LocalDef) t);
if (t instanceof TypeParamSubst) return (T) reinstantiateTPS((TypeParamSubst) t);
assert false : t;
return t;
}
private TypeParamSubst reinstantiateTPS(TypeParamSubst t) {
//List<? extends Type> tas = reinstantiate(t.typeArguments);
List<ParameterType> tps = reinstantiate(t.typeParameters);
return new TypeParamSubst(ts, t.typeArguments, tps, t.eager);
}
private X10LocalInstance reinstantiateLI(X10LocalInstance t) {
if (eager) {
X10LocalInstance li = t;
li = li.type(reinstantiate(li.type()));
return li;
}
return new ReinstantiatedLocalInstance(this, t.typeSystem(), t.position(), Types.ref(t.x10Def()), t);
}
/*
public X10LocalInstance reinstantiateLD(X10LocalDef t) {
final X10LocalDef ld = (X10LocalDef) t.copy();
return new ReinstantiatedLocalDef(this, ld.typeSystem(), ld.position(), Types.ref(ld.x10Def()), ld);
}
*/
private ClosureInstance reinstantiateClosure(ClosureInstance t) {
if (eager) {
final ClosureInstance fi = t;
ClosureInstance res = new ClosureInstance_c(fi.typeSystem(), fi.position(), fi.errorPosition(), Types.ref(fi.def()));
res = (ClosureInstance) res.returnType(reinstantiate(fi.returnType()));
res = (ClosureInstance) res.formalNames(reinstantiate(fi.formalNames()));
res = (ClosureInstance) res.formalTypes(reinstantiate(fi.formalTypes()));
//res = (ClosureInstance) res.throwTypes(reinstantiate(fi.throwTypes()));
res = (ClosureInstance) res.guard(reinstantiate(fi.guard()));
return res;
}
return new ReinstantiatedClosureInstance_c(this, t.typeSystem(), t.position(), Types.ref(t.def()), t);
}
private <T> Ref<T> reinstantiateRef(final Ref<T> t) {
if (eager || t.known()) {
return Types.ref(reinstantiate(t.get()));
}
final LazyRef<T> r = Types.lazyRef(null);
r.setResolver(new Runnable() {
public void run() {
r.update(reinstantiate(t.get()));
}
});
return r;
}
public static CConstraint reinstantiateConstraint(X10ClassType ct, CConstraint c) {
if (c == null || c.valid())
return c;
CConstraint result = c;
if (ct instanceof X10ParsedClassType_c) {
X10ParsedClassType_c t = (X10ParsedClassType_c) ct;
if (t.isIdentityInstantiation()) {
return c;
}
result = t.subst().reinstantiateConstraint(c);
}
return result;
}
public static TypeConstraint reinstantiateTypeConstraint(X10ClassType ct, TypeConstraint c) {
if (c == null)
return c;
TypeConstraint result = c;
if (ct instanceof X10ParsedClassType_c) {
X10ParsedClassType_c t = (X10ParsedClassType_c) ct;
if (t.isIdentityInstantiation()) {
return c;
}
result = t.subst().reinstantiateTypeConstraint(c);
}
return result;
}
private CConstraint reinstantiateConstraint(CConstraint c) {
int n = typeParameters.size();
assert typeArguments.size() == n;
XTerm[] ys = new XTerm[n];
XVar[] xs = new XVar[n];
for (int i = 0; i < n; i++) {
ParameterType pt = typeParameters.get(i);
Type at = typeArguments.get(i);
XTerm p = ts.xtypeTranslator().translate(pt);
XTerm a = ts.xtypeTranslator().translate(at);
ys[i] = a;
if (p instanceof XVar) {
xs[i] = (XVar) p;
}
else {
xs[i] = ConstraintManager.getConstraintSystem().makeLit("error");
}
}
CConstraint result;
try {
result = c.substitute(ys, xs);
}
catch (XFailure e) {
result = ConstraintManager.getConstraintSystem().makeCConstraint();
result.setInconsistent();
}
return result;
}
private TypeConstraint reinstantiateTypeConstraint(TypeConstraint c) {
int n = typeParameters.size();
assert typeArguments.size() == n;
boolean changed = false;
List<SubtypeConstraint> terms = new ArrayList<SubtypeConstraint>(c.terms().size());
for (SubtypeConstraint s : c.terms()) {
Type sub = s.subtype();
Type sup = s.supertype();
Type sub1 = reinstantiate(sub);
Type sup1 = reinstantiate(sup);
if (sub != sub1 || sup != sup1) {
changed = true;
s = new SubtypeConstraint(sub1, sup1, s.kind());
}
terms.add(s);
}
if (changed) {
c = new TypeConstraint();
c.addTerms(terms);
}
return c;
}
private XTerm reinstantiateTerm(XTerm t) {
int n = typeParameters.size();
assert typeArguments.size() == n;
for (int i = 0; i < n; i++) {
ParameterType pt = typeParameters.get(i);
Type at = typeArguments.get(0);
XTerm p = ts.xtypeTranslator().translate(pt);
XTerm a = ts.xtypeTranslator().translate(at);
if (p instanceof XVar) {
t = t.subst(p, (XVar) a);
}
}
return t;
}
private X10ConstructorInstance reinstantiateCI(X10ConstructorInstance t) {
if (eager) {
X10ConstructorInstance ci = t;
ci = (X10ConstructorInstance) ci.returnType(reinstantiate(ci.returnType()));
ci = (X10ConstructorInstance) ci.formalNames(reinstantiate(ci.formalNames()));
ci = (X10ConstructorInstance) ci.formalTypes(reinstantiate(ci.formalTypes()));
ci = (X10ConstructorInstance) ci.throwTypes(reinstantiate(ci.throwTypes()));
ci = (X10ConstructorInstance) ci.typeParameters(reinstantiate(ci.typeParameters()));
ci = (X10ConstructorInstance) ci.container(reinstantiate(ci.container()));
ci = (X10ConstructorInstance) ci.guard(reinstantiate(ci.guard()));
ci = (X10ConstructorInstance) ci.typeGuard(reinstantiate(ci.typeGuard()));
return ci;
}
return new ReinstantiatedConstructorInstance(this, t.typeSystem(), t.position(), Types.ref(t.x10Def()), t);
}
private MethodInstance reinstantiateMI(MethodInstance t) {
if (eager) {
MethodInstance mi = t;
mi = (MethodInstance) mi.typeParameters(reinstantiate(mi.typeParameters()));
mi = (MethodInstance) mi.returnType(reinstantiate(mi.returnType()));
mi = (MethodInstance) mi.formalNames(reinstantiate(mi.formalNames()));
mi = (MethodInstance) mi.formalTypes(reinstantiate(mi.formalTypes()));
mi = (MethodInstance) mi.throwTypes(reinstantiate(mi.throwTypes()));
mi = (MethodInstance) mi.container(reinstantiate(mi.container()));
mi = (MethodInstance) mi.guard(reinstantiate(mi.guard()));
mi = (MethodInstance) mi.typeGuard(reinstantiate(mi.typeGuard()));
return mi;
}
return new ReinstantiatedMethodInstance(this, t.typeSystem(), t.position(), Types.ref(t.x10Def()), t);
}
private X10FieldInstance reinstantiateFI(X10FieldInstance t) {
if (eager) {
X10FieldInstance fi = t;
fi = (X10FieldInstance) fi.type(reinstantiate(fi.type()));
fi = (X10FieldInstance) fi.container(reinstantiate(fi.container()));
return fi;
}
return new ReinstantiatedFieldInstance(this, t.typeSystem(), t.position(), Types.ref(t.x10Def()), t);
}
public <T> List<T> reinstantiate(List<T> list) {
if (isIdentityInstantiation()) {
return list;
}
if (list == null)
return null;
if (eager) {
boolean changed = false;
List<T> res = new ArrayList<T>();
for (T t : list) {
T r = reinstantiate(t);
changed |= (r != t);
res.add(r);
}
if (!changed)
return list;
return res;
}
return new TransformingList<T, T>(list, new Transformation<T, T>() {
public T transform(T o) {
return reinstantiate(o);
}
});
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(typeArguments);
sb.append("/");
sb.append(typeParameters);
sb.append("]");
return sb.toString();
}
}