package org.simantics.scl.compiler.types; import java.util.ArrayList; import org.simantics.scl.compiler.common.exceptions.InternalCompilerError; import org.simantics.scl.compiler.environment.Environment; import org.simantics.scl.compiler.internal.types.HashCodeUtils; import org.simantics.scl.compiler.internal.types.TypeHashCodeContext; import org.simantics.scl.compiler.internal.types.ast.TVarAst; import org.simantics.scl.compiler.internal.types.ast.TypeAst; import org.simantics.scl.compiler.types.exceptions.KindUnificationException; import org.simantics.scl.compiler.types.exceptions.UnificationException; import org.simantics.scl.compiler.types.kinds.KMetaVar; import org.simantics.scl.compiler.types.kinds.Kind; import org.simantics.scl.compiler.types.kinds.Kinds; import org.simantics.scl.compiler.types.util.Polarity; import org.simantics.scl.compiler.types.util.TypeUnparsingContext; import gnu.trove.map.hash.THashMap; import gnu.trove.set.hash.THashSet; /** * A meta-variable, a type that is currently unknown. This class * should occur only during type inference. Method removeMetaVars * can be used to remove all instances of TMetaVar from the type. * * @author Hannu Niemistö */ public class TMetaVar extends Type { public static final TMetaVar[] EMPTY_ARRAY = new TMetaVar[0]; public static final boolean DEBUG = false; Type ref = null; Type skeletonRef = null; Polarity polarity = Polarity.NO_POLARITY; private Kind kind; private TMetaVarListener listener; public static abstract class TMetaVarListener { private Object prev; // TMetaVarListener or TMetaVar private TMetaVarListener next; public abstract void notifyAboutChange(); public void remove() { if(prev == null) return; // Not added or not anymore listening TMetaVar if(prev instanceof TMetaVar) ((TMetaVar)prev).listener = next; else ((TMetaVarListener)prev).next = next; if(next != null) { next.prev = prev; next = null; } prev = null; } } TMetaVar(Kind kind) { this.kind = kind; } TMetaVar(Kind kind, Polarity polarity) { this.kind = kind; this.polarity = polarity; } public Kind getKind() { if(kind instanceof KMetaVar) kind = Kinds.canonical(kind); return kind; } @Override public Type replace(TVar var, Type replacement) { if(ref == null) return this; else { Type newRef = ref.replace(var, replacement); if(newRef != ref) return newRef; else // We could also return newRef here, but in this way // we don't have to copy so many parent types. return this; } } @Override public TypeAst toTypeAst(TypeUnparsingContext context) { if(ref == null) return new TVarAst(/*polarity.getSymbol() +*/ context.getName(this)); else return ref.toTypeAst(context); } @Override public void updateHashCode(TypeHashCodeContext context) { if(ref == null) context.append(System.identityHashCode(this)); else ref.updateHashCode(context); } @Override public void collectFreeVars(ArrayList vars) { if(ref != null) ref.collectFreeVars(vars); } @Override public void collectMetaVars(ArrayList vars) { if(ref == null) vars.add(this); else ref.collectMetaVars(vars); } @Override public void collectMetaVars(THashSet vars) { if(ref == null) vars.add(this); else ref.collectMetaVars(vars); } @Override public void collectEffectMetaVars(ArrayList vars) { if(ref != null) ref.collectEffectMetaVars(vars); } public void setRef(Type type) throws UnificationException { if(type instanceof TMetaVar && ((TMetaVar)type).ref != null) throw new InternalCompilerError("Not canonical!"); if(type == this) throw new InternalCompilerError("Illegal setRef"); if(DEBUG) System.out.println("setRef " + System.identityHashCode(this) + " -> " + type); if(ref != null) throw new InternalCompilerError("Method setRef should be called only for unbound meta variables."); if(type.contains(this)) throw new UnificationException(this, type); ref = type; if(polarity != Polarity.NO_POLARITY) type.addPolarity(polarity); if(skeletonRef != null) { Type skeleton = skeletonRef; skeletonRef = null; Skeletons.unifySkeletons(skeleton, type); } fireNotifyAboutChange(); } public Type getRef() { return ref; } @Override public boolean contains(TMetaVar other) { if(ref != null) return ref.contains(other); else if(skeletonRef != null) return skeletonRef.contains(other); else return this == other; } @Override public Type convertMetaVarsToVars() { if(ref == null) { if(kind == Kinds.EFFECT && !polarity.isNegative()) ref = Types.NO_EFFECTS; else ref = Types.var(getKind()); return ref; } else return ref.convertMetaVarsToVars(); } @Override public boolean isGround() { if(ref == null) return false; else return ref.isGround(); } public Kind inferKind(Environment context) throws KindUnificationException { return Kinds.metaVar(); } @Override public boolean containsMetaVars() { if(ref == null) return true; else return ref.containsMetaVars(); } @Override public void toName(TypeUnparsingContext context, StringBuilder b) { if(ref != null) ref.toName(context, b); else b.append(context.getName(this)); } @Override public int getClassId() { return METAVAR_ID; } @Override public boolean isMaximal() { return ref != null && ref.isMaximal(); } @Override public boolean isMinimal() { return ref != null && ref.isMinimal(); } @Override public void addPolarity(Polarity polarity) { if(ref != null) ref.addPolarity(polarity); else this.polarity = this.polarity.add(polarity); } public Polarity getPolarity() { return polarity; } @Override public void collectConcreteEffects(ArrayList concreteEffects) { if(ref != null) ref.collectConcreteEffects(concreteEffects); } @Override public Type head() { if(ref != null) return ref.head(); else return this; } @Override public Type copySkeleton(THashMap metaVarMap) { if(ref != null) return ref.copySkeleton(metaVarMap); else { TMetaVar result = metaVarMap.get(this); if(result == null) { result = new TMetaVar(kind, polarity); metaVarMap.put(this, result); } return result; } } public void setSkeletonRef(Type type) throws UnificationException { if(DEBUG) System.out.println("setSkeletonRef " + System.identityHashCode(this) + " -> " + type); if(ref != null || skeletonRef != null) throw new InternalCompilerError("Method setRef should be called only for unbound meta variables."); if(type.contains(this)) throw new UnificationException(this, type); this.skeletonRef = type; fireNotifyAboutChange(); } @Override public int hashCode() { if(ref == null) return System.identityHashCode(this); else return ref.hashCode(); } @Override public int hashCode(int hash) { if(ref == null) return HashCodeUtils.update(hash, System.identityHashCode(this)); else return ref.hashCode(hash); } @Override public int hashCode(int hash, TVar[] boundVars) { if(ref == null) return HashCodeUtils.update(hash, System.identityHashCode(this)); else return ref.hashCode(hash, boundVars); } @Override public int skeletonHashCode() { if(ref != null) return ref.skeletonHashCode(); else if(skeletonRef != null) return skeletonRef.skeletonHashCode(); else return System.identityHashCode(this); } @Override public int skeletonHashCode(int hash) { if(ref != null) return ref.skeletonHashCode(hash); else if(skeletonRef != null) return skeletonRef.skeletonHashCode(hash); else return HashCodeUtils.update(hash, System.identityHashCode(this)); } @Override public int skeletonHashCode(int hash, TVar[] boundVars) { if(ref != null) return ref.skeletonHashCode(hash, boundVars); else if(skeletonRef != null) return skeletonRef.skeletonHashCode(hash, boundVars); else return HashCodeUtils.update(hash, System.identityHashCode(this)); } @Override public boolean equalsCanonical(Type other) { return this == other; } @Override public Type canonical() { if(ref == null) return this; else return ref = ref.canonical(); } public void addListener(TMetaVarListener newListener) { if(DEBUG) System.out.println("addListener " + System.identityHashCode(this)); newListener.next = listener; newListener.prev = this; if(listener != null) listener.prev = newListener; listener = newListener; } private void fireNotifyAboutChange() { if(DEBUG) System.out.println("fireNotifyAboutChange " + System.identityHashCode(this) + " " + ref); TMetaVarListener cur = listener; listener = null; while(cur != null) { if(DEBUG) System.out.println(" call listener"); cur.prev = null; // This prevents TMetaVarListener.remove from doing anything cur.notifyAboutChange(); TMetaVarListener next = cur.next; cur.next = null; cur = next; } } public TMetaVarListener getLatestListener() { return listener; } @Override public Kind getKind(Environment context) { return kind; } @Override public Type[] skeletonCanonicalChildren() { // Assumes that this is already canonical skeleton return EMPTY_ARRAY; } }