X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scl.compiler%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fcompiler%2Ftypes%2FTMetaVar.java;h=1596467bc2931541350fdfd0fd27a424e084e882;hb=e515d1fda563f0fa3b8b71f9099696cf49a06d25;hp=8d2307b232423d33182cfb8a130bc912af52012a;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/types/TMetaVar.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/types/TMetaVar.java index 8d2307b23..1596467bc 100644 --- a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/types/TMetaVar.java +++ b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/types/TMetaVar.java @@ -1,11 +1,11 @@ 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; @@ -17,6 +17,9 @@ 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; + /** @@ -28,10 +31,34 @@ import org.simantics.scl.compiler.types.util.TypeUnparsingContext; */ 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; @@ -65,18 +92,12 @@ public class TMetaVar extends Type { @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 @@ -115,25 +136,129 @@ public class TMetaVar extends Type { 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 seenVars = new IdentityHashSet(); + refType(b, t, seenVars); + return b.toString(); + } + + private void refStructure(StringBuilder b, IdentityHashSet 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 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 seenVars = new IdentityHashSet(); + 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 @@ -231,4 +356,124 @@ public class TMetaVar extends Type { 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; + } }