]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/query/QAtom.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / query / QAtom.java
1 package org.simantics.scl.compiler.elaboration.query;
2
3 import java.util.ArrayList;
4 import java.util.Set;
5
6 import org.simantics.scl.compiler.elaboration.contexts.ReplaceContext;
7 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
8 import org.simantics.scl.compiler.elaboration.expressions.ESimpleLet;
9 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
10 import org.simantics.scl.compiler.elaboration.expressions.Expression;
11 import org.simantics.scl.compiler.elaboration.expressions.QueryTransformer;
12 import org.simantics.scl.compiler.elaboration.expressions.Variable;
13 import org.simantics.scl.compiler.elaboration.expressions.VariableProcedure;
14 import org.simantics.scl.compiler.elaboration.java.EqRelation;
15 import org.simantics.scl.compiler.elaboration.query.compilation.ConstraintCollectionContext;
16 import org.simantics.scl.compiler.elaboration.query.compilation.DerivateException;
17 import org.simantics.scl.compiler.elaboration.query.compilation.EnforcingContext;
18 import org.simantics.scl.compiler.elaboration.query.compilation.ExpressionConstraint;
19 import org.simantics.scl.compiler.elaboration.query.compilation.RelationConstraint;
20 import org.simantics.scl.compiler.elaboration.relations.CompositeRelation;
21 import org.simantics.scl.compiler.elaboration.relations.LocalRelation;
22 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
23 import org.simantics.scl.compiler.errors.Locations;
24 import org.simantics.scl.compiler.types.TVar;
25 import org.simantics.scl.compiler.types.Type;
26 import org.simantics.scl.compiler.types.Types;
27
28 import gnu.trove.map.hash.THashMap;
29 import gnu.trove.map.hash.TIntObjectHashMap;
30 import gnu.trove.set.hash.TIntHashSet;
31
32 public class QAtom extends Query {
33     public SCLRelation relation;
34     public Type[] typeParameters;
35     public Expression[] parameters;
36
37     public QAtom(SCLRelation relation, Expression ... parameters) {
38         this.relation = relation;
39         this.parameters = parameters;
40     }
41
42     public QAtom(SCLRelation relation, Type[] typeParameters, Expression ... parameters) {
43         this.relation = relation;
44         this.typeParameters = typeParameters;
45         this.parameters = parameters;
46     }
47
48     @Override
49     public void checkType(TypingContext context) {
50         // Type parameters
51         TVar[] typeVariables = relation.getTypeVariables();
52         typeParameters = new Type[typeVariables.length];
53         for(int i=0;i<typeVariables.length;++i)
54             typeParameters[i] = Types.metaVar(typeVariables[i].getKind());
55
56         // Check parameter types
57         Type[] parameterTypes = relation.getParameterTypes();
58         if(parameterTypes.length != parameters.length)
59             context.getErrorLog().log(location, "Relation is applied with wrong number of parameters.");
60         else
61             for(int i=0;i<parameters.length;++i)
62                 parameters[i] = parameters[i]
63                         .checkType(context, parameterTypes[i].replace(typeVariables, typeParameters));
64     }
65
66     public Expression generateEnforce(EnforcingContext context) {
67         Variable[] variables = new Variable[parameters.length];
68         for(int i=0;i<variables.length;++i)
69             if(parameters[i] instanceof EVariable)
70                 variables[i] = ((EVariable)parameters[i]).getVariable();
71             else
72                 variables[i] = new Variable("p" + i, parameters[i].getType());
73         Expression result = relation.generateEnforce(location, context, typeParameters, variables);
74         for(int i=variables.length-1;i>=0;--i)
75             if(!(parameters[i] instanceof EVariable))
76                 result = new ESimpleLet(
77                         variables[i],
78                         parameters[i],
79                         result
80                         );
81         return result;
82     }
83
84     private static class VariableMaskProcedure implements VariableProcedure {
85         ConstraintCollectionContext context;
86         long requiredVariablesMask = 0L;
87
88         public VariableMaskProcedure(ConstraintCollectionContext context) {
89             this.context = context;
90         }
91
92         @Override
93         public void execute(long location, Variable variable) {
94             int id = context.getVariableMap().get(variable);
95             if(id >= 0)
96                 requiredVariablesMask |= 1L << id;
97         }
98     }
99
100     @Override
101     public void collectConstraints(ConstraintCollectionContext context) {
102         try {
103             // Analyze parameters and find required and optional variables
104             VariableMaskProcedure procedure = new VariableMaskProcedure(context);
105             int[] optionalVariableByParameter = new int[parameters.length];
106             Variable[] varParameters = new Variable[parameters.length];
107             for(int i=0;i<parameters.length;++i) {
108                 Expression parameter = parameters[i];
109                 if(parameter instanceof EVariable) {
110                     Variable variable = ((EVariable)parameter).getVariable();
111                     optionalVariableByParameter[i] = context.getVariableMap().get(variable);
112                     varParameters[i] = variable;
113                 }
114                 else {
115                     Variable temp = new Variable("temp", parameter.getType());
116                     varParameters[i] = temp;
117                     if(parameter.isPattern(0)) {
118                         int tempId = context.addVariable(temp);
119                         context.addConstraint(new ExpressionConstraint(context, temp, parameter, true));
120                         optionalVariableByParameter[i] = tempId;
121                     }
122                     else {
123                         optionalVariableByParameter[i] = -1;
124                         parameter.forVariableUses(procedure);
125                     }
126                 }
127             }
128
129             // Combine required and optional variables
130             TIntHashSet allVariablesSet = new TIntHashSet();
131             for(int v : optionalVariableByParameter)
132                 if(v >= 0)
133                     allVariablesSet.add(v);
134
135             context.addConstraint(new RelationConstraint(allVariablesSet.toArray(), varParameters, this,
136                     optionalVariableByParameter, procedure.requiredVariablesMask));
137         } catch(Exception e) {
138             context.getQueryCompilationContext().getTypingContext().getErrorLog().log(location, e);
139         }
140     }
141
142     @Override
143     public Query replace(ReplaceContext context) {
144         Type[] newTypeParameters;
145         if(typeParameters == null)
146             newTypeParameters = null;
147         else {
148             newTypeParameters = new Type[typeParameters.length];
149             for(int i=0;i<typeParameters.length;++i)
150                 newTypeParameters[i] = typeParameters[i].replace(context.tvarMap);
151         }
152         return new QAtom(relation,
153                 newTypeParameters,
154                 Expression.replace(context, parameters));
155     }
156
157     @SuppressWarnings("unchecked")
158     @Override
159     public Diff[] derivate(THashMap<LocalRelation, Diffable> diffables) throws DerivateException {
160         Diffable diffable = diffables.get(relation);
161         if(diffable == null) {
162             if(relation instanceof CompositeRelation && 
163                     containsReferenceTo((CompositeRelation)relation,
164                             (THashMap<SCLRelation, Diffable>)(THashMap)diffables))
165                 throw new DerivateException(location);
166             return NO_DIFF;
167         }
168         else {
169             Query[] eqs = new Query[parameters.length];
170             for(int i=0;i<parameters.length;++i) {
171                 QAtom eq = new QAtom(EqRelation.INSTANCE, new Expression[] {
172                         new EVariable(diffable.parameters[i]),
173                         parameters[i]
174                 });
175                 eq.setLocationDeep(location);
176                 eq.typeParameters = new Type[] {parameters[i].getType()};
177                 eqs[i] = eq;
178             }
179             return new Diff[] { new Diff(diffable.id, new QConjunction(eqs)) };
180         }
181     }
182
183     private static boolean containsReferenceTo(
184             CompositeRelation relation,
185             THashMap<SCLRelation, Diffable> diffables) {
186         for(SCLRelation r : relation.getSubrelations())
187             if(diffables.containsKey(r))
188                 return true;
189             else if(r instanceof CompositeRelation &&
190                     containsReferenceTo((CompositeRelation)r, diffables))
191                 return true;
192         return false;
193     }
194
195     @Override
196     public Query removeRelations(Set<SCLRelation> relations) {
197         if(relations.contains(relation))
198             return EMPTY_QUERY;
199         else
200             return this;
201     }
202
203     @Override
204     public void setLocationDeep(long loc) {
205         if(location == Locations.NO_LOCATION) {
206             location = loc;
207             for(Expression parameter : parameters)
208                 parameter.setLocationDeep(loc);
209         }
210     }
211
212     @Override
213     public void accept(QueryVisitor visitor) {
214         visitor.visit(this);
215     }
216
217     @Override
218     public void splitToPhases(TIntObjectHashMap<ArrayList<Query>> result) {
219         int phase = relation.getPhase();
220         ArrayList<Query> list = result.get(phase);
221         if(list == null) {
222             list = new ArrayList<Query>();
223             result.put(phase, list);
224         }
225         list.add(this);
226     }
227
228     @Override
229     public Query accept(QueryTransformer transformer) {
230         return transformer.transform(this);
231     }
232
233 }