package org.simantics.scl.compiler.types;
-import gnu.trove.map.hash.THashMap;
-import gnu.trove.set.hash.THashSet;
-
import java.util.ArrayList;
+import org.simantics.databoard.util.IdentityHashSet;
+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.util.Polarity;
import org.simantics.scl.compiler.types.util.TypeUnparsingContext;
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
/**
*/
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;
@Override
public TypeAst toTypeAst(TypeUnparsingContext context) {
- if(ref == null)
- return new TVarAst(/*polarity.getSymbol() +*/ context.getName(this));
- else
+ if(ref != null)
return ref.toTypeAst(context);
- }
-
- @Override
- public int hashCode() {
- if(ref == null)
- return System.identityHashCode(this);
+ else if(context.showSkeletons && skeletonRef != null)
+ return skeletonRef.toTypeAst(context);
else
- return ref.hashCode();
+ return new TVarAst(/*polarity.getSymbol() +*/ context.getName(this));
}
@Override
ref.collectEffectMetaVars(vars);
}
- public void setRef(Type a) throws UnificationException {
- a = Types.weakCanonical(a);
- if(a.contains(this))
- throw new UnificationException(this, a);
- ref = a;
+ public void setRef(Type type) throws UnificationException {
+ //System.out.println("----");
+ //System.out.println("this = " + refStructure(this));
+ //System.out.println("type = " + refStructure(type));
+ 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.");
+ Type thisSkeleton = Skeletons.canonicalSkeleton(this);
+
+ if(type instanceof TMetaVar) {
+ TMetaVar other = (TMetaVar)type;
+ if(other.ref != null)
+ throw new InternalCompilerError("Not canonical!");
+
+ Type typeSkeleton = Skeletons.canonicalSkeleton(type);
+ if(thisSkeleton == typeSkeleton) {
+ if(skeletonRef != null)
+ setRefBase(type);
+ else
+ other.setRefBase(this);
+ return;
+ }
+ else if(thisSkeleton instanceof TMetaVar && type.contains((TMetaVar)thisSkeleton))
+ throw new UnificationException(this, type);
+ }
+ else if(thisSkeleton instanceof TMetaVar && type.contains((TMetaVar)thisSkeleton))
+ throw new UnificationException(this, type);
+
+ // Common case
+ if(skeletonRef != null) {
+ Skeletons.unifySkeletons(thisSkeleton, type);
+ if(ref != null) {
+ Types.unify(this, type);
+ return;
+ }
+ }
+ setRefBase(type);
+ }
+
+ private void setRefBase(Type type) throws UnificationException {
+ skeletonRef = null;
+ ref = type;
if(polarity != Polarity.NO_POLARITY)
- a.addPolarity(polarity);
+ type.addPolarity(polarity);
+ //System.out.println("result = " + refStructure(this));
+ //checkRefLoop(this);
+ fireNotifyAboutChange();
}
+ private static String refStructure(Type t) {
+ StringBuilder b = new StringBuilder();
+ IdentityHashSet<TMetaVar> seenVars = new IdentityHashSet<TMetaVar>();
+ refType(b, t, seenVars);
+ return b.toString();
+ }
+
+ private void refStructure(StringBuilder b, IdentityHashSet<TMetaVar> seenVars) {
+ b.append(System.identityHashCode(this));
+ if(!seenVars.add(this))
+ b.append(" (loop)");
+ else if(ref != null) {
+ b.append(" => ");
+ refType(b, ref, seenVars);
+ }
+ else if(skeletonRef != null) {
+ b.append(" -> ");
+ refType(b, skeletonRef, seenVars);
+ }
+ else
+ b.append(" (canonical)");
+ }
+
+ private static void refType(StringBuilder b, Type t, IdentityHashSet<TMetaVar> seenVars) {
+ if(t instanceof TMetaVar)
+ ((TMetaVar)t).refStructure(b, seenVars);
+ else {
+ b.append('[');
+ t.toString(new TypeUnparsingContext(), b);
+ b.append(']');
+ }
+ }
+
+ private void checkRefLoop(TMetaVar var) {
+ IdentityHashSet<TMetaVar> seenVars = new IdentityHashSet<TMetaVar>();
+ StringBuilder b = new StringBuilder();
+ while(true) {
+ b.append(var);
+ if(!seenVars.add(var))
+ throw new InternalCompilerError("Cyclic meta var references: " + b);
+ if(var.ref != null) {
+ b.append(" => ");
+ if(var.ref instanceof TMetaVar)
+ var = (TMetaVar)var.ref;
+ else
+ return;
+ }
+ else if(var.skeletonRef != null) {
+ b.append(" -> ");
+ if(var.skeletonRef instanceof TMetaVar)
+ var = (TMetaVar)var.skeletonRef;
+ else
+ return;
+ }
+ else
+ return;
+ }
+ }
+
public Type getRef() {
return ref;
}
@Override
public boolean contains(TMetaVar other) {
- if(ref == null)
- return this == other;
- else
+ if(ref != null)
return ref.contains(other);
+ else if(skeletonRef != null)
+ return skeletonRef.contains(other);
+ else
+ return this == other;
}
@Override
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;
+ //checkRefLoop(this);
+ 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;
+ }
}