]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/contexts/TranslationContext.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / contexts / TranslationContext.java
1 package org.simantics.scl.compiler.elaboration.contexts;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5
6 import org.simantics.scl.compiler.common.names.Name;
7 import org.simantics.scl.compiler.common.precedence.Associativity;
8 import org.simantics.scl.compiler.common.precedence.Precedence;
9 import org.simantics.scl.compiler.compilation.CompilationContext;
10 import org.simantics.scl.compiler.elaboration.chr.CHRRuleset;
11 import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
12 import org.simantics.scl.compiler.elaboration.expressions.Case;
13 import org.simantics.scl.compiler.elaboration.expressions.EAmbiguous;
14 import org.simantics.scl.compiler.elaboration.expressions.EConstant;
15 import org.simantics.scl.compiler.elaboration.expressions.EError;
16 import org.simantics.scl.compiler.elaboration.expressions.EFieldAccess;
17 import org.simantics.scl.compiler.elaboration.expressions.ELambda;
18 import org.simantics.scl.compiler.elaboration.expressions.EVar;
19 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
20 import org.simantics.scl.compiler.elaboration.expressions.Expression;
21 import org.simantics.scl.compiler.elaboration.expressions.Variable;
22 import org.simantics.scl.compiler.elaboration.expressions.accessor.FieldAccessor;
23 import org.simantics.scl.compiler.elaboration.expressions.accessor.IdAccessor;
24 import org.simantics.scl.compiler.elaboration.expressions.block.LetStatement;
25 import org.simantics.scl.compiler.elaboration.modules.SCLValue;
26 import org.simantics.scl.compiler.elaboration.query.pre.PreQuery;
27 import org.simantics.scl.compiler.elaboration.relations.SCLRelation;
28 import org.simantics.scl.compiler.environment.AmbiguousNameException;
29 import org.simantics.scl.compiler.environment.Environments;
30 import org.simantics.scl.compiler.environment.LocalEnvironment;
31 import org.simantics.scl.compiler.environment.Namespace;
32 import org.simantics.scl.compiler.environment.filter.AcceptAllNamespaceFilter;
33 import org.simantics.scl.compiler.errors.Locations;
34 import org.simantics.scl.compiler.internal.parsing.declarations.DValueAst;
35 import org.simantics.scl.compiler.module.debug.ModuleDebugInfo;
36 import org.simantics.scl.compiler.module.debug.SymbolReference;
37 import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
38 import org.simantics.scl.compiler.types.Type;
39
40 import gnu.trove.list.array.TIntArrayList;
41 import gnu.trove.map.hash.THashMap;
42 import gnu.trove.procedure.TObjectProcedure;
43 import gnu.trove.set.hash.THashSet;
44
45 public class TranslationContext extends TypeTranslationContext implements EnvironmentalContext {
46
47     public static class ExistentialFrame {
48         THashSet<String> variables = new THashSet<String>(4);
49         ArrayList<Variable> blanks = new ArrayList<Variable>(2); 
50         public boolean disallowNewExistentials;
51         
52         public EVariable createBlank(long location) {
53             Variable variable = new Variable("_");
54             blanks.add(variable);
55             EVariable result = new EVariable(variable);
56             result.location = location;
57             return result;
58         }
59     }
60     
61     THashMap<String, Variable> variables = new THashMap<String, Variable>();
62     ArrayList<Entry> variableEntries = new ArrayList<Entry>();
63     LocalEnvironment localEnvironment;
64     TIntArrayList frames = new TIntArrayList();
65     ArrayList<THashSet<String>> frameNameSets = new ArrayList<THashSet<String>>(); 
66     ArrayList<ExistentialFrame> existentialFrames = new ArrayList<ExistentialFrame>(2);
67     SCLValue bindFunction;
68     
69     public PreQuery currentPreQuery;
70     
71     THashMap<String, SCLRelation> relations = new THashMap<String, SCLRelation>();
72     TIntArrayList relationFrames = new TIntArrayList();
73     ArrayList<RelationEntry> relationEntries = new ArrayList<RelationEntry>();
74     
75     THashMap<String, CHRConstraint> chrConstraints = new THashMap<String, CHRConstraint>();
76     TIntArrayList chrConstraintFrames = new TIntArrayList();
77     ArrayList<CHRConstraintEntry> chrConstraintEntries = new ArrayList<CHRConstraintEntry>();
78     
79     private THashSet<Expression> expandedFromWildcard;
80     
81     public CHRRuleset currentRuleset;
82     
83     public ModuleDebugInfo moduleDebugInfo;
84     
85     private String definitionName;
86     
87     static class Entry {
88         String name;
89         Variable variable;
90         public Entry(String name, Variable variable) {
91             this.name = name;
92             this.variable = variable;
93         }
94     }
95     
96     static class RelationEntry {
97         String name;
98         SCLRelation relation;
99         public RelationEntry(String name, SCLRelation relation) {
100             this.name = name;
101             this.relation = relation;
102         }
103     }
104     
105     static class CHRConstraintEntry {
106         String name;
107         CHRConstraint constraint;
108         public CHRConstraintEntry(String name, CHRConstraint constraint) {
109             this.name = name;
110             this.constraint = constraint;
111         }
112     }
113     
114     public TranslationContext(CompilationContext compilationContext, LocalEnvironment localEnvironment, String definitionName) {
115         super(compilationContext);
116         this.localEnvironment = localEnvironment;
117         this.moduleDebugInfo = compilationContext.moduleDebugInfo;
118         this.definitionName = definitionName;
119     }
120     
121     public static boolean isConstructorName(String name) {
122         int p = name.lastIndexOf('.');
123         char firstChar = name.charAt(p<0 ? 0 : p+1);
124         return Character.isUpperCase(firstChar) || firstChar == '(';
125     }
126     
127     /* Tries to resolve name as a local variable. It is assumed
128      * that name does not contain '.'.
129      */
130     private Expression resolveLocalVariable(long location, String name) {
131         Variable variable = variables.get(name);
132         if(variable != null)
133             return new EVariable(location, variable);
134         
135         char c = name.charAt(0);
136         switch(c) {
137         case '?': {
138             ExistentialFrame existentialFrame = getCurrentExistentialFrame();
139             if(existentialFrame == null || existentialFrame.disallowNewExistentials) {
140                 errorLog.log(location, "New existential variables can be defined only in queries.");
141                 return new EError(location);
142             }
143             variable = new Variable(name);
144             variables.put(name, variable);
145             existentialFrame.variables.add(name);
146             return new EVariable(location, variable);
147         }
148         case '_': {
149             if(name.length()==1) {
150                 ExistentialFrame existentialFrame = getCurrentExistentialFrame();
151                 if(existentialFrame == null || existentialFrame.disallowNewExistentials) {
152                     errorLog.log(location, "Blank variables can be used only in queries.");
153                     return new EError(location);
154                 }
155                 return existentialFrame.createBlank(location);
156             }
157             break;
158         }
159         }
160         return null;
161     }
162     
163     public ExistentialFrame getCurrentExistentialFrame() {
164         int size = existentialFrames.size(); 
165         if(size == 0)
166             return null;
167         else
168             return existentialFrames.get(size-1);
169     }
170     
171     private Expression resolveFieldAccess(long location, Expression base, int pos, String name) {
172         while(pos != -1) {
173             int p = findSeparator(name, pos+1);
174             int endPos = p==-1 ? name.length() : p;
175             FieldAccessor accessor = new IdAccessor(
176                     name.charAt(pos),
177                     name.substring(pos+1, endPos));
178             accessor.location = Locations.sublocation(location, pos+1, endPos);
179             base = new EFieldAccess(base, accessor);
180             base.location = Locations.sublocation(location, 0, endPos);
181             pos = p;
182         }
183         return base;
184     }
185     
186     private static int findSeparator(String name, int fromIndex) {
187         ++fromIndex; // the first character (that can be #) is never a separator
188         while(fromIndex < name.length()) {
189             char c = name.charAt(fromIndex);
190             if(c == '.' || c == '#')
191                 return fromIndex;
192             ++fromIndex;
193         }
194         return -1;
195     }
196     
197     public SCLValue resolveSCLValue(long location, Namespace namespace, String name) throws AmbiguousNameException {
198         SCLValue value = namespace.getValue(name);
199         if(value == null)
200             return null;
201         String deprecatedDescription = value.isDeprecated();
202         if(deprecatedDescription != null)
203             errorLog.logWarning(location, "Deprecated value " + value.getName().name + "." + (deprecatedDescription.isEmpty() ? "" : " " + deprecatedDescription));
204         if(moduleDebugInfo != null)
205             moduleDebugInfo.symbolReferences.add(new SymbolReference(value.getName(), Name.create(compilationContext.module.getName(), definitionName), location));
206         return value;
207     }
208     
209     public Expression resolveValue(long location, Namespace namespace, String name) {
210         try {
211             SCLValue value = resolveSCLValue(location, namespace, name);
212             if(value == null)
213                 return null;
214             return new EConstant(location, value);
215         } catch (AmbiguousNameException e) {
216             if(SCLCompilerConfiguration.ALLOW_OVERLOADING)
217                 return resolveAmbigious(location, e.conflictingModules, name);
218             else {
219                 errorLog.log(location, e.getMessage());
220                 return new EError(location);
221             }
222         }
223     }
224     
225     private Expression resolveAmbigious(long location, String[] conflictingModules, String name) {
226         EAmbiguous.Alternative[] alternatives = new EAmbiguous.Alternative[conflictingModules.length];
227         for(int i=0;i<conflictingModules.length;++i) {
228             Name altName = Name.create(conflictingModules[i], name);
229             SCLValue altValue = environment.getValue(altName);
230             alternatives[i] = new EAmbiguous.Alternative() {
231                 @Override
232                 public Type getType() {
233                     return altValue.getType();
234                 }
235
236                 @Override
237                 public Expression realize() {
238                     EConstant expression = new EConstant(altValue);
239                     expression.location = location;
240                     if(moduleDebugInfo != null)
241                         moduleDebugInfo.symbolReferences.add(new SymbolReference(altValue.getName(), Name.create(compilationContext.module.getName(), definitionName), location));
242                     return expression;
243                 }
244
245                 @Override
246                 public String toString() {
247                     return altValue.getName().toString().replace('/', '.');
248                 }
249             };
250         }
251         EAmbiguous expression = new EAmbiguous(alternatives);
252         expression.location = location;
253         return expression;
254     }
255     
256     public Expression resolveVariable(long location, Namespace namespace, String name, int begin) {
257         int end = findSeparator(name, begin);
258         String part = end == -1 ? (begin == 0 ? name : name.substring(begin)) : name.substring(begin, end);
259
260         if(begin == 0) {
261             Expression result = resolveLocalVariable(location, part);
262             if(result != null)
263                 return end == -1 ? result : resolveFieldAccess(location, result, end, name);
264             
265             // FIXME.. support for records
266             if(localEnvironment != null) {
267                 result = localEnvironment.resolve(environment, name);
268                 if(result != null) {
269                     result.setLocationDeep(location);
270                     return result;
271                 }
272             }
273         }
274         if(end != -1 && name.charAt(end) == '.') {
275             Namespace childNamespace = namespace.getNamespace(part);
276             if(childNamespace != null)
277                 return resolveVariable(location, childNamespace, name, end+1);
278         }
279         {
280             Expression result = null;
281             if(end != -1) {
282                 for(int end2 = name.length();end2 > end;end2 = name.lastIndexOf('.', end2-1)) {
283                     part = name.substring(begin, end2);
284                     result = resolveValue(location, namespace, part);
285                     if(result != null) {
286                         end = end2 == name.length() ? -1 : end2;
287                         break;
288                     }
289                 }
290             }
291             if(result == null)
292                 result = resolveValue(location, namespace, part);
293             if(result != null)
294                 return end == -1 ? result : resolveFieldAccess(location, result, end, name);
295         }
296         reportResolveFailure(location, namespace, part);
297         return new EError(location);
298     }
299     
300     public SCLValue resolveRecordConstructor(long location, String name) throws AmbiguousNameException {
301         return resolveRecordConstructor(location, getEnvironment().getLocalNamespace(), name, 0);
302     }
303     
304     public SCLValue resolveRecordConstructor(long location, Namespace namespace, String name, int begin) throws AmbiguousNameException {
305         int end = findSeparator(name, begin);
306         String part = end == -1 ? (begin == 0 ? name : name.substring(begin)) : name.substring(begin, end);
307
308         if(end != -1 && name.charAt(end) == '.') {
309             Namespace childNamespace = namespace.getNamespace(part);
310             if(childNamespace == null)
311                 return null;
312             else
313                 return resolveRecordConstructor(location, childNamespace, name, end+1);
314         }
315         else
316             return resolveSCLValue(location, namespace, part);
317     }
318     
319     private void reportResolveFailure(long location, Namespace namespace, String name) {
320         StringBuilder message = new StringBuilder();
321         message.append("Couldn't resolve ").append(name).append(".");
322
323         final THashSet<String> candidateNames = new THashSet<String>(4);
324         namespace.findValuesForPrefix("", AcceptAllNamespaceFilter.INSTANCE,
325                 new TObjectProcedure<SCLValue>() {
326             @Override
327             public boolean execute(SCLValue value) {
328                 if(value == null) {
329                     new Exception().printStackTrace();
330                     return true;
331                 }
332                 String valueName = value.getName().name;
333                 if(name.equalsIgnoreCase(valueName))
334                     candidateNames.add(valueName);
335                 return true;
336             }
337         });
338         if(localEnvironment != null)
339             localEnvironment.forNames(new TObjectProcedure<String>() {
340                 @Override
341                 public boolean execute(String valueName) {
342                     if(name.equalsIgnoreCase(valueName))
343                         candidateNames.add(valueName);
344                     return true;
345                 }
346             });
347
348         if(candidateNames.size() > 0) {
349             message.append(" Did you mean ");
350             String[] ns = candidateNames.toArray(new String[candidateNames.size()]);
351             Arrays.sort(ns);
352             for(int i=0;i<ns.length;++i) {
353                 if(i > 0) {
354                     message.append(", ");
355                     if(i == ns.length-1)
356                         message.append("or ");
357                 }
358                 message.append(ns[i]);
359             }
360             message.append('?');
361         }
362
363         errorLog.log(location, message.toString());
364     }
365     
366     public Expression resolveVariable(long location, String name) {
367         return resolveVariable(location, environment.getLocalNamespace(), name, 0);
368     }
369     
370     public Expression resolvePattern(EVar name) {
371         char firstChar = name.name.charAt(0);
372         if(firstChar == '_' && name.name.length()==1) {
373             return new EVariable(new Variable("_"));
374         }
375         else if(!Character.isUpperCase(firstChar)) {
376             if(!frameNameSets.get(frameNameSets.size()-1).add(name.name))
377                 errorLog.log(name.location, "Repeated variable "+name.name+" in pattern.");
378             return new EVariable(name.location, newVariable(name.name));
379         }
380         else 
381             return resolveVariable(name.location, name.name);
382     }
383
384     /**
385      * Starts a new environment frame. New variables defined in this frame shadow
386      * the old variables and when the frame is popped, the old variables are again
387      * visible.
388      */
389     public void pushFrame() {
390         frames.add(variableEntries.size());
391         frameNameSets.add(new THashSet<String>());
392     }
393     
394     /**
395      * Ends an environment frame. See {@link #pushFrame}.
396      */
397     public void popFrame() {
398         int frame = frames.removeAt(frames.size()-1);
399         int i = variableEntries.size();
400         while(i > frame) {
401             --i;
402             Entry entry = variableEntries.remove(i);
403             if(entry.variable == null)
404                 variables.remove(entry.name);
405             else
406                 variables.put(entry.name, entry.variable);
407         }
408         frameNameSets.remove(frameNameSets.size()-1);
409     }
410
411     public void pushRelationFrame() {
412         relationFrames.add(relationEntries.size());
413     }
414     
415     public void popRelationFrame() {
416         int frame = relationFrames.removeAt(relationFrames.size()-1);
417         int i = relationEntries.size();
418         while(i > frame) {
419             --i;
420             RelationEntry entry = relationEntries.remove(i);
421             if(entry.relation == null)
422                 relations.remove(entry.name);
423             else
424                 relations.put(entry.name, entry.relation);
425         }
426     }
427     
428     public void pushCHRConstraintFrame() {
429         chrConstraintFrames.add(chrConstraintEntries.size());
430     }
431     
432     public void popCHRConstraintFrame(CHRRuleset ruleset) {
433         int frame = chrConstraintFrames.removeAt(chrConstraintFrames.size()-1);
434         int i = chrConstraintEntries.size();
435         while(i > frame) {
436             --i;
437             CHRConstraintEntry entry = chrConstraintEntries.remove(i);
438             CHRConstraint newConstraint;
439             if(entry.constraint == null)
440                 newConstraint = chrConstraints.remove(entry.name);
441             else
442                 newConstraint = chrConstraints.put(entry.name, entry.constraint);
443             if(newConstraint.implicitlyDeclared)
444                 ruleset.addConstraint(newConstraint);
445         }
446     }
447     
448     public void pushExistentialFrame() {
449         pushFrame();
450         existentialFrames.add(new ExistentialFrame());
451     }
452     
453     public Variable[] popExistentialFrame() {
454         popFrame();
455         ExistentialFrame frame = existentialFrames.remove(existentialFrames.size()-1);
456         Variable[] result = new Variable[frame.variables.size() + frame.blanks.size()];
457         int i=0;
458         for(String name : frame.variables)
459             result[i++] = variables.remove(name);
460         for(Variable blank : frame.blanks)
461             result[i++] = blank;
462         return result;
463     }
464     
465     public Variable newVariable(String name) {
466         Variable variable = new Variable(name);
467         Variable oldVariable = variables.put(name, variable);
468         variableEntries.add(new Entry(name, oldVariable));
469         return variable;
470     }
471     
472     public THashMap<String, Variable> getVariables() {
473         return variables;
474     }
475     
476     public void newRelation(String name, SCLRelation relation) {
477         SCLRelation oldRelation = relations.put(name, relation);
478         relationEntries.add(new RelationEntry(name, oldRelation));
479     }
480     
481     public void newCHRConstraint(String name, CHRConstraint constraint) {
482         CHRConstraint oldConstraint = chrConstraints.put(name, constraint);
483         chrConstraintEntries.add(new CHRConstraintEntry(name, oldConstraint));
484     }
485             
486     public Precedence getPrecedence(Name op) {
487         Precedence prec = environment.getValue(op).getPrecedence();
488         if(prec == null)
489             return new Precedence(1, Associativity.NONASSOC);
490         else
491             return prec;
492     }
493     
494     public Case translateCase(Expression lhs, Expression rhs) {        
495         ArrayList<Expression> parameters = new ArrayList<Expression>(4);  
496         lhs.getParameters(this, parameters);
497         Expression[] patterns = new Expression[parameters.size()];
498         pushFrame();
499         for(int i=0;i<patterns.length;++i) {
500             Expression pattern = parameters.get(i);
501             pattern = pattern.resolveAsPattern(this);
502             patterns[i] = pattern;
503         }
504         rhs = rhs.resolve(this);
505         popFrame();
506         Case case_ = new Case(patterns, rhs);
507         case_.setLhs(lhs.location);
508         return case_;
509     }
510     
511     public Expression translateCases2(ArrayList<DValueAst> definitions) {
512         Case[] cases = new Case[definitions.size()];
513         for(int i=0;i<cases.length;++i) {
514             DValueAst def = definitions.get(i);
515             cases[i] = translateCase(def.lhs, def.value);
516         }
517         // check arity consistency
518         int arity = cases[0].patterns.length;
519         for(int i=1;i<cases.length;++i)
520             if(cases[i].patterns.length != arity)
521                 errorLog.log(definitions.get(i).lhs.location, 
522                         "Inconsistent arity. " + 
523                         "This case has arity " + cases[i].patterns.length + 
524                                 " while previous cases had arity " + arity + ".");
525         if(cases.length == 1 && cases[0].patterns.length == 0)
526             return cases[0].value;
527         else
528             return new ELambda(
529                     Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
530                     cases);
531     }
532     
533     public Expression translateCases(ArrayList<LetStatement> definitions) {
534         Case[] cases = new Case[definitions.size()];
535         for(int i=0;i<cases.length;++i) {
536             LetStatement def = definitions.get(i);
537             cases[i] = translateCase(def.pattern, def.value);
538         }
539         // check arity concistency
540         int arity = cases[0].patterns.length;
541         for(int i=1;i<cases.length;++i)
542             if(cases[i].patterns.length != arity)
543                 errorLog.log(definitions.get(i).pattern.location, 
544                         "Inconsistent arity. " + 
545                         "This case has arity " + cases[i].patterns.length + 
546                         " while previous cases had arity " + arity + ".");
547         if(arity == 0) {
548             if(cases.length > 1)
549                 errorLog.log(cases[1].value.location, "Cannot give multiple cases for arity 0 function.");
550             return cases[0].value;
551         }
552         return new ELambda(
553                 Locations.combine(definitions.get(0).location, definitions.get(definitions.size()-1).location),
554                 cases);
555     }
556
557     public SCLRelation resolveRelation(long location, String name) {
558         SCLRelation relation = relations.get(name);
559         if(relation != null)
560             return relation;
561         
562         try {
563             relation = Environments.getRelation(environment, name);
564             /*if(relation == null) {
565                 errorLog.log(location, "Couldn't resolve relation " + name + ".");
566                 return null;
567             }*/
568             return relation;
569         } catch (AmbiguousNameException e) {
570             errorLog.log(location, e.getMessage());
571             return null;
572         }
573     }
574     
575     public CHRConstraint resolveCHRConstraint(String name) {
576         return chrConstraints.get(name);
577     }
578
579     @Override
580     public SCLValue getValue(Name name) {
581         return environment.getValue(name);
582     }
583
584     public CHRRuleset resolveRuleset(String name) throws AmbiguousNameException {
585         return Environments.getRuleset(environment, name);
586     }
587
588     /**
589      * Tells that new existential variables are no longer allowed in this context.
590      */
591     public void disallowNewExistentials() {
592         getCurrentExistentialFrame().disallowNewExistentials = true;
593     }
594
595     /**
596      * Marks that the expression is a result of expanding .. wildcard pattern in records.
597      */
598     public void addExpandedFromWildcard(Expression expression) {
599         if(expandedFromWildcard == null)
600             expandedFromWildcard = new THashSet<>();
601         expandedFromWildcard.add(expression);
602     }
603     
604     /**
605      * Asks if the expression is a result of expanding .. wildcard pattern in records.
606      */
607     public boolean isExpandedFromWildcard(Expression expression) {
608         if(expandedFromWildcard == null)
609             return false;
610         else
611             return expandedFromWildcard.contains(expression);
612     }
613 }