]> 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 0d3114fc5765b0bad9a3ad11df5aaef8dc803bf1..1596467bc2931541350fdfd0fd27a424e084e882 100644 (file)
@@ -2,6 +2,7 @@ package org.simantics.scl.compiler.types;
 
 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;
@@ -91,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
@@ -142,23 +137,116 @@ public class TMetaVar extends Type {
     }
 
     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.");
-        if(type.contains(this))
+        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)
             type.addPolarity(polarity);
-        if(skeletonRef != null) {
-            Type skeleton = skeletonRef;
-            skeletonRef = null;
-            Skeletons.unifySkeletons(skeleton, type);
-        }
+        //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;
     }
@@ -277,9 +365,18 @@ public class TMetaVar extends Type {
         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)
@@ -296,6 +393,36 @@ public class TMetaVar extends Type {
             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;
@@ -343,4 +470,10 @@ public class TMetaVar extends Type {
     public Kind getKind(Environment context) {
         return kind;
     }
+
+    @Override
+    public Type[] skeletonCanonicalChildren() {
+        // Assumes that this is already canonical skeleton
+        return EMPTY_ARRAY;
+    }
 }