]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/types/TMetaVar.java
Fixed incorrect interaction of EAmbigious and TMetaVar.setRef
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / types / TMetaVar.java
index 8d2307b232423d33182cfb8a130bc912af52012a..1596467bc2931541350fdfd0fd27a424e084e882 100644 (file)
@@ -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<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
@@ -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;
+    }
 }