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.scl.compiler.environment.Environment; 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; /** * 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]; Type ref = null; Polarity polarity = Polarity.NO_POLARITY; private Kind kind; 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 int hashCode() { if(ref == null) return System.identityHashCode(this); else return ref.hashCode(); } @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 a) throws UnificationException { a = Types.weakCanonical(a); if(a.contains(this)) throw new UnificationException(this, a); ref = a; if(polarity != Polarity.NO_POLARITY) a.addPolarity(polarity); } public Type getRef() { return ref; } @Override public boolean contains(TMetaVar other) { if(ref == null) return this == other; else return ref.contains(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; } } }