]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TypingContext.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / contexts / TypingContext.java
diff --git a/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TypingContext.java b/bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TypingContext.java
new file mode 100644 (file)
index 0000000..de58a05
--- /dev/null
@@ -0,0 +1,514 @@
+package org.simantics.scl.compiler.elaboration.contexts;
+
+import gnu.trove.map.hash.THashMap;
+import gnu.trove.set.hash.THashSet;
+
+import java.util.ArrayList;
+
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
+import org.simantics.scl.compiler.common.names.Name;
+import org.simantics.scl.compiler.constants.NoRepConstant;
+import org.simantics.scl.compiler.elaboration.expressions.EApply;
+import org.simantics.scl.compiler.elaboration.expressions.EConstant;
+import org.simantics.scl.compiler.elaboration.expressions.EError;
+import org.simantics.scl.compiler.elaboration.expressions.ELiteral;
+import org.simantics.scl.compiler.elaboration.expressions.EPlaceholder;
+import org.simantics.scl.compiler.elaboration.expressions.EVariable;
+import org.simantics.scl.compiler.elaboration.expressions.Expression;
+import org.simantics.scl.compiler.elaboration.expressions.Variable;
+import org.simantics.scl.compiler.elaboration.modules.SCLValue;
+import org.simantics.scl.compiler.environment.Environment;
+import org.simantics.scl.compiler.errors.ErrorLog;
+import org.simantics.scl.compiler.errors.Locations;
+import org.simantics.scl.compiler.internal.elaboration.constraints.Constraint;
+import org.simantics.scl.compiler.internal.elaboration.constraints.ConstraintEnvironment;
+import org.simantics.scl.compiler.internal.elaboration.constraints.ConstraintSolver;
+import org.simantics.scl.compiler.internal.elaboration.constraints.ExpressionAugmentation;
+import org.simantics.scl.compiler.internal.elaboration.constraints.ReducedConstraints;
+import org.simantics.scl.compiler.internal.elaboration.subsumption.SubSolver;
+import org.simantics.scl.compiler.internal.elaboration.subsumption.Subsumption;
+import org.simantics.scl.compiler.types.TApply;
+import org.simantics.scl.compiler.types.TCon;
+import org.simantics.scl.compiler.types.TForAll;
+import org.simantics.scl.compiler.types.TFun;
+import org.simantics.scl.compiler.types.TMetaVar;
+import org.simantics.scl.compiler.types.TPred;
+import org.simantics.scl.compiler.types.TUnion;
+import org.simantics.scl.compiler.types.TVar;
+import org.simantics.scl.compiler.types.Type;
+import org.simantics.scl.compiler.types.Types;
+import org.simantics.scl.compiler.types.exceptions.UnificationException;
+import org.simantics.scl.compiler.types.kinds.Kinds;
+import org.simantics.scl.compiler.types.util.TypeUnparsingContext;
+
+public class TypingContext implements EnvironmentalContext {
+
+    private ErrorLog errorLog;
+    
+    // Subsumption
+    private ArrayList<Subsumption> effectSubsumptions = new ArrayList<Subsumption>();
+    private ArrayList<Subsumption> subsumptions = new ArrayList<Subsumption>();
+    private ArrayList<Type> potentialSingletonEffects = new ArrayList<Type>();
+    
+    // Effects
+    private ArrayList<Type> effectUpperBounds = new ArrayList<Type>();
+    
+    // Constraints
+    private ArrayList<EVariable> constraintDemand = new ArrayList<EVariable>(); 
+    private ArrayList<Variable[]> constraintFrames = new ArrayList<Variable[]>();
+
+    // Patterns
+    private boolean isInPattern;
+    
+    // Recursion
+    public THashSet<SCLValue> recursiveValues;
+    public ArrayList<EPlaceholder> recursiveReferences;
+    
+    //TypeUnparsingContext tuc = new TypeUnparsingContext();   
+    
+    Environment environment;
+    THashMap<Name, SCLValue> constants = new THashMap<Name, SCLValue>();
+    
+    public TypingContext(ErrorLog errorLog, Environment environment) {
+        this.errorLog = errorLog;
+        this.environment = environment;
+    }
+
+    /**
+     * Records the fact that {@code a} should be a subeffect
+     * of {@code b}. Only types {@code TMetaVar}, {@code TVar}, {@code TCon}, {@code TUnion}
+     * are allowed.
+     */
+    public void subsumeEffect(long loc, Type a, Type b) {
+        a = Types.canonical(a);
+        if(a == Types.NO_EFFECTS)
+            return;
+        b = Types.canonical(b);
+        if(a == b)
+            return;
+        if(b == Types.NO_EFFECTS) {
+            try {
+                Types.unify(a, Types.NO_EFFECTS);
+            } catch(UnificationException e) {
+                errorLog.log(loc, "No side-effects allowed here.");
+                return;
+            }
+        }
+        
+        //System.out.println("subsumeEffect(" + a.toString(tuc) + ", " + b.toString(tuc) + ")");
+
+        /*if(a instanceof TUnion) {
+            TUnion union = (TUnion)a;
+            for(Type e : union.effects)
+                subsumeEffect(loc, e, b);
+        }
+        else*/
+        effectSubsumptions.add(new Subsumption(loc, a, b));
+    }
+    
+    public void subsume(long loc, Type a, Type b) throws UnificationException {
+        a = Types.canonical(a);
+        b = Types.canonical(b);
+        if(a == b)
+            return;
+        
+        //System.out.println("subsume(" + a.toString(tuc) + ", " + b.toString(tuc) + ")");
+        
+        if(a instanceof TMetaVar) {
+            TMetaVar aVar = (TMetaVar)a;
+            if(b instanceof TMetaVar) 
+                subsumptions.add(new Subsumption(loc, a, b));
+            else {
+                if(b.contains(aVar))
+                    throw new UnificationException(a, b);
+                aVar.setRef(createSubtypeTemplate(loc, b));
+            }
+            return;
+        }
+        else if(b instanceof TMetaVar) {
+            TMetaVar bVar = (TMetaVar)b;
+            if(a.contains(bVar))
+                throw new UnificationException(a, b);
+            bVar.setRef(createSupertypeTemplate(loc, a));
+            return;
+        }
+        if(a instanceof TFun) {
+            if(!(b instanceof TFun))
+                throw new UnificationException(a, b);
+            TFun aFun = (TFun)a;
+            TFun bFun = (TFun)b;
+            subsume(loc, bFun.domain, aFun.domain);
+            subsumeEffect(loc, aFun.effect, bFun.effect);
+            subsume(loc, aFun.range, bFun.range);
+        }
+        else if(a instanceof TApply) {
+            Types.unify(a, b);
+        }
+        else if(a instanceof TPred) {
+            Types.unify(a, b);
+        }
+        else // a instanceof TUnion
+            throw new UnificationException(a, b);
+    }
+    
+    /**
+     * Creates a template for all subtypes of the given type
+     * where all possible varying types are replaced by metavars
+     * with properly set inequalities.
+     */
+    private Type createSubtypeTemplate(long loc, Type type) {
+        type = Types.canonical(type);
+        if(type instanceof TCon)
+            return type;
+        else if(type instanceof TApply)
+            return type;
+        else if(type instanceof TPred)
+            return type;
+        else if(type instanceof TFun) {
+            TFun fun = (TFun)type;
+            Type funEffect = Types.canonical(fun.effect);
+            Type effect;
+            if(funEffect == Types.NO_EFFECTS)
+                effect = Types.NO_EFFECTS;
+            else {
+                effect = Types.metaVar(Kinds.EFFECT);
+                effectSubsumptions.add(new Subsumption(loc, effect, funEffect));
+            }            
+            return Types.functionE(
+                    createSupertypeTemplate(loc, fun.domain),
+                    effect,
+                    createSubtypeTemplate(loc, fun.range));
+        }
+        else if(type instanceof TMetaVar) {
+            TMetaVar var = (TMetaVar)type;
+            TMetaVar newVar = Types.metaVar(var.getKind());
+            subsumptions.add(new Subsumption(loc, newVar, var));
+            return newVar;
+        }
+        else if(type instanceof TVar)
+            return type;
+        else if(type instanceof TUnion) {
+            TUnion union = (TUnion)type;
+            if(union.isMinimal())
+                return type;
+            TMetaVar newVar = Types.metaVar(Kinds.EFFECT);
+            subsumptions.add(new Subsumption(loc, newVar, type));
+            return newVar;
+        }
+        else if(type instanceof TForAll) {
+            TForAll forAll = (TForAll)type;
+            Type t = createSubtypeTemplate(loc, forAll.type);
+            if(t == forAll.type)
+                return type;
+            else
+                return Types.forAll(forAll.var, t);
+        }
+        else
+            throw new InternalCompilerError("Unsupported type " + type + ".");
+    }
+    
+    /**
+     * Creates a template for all supertypes of the given type
+     * where all possible varying types are replaced by metavars
+     * with properly set inequalities.
+     */
+    private Type createSupertypeTemplate(long loc, Type type) {
+        type = Types.canonical(type);
+        if(type instanceof TCon)
+            return type;
+        else if(type instanceof TApply)
+            return type;
+        else if(type instanceof TPred)
+            return type;
+        else if(type instanceof TFun) {
+            TFun fun = (TFun)type;
+            TMetaVar effect = Types.metaVar(Kinds.EFFECT);
+            effectSubsumptions.add(new Subsumption(loc, fun.effect, effect));
+            return Types.functionE(
+                    createSubtypeTemplate(loc, fun.domain),
+                    effect,
+                    createSupertypeTemplate(loc, fun.range));
+        }
+        else if(type instanceof TMetaVar) {
+            TMetaVar var = (TMetaVar)type;
+            TMetaVar newVar = Types.metaVar(var.getKind());
+            subsumptions.add(new Subsumption(loc, var, newVar));
+            return newVar;
+        }
+        else if(type instanceof TVar)
+            return type;
+        else if(type instanceof TUnion) {
+            TMetaVar newVar = Types.metaVar(Kinds.EFFECT);
+            subsumptions.add(new Subsumption(loc, type, newVar));
+            return newVar;
+        }
+        else if(type instanceof TForAll) {
+            TForAll forAll = (TForAll)type;
+            Type t = createSupertypeTemplate(loc, forAll.type);
+            if(t == forAll.type)
+                return type;
+            else
+                return Types.forAll(forAll.var, t);
+        }
+        else
+            throw new InternalCompilerError("Unsupported type " + type + ".");
+    }
+    
+    /**
+     * Instantiates type abstractions and constraints from the value.
+     */
+    public Expression instantiate(Expression expr) {
+        Type type = Types.weakCanonical(expr.getType());
+        while(type instanceof TForAll) {
+            TForAll forAll = (TForAll)type;
+            TVar var = forAll.var;
+            TMetaVar mVar = Types.metaVar(var.getKind());
+            type = Types.canonical(forAll.type).replace(var, mVar);
+            expr = expr.applyType(mVar);
+        }
+        while(type instanceof TFun) {
+            TFun fun = (TFun)type;
+            if(fun.domain instanceof TPred) { // No need to canonicalize
+                TPred pred = (TPred)fun.domain;            
+                type = fun.range;
+                
+                long loc = expr.getLocation();
+                EVariable var = new EVariable(loc, null);
+                var.setType(pred);
+                expr = new EApply(loc, expr, var);
+                addConstraintDemand(var);
+            }
+            else if(fun.domain == Types.PUNIT) {                
+                type = fun.range;
+                
+                long loc = expr.getLocation();
+                declareEffect(expr.location, fun.effect);
+                expr = new EApply(loc, fun.effect, expr, new ELiteral(loc, NoRepConstant.PUNIT));
+            }
+            else
+                break;
+        }
+        expr.setType(type);
+        return expr;
+    }
+    
+    /**
+     * Enforces {@code expr} to have type {@code b} by adding
+     * type applications, lambdas and effect subsumptions.
+     */
+    public Expression subsume(Expression expr, Type b) {
+        b = Types.weakCanonical(b);
+        /*if(b instanceof TForAll) {
+            TForAll forAll = (TForAll)b;
+            TVar var = forAll.var;
+            TVar newVar = Types.var(var.getKind());
+            b = Types.canonical(forAll.type).replace(var, newVar);
+            return new ELambdaType(new TVar[] {newVar}, subsume(expr, b));
+        }*/
+        expr = instantiate(expr);
+
+        try {
+            subsume(expr.getLocation(), expr.getType(), b);
+        } catch(UnificationException e) {
+            typeError(expr.getLocation(), b, expr.getType());
+        }
+        return expr;
+    }
+
+    private boolean expandSubsumptions() {
+        boolean nontrivialSubs = true;
+        int iterationCount = 0;
+        while(!subsumptions.isEmpty() && nontrivialSubs) {
+            ArrayList<Subsumption> oldSubsumptions = subsumptions; 
+            subsumptions = new ArrayList<Subsumption>();
+            nontrivialSubs = false;
+            for(Subsumption sub : oldSubsumptions) {
+                Type a = sub.a = Types.canonical(sub.a);
+                Type b = sub.b = Types.canonical(sub.b);
+                if(b instanceof TMetaVar && a instanceof TMetaVar) {
+                    subsumptions.add(sub);
+                }
+                else {
+                    try {
+                        subsume(sub.loc, a, b);
+                    } catch (UnificationException e) {
+                        errorLog.log(sub.loc, "Type " + a + " is not a subtype of " + b + ".");
+                    }
+                    nontrivialSubs = true;
+                }
+            }
+            ++iterationCount;
+            if(iterationCount == 5) {
+                // Test that the skeletons are unifiable
+                THashMap<TMetaVar,TMetaVar> metaVarMap =
+                        new THashMap<TMetaVar,TMetaVar>();
+                ArrayList<Subsumption> subCopies = new ArrayList<Subsumption>(subsumptions.size());
+                for(Subsumption sub : subsumptions)
+                    if(!isEffect(sub.a))
+                        subCopies.add(new Subsumption(sub.loc,
+                                sub.a.copySkeleton(metaVarMap), sub.b.copySkeleton(metaVarMap)));
+
+                /*System.out.println("----");
+                TypeUnparsingContext tuc = new TypeUnparsingContext();  
+                for(Subsumption sub : subCopies)
+                    System.out.println(sub.a.toString(tuc) + " == " + sub.b.toString(tuc));*/
+                             
+                for(Subsumption sub : subCopies)
+                    try {
+                        Types.unify(sub.a, sub.b);
+                    } catch (UnificationException e) {
+                        errorLog.log(sub.loc, "Unification of types failed.");
+                        return false;
+                    }
+                
+                /*System.out.println("----");
+                for(Subsumption sub : subCopies)
+                    System.out.println(sub.a.toString(tuc) + " === " + sub.b.toString(tuc));*/
+            }
+        }
+        for(Subsumption sub : subsumptions)
+            try {
+                // FIXME This is not correct in all cases, should emit unsolved subsumptions to the type of the value
+                Types.unify(sub.a, sub.b);
+            } catch (UnificationException e) {
+                // Should not happen. Both types should be metavars.
+                throw new InternalCompilerError();
+            }
+        subsumptions.clear();
+        return true;
+    }
+    
+    private boolean isEffect(Type type) {
+        if(type instanceof TMetaVar)
+            return ((TMetaVar)type).getKind() == Kinds.EFFECT;
+        else if(type instanceof TCon)
+            return environment.getTypeConstructor((TCon)type).kind == Kinds.EFFECT;
+        else if(type instanceof TVar)
+            return ((TVar)type).getKind() == Kinds.EFFECT;
+        else if(type instanceof TUnion)
+            return true;
+        else
+            return false;
+        
+    }
+    
+    public void solveSubsumptions(long globalLoc) {
+        if(expandSubsumptions())
+            new SubSolver(errorLog, effectSubsumptions, potentialSingletonEffects, globalLoc).solve();
+    }
+    
+    public void declareEffect(long loc, Type effect) {
+        subsumeEffect(loc, effect, effectUpperBounds.get(effectUpperBounds.size()-1));
+    }
+    
+    public void pushEffectUpperBound(long loc, Type effect) {
+        effectUpperBounds.add(effect);
+        potentialSingletonEffects.add(effect);
+    }
+    
+    public Type popEffectUpperBound() {
+        return effectUpperBounds.remove(effectUpperBounds.size()-1);
+    }
+
+    public ErrorLog getErrorLog() {
+        return errorLog;
+    }
+
+    public boolean isInPattern() {
+        return isInPattern;
+    }
+
+    public void setInPattern(boolean isInPattern) {
+        this.isInPattern = isInPattern;
+    }
+
+    public void pushConstraintFrame(Variable[] array) {
+        constraintFrames.add(array);
+    }
+
+    public void popConstraintFrame() {
+        constraintFrames.remove(constraintFrames.size()-1);
+    }
+
+    public void addConstraintDemand(EVariable var) {
+        for(int i=constraintFrames.size()-1;i>=0;--i)
+            for(Variable con : constraintFrames.get(i))
+                if(Types.equals(var.getType(), con.getType())) {
+                    var.setVariable(con);
+                    return;
+                }
+        constraintDemand.add(var);
+    }
+    
+    public Expression addConstraint(TPred pred) {
+        EVariable evidence = new EVariable(null);
+        evidence.setType(pred);
+        addConstraintDemand(evidence);
+        return evidence;
+    }
+    
+    public Expression[] addConstraints(TPred[] preds) {
+        if(preds.length == 0)
+            return Expression.EMPTY_ARRAY;
+        Expression[] evidences = new Expression[preds.length];
+        for(int i=0;i<preds.length;++i)
+            evidences[i] = addConstraint(preds[i]);
+        return evidences;
+    }
+
+    public ArrayList<EVariable> getConstraintDemand() {
+        return constraintDemand;
+    }
+
+    public void resetConstraintDemand() {
+        constraintDemand = new ArrayList<EVariable>();
+    }
+
+    public void typeError(long loc, Type requiredType, Type givenType) {
+        TypeUnparsingContext tuc = new TypeUnparsingContext();
+        errorLog.log(loc, "Expected <" + requiredType.toString(tuc) + "> got <" + givenType.toString(tuc) + ">.");
+    }
+
+    public Expression solveConstraints(Environment environment, Expression expression) {
+        ArrayList<EVariable> constraintDemand = getConstraintDemand();
+        if(!constraintDemand.isEmpty()) {
+            ConstraintEnvironment ce = new ConstraintEnvironment(environment);
+            ReducedConstraints red = ConstraintSolver.solve(
+                    ce, new ArrayList<TPred>(0), constraintDemand,
+                    true);                                        
+
+            expression = ExpressionAugmentation.augmentSolved(
+                    red.solvedConstraints, 
+                    expression);
+
+            for(Constraint c : red.unsolvedConstraints)
+                errorLog.log(c.getDemandLocation(), "There is no instance for <"+c.constraint+">.");
+        }
+        else
+            expression = expression.decomposeMatching();
+        
+        return expression;
+    }
+    
+    public SCLValue getValue(Name name) {
+        if(constants.containsKey(name))
+            return constants.get(name);
+        SCLValue value = environment.getValue(name);
+        if(value == null)
+            errorLog.log(Locations.NO_LOCATION, "Couldn't find " + name + ".");
+        constants.put(name, value);
+        return value;
+    }
+    
+    public Expression getConstant(Name name, Type ... typeParameters) {
+        SCLValue value = getValue(name);
+        if(value == null)
+            return new EError(Locations.NO_LOCATION);
+        return new EConstant(value, typeParameters);
+    }
+
+    public Environment getEnvironment() {
+        return environment;
+    }
+
+}