source: branches/dev-mj-1626/Compiler/ModelicaFrontEnd/src/jastadd/errorcheck/TypeCheck.jrag @ 13095

Last change on this file since 13095 was 13095, checked in by tgutzmann, 5 months ago

Merge from trunk

File size: 93.3 KB
Line 
1/*
2    Copyright (C) 2009-2013 Modelon AB
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, version 3 of the License.
7
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12
13    You should have received a copy of the GNU General Public License
14    along with this program.  If not, see <http://www.gnu.org/licenses/>.
15*/
16
17import java.util.Arrays;
18import java.util.Collections;
19import java.util.Set;
20import java.util.HashSet;
21import java.util.ArrayList;
22import org.jmodelica.util.Criteria;
23import org.jmodelica.util.ErrorCheckType;
24import org.jmodelica.util.problemHandling.ErrorProducerUnlessDisabled;
25import org.jmodelica.util.problemHandling.AbstractErrorProducerUnlessDisabled;
26import org.jmodelica.util.collections.ReverseListIterable;
27
28aspect FlatTypeCheck {
29
30    public void ASTNode.typeCheck(ErrorCheckType checkType) {}
31
32    public abstract class ErrorChecker {
33        public static class TypeChecker extends ErrorChecker {
34            public TypeChecker() {
35                super("TypeCheck");
36            }
37
38            @Override
39            public void check(ASTNode node, ErrorCheckType checkType) {
40                node.typeCheck(checkType);
41            }
42        }
43    }
44
45    private static ErrorChecker ASTNode.TYPE_CHECKER = addErrorChecker(new ErrorChecker.TypeChecker());
46
47    public static final SimpleProblemProducer ASTNode.CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS =
48            new SimpleErrorProducer("CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS", ProblemKind.SEMANTIC,
49                    "In the declaration '%s', the declared class is not a subtype of the " + 
50                    "constraining class,\n    because %s");
51    public static final SimpleProblemProducer ASTNode.REPLACING_CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS =
52            new SimpleErrorProducer("REPLACING_CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS", ProblemKind.SEMANTIC,
53                    "In the declaration '%s', the replacing class is not a subtype of the " + 
54                    "constraining class from the declaration '%s',\n    because %s");
55    public static final SimpleProblemProducer ASTNode.PREV_REDECLARE_NOT_REPLACEABLE =
56            new SimpleWarningProducer("PREV_REDECLARE_NOT_REPLACEABLE", ProblemKind.SEMANTIC,
57                    "In the declaration '%s', %s can't be redeclared since it has already been " + 
58                    "redeclared without 'replaceable'");
59
60    public void InstComposite.typeCheck(ErrorCheckType checkType) {
61        super.typeCheck(checkType);
62        // Note that modifiers (including redeclarations) in a constraining clause
63        // are applied to the declaration itself and is therefore also type checked.
64        if (hasInstConstrainingComponent()) {
65              InstNode superType = getInstConstrainingComponent().getInstNode();
66              InstComponentDecl declaredType = this;
67              String subTypeMsg = declaredType.subType(superType);
68              if (subTypeMsg != null) {
69                  SrcComponentClause cc = declaredType.myComponentClause();
70                  CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS.invoke(declaredType, cc, subTypeMsg);
71              }
72        }
73    }
74
75    public void InstReplacingRecord.typeCheck(ErrorCheckType checkType) {
76        typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
77    }
78
79    public void InstReplacingComposite.typeCheck(ErrorCheckType checkType) {
80        typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
81    }
82
83    public void InstReplacingPrimitive.typeCheck(ErrorCheckType checkType) {
84        typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
85    }
86
87    public void InstReplacingExpandableConnectorDecl.typeCheck(ErrorCheckType checkType) {
88        typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
89    }
90
91    public void InstComponentDecl.typeCheckReplacingComponent(InstComponentDecl declaredType, ErrorCheckType checkType) {
92        super.typeCheck(checkType);
93       
94        // Type check the original component
95        InstComponentDecl superType = declaredType.constrainingInstComponentDecl();
96        if (declaredType.hasInstConstrainingComponent()) {
97            String subTypeMsg = declaredType.subType(superType);
98            if (subTypeMsg != null) {
99                SrcComponentClause cc = declaredType.myComponentClause();
100                CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS.invoke(declaredType, cc, subTypeMsg);
101            }
102        }
103
104        // The environment should be traversed backwards in order to perform correct
105        // subtype tests in redeclaration chains.
106        InstComponentRedeclare prevRedeclare = null;
107        for (InstComponentRedeclare redeclare : myEnvironment().reverseComponentRedeclares(name())) {
108            InstComponentDecl declaredSubType = redeclare.getInstComponentDecl();
109            InstComponentDecl constrainingSubType = declaredSubType.constrainingInstComponentDecl();
110               
111            // Check consistency of the redeclaring component
112            if (declaredSubType.hasInstConstrainingComponent()) {
113                String subTypeMsg = declaredSubType.subType(constrainingSubType);
114                if (subTypeMsg != null) {
115                    SrcComponentClause cc = declaredSubType.myComponentClause();
116                    CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS.invoke(declaredSubType, cc, subTypeMsg);
117                }
118            }
119
120            // It is ok to check against the constrainingSubType, since the declaredSubType is a subtype
121            // of the constrainingSubType. Then if constrainingSubType is a subtype of superType, then it
122            // follows that declaredSubType is a subtype of superType by transitivity.
123            String subTypeMsg = constrainingSubType.subType(superType);
124            if (subTypeMsg != null) {
125                SrcComponentClause cc = constrainingSubType.myComponentClause();
126                SrcComponentClause scc = superType.myComponentClause();
127                REPLACING_CLASS_NOT_SUBTYPE_OF_CONSTRAINING_CLASS.invoke(constrainingSubType, 
128                        cc, scc, subTypeMsg);
129            }
130       
131            // If the redeclaring declaration has a constraining clause, the constraining
132            // type of the redeclaring declaration of should be used in following subtype-test
133            // instead of the constraining type of the original declaration.
134            if (declaredSubType.hasInstConstrainingComponent()) 
135                superType = constrainingSubType;
136           
137            // If the previous redeclare is declared without "replaceable", then it is illegal
138            // to redeclare further. The check on commonAncestor() checks that this redeclare actually
139            // replaces the previous one.
140            InstNode containingNode = redeclare.myInstNode();
141            if (prevRedeclare != null && !prevRedeclare.getInstComponentDecl().isReplaceable() && 
142                    prevRedeclare.myInstNode().commonAncestor(containingNode) == containingNode) {
143                PREV_REDECLARE_NOT_REPLACEABLE.invoke(declaredSubType, 
144                        declaredSubType.myComponentClause(), declaredSubType.name());
145            }
146           
147            prevRedeclare = redeclare;
148        }
149    }
150   
151    public Iterable<InstComponentRedeclare> Environment.reverseComponentRedeclares(String name) {
152        Set<SrcModificationOrRedeclareElement> seen = new HashSet<>();
153        ArrayList<InstComponentRedeclare> list = new ArrayList<>();
154       
155        for (InstModification im : this) {
156            InstComponentRedeclare red = im.matchInstComponentRedeclare(name);
157            if (red != null) {
158                SrcModificationOrRedeclareElement mod = red.getSrcModification();
159                if (!seen.contains(mod)) {
160                    seen.add(mod);
161                    list.add(red);
162                }
163            }
164        }
165       
166        return new ReverseListIterable(list);
167    }
168   
169   
170    public static final SimpleProblemProducer ASTNode.CANNOT_INFER_ARRAY_SIZE_OF_VARIABLE =
171            new SimpleErrorProducer("CANNOT_INFER_ARRAY_SIZE_OF_VARIABLE", ProblemKind.SEMANTIC,
172                    "Can not infer array size of the variable %s");
173    public static final SimpleProblemProducer ASTNode.CANNOT_INFER_ARRAY_SIZE_OF_FUNCTION_OUTPUT =
174            new SimpleErrorProducer("CANNOT_INFER_ARRAY_SIZE_OF_FUNCTION_OUTPUT", ProblemKind.COMPLIANCE,
175                    "Can not infer array size of the function output %s");
176    public static final SimpleProblemProducer ASTNode.BINDING_EXPRESSION_TYPE_MISMATCH =
177            new SimpleErrorProducer("BINDING_EXPRESSION_TYPE_MISMATCH", ProblemKind.SEMANTIC,
178                    "The binding expression of the variable %s does not match the declared type of the variable");
179    public static final SimpleProblemProducer ASTNode.ASSUMING_EACH =
180            new SimpleWarningProducer("ASSUMING_EACH", ProblemKind.SEMANTIC, "Assuming 'each' for the modification '%s'");
181    public static final SimpleProblemProducer ASTNode.IGNORING_EACH =
182            new SimpleWarningProducer("IGNORING_EACH", ProblemKind.SEMANTIC, "Ignoring erroneous 'each' for the modification '%s'");
183    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_DECLARATION =
184            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_DECLARATION", ProblemKind.SEMANTIC,
185                    "Array size mismatch in declaration of %s, size of declaration is %s and size of binding expression is %s");
186    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_MODIFICATION =
187            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_MODIFICATION", ProblemKind.SEMANTIC,
188                    "Array size mismatch in modification of %s, expected size is %s and size of binding expression is %s");
189    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_MODIFICATION_DUE_TO_EACH =
190            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_MODIFICATION_DUE_TO_EACH", ProblemKind.SEMANTIC,
191                    "Array size mismatch in modification of %s, expected size is (due to 'each') %s and size of binding expression is %s");
192    public static final SimpleProblemProducer ASTNode.NON_SCALAR_CONDITIONAL_GUARD =
193            new SimpleErrorProducer("NON_SCALAR_CONDITIONAL_GUARD", ProblemKind.SEMANTIC,
194                    "The guard expression of a conditional component should be a scalar expression");
195    public static final SimpleProblemProducer ASTNode.NON_BOOLEAN_CONDITIONAL_GUARD =
196            new SimpleErrorProducer("NON_BOOLEAN_CONDITIONAL_GUARD", ProblemKind.SEMANTIC,
197                    "The guard expression of a conditional component should be a boolean expression");
198    public static final SimpleProblemProducer ASTNode.NON_FIXED_CONDITIONAL_GUARD =
199            new SimpleErrorProducer("NON_FIXED_CONDITIONAL_GUARD", ProblemKind.SEMANTIC,
200                    "The guard expression of a conditional component should have parameter or constant variability");
201
202    public void InstAssignable.typeCheck(ErrorCheckType checkType) {
203        FExp bexp = myBindingInstExp();
204        boolean function = inFunction();
205        boolean record = inRecordDecl();
206        boolean output = isOutput();
207        boolean incompleteAllowed = (function && !output) || record;
208        boolean unknownAllowed = function || record;
209        Size componentSize = size();
210        if (!componentSize.isComplete() && !incompleteAllowed && !checkType.allowIncompleteSizes()) {
211            if (function && output) {
212                CANNOT_INFER_ARRAY_SIZE_OF_FUNCTION_OUTPUT.invoke(surroundingInstClass(), qualifiedName());
213            } else {
214                CANNOT_INFER_ARRAY_SIZE_OF_VARIABLE.invoke(this, name());
215            }
216        }
217        if (bexp != null && !inRecordWithBindingExp() && !bexp.type().isUnknown() && checkType.checkTypes()) {
218            Index arrayIndex = indexInModification();
219           
220            CommonType expectedBT = expectedBindingType();
221            CommonType actualBT = actualBindingType();
222           
223            boolean reportedError = false;
224            if (!expectedBT.size().equivalent(actualBT.size(), unknownAllowed)) {
225                InstValueModification ivm = myInstValueMod();
226                InstModification needsEach = ivm.findModificationLackingEach(actualBT.size());
227                if (!expectedBT.scalarType().typeCompatible(actualBT.scalarType())) {
228                    BINDING_EXPRESSION_TYPE_MISMATCH.invoke(bexp, name());
229                    reportedError = true;
230                } else if (needsEach != null) {
231                    ASSUMING_EACH.invoke(needsEach, needsEach);
232                    actualBT = actualBT.expand(expectedBT.size().contractLeft(expectedBT.ndims() - actualBT.ndims()));
233                } else if (ivm.expectedSizeNoEach().equivalent(actualBT.size(), unknownAllowed)) {
234                    InstModification parIM = ivm.parentInstModification();
235                    IGNORING_EACH.invoke(parIM, parIM);
236                    expectedBT = expectedBindingTypeNoEach();
237                    arrayIndex = indexInModificationNoEach();
238                } else {
239                    ErrorProducerUnlessDisabled reporter;
240                    if (ivm.myInstNode() == this) {
241                        reporter = ARRAY_SIZE_MISMATCH_IN_DECLARATION;
242                    } else if (ivm.hasEach()) {
243                        reporter = ARRAY_SIZE_MISMATCH_IN_MODIFICATION_DUE_TO_EACH;
244                    } else {
245                        reporter = ARRAY_SIZE_MISMATCH_IN_MODIFICATION;
246                    }
247                    reporter.invokeWithCondition(bexp, expectedBT.ndims() == actualBT.ndims(), name(), expectedBT.size(), actualBT.size());
248                    reportedError = true;
249                }
250            }
251           
252            if (!reportedError) {
253                if (expectedBT.size().isEmpty() || expectedBT.size().isUnknown() || actualBT.size().isUnknown()) {
254                    typeCheckCell(expectedBT.scalarType(), actualBT.scalarType(), bexp, unknownAllowed);
255                } else {
256                    Size arraySize = expectedBT.size().contractLeft(expectedBT.ndims() - arrayIndex.ndims());
257                    for (Index index : Indices.create(arraySize)) {
258                        index = arrayIndex.expand(index);
259                        CommonType expected = expectedBT.cell(index);
260                        CommonType actual = actualBT.cell(index);
261                       
262                        typeCheckCell(expected, actual, bexp, unknownAllowed);
263                    }
264                }
265            }
266        }
267       
268        if (hasConditionalAttribute()) {
269            FExp cond = getConditionalAttribute();
270            if (!cond.type().isUnknown()) {
271                if (!cond.type().isScalar()) 
272                    NON_SCALAR_CONDITIONAL_GUARD.invoke(this);
273                if (!cond.type().isBoolean()) 
274                    NON_BOOLEAN_CONDITIONAL_GUARD.invoke(this);
275                if (!cond.variability().parameterOrLess()) {
276                    NON_FIXED_CONDITIONAL_GUARD.invoke(this);
277                } else {
278                    cond.markAsStructuralParameter(checkType);
279                }
280            }
281        }
282       
283        typeCheckAttributes();
284        super.typeCheck(checkType);
285    }
286   
287    private void InstAssignable.typeCheckCell(CommonType expectedBT, CommonType actualBT, FExp bexp, boolean unknownAllowed) {
288        if (!expectedBT.typeCompatible(actualBT, unknownAllowed) && !expectedBT.isUnknown()) {
289            if (!expectedBT.scalarType().typeCompatible(actualBT.scalarType())) {
290                BINDING_EXPRESSION_TYPE_MISMATCH.invoke(bexp, name());
291            }
292        }
293    }
294
295    inh InstModification InstModification.parentInstModification();
296    eq InstComponentModification.getChild().parentInstModification()             = this;
297    eq InstNode.getElementInstModification(int i).parentInstModification()       = getElementInstModification(i);
298    eq InstComponentDecl.getInstModification().parentInstModification()          = getInstModification();
299    eq InstComponentDecl.getAnnotation().parentInstModification()                = getAnnotation();
300    eq InstClassDecl.getClassAnnotation().parentInstModification()               = getClassAnnotation();
301    eq InstConstraining.getInstClassModification().parentInstModification()      = getInstClassModification();
302    eq InstRecordConstructor.getInstModification(int i).parentInstModification() = getInstModification(i);
303
304    inh boolean InstComponentDecl.inRecordWithBindingExp();
305    eq InstRecord.getChild().inRecordWithBindingExp()             = myBindingInstExp() != null;
306    eq InstArrayComponentDecl.getChild().inRecordWithBindingExp() = inRecordWithBindingExp();
307    eq InstComponentDecl.getChild().inRecordWithBindingExp()      = false;
308    eq InstClassDecl.getChild().inRecordWithBindingExp()          = false;
309    eq Root.getChild().inRecordWithBindingExp()                   = false;
310   
311    public void InstAssignable.typeCheckAttributes() {}
312   
313    public void InstPrimitive.typeCheckAttributes() {
314            for (InstModification im : totalMergedEnvironment()) 
315                im.typeCheckAttribute(this);
316     }
317
318    public void InstModification.typeCheckAttribute(InstNode owner) {}
319
320    public static final SimpleProblemProducer ASTNode.TYPE_MISMATCH_IN_ATTRIBUTE_MODIFICATION =
321            new SimpleErrorProducer("TYPE_MISMATCH_IN_ATTRIBUTE_MODIFICATION", ProblemKind.SEMANTIC,
322                    "The type of the binding expression of the attribute %s for the %s %s does not match the declared type of the variable");
323    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_ATTRIBUTE_MODIFICATION =
324            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_ATTRIBUTE_MODIFICATION", ProblemKind.SEMANTIC,
325                    "Array size mismatch in modification of the attribute %s for the %s %s, expected size is %s and size of %s expression is %s");
326    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_ATTRIBUTE_MODIFICATION_DUE_TO_EACH =
327            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_ATTRIBUTE_MODIFICATION_DUE_TO_EACH", ProblemKind.SEMANTIC,
328                    "Array size mismatch in modification of the attribute %s for the %s %s, expected size is (due to 'each') %s and size of %s expression is %s");
329
330    public void InstComponentModification.typeCheckAttribute(InstNode owner) {
331        if (hasInstModification() && getInstModification().hasInstValueMod()) {
332            FExp valMod = getInstModification().instValueMod();
333            if (!type().isUnknown() && !valMod.type().isUnknown()) {
334                Size attrSize = expectedSize();
335                Size modSize = valMod.size();
336                if (!type().typeCompatible(valMod.type().scalarType())) {
337                    TYPE_MISMATCH_IN_ATTRIBUTE_MODIFICATION.invoke(this, name(), owner.kindDescription(), owner.name());
338                } else if (!attrSize.equivalent(modSize, true)) {
339                    InstModification im = findModificationLackingEach(modSize);
340                    if (im != null) {
341                        ASSUMING_EACH.invoke(im, im);
342                    } else if (expectedSizeNoEach().equivalent(modSize, false)) {
343                        IGNORING_EACH.invoke(this, this);
344                    } else {
345                        ErrorProducerUnlessDisabled producer;
346                        if (hasEach()) {
347                            producer = ARRAY_SIZE_MISMATCH_IN_ATTRIBUTE_MODIFICATION_DUE_TO_EACH;
348                        } else {
349                            producer = ARRAY_SIZE_MISMATCH_IN_ATTRIBUTE_MODIFICATION;
350                        }
351                        producer.invokeWithCondition(this, attrSize.ndims() != valMod.ndims(), name(),
352                                owner.kindDescription(), owner.name(), attrSize, name(), modSize);
353                    }
354                } else {
355                    if (name().equals("nominal")) {
356                        checkNominalAttribute(owner, valMod);
357                    }
358                }
359            }
360        }
361    }
362
363    public static final SimpleProblemProducer ASTNode.NOMINAL_EQUAL_TO_ZERO =
364            new SimpleErrorProducer("NOMINAL_EQUAL_TO_ZERO", ProblemKind.SEMANTIC,
365                    "The attribute nominal for the %s %s is set to %s, evaluating to 0.0. A nominal value of zero is not meaningful. Please set the nominal value to the expected magnitude of the variable.");
366    public static final SimpleProblemProducer ASTNode.NOMINAL_ELEMENT_EQUAL_TO_ZERO =
367            new SimpleErrorProducer("NOMINAL_ELEMENT_EQUAL_TO_ZERO", ProblemKind.SEMANTIC,
368                    "The attribute nominal for the %s %s is set to %s, where element %s evaluates to 0.0. A nominal value of zero is not meaningful. Please set the nominal value to the expected magnitude of the variable.");
369
370    public void InstComponentModification.checkNominalAttribute(InstNode owner, FExp valMod) {
371        try {
372            CValue val = valMod.ceval();
373            if (val.isArray()) {
374                CValueArray arr = (CValueArray) val;
375                for (Index i : arr.indices()) {
376                    CValue cell = arr.getCell(i);
377                    if (cell.hasRealValue() && cell.realValue() == 0.0) {
378                        NOMINAL_ELEMENT_EQUAL_TO_ZERO.invoke(this, owner.kindDescription(), owner.name(), valMod, i);
379                        break;
380                    }
381                }
382            } else {
383                if (val.hasRealValue() && val.realValue() == 0.0) {
384                    NOMINAL_EQUAL_TO_ZERO.invoke(this, owner.kindDescription(), owner.name(), valMod);
385                }
386            }
387        } catch (ConstantEvaluationException e) {}
388    }
389
390    syn String InstNode.kindDescription() {
391        throw new UnsupportedOperationException();
392    }
393    eq InstComponentDecl.kindDescription() = myInstClass().kindDescription() + " instance";
394    eq InstPrimitive.kindDescription()     = "variable";
395    eq InstBaseClassDecl.kindDescription() = getInstRestriction().toString();
396
397    public static final SimpleProblemProducer ASTNode.EACH_APPLIED_ON_SCALAR =
398            new SimpleWarningProducer("EACH_APPLIED_ON_SCALAR", ProblemKind.SEMANTIC,
399                    "The 'each' keyword should not be applied to a modification of a scalar component: %s");
400
401    public void InstArgument.typeCheck(ErrorCheckType checkType) {
402        if (checkType.checkTypes() && getEach() && expectedSizeFromParent() == Size.SCALAR) 
403            EACH_APPLIED_ON_SCALAR.invoke(this, this);
404    }
405
406    inh boolean InstNode.isInRedeclareMod();
407    eq InstElementRedeclare.getChild().isInRedeclareMod() = true;
408    eq InstClassDecl.getChild().isInRedeclareMod()        = false;
409    eq InstRoot.getChild().isInRedeclareMod()             = false;
410    eq Root.getChild().isInRedeclareMod()                 = false;
411
412    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_EQUATION =
413            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_EQUATION", ProblemKind.SEMANTIC,
414                    "The array sizes of right and left hand side of equation are not compatible, size of left-hand side is %s, and size of right-hand side is %s");
415    public static final SimpleProblemProducer ASTNode.TYPE_MISMATCH_IN_EQUATION =
416            new SimpleErrorProducer("TYPE_MISMATCH_IN_EQUATION", ProblemKind.SEMANTIC,
417                    "The right and left expression types of equation are not compatible, type of left-hand side is %s, and type of right-hand side is %s");
418
419    public void FEquation.typeCheck(ErrorCheckType checkType) {
420        typeCheckLocalIteration(checkType);
421        FType left = getLeft().type();
422        FType right = getRight().type();
423        if (!left.isUnknown() && !right.isUnknown()) {
424            if (!left.equivalentTo(right)) {
425                if (left.equivalentExceptLengths(right)) {
426                    if (!lockBranch(checkType))
427                        ARRAY_SIZE_MISMATCH_IN_EQUATION.invoke(this, left.size(), right.size());
428                } else {
429                    TYPE_MISMATCH_IN_EQUATION.invoke(this, left, right);
430                }
431            }
432        }
433    }
434
435    public static final SimpleProblemProducer ASTNode.CONNECT_WITH_INVALID_TYPE =
436            new SimpleErrorProducer("CONNECT_WITH_INVALID_TYPE", ProblemKind.SEMANTIC,
437                    "Connecting to an instance of a non-connector type is not allowed");
438    public static final SimpleProblemProducer ASTNode.CONNECT_EXPANDABLE_AND_NON_EXPANDABLE_TYPE =
439            new SimpleErrorProducer("CONNECT_EXPANDABLE_AND_NON_EXPANDABLE_TYPE", ProblemKind.SEMANTIC,
440                    "Connecting an expandable connector to a non-expandable connector is not allowed");
441    public static final SimpleProblemProducer ASTNode.TYPE_MISMATCH_IN_CONNECT =
442            new SimpleErrorProducer("TYPE_MISMATCH_IN_CONNECT", ProblemKind.SEMANTIC,
443                    "Types of connected components do not match");
444    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_CONNECT =
445            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_CONNECT", ProblemKind.SEMANTIC,
446                    "Sizes do not match in connection, size of '%s' is %s and size of '%s' is %s");
447    public static final ErrorProducerUnlessDisabled ASTNode.EXPANDABLE_ARRAY_SIZE_MISMATCH_IN_CONNECT =
448            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_CONNECT", ProblemKind.SEMANTIC,
449                    "Sizes do not match in connection, size of the part of '%s' referring to " + 
450                    "the expandable connector is %s and size of '%s' is %s");
451
452    public void FConnectClause.typeCheck(ErrorCheckType checkType) {
453        InstAccess left  = getConnector1();
454        InstAccess right = getConnector2();
455        boolean checkTypes = !isDisabled();
456        boolean isExpandable = false;
457        boolean expandableSame = true;
458        for (InstAccess access = left; access != null; access = (access == left) ? right : null) {
459            if (access.isExpandableConnectorPart()) {
460                isExpandable = true;
461            } else if (!access.isUnknown() && !access.myInstComponentDecl().myInstClass().isUnknown()) {
462                if (!access.myInstComponentDecl().isConnector())
463                    CONNECT_WITH_INVALID_TYPE.invoke(access);
464            } else {
465                checkTypes = false;
466            }
467            if (access.myInstComponentDecl().isExpandableConnector()) {
468                expandableSame = !expandableSame;
469            }
470        }
471        if (!expandableSame && !left.isUnknown() && !right.isUnknown()) {
472            CONNECT_EXPANDABLE_AND_NON_EXPANDABLE_TYPE.invoke(this);
473        }
474        if (checkTypes) { 
475            if (isExpandable) {
476                boolean leftUnknown = left.isExpandableConnectorPart();
477                InstAccess known   = leftUnknown ? right : left;
478                InstAccess unknown = leftUnknown ? left  : right;
479                Size knownSize   = known.size();
480                Size unknownSize = unknown.findExpandableConnectorPart().partSize();
481                if (knownSize.ndims() < unknownSize.ndims() || 
482                        !unknownSize.equivalent(knownSize.contractRight(unknownSize.ndims()), false)) {
483                    EXPANDABLE_ARRAY_SIZE_MISMATCH_IN_CONNECT.invoke(
484                            this, unknown, unknownSize, known, knownSize);
485                }
486            } else {
487                InstComponentDecl leftComp  = left.lookupEvaluatingIndices();
488                InstComponentDecl rightComp = right.lookupEvaluatingIndices();
489                if (!leftComp.connectableTo(rightComp)) {
490                    TYPE_MISMATCH_IN_CONNECT.invoke(this);
491                } else if (!left.size().equivalent(right.size(), false)) {
492                    ARRAY_SIZE_MISMATCH_IN_CONNECT.invokeWithCondition(this, left.ndims() == right.ndims(), 
493                            left, left.size(), right, right.size());
494                }
495            }
496        }
497    }
498
499    // Generic typeCheck(checkType) that calls typeError() if type is unknown and no FExp child has unknown type
500    public void FExp.typeCheck(ErrorCheckType checkType) {
501        if (generateTypeError()) {
502            for (FExp exp : childFExps())
503                if (exp.type().isUnknown())
504                    return;
505            typeError(checkType);
506        } else {
507            size().markAsStructuralParameter(checkType, this);
508        }
509    }
510
511    syn boolean FExp.generateTypeError() = type().isUnknown();
512    eq FEqRelExp.generateTypeError()     = super.generateTypeError() ||
513            (getLeft().type().isReal() || getRight().type().isReal()) && !inFunction();
514
515    public void InstIfExp.typeCheck(ErrorCheckType checkType) {
516        if (!generateTypeError()) {
517            if (!getThenExp().type().equivalentTo(getElseExp().type())) {
518                getIfExp().markAsStructuralParameter(checkType);
519            }
520        }
521        super.typeCheck(checkType);
522    }
523
524    public void Size.markAsStructuralParameter(ErrorCheckType checkType, FExp src) {}
525   
526    public void MutableSize.markAsStructuralParameter(ErrorCheckType checkType, FExp src) {
527        if (src.errorCheckUnknownSize()) {
528            for (int i = 0; i < ndims(); i++) {
529                FExp exp = exps[i];
530                if (exp != null) {
531                    exp.markAsStructuralParameter(checkType);
532                    if (!exp.variability().parameterOrLess()) {
533                        ASTNode.NON_PARAMETER_SIZE_IN_EXPRESSION.invoke(src, exp, i, src);
534                    }
535                }
536            }
537        }
538    }
539   
540    syn boolean FExp.errorCheckUnknownSize() = !inFunction() && !allowUnknownSize();
541
542    syn boolean BaseNode.allowUnknownSize()    = false;
543    eq InstFunctionArgument.allowUnknownSize() = inAllowUnknownSize();
544    eq FIfExp.allowUnknownSize()               = inAllowUnknownSize();
545    eq InstFunctionCall.allowUnknownSize()     = inAllowUnknownSize() && callsExternal();
546    eq FFunctionCall.allowUnknownSize()        = inAllowUnknownSize() && callsExternal();
547
548    inh boolean BaseNode.inAllowUnknownSize();
549    eq BaseNode.getChild().inAllowUnknownSize()         = allowUnknownSize();
550    eq InstFunctionCall.getChild().inAllowUnknownSize() = allowUnknownSize() || callsExternal();
551    eq FFunctionCall.getChild().inAllowUnknownSize()    = allowUnknownSize() || callsExternal();
552
553    class FFunctionDecl {
554        public enum FunctionType {
555            EXTERNAL, LOADRESOURCE;
556           
557            public boolean containsFunction(CommonCallable cc) {
558                if (this == EXTERNAL) {
559                    return cc.isExternalFunction();
560                } else {
561                    return cc.containsFunction(this);
562                }
563            }
564        }
565    }
566
567    interface CommonCallable {
568        public boolean containsFunction(FFunctionDecl.FunctionType ft);
569        public boolean isExternalFunction();
570    }
571
572    syn boolean InstPartialFunction.isExternalFunction() = false;
573    syn boolean FFunctionVariable.isExternalFunction()   = false;
574
575    syn boolean InstClassDecl.isExternalFunction() = findFunctionExternal() != null;
576    syn boolean FFunctionDecl.isExternalFunction() = getFirstExternalStmt() != null;
577
578    syn FExternalStmt FFunctionDecl.getFirstExternalStmt() {
579        for (FStatement stmt : getFAlgorithm().getFStatements()) {
580            if (stmt.isExternalStatement()) {
581                return (FExternalStmt) stmt;
582            }
583        }
584        return null;
585    }
586
587    syn boolean FStatement.isExternalStatement() = false;
588    eq FExternalStmt.isExternalStatement()       = true;
589
590    syn boolean FAbstractFunctionCall.callsExternal() =
591            FFunctionDecl.FunctionType.EXTERNAL.containsFunction(myCommonCallable());
592    syn boolean FAbstractFunctionCall.callsLoadResource() =
593            FFunctionDecl.FunctionType.LOADRESOURCE.containsFunction(myCommonCallable());
594   
595    syn boolean ASTNode.containsFunction(FFunctionDecl.FunctionType ft) {
596        for (ASTNode n : this) {
597            if (n.containsFunction(ft)) {
598                return true;
599            }
600        }
601        return false;
602    }
603
604    @Override
605    syn boolean InstNode.containsFunction(FFunctionDecl.FunctionType ft) =
606        getInstComponentDecls().containsFunction(ft) || getInstExtendss().containsFunction(ft) ||
607        getFAbstractEquations().containsFunction(ft) || super.containsFunction(ft);
608
609    eq InstComponentDecl.containsFunction(FFunctionDecl.FunctionType ft) =
610        (hasInstModification() && getInstModification().containsFunction(ft)) ||  super.containsFunction(ft);
611    eq InstValueModification.containsFunction(FFunctionDecl.FunctionType ft) =
612        getFExp().containsFunction(ft) ||  super.containsFunction(ft);
613    eq InstFunctionCall.containsFunction(FFunctionDecl.FunctionType ft) =
614        ft.containsFunction(myCommonCallable()) || super.containsFunction(ft);
615    eq FFunctionCall.containsFunction(FFunctionDecl.FunctionType ft) =
616        ft.containsFunction(myCommonCallable()) || super.containsFunction(ft);
617    eq FLoadResource.containsFunction(FFunctionDecl.FunctionType ft) =
618        ft == FFunctionDecl.FunctionType.LOADRESOURCE || super.containsFunction(ft);
619
620    syn lazy boolean InstClassDecl.containsFunction(FFunctionDecl.FunctionType ft) circular [false] =
621            super.containsFunction(ft);
622    eq InstSimpleShortClassDecl.containsFunction(FFunctionDecl.FunctionType ft) =
623            actualInstClass().containsFunction(ft);
624
625    public static final ErrorProducerUnlessDisabled ASTNode.NON_PARAMETER_SIZE_IN_EXPRESSION =
626            new ErrorProducerUnlessDisabled("NON_PARAMETER_SIZE_IN_EXPRESSION", ProblemKind.COMPLIANCE,
627                    "Non-parameter expression sizes not supported, '%s', dimension %d in '%s'");
628   
629    @Override
630    public void FLoadResource.typeCheck(ErrorCheckType checkType) {
631        super.complianceCheck(checkType);
632        getFExp().forceVariability(checkType, Variability.LOADRESOURCEPARAMETER);
633    }
634   
635    public class FExp {
636        public static class ExpTypeErrorProducer extends AbstractErrorProducerUnlessDisabled<ReporterNode> {
637            private final String message;
638           
639            public ExpTypeErrorProducer(String identifier, String message) {
640                super(identifier, ProblemKind.SEMANTIC);
641                this.message = message;
642            }
643
644            public void invoke(FExp node) {
645                StringBuilder buf = new StringBuilder(message);
646                buf.append(": ");
647                buf.append(node);
648                for (FExp exp : node.childFExps()) {
649                    buf.append("\n    type of '");
650                    buf.append(exp);
651                    buf.append("' is ");
652                    buf.append(exp.type());
653                }
654                super.invokeWithCondition(node, node.typeErrorOnlyLengths(), buf.toString());
655            }
656
657            @Override
658            public String description() {
659                return message;
660            }
661           
662        }
663    }
664   
665    public static final FExp.ExpTypeErrorProducer ASTNode.TYPE_MISMATCH_IN_EXPRESSION =
666            new FExp.ExpTypeErrorProducer("TYPE_MISMATCH_IN_EXPRESSION", "Type error in expression");
667
668    public void FExp.typeError(ErrorCheckType checkType) {
669        TYPE_MISMATCH_IN_EXPRESSION.invoke(this);
670    }
671
672    /**
673     * Check if type error is only in array lengths.
674     */
675    syn boolean FExp.typeErrorOnlyLengths() = typeErrorOnlyLengths(childFExps());
676    eq FAbstractCat.typeErrorOnlyLengths()  = typeErrorOnlyLengths(getFExps());
677
678    /**
679     * Check if type error is only in array lengths.
680     */
681    syn boolean FExp.typeErrorOnlyLengths(Iterable<FExp> exps) {
682        FType first = null;
683        for (FExp exp : exps) {
684            FType cur = exp.type();
685            if (first == null) {
686                first = cur;
687            } else if (!first.equivalentExceptLengths(cur)) {
688                return false;
689            }
690        }
691        return true;
692    }
693
694    /**
695     * The message to use in default type error.
696     */
697    public static final FExp.ExpTypeErrorProducer ASTNode.EQUALITY_COMPARISON_OF_REAL =
698            new FExp.ExpTypeErrorProducer("EQUALITY_COMPARISON_OF_REALS", "Equality comparisons do not allow real operands");
699
700    @Override
701    public void FEqRelExp.typeError(ErrorCheckType checkType) {
702        if ((getLeft().type().isRealScalar() || getRight().type().isRealScalar()) && !inFunction()) {
703            EQUALITY_COMPARISON_OF_REAL.invoke(this);
704        } else {
705            super.typeError(checkType);
706        }
707    }
708   
709    public void FLinspace.typeCheck(ErrorCheckType checkType) {
710        getN().markAsStructuralParameter(checkType);
711        super.typeCheck(checkType);
712    }
713
714    public void FLinspace.typeError(ErrorCheckType checkType) {
715        boolean badVar = !getN().variability().fixedParameterOrLess();
716        boolean canCeval = getN().canCeval();
717        boolean badVal = canCeval && getN().ceval().intValue() < 2;
718        if (badVar || !canCeval || badVal) {
719            String msg = "Third argument of linspace() must be a scalar parameter Integer expression that is greater than 1";
720            if (badVar)
721                error("%s\n    '%s' is of %s variability", msg, getN(), getN().variability().toStringLiteral());
722            else if (canCeval && !lockBranch(checkType))
723                errorUnlessDisabled("%s\n    '%s' evaluates to %d", msg, getN(), getN().ceval().intValue());
724        } else {
725            super.typeError(checkType);
726        }
727    }
728
729    public void FIdentity.typeCheck(ErrorCheckType checkType) {
730        getFExp().markAsStructuralParameter(checkType);
731        super.typeCheck(checkType);
732    }
733
734    public void FIdentity.typeError(ErrorCheckType checkType) {
735        if (!getFExp().variability().parameterOrLess()) 
736            error("Argument of identity() must be a scalar parameter Integer expression\n    '%s' is of %s variability", 
737                    getFExp(), getFExp().variability().toStringLiteral());
738        else 
739            super.typeError(checkType);
740    }
741
742    public void FSizeExp.typeCheck(ErrorCheckType checkType) {
743        if (hasDim())
744            getDim().markAsStructuralParameter(checkType);
745        super.typeCheck(checkType);
746    }
747
748    public void FSizeExp.typeError(ErrorCheckType checkType) {
749        if (hasDim()) {
750            if (!getDim().type().isIntegerScalar())
751                return;  // Error is reported for argument in this case
752            boolean badVar = !getDim().variability().parameterOrLess();
753            boolean canCeval = getDim().canCeval();
754            int dimVal = canCeval ? getDim().ceval().intValue() : 1;
755            int ndims = getFExp().ndims();
756            if (badVar || !canCeval || dimVal < 1 || dimVal > ndims) {
757                String msg = "Second argument of size() must be a scalar parameter Integer expression that evaluates to a valid dimension of the first argument";
758                if (badVar)
759                    error("%s\n    '%s' is of %s variability", msg, getDim(), getDim().variability().toStringLiteral());
760                else if (canCeval)
761                    error("%s\n    '%s' evaluates to %d, and '%s' has %d dimensions", msg, getDim(), dimVal, getFExp(), ndims);
762                return;
763            }
764        }
765        super.typeError(checkType);
766    }
767
768    public void CommonAccess.typeError(ErrorCheckType checkType, CommonAccessExp access) {
769        access.typeError(checkType);
770    }
771
772    public void InstAccess.typeError(ErrorCheckType checkType, CommonAccessExp access) {
773        typeError();
774    }
775
776  public void InstAccess.typeError() {
777          typeError(this);
778  }
779 
780  protected void InstAccess.typeError(InstAccess top) {}
781 
782  protected void InstDot.typeError(InstAccess top) {
783          getLastInstAccess().typeError(top);
784  }
785 
786  protected void InstGlobalAccess.typeError(InstAccess top) {
787          getInstAccess().typeError(top);
788  }
789 
790    protected void InstComponentAccess.typeError(InstAccess top) {
791        InstComponentDecl icd = myInstComponentDecl();
792        if (!icd.isUnknown() && !icd.myInstClass().isUnknown() && !inCardinality()) {
793            if (!icd.isPrimitive() && !icd.isRecord()) {
794                top.error("Accesses to composite components other than records are not allowed: " + top.name());
795            }
796        }
797    }
798 
799    protected void InstComponentArrayAccess.typeError(InstAccess top) {
800        InstComponentDecl icd = myInstComponentDecl();
801        if (!icd.isUnknown() && !icd.myInstClass().isUnknown() && !inCardinality()) {
802            if (!icd.isPrimitive() && !icd.isRecord()) {
803                top.error("Accesses to composite components other than records are not allowed: " + top.name());
804            }
805        }
806    }
807 
808  inh boolean InstAccess.inCardinality();
809  eq FCardinality.getChild().inCardinality() = true;
810  eq InstNode.getChild().inCardinality()     = false;
811  eq Root.getChild().inCardinality()         = false;
812 
813  protected void InstClassAccess.typeError(InstAccess top) {
814          if (!isComponentSizeClass() || !isInstComponentSize())
815                  top.error("Illegal access to class in expression: " + top.name());
816  }
817
818    public void InstIfExp.typeError(ErrorCheckType checkType) {
819        FType thenType = getThenExp().type();
820        FType elseType = getElseExp().type();
821        FType scalar = thenType.scalarType().typePromotion(elseType.scalarType());
822       
823        if (!getIfExp().isOKTestExp()) {
824            getIfExp().error("Test expression of if expression must be scalar boolean:\n    %s is %s", 
825                    getIfExp(), getIfExp().type());
826        } else if (thenType.ndims() != elseType.ndims()) { 
827            error("Branches of if expression have different number of array dimensions:" + 
828                    "\n    %s has size %s\n    %s has size %s", 
829                getThenExp(), thenType.size(), getElseExp(), elseType.size());
830        } else if (scalar.isUnknown()) { 
831            error("Branches of if expression have incompatible types:\n    %s is %s\n    %s is %s", 
832                    getThenExp(), thenType, getElseExp(), elseType);
833        } else {
834            if (isParameterIf())
835                error("Failed to evaluate test expression in if expression with branches that have different array sizes:" + 
836                        "\n    %s has size %s\n    %s has size %s", 
837                    getThenExp(), thenType.size(), getElseExp(), elseType.size());
838            else
839                error("If expression with branches that have different array sizes must have parameter test expression:" + 
840                        "\n    %s has size %s\n    %s has size %s", 
841                    getThenExp(), thenType.size(), getElseExp(), elseType.size());
842        }
843    }
844
845  public void CommonAccessExp.typeCheck(ErrorCheckType checkType) {
846          if (!getAccess().isUnknown() && type().isUnknown() && !accessToModelOK())
847            getAccess().typeError(checkType, this);
848  }
849 
850        public void CommonAccess.typeCheck(ErrorCheckType checkType) {
851        if (inAlgorithm() && useIsAssignment()) {
852            if (variability().constantVariability()) {
853                error("Assignments to constants is not allowed in algorithms");
854            } else if (variability().parameterVariability()) {
855                if (isForIndex()) {
856                    error("Can not assign a value to a for loop index");
857                } else if (!(myFAlgorithm().getType().isInitial())) {
858                    error("Assignments to parameters in algorithms is only allowed in initial algorithms");
859                }
860            }
861        }
862        }
863
864  inh boolean CommonAccessExp.accessToModelOK();
865  inh boolean InstFunctionArgument.accessToModelOK();
866  eq BaseNode.getChild().accessToModelOK()             = false;
867  eq InstFunctionArgument.getChild().accessToModelOK() = accessToModelOK();
868  eq FSizeExp.getFExp().accessToModelOK()              = true;
869  eq FSizeExp.getOriginalArg(int i).accessToModelOK()  = i == 0;
870 
871 
872  /**
873   * Check if the FExp of this FIterExp must be scalar.
874   */
875  inh boolean FIterExp.iterExpMustBeScalar();
876  eq FExp.getChild().iterExpMustBeScalar()       = false;
877  eq Root.getChild().iterExpMustBeScalar()       = false;
878  eq InstNode.getChild().iterExpMustBeScalar()   = false;
879  eq FMinMaxExp.getChild().iterExpMustBeScalar() = true;
880  // TODO: Add product() when it is implemented
881
882    public void FMulExp.typeCheck(ErrorCheckType checkType) {
883        if (type().isOperatorRecord() && !isElementWise()) {
884            int left = getLeft().size().get(1);
885            int inner = (left == Size.UNKNOWN) ? getRight().size().get(0) : left;
886            if ((inner == 0 || inner == Size.UNKNOWN) && type().matchOverloadedZero() == null) 
887                error("Matrix multiplication of operator records with an inner dimension of 0 or : requires that an '0' operator is defined");
888        }
889        super.typeCheck(checkType);
890    }
891
892  public void FIterExp.typeCheck(ErrorCheckType checkType) {
893          super.typeCheck(checkType);
894          if (iterExpMustBeScalar() && !getFExp().type().isUnknown() && getFExp().ndims() != 0)
895                  error("The expression of a reduction-expression must be scalar, except for sum(): " +
896                                  getFExp() + " has " + getFExp().ndims() + " dimension(s)");
897  }
898 
899  public void FStreamBuiltIn.typeCheck(ErrorCheckType checkType) {
900          if (!getFExp().isAccessToStream()) 
901                  error("Argument of " + builtInName() + "() must be a stream variable");
902  }
903 
904  /**
905   * Returns true if this expression can be interpreted as some form of Access
906   */
907  syn boolean FExp.isAccessLikeExp() = false;
908  eq CommonAccessExp.isAccessLikeExp() = true;
909 
910  syn boolean FExp.isAccess()  = false;
911  eq CommonAccessExp.isAccess()= true;
912  syn CommonAccess FExp.asCommonAccess() {
913          throw new UnsupportedOperationException("asCommonAccess() is not supported for class type " + getClass().getSimpleName());
914  }
915  eq CommonAccessExp.asCommonAccess() = getAccess();
916 
917  syn CommonAccessExp FExp.asCommonAccessExp() {
918      throw new UnsupportedOperationException("asCommonAccessExp() is not supported for class type " + getClass().getSimpleName());
919  }
920  eq CommonAccessExp.asCommonAccessExp() = this;
921 
922  syn FAccessExp FExp.asFAccessExp() {
923      throw new UnsupportedOperationException("asFAccessExp() is not supported for class type " + getClass().getSimpleName());
924  }
925  eq FAccessExp.asFAccessExp() = this;
926 
927    syn boolean CommonAccess.isInstAccess() = false;
928    eq InstAccess.isInstAccess() = true;
929    syn boolean CommonAccess.isFAccess() = false;
930    eq FAccess.isFAccess() = true;
931   
932        syn InstAccess FExp.asInstAccess() {
933                throw new UnsupportedOperationException();
934        }
935    eq InstAccessExp.asInstAccess() = getInstAccess();
936   
937    syn InstAccess CommonAccess.asInstAccess() {
938        throw new UnsupportedOperationException();
939    }
940    eq InstAccess.asInstAccess() = this;
941   
942  syn boolean FExp.isAccessToStream()  = false;
943  eq CommonAccessExp.isAccessToStream()= getAccess().isAccessToStream();
944 
945  syn boolean CommonAccess.isAccessToStream() = false;
946  eq InstAccess.isAccessToStream()      = myInstComponentDecl().isStream();
947
948
949
950    public void InstForIndexWithExp.typeCheck(ErrorCheckType checkType) {
951        if (!getFExp().type().isUnknown() && getFExp().ndims() != 1)
952            error("The expression of for index " + name() + " must be a vector expression: " + 
953                    getFExp() + " has " + getFExp().ndims() + " dimension(s)");
954    }
955
956    public void InstForIndexNoExp.typeCheck(ErrorCheckType checkType) {
957        hasFExp(); // Force eval of NTA, thus checking if a consistent range can be computed
958    }
959
960    public static final SimpleErrorProducer InstForIndexNoExp.IMPLICIT_FOR_RANGE_NON_INTEGER =
961            new SimpleErrorProducer("IMPLICIT_FOR_RANGE_NON_INTEGER", ProblemKind.COMPLIANCE, 
962                    "Non-integer for iteration range not supported");
963
964    public static final SimpleErrorProducer InstForIndexNoExp.IMPLICIT_FOR_RANGE_INCONSISTENT =
965            new SimpleErrorProducer("IMPLICIT_FOR_RANGE_INCONSISTENT", ProblemKind.SEMANTIC, 
966                    "For index with implicit iteration range used for inconsistent sizes, here used for size %s and earlier for size %s");
967
968    public static final SimpleErrorProducer InstForIndexNoExp.IMPLICIT_FOR_RANGE_NOT_USED =
969            new SimpleErrorProducer("IMPLICIT_FOR_RANGE_NOT_USED", ProblemKind.SEMANTIC, 
970                    "For index with implicit iteration range must be used as array index");
971
972    // TODO: handle other index types than integer (see #3597)
973    syn lazy Opt<FExp> InstForIndexNoExp.getFExpOpt() {
974        Size size = null;
975        for (FExpSubscript use : mySubscriptUses()) {
976            Size useSize = use.mySize();
977            if (!use.myIndexType().isInteger()) {
978                IMPLICIT_FOR_RANGE_NON_INTEGER.invoke(use);
979            }
980            if (size == null) {
981                size = useSize;
982            } else if (!size.equivalent(useSize, true)) {
983                IMPLICIT_FOR_RANGE_INCONSISTENT.invoke(use, useSize, size);
984            }
985        }
986        if (size == null) {
987            IMPLICIT_FOR_RANGE_NOT_USED.invoke(this);
988            return new Opt();
989        } else {
990            return new Opt(size.createRangeExp(0));
991        }
992    }
993
994    syn lazy Collection<FExpSubscript> InstForIndexNoExp.mySubscriptUses() = mySubscriptUses(name());
995
996    inh Collection<FExpSubscript> InstForIndexNoExp.mySubscriptUses(String name);
997    eq InstForClauseE.getInstForIndex(int i).mySubscriptUses(String name) = collectSubscriptUses(name);
998    eq InstForStmt.getInstForIndex(int i).mySubscriptUses(String name)    = collectSubscriptUses(name);
999    eq FIterExp.getForIndex(int i).mySubscriptUses(String name)           = collectSubscriptUses(name);
1000
1001    public Collection<FExpSubscript> ASTNode.collectSubscriptUses(String name) {
1002        Collection<FExpSubscript> nodes = new ArrayList<FExpSubscript>();
1003        collectSubscriptUses(name, nodes);
1004        return nodes;
1005    }
1006
1007    public void ASTNode.collectSubscriptUses(String name, Collection<FExpSubscript> nodes) {
1008        for (ASTNode n : this) {
1009            n.collectSubscriptUses(name, nodes);
1010        }
1011    }
1012
1013    public void FExpSubscript.collectSubscriptUses(String name, Collection<FExpSubscript> nodes) {
1014        if (getFExp().isIdentifier(name)) {
1015            nodes.add(this);
1016        }
1017        super.collectSubscriptUses(name, nodes);
1018    }
1019
1020    public void FSubscript.typeCheckAsSize(ErrorCheckType checkType) {
1021        if (ndims() > 0) { 
1022            error("Array size must be scalar expression: " + toString());
1023        } else if (!type().isUnknown() && !canBeComponentSize()) {
1024            error("Array size must be Integer expression, Boolean type or enumeration type: " + toString());
1025        } else if (!inFunction()) { 
1026            if (!variability().parameterOrLess()) {
1027                error("Array size must be constant or parameter: " + toString());
1028            } else if (!checkType.allowIncompleteSizes()) {
1029                boolean ok = false;
1030                try {
1031                    ok = ceval().hasIntValue();
1032                } catch (ConstantEvaluationException e) {}
1033                if (!ok) {
1034                    error("Could not evaluate array size expression: " + toString());
1035                }
1036            }
1037        }
1038    }
1039
1040    public void FSubscript.typeCheckAsIndex(ErrorCheckType checkType) {
1041        if (ndims() > 1) { 
1042            error("Array index must be scalar or vector expression: " + toString());
1043        } else if (!type().isUnknown() && !type().canBeIndex()) {
1044            error("Array index must be Integer, Boolean, or enumeration expression: " + toString());
1045        } else if (!type().isUnknown() && !myIndexType().isUnknown() && !type().scalarType().typeCompatible(myIndexType())) {
1046            error("Expected array index of type '%s' found '%s'", myIndexType().name(), type().scalarType().name());
1047        } else if (!inFunction()) { 
1048            if (!variability().fixedParameterOrLess()) {
1049                warning("Variable array index in equation can result in slow simulation time");
1050            } else if (!inUnknownAccess() && !mySize().isUnknown()) {
1051                typeCheckOutOfBounds(checkType);
1052            }
1053        }
1054    }
1055
1056    public void FSubscript.typeCheckOutOfBounds(ErrorCheckType checkType) {}
1057    public void FExpSubscript.typeCheckOutOfBounds(ErrorCheckType checkType) {
1058        if (!reportedOutOfBound) {
1059            // Check array bounds
1060            // TODO: Perform bounds check in functions if index has parameter variability or lower?
1061            try {
1062                int max = mySize().get(0);
1063                for (FExp e : getFExp().getArray().iterable()) {
1064                    CValue cval = e.ceval();
1065                    if (cval.hasIntValue()) {
1066                        int i = cval.intValue();
1067                        if (i < 1 || i > max) {
1068                            if (!lockBranch(checkType)) {
1069                                errorUnlessDisabled("Array index out of bounds: " + i + 
1070                                                    ", index expression: " + getFExp());
1071                            }
1072                            reportedOutOfBound = true;
1073                            return;
1074                        }
1075                    }
1076                }
1077            } catch (ConstantEvaluationException e) {
1078            }
1079        }
1080    }
1081    public void FIntegerSubscript.typeCheckOutOfBounds(ErrorCheckType checkType) {
1082        int max = mySize().get(0);
1083        int i = getValue();
1084        if ((i < 1 || i > max) && !lockBranch(checkType)) {
1085            errorUnlessDisabled("Array index out of bounds: " + i +
1086                                ", index expression: " + i);
1087        }
1088    }
1089
1090  syn FType FSubscript.myIndexType() = myIndexType(myDim());
1091  inh FType FSubscript.myIndexType(int i);
1092  eq Root.getChild().myIndexType(int i)                                = fUnknownType();
1093  eq InstArrayAccess.getFArraySubscripts().myIndexType(int i)          = fUnknownType();
1094  eq InstComponentArrayAccess.getFArraySubscripts().myIndexType(int i) = myInstComponentDecl().myIndexType(i);
1095 
1096  syn FType InstComponentDecl.myIndexType(int dim) {
1097    FArraySubscripts fas = getFArraySubscripts();
1098    if (fas != null && fas.ndims() > dim)
1099        return fas.subscript(dim).type().scalarType();
1100    else
1101        return fUnknownType();
1102  }
1103 
1104  private boolean FExpSubscript.reportedOutOfBound = false;
1105 
1106 
1107  public void FSubscript.typeCheck(ErrorCheckType checkType) {
1108          if (isInstComponentSize()) 
1109                  typeCheckAsSize(checkType);
1110          else 
1111                  typeCheckAsIndex(checkType);
1112  }
1113  public void FColonSubscript.typeCheck(ErrorCheckType checkType) {}
1114 
1115  inh boolean FSubscript.inUnknownAccess();
1116  eq FAccess.getChild().inUnknownAccess()                  = myFV().isUnknown();
1117  eq InstScalarAccess.getChild().inUnknownAccess()        = myInstComponentDecl().isUnknown();
1118  eq InstArrayAccess.getChild().inUnknownAccess()         = myInstComponentDecl().isUnknown();
1119  eq FlatRoot.getChild().inUnknownAccess()                = false;
1120  eq InstRoot.getChild().inUnknownAccess()                = false;
1121 
1122  syn boolean FExp.canBeComponentSize()  = type().isInteger();
1123  eq CommonAccessExp.canBeComponentSize()= type().isInteger() || getAccess().isComponentSizeClass();
1124 
1125  syn boolean FSubscript.canBeComponentSize() = true;
1126  eq FExpSubscript.canBeComponentSize() = getFExp().canBeComponentSize();
1127 
1128  syn boolean CommonAccess.isComponentSizeClass() = false;
1129  eq InstAccess.isComponentSizeClass()      = myInstClassDecl().isComponentSizeClass();
1130 
1131  syn boolean InstClassDecl.isComponentSizeClass() = isBoolean();
1132  eq InstEnumClassDecl.isComponentSizeClass()      = true;
1133 
1134  inh boolean InstClassAccess.isInstComponentSize();
1135    inh boolean CommonAccessExp.isInstComponentSize();
1136  inh boolean FSubscript.isInstComponentSize();
1137  inh boolean FArraySubscripts.isInstComponentSize();
1138  eq InstComponentDecl.getLocalFArraySubscripts().isInstComponentSize() = true;
1139  eq InstComponentDecl.getFArraySubscripts().isInstComponentSize()      = true;
1140  eq InstShortClassDecl.getFArraySubscripts().isInstComponentSize()     = true;
1141  eq InstAccess.getChild().isInstComponentSize()                        = false;
1142  eq FAccessPart.getChild().isInstComponentSize()                       = false;
1143  eq CommonAccess.getChild().isInstComponentSize()                      = false;
1144  eq FExp.getChild().isInstComponentSize()                              = false;
1145    eq CommonAccessExp.getChild().isInstComponentSize()                 = isInstComponentSize();
1146  eq FAbstractVariable.getChild().isInstComponentSize()                 = false;
1147  eq FAbstractEquation.getChild().isInstComponentSize()                 = false;
1148  eq InstNode.getChild().isInstComponentSize()                          = false;
1149  eq FlatRoot.getChild().isInstComponentSize()                          = false;
1150       
1151  syn boolean FArraySubscripts.isFullSize() = false;
1152  eq FArrayExpSubscripts.isFullSize() {
1153      for (FSubscript fs : getFSubscripts())
1154          if (!fs.isColon())
1155              return false;
1156      return true;
1157  }
1158
1159  public void FAssignStmt.typeCheck(ErrorCheckType checkType) {
1160      FType left = getLeft().type();
1161      FType right = getRight().type();
1162      if (!left.isUnknown() && !right.isUnknown()) {
1163          if (!left.typeCompatible(right, true)) {
1164              if (left.typeCompatibleExceptLengths(right)) {
1165                  // TODO: should use lockBranch() here as well, but no support yet
1166                  errorUnlessDisabled("The array sizes of right and left hand side of assignment are not compatible, " + 
1167                          "size of left-hand side is %s, and size of right-hand side is %s", left.size(), right.size());
1168              } else {
1169                  error("The right and left expression types of assignment are not compatible, " + 
1170                          "type of left-hand side is %s, and type of right-hand side is %s", left, right);
1171              }
1172          }
1173      }
1174  }
1175
1176  public void FWhileStmt.typeCheck(ErrorCheckType checkType) {
1177          if (!getTest().type().isUnknown() && !fBooleanScalarType().typeCompatible(getTest().type())) 
1178                  error("Type of test expression of while statement is not Boolean");
1179  }
1180 
1181  public void FIfClause.typeCheck(ErrorCheckType checkType) {
1182          if (!getTest().type().isUnknown() && !fBooleanScalarType().typeCompatible(getTest().type())) 
1183                  error("Type of test expression of if statement is not Boolean");
1184  }
1185 
1186  public void FWhenClause.typeCheck(ErrorCheckType checkType) {
1187          if (!getTest().type().isUnknown()) {
1188                  if (!getTest().type().isBoolean() || getTest().type().ndims() > 1) 
1189                          error("Test expression of when statement isn't Boolean scalar or vector expression");
1190          }
1191  }
1192 
1193  public void FIfEquation.typeCheck(ErrorCheckType checkType) {
1194          if (!getTest().type().isUnknown() && !fBooleanScalarType().typeCompatible(getTest().type())) 
1195                  error("Type of test expression of if equation is not Boolean");
1196  }
1197 
1198  public void FWhenEquation.typeCheck(ErrorCheckType checkType) {
1199          if (!getTest().type().isUnknown()) {
1200                  if (!getTest().type().isBoolean() || getTest().type().ndims() > 1) 
1201                          error("Test expression of when equation isn't Boolean scalar or vector expression");
1202          }
1203  }
1204
1205
1206    public void FInfArgsFunctionCall.typeCheck(ErrorCheckType checkType) {
1207        super.typeCheck(checkType);
1208        if (numArgs() < minNumArgs()) 
1209            error("Too few arguments to " + builtInName() + "(), must have at least " + minNumArgs());
1210        else 
1211            typeCheckFExps();
1212    }
1213
1214  public void FInfArgsFunctionCall.typeCheckFExps() {
1215      for (FExp exp : getFExps()) {
1216          if (!exp.type().isUnknown() && !typeOfArgIsOK(exp)) {
1217              exp.error("Argument of " + builtInName() + "() is not " + 
1218                          getOKArgTypeString() + ": " + exp);
1219          } else if (!variabilityOfArgIsOK(exp)) {
1220                  exp.error("Argument of " + builtInName() + "() does not have " + 
1221                                  getOKArgVariabilityString() + " variability: " + exp);
1222          }
1223      }
1224  }
1225 
1226  public void FCatExp.typeCheckFExps() {
1227          if (!getDim().type().isUnknown() && !fIntegerScalarType().typeCompatible(getDim().type()))
1228                  error("Dimension argument of cat() is not compatible with Integer: " + getDim());
1229          else if (!getDim().type().isUnknown() && !getDim().variability().parameterOrLess())
1230                  error("Dimension argument of cat() does not have constant variability: " + getDim());
1231  }
1232
1233    public void FAbstractCat.typeCheckFExps() {}
1234
1235    public void FAbstractCat.typeError(ErrorCheckType checkType) {
1236        errorUnlessDisabledAnd(typeErrorOnlyLengths(), "Types do not match in array concatenation");
1237    }
1238
1239    syn int FInfArgsFunctionCall.minNumArgs() = 1;
1240    eq FFillExp.minNumArgs()                  = 2;
1241    eq FCatExp.minNumArgs()                   = 2;
1242    eq FAbstractCat.minNumArgs()              = 0;
1243
1244    syn int FInfArgsFunctionCall.numArgs() = getNumFExp();
1245    eq FFillExp.numArgs()                  = getNumFExp() + 1;
1246    eq FCatExp.numArgs()                   = getNumFExp() + 1;
1247
1248  syn boolean FInfArgsFunctionCall.typeOfArgIsOK(FExp exp)        = 
1249                  fIntegerScalarType().typeCompatible(exp.type());
1250  syn boolean FInfArgsFunctionCall.variabilityOfArgIsOK(FExp exp) = 
1251                  exp.variability().parameterOrLess() || inFunction();
1252  syn String FInfArgsFunctionCall.getOKArgTypeString()            = 
1253                  "compatible with Integer";
1254  syn String FInfArgsFunctionCall.getOKArgVariabilityString()     = 
1255                  "constant or parameter";
1256
1257    public void FBuiltInFunctionCall.typeCheck(ErrorCheckType checkType) {
1258        if (checkTypeAsExpression()) {
1259            super.typeCheck(checkType);
1260        } else if (!inFunction()) {
1261            size().markAsStructuralParameter(checkType, this);
1262        }
1263        int n = builtInNumOutput();
1264        if (myLefts().size() > n)
1265            error("Too many components assigned from function call: " + builtInName() + 
1266                    "() has " + n + " output(s)");
1267        else if (!isFunctionCallClause() && n == 0)
1268            error("Function " + builtInName() + "() has no outputs, but is used in expression");
1269    }
1270
1271    public void FSemiLinearExp.typeCheck(ErrorCheckType checkType) {
1272        checkVectorizedSizes(checkType);
1273        super.typeCheck(checkType);
1274    }
1275
1276    public void FBuiltInFunctionCall.checkVectorizedSizes(ErrorCheckType checkType) {
1277        if (isArray()) {
1278            boolean error = false;
1279            boolean onlyLengths = true;
1280            Size s = size();
1281            for (FExp arg : myArgs()) {
1282                if (arg.isArray() && !arg.size().equivalent(s, false)) {
1283                    error = true;
1284                    if (arg.ndims() != s.ndims()) {
1285                        onlyLengths = false;
1286                        break;
1287                    }
1288                }
1289            }
1290            if (error) {
1291                errorUnlessDisabledAnd(onlyLengths, 
1292                        "Mismatching sizes in %s. All non-scalar arguments need matching sizes", builtInName());
1293            }
1294        }
1295    }
1296
1297  public void FEnumIntegerExp.typeCheck(ErrorCheckType checkType) {
1298          super.typeCheck(checkType);
1299          // We can't define the function Integer in PredefinedTypes.jrag - conflict with the type Integer
1300          int n = getNumOriginalArg();
1301          if (n != 1)
1302                  error("Calling function Integer(): too " + (n > 1 ? "many" : "few") + " arguments");
1303  }
1304 
1305  public void InstPreExp.typeCheck(ErrorCheckType checkType) {
1306        if (!getFExp().isAccess()) {
1307                error("Calling function pre(): argument must be variable access");
1308                return;
1309        }
1310  }
1311 
1312  public void FAssert.typeCheck(ErrorCheckType checkType) {
1313          if (hasLevel()) {
1314                  if (getLevel().isParameterExp())
1315                          getLevel().markAsStructuralParameter(checkType);
1316                  else if (!getLevel().isConstantExp())
1317                          error("Level of assert() must be constant or parameter");
1318          }
1319  }
1320 
1321  public void FReinit.typeCheck(ErrorCheckType checkType) {
1322      if (!getVar().isAccess() || !getVar().type().isReal())
1323          error("First argument to reinit() must be an access to a Real variable");
1324      else if (!getVar().type().typeCompatible(getFExp().type()))
1325          error("Arguments to reinit() must be of compatible types");
1326  }
1327
1328    public void FEdgeExp.typeCheck(ErrorCheckType checkType) {
1329        if (!getFExp().isAccess() || !getFExp().type().isBoolean()) 
1330            error("Calling function edge(): argument must be a boolean variable access");
1331    }
1332
1333    public void FChangeExp.typeCheck(ErrorCheckType checkType) {
1334        if (!getFExp().isAccess()) 
1335            error("Calling function change(): argument must be a variable access");
1336    }
1337
1338    public static final SimpleProblemProducer ASTNode.DELAY_MAX_NOT_PARAMETER =
1339            new SimpleErrorProducer("DELAY_MAX_NOT_PARAMETER", ProblemKind.SEMANTIC,
1340                    "Calling function delay(): third argument must be of parameter variability: %s");
1341    public static final SimpleProblemProducer ASTNode.DELAY_NOT_PARAMETER =
1342            new SimpleErrorProducer("DELAY_NOT_PARAMETER", ProblemKind.SEMANTIC,
1343                    "Calling function delay(): second argument must be of parameter variability when third argument is not given: %s");
1344    public static final SimpleProblemProducer ASTNode.DELAY_OVER_MAX =
1345            new SimpleErrorProducer("DELAY_OVER_MAX", ProblemKind.SEMANTIC,
1346                    "Calling function delay(): second argument may not be larger than third argument: %s = %s > %s = %s");
1347    public static final SimpleProblemProducer ASTNode.DELAY_NEGATIVE =
1348            new SimpleErrorProducer("DELAY_NEGATIVE", ProblemKind.SEMANTIC,
1349                    "Calling function delay(): second argument may not be negative: %s = %s < 0");
1350    public static final SimpleProblemProducer ASTNode.DELAY_MAX_NEGATIVE =
1351            new SimpleErrorProducer("DELAY_MAX_NEGATIVE", ProblemKind.SEMANTIC,
1352                    "Calling function delay(): third argument may not be negative: %s = %s < 0");
1353
1354    public void FDelayExp.typeCheck(ErrorCheckType checkType) {
1355        boolean max = hasMax();
1356        boolean maxParam = max ? getMax().variability().parameterOrLess() : false;
1357        boolean delayParam = getDelay().variability().parameterOrLess();
1358        if (max && !maxParam) {
1359            DELAY_MAX_NOT_PARAMETER.invoke(this, getMax());
1360        }
1361        if (!max && !delayParam) {
1362            DELAY_NOT_PARAMETER.invoke(this, getDelay());
1363        }
1364       
1365        double maxVal = 0.0;
1366        boolean maxEval = false;
1367        if (max && maxParam) {
1368            try {
1369                CValue maxCVal = getMax().ceval();
1370                if (maxCVal.hasRealValue()) {
1371                    maxVal = maxCVal.realValue();
1372                    maxEval = true;
1373                }
1374            } catch (ConstantEvaluationException e) {}
1375        }
1376        double delayVal = 0.0;
1377        boolean delayEval = false;
1378        if (delayParam) {
1379            try {
1380                CValue delayCVal = getDelay().ceval();
1381                if (delayCVal.hasRealValue()) {
1382                    delayVal = delayCVal.realValue();
1383                    delayEval = true;
1384                }
1385            } catch (ConstantEvaluationException e) {}
1386        }
1387        if (maxEval && delayEval && delayVal > maxVal) {
1388            DELAY_OVER_MAX.invoke(this, getDelay(), delayVal, getMax(), maxVal);
1389        }
1390        if (delayEval && delayVal < 0.0) {
1391            DELAY_NEGATIVE.invoke(this, getDelay(), delayVal);
1392        }
1393        if (maxEval && maxVal < 0.0) {
1394            DELAY_MAX_NEGATIVE.invoke(this, getMax(), maxVal);
1395        }
1396    }
1397   
1398    public void FSpatialDistExp.typeCheck(ErrorCheckType checkType) {
1399        super.typeCheck(checkType);
1400        if (!getIn0().size().equivalent(getIn1().size(), true)) {
1401            errorUnlessDisabledAnd(getIn0().ndims() == getIn1().ndims(), 
1402                    "Calling function spatialDistribution(): first and second arguments 'in0' and 'in1' needs equivalent sizes");
1403        }
1404        if (isArray()) {
1405            if (getX().isArray()) {
1406                error("Calling function spatialDistribution(): third argument 'x' cannot be vectorized");
1407            }
1408            if (getPositiveVelocity().isArray()) {
1409                error("Calling function spatialDistribution(): fourth argument 'positiveVelocity' cannot be vectorized");
1410            }
1411        }
1412    }
1413
1414        public void FStringExp.typeCheck(ErrorCheckType checkType) {
1415                FType valueType = getValue().type();
1416                if (hasSignificantDigits() && !valueType.isReal())
1417                        error("Calling function String(): named argument significantDigits can only be used when first argument is real");
1418                if (hasFormat() && (hasMinimumLength() || hasLeftJustified() || hasSignificantDigits()))
1419                        error("Calling function String(): named argument format can not be used together with minimumLength, leftJustified or significantDigits");
1420        }
1421
1422    public void FHomotopyExp.typeCheck(ErrorCheckType checkType) {
1423        checkVectorizedSizes(checkType);
1424        super.typeCheck(checkType);
1425    }
1426 
1427    public void InstDerExp.typeCheck(ErrorCheckType checkType) {
1428        FType type = getFExp().type();
1429        if (!(type.isReal() && (type.isScalar() || type.isArray())))
1430            error("Only real typed expressions are allowed in der() operator");
1431    }
1432 
1433  syn int FBuiltInFunctionCall.builtInNumOutput() = 1;
1434  eq FIgnoredBuiltIn.builtInNumOutput() = 0;
1435  eq FConnectionsOp.builtInNumOutput()  = 0;
1436  eq FConnBoolOp.builtInNumOutput()     = 1;
1437  eq FSpatialDistExp.builtInNumOutput() = 2;
1438 
1439  syn boolean FBuiltInFunctionCall.checkTypeAsExpression() = false;
1440  eq FSizeExp.checkTypeAsExpression()     = true;
1441  eq FMinMaxExp.checkTypeAsExpression()   = true;
1442  eq FIdentity.checkTypeAsExpression()    = true;
1443  eq FLinspace.checkTypeAsExpression()    = true;
1444  eq FAbstractCat.checkTypeAsExpression() = dimensionIsOk();
1445 
1446  syn boolean FAbstractCat.dimensionIsOk(); 
1447  eq FCatExp.dimensionIsOk()    = getDim().isConstantExp() && 
1448                                                                  getDim().type().isInteger() && getDim().type().isScalar();
1449  eq FMatrix.dimensionIsOk()    = true;
1450  eq FMatrixRow.dimensionIsOk() = true;
1451    public static final SimpleProblemProducer ASTNode.CANNOT_INFER_ARRAY_SIZE_OF_OUTPUT =
1452            new SimpleErrorProducer("CANNOT_INFER_ARRAY_SIZE_OF_OUTPUT", ProblemKind.SEMANTIC,
1453                    "Could not evaluate array size of output %s");
1454
1455    public void InstFunctionCall.typeCheck(ErrorCheckType checkType) {
1456        if (!isFunctionCallClause() && !getName().myInstClassDecl().isRecord() && !hasOutputs() && !isPartialFunctionCall()) {
1457            error("Function " + getName().name() + "() has no outputs, but is used in expression");
1458        } else if (!isFunctionCallClause() && size().isUnknown() && errorCheckUnknownSize()) {
1459            CANNOT_INFER_ARRAY_SIZE_OF_OUTPUT.invoke(this, expOutput().name());
1460        }
1461        if (myCallOutputs().size() < myLefts().size()) {
1462            error("Too many components assigned from function call: " + getName().name() + 
1463                    "() has " + myCallOutputs().size() + " output(s)");
1464        }
1465        if (isFunctionCallClause()) {
1466            for (int i = 0, n = myCallOutputs().size(); i < n; i++) {
1467                sizeOfOutput(i).markAsStructuralParameter(checkType, this);
1468            }
1469        } else {
1470            size().markAsStructuralParameter(checkType, this);
1471        }
1472        if (callsLoadResource()) {
1473            getArgs().forceUsesVariability(checkType, Variability.LOADRESOURCEPARAMETER);
1474        }
1475    }
1476 
1477  syn boolean InstFunctionCall.isPartialFunctionCall() = false;
1478  eq InstPartialFunctionCall.isPartialFunctionCall()   = true;
1479
1480    public void FFunctionCallLeft.typeCheck(ErrorCheckType checkType) {
1481        if (hasFExp() && !myOutput().isUnknown() && !type().isUnknown()) { // Avoid duplicate error
1482            if (getFExp() instanceof CommonAccessExp) {  // Should never be false - add check?
1483                CommonAccess use = getFExp().asCommonAccess();
1484                FType useType = getFExp().type();
1485                FType outType = type();
1486                if (!use.isForIndex()) {
1487                    if (outType.size().isUnknown() && !inFunction() && !checkType.allowIncompleteSizes()) {
1488                        error(functionCallDecription() + ": could not evaluate array size of output " + 
1489                                myOutput().name());
1490                    } else if (!useType.typeCompatible(outType, true)) {
1491                        if (useType.typeCompatibleExceptLengths(outType)) {
1492                            if (!lockBranch(checkType))
1493                                errorUnlessDisabled("%s: component %s is of size %s and output %s is of size %s - they are not compatible", 
1494                                        functionCallDecription(), use.name(), useType.size(), myOutput().name(), outType.size());
1495                        } else {
1496                            error("%s: component %s is of type %s and output %s is of type %s - they are not compatible", 
1497                                    functionCallDecription(), use.name(), useType, myOutput().name(), outType);
1498                        }
1499                    }
1500                }
1501            }
1502        }
1503    }
1504
1505  inh String InstFunctionArgument.functionCallDecription();
1506  inh String FFunctionCallLeft.functionCallDecription();
1507  eq FAbstractFunctionCall.getChild().functionCallDecription() = functionCallDecription();
1508  eq FFunctionCallEquation.getChild().functionCallDecription() = getCall().functionCallDecription();
1509  eq FFunctionCallStmt.getChild().functionCallDecription()     = getCall().functionCallDecription();
1510
1511    public void InstFunctionArgument.typeCheck(ErrorCheckType checkType) {
1512        boolean typeOk = true;
1513        FType type = getFExp().type();
1514        if (!type.isUnknown()) {
1515            if (argumentDefinedTypeValid()) {
1516                FType boundType = getBoundInput().type();
1517                if (!boundType.isUnknown()) {
1518                    if (isVectorized()) 
1519                        boundType = boundType.sizedType(boundType.size().expand(vectorizedSize()));
1520                    else if (isDestructorArgument())
1521                        boundType = boundType.sizedType(type.size());
1522                    typeOk = boundType.typeCompatible(type, true) || lockBranch(checkType);
1523                }
1524            } else {
1525                typeOk = argumentTypeValid(type);
1526            }
1527        }
1528        if (!typeOk) {
1529            String msg = argumentTypeError();
1530            if (msg != null) {
1531                error(msg);
1532            } else {
1533                error("%s: types of %s and input %s are not compatible\n" + 
1534                      "    type of '%s' is %s\n" + 
1535                      "    expected type is %s", 
1536                      functionCallDecription(), argumentDesc(), getBoundInput().name(), 
1537                      getFExp(), getFExp().type(),
1538                      argumentDefinedTypeValid() ? getBoundInput().type().toString() : expectedArgumentType());
1539            }
1540        }
1541    }
1542
1543    public void InstBadArgument.typeCheck(ErrorCheckType checkType) {
1544        if (!isDestructorArgument()) {
1545            error(functionCallDecription() + ": " + errorString() + " " + getBoundInput().name());
1546        }
1547    }
1548
1549    syn String InstBadArgument.errorString();
1550    eq InstMissingArgument      .errorString() = "missing argument for required input";
1551    eq InstUnknownArgument      .errorString() = "could not resolve argument for required input";
1552    eq InstMultipleBoundArgument.errorString() = "multiple arguments matches input";
1553
1554    inh boolean InstFunctionArgument.isDestructorArgument();
1555    eq InstFunctionCall.getArg().isDestructorArgument() = isDestructorCall();
1556    eq BaseNode.getChild().isDestructorArgument()       = false;
1557
1558    inh boolean InstClassDecl.inExternalObject();
1559    eq InstNode.getChild().inExternalObject()              = isExternalObject();
1560    eq InstRecordConstructor.getChild().inExternalObject() = false;
1561
1562    public void InstDefaultArgument.typeCheck(ErrorCheckType checkType) {}
1563
1564  inh String InstFunctionArgument.argumentTypeError();
1565  eq FExp.getChild().argumentTypeError()         = null;
1566  eq FCardinality.getChild().argumentTypeError() = "The argument of cardinality() must be a scalar reference to a connector";
1567
1568    syn String InstFunctionArgument.argumentDesc() = null;
1569    eq InstPositionalArgument.argumentDesc() = "positional argument " + (getPos() + 1);
1570    eq InstNamedArgument.argumentDesc()      = "named argument " + getName();
1571
1572  public void InstAccess.typeCheck(ErrorCheckType checkType) {
1573      if (myInstComponentDecl().size().isUndefined() && myInstComponentDecl().inFunction() && 
1574              !myInstComponentDecl().isInput() && !checkType.allowIncompleteSizes()) {
1575          compliance("Using variables with undefined size is not supported");
1576      }
1577      super.typeCheck(checkType);
1578  }
1579 
1580  public boolean Size.isUndefined() {
1581      for (int i = 0; i < ndims(); i++) {
1582          if (size[i] == Size.UNKNOWN) {
1583              return true;
1584          }
1585      }
1586      return false;
1587  }
1588
1589  public boolean MutableSize.isUndefined() {
1590      for (int i = 0; i < ndims(); i++) {
1591          if (size[i] == Size.UNKNOWN && (exps[i] == null || exps[i] instanceof FColonSizeExp)) {
1592              return true;
1593          }
1594      }
1595      return false;
1596  }
1597
1598    public static final SimpleProblemProducer ASTNode.ACCESS_TO_CLASS_THROUGH_COMPONENT = 
1599            new SimpleErrorProducer("ACCESS_TO_CLASS_THROUGH_COMPONENT", ProblemKind.SEMANTIC,
1600                    "Can not access non-function class through component access: '%s'");
1601    public static final SimpleProblemProducer ASTNode.ACCESS_TO_FUNCTION_THROUGH_MULTIPLE_COMPONENTS = 
1602            new SimpleErrorProducer("ACCESS_TO_FUNCTION_THROUGH_MULTIPLE_COMPONENTS", ProblemKind.SEMANTIC,
1603                    "Can not access function through component unless only the first part of the name is a component: '%s'");
1604    public static final SimpleProblemProducer ASTNode.ACCESS_TO_FUNCTION_THROUGH_ARRAY_COMPONENT = 
1605            new SimpleErrorProducer("ACCESS_TO_FUNCTION_THROUGH_ARRAY_COMPONENT", ProblemKind.SEMANTIC,
1606                    "Can not access function through array component access: '%s'");
1607
1608    public void InstDot.typeCheck(ErrorCheckType checkType) {
1609        // Check for accesses to component in class or class in component
1610        int n = getNumInstAccess();
1611        for (int i = 0; i < n - 1; i++)
1612            getInstAccess(i).checkMixedAccessLeft(getInstAccess(i + 1), this, checkType, i, n);
1613        super.typeCheck(checkType);
1614    }
1615
1616    public void InstAccess.checkMixedAccessLeft(
1617            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {}
1618
1619    public void InstClassAccess.checkMixedAccessLeft(
1620            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {
1621        right.checkMixedAccessRight(myInstClassDecl(), top, checkType, i, n);
1622    }
1623
1624    public void InstComponentAccess.checkMixedAccessLeft(
1625            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {
1626        right.checkMixedAccessRight(myInstComponentDecl(), top, checkType, isArray(), i, n);
1627    }
1628
1629    public void InstComponentArrayAccess.checkMixedAccessLeft(
1630            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {
1631        right.checkMixedAccessRight(myInstComponentDecl(), top, checkType, true, i, n);
1632    }
1633
1634    public void InstAccess.checkMixedAccessRight(
1635            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {}
1636
1637    public void InstComponentAccess.checkMixedAccessRight(
1638            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {
1639        checkMixedAccessRightForComponent(left, top, checkType, i, n);
1640    }
1641
1642    public void InstComponentArrayAccess.checkMixedAccessRight(
1643            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {
1644        checkMixedAccessRightForComponent(left, top, checkType, i, n);
1645    }
1646
1647    public void InstAccess.checkMixedAccessRightForComponent(
1648            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {
1649        String type = null;
1650        if (left.extendsEnum()) {
1651            if (!myInstComponentDecl().isEnumLiteral())
1652                type = "attribute of primitive with dot notation";
1653        } else if (left.isPackage()) {
1654            left.checkRestriction(checkType);
1655        } else if (!left.isOkPackage()) {
1656            type = "component in non-package class";
1657        }
1658        if (type != null) {
1659            top.error("Can not access " + type + ": " + top.name());
1660        }
1661    }
1662
1663    public void InstAccess.checkMixedAccessRight(
1664            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {}
1665
1666    public void InstClassAccess.checkMixedAccessRight(
1667            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1668        if (!top.myInstClassDecl().isFunction()) {
1669            ACCESS_TO_CLASS_THROUGH_COMPONENT.invoke(top, top);
1670        } else if (i != 0) {
1671            ACCESS_TO_FUNCTION_THROUGH_MULTIPLE_COMPONENTS.invoke(top, top);
1672        } else if (array) {
1673            ACCESS_TO_FUNCTION_THROUGH_ARRAY_COMPONENT.invoke(top, top);
1674         }
1675    }
1676
1677    public void InstComponentAccess.checkMixedAccessRight(
1678            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1679        checkMixedAccessRightForComponent(left, top, checkType, array, i, n);
1680    }
1681
1682    public void InstComponentArrayAccess.checkMixedAccessRight(
1683            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1684        checkMixedAccessRightForComponent(left, top, checkType, array, i, n);
1685    }
1686
1687    public void InstAccess.checkMixedAccessRightForComponent(
1688            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1689        if (left.isPrimitive()) {
1690            top.error("Can not access attribute of primitive with dot notation: " + top.name());
1691        }
1692    }
1693
1694    /**
1695     * Check if the type defined for this argument in the built-in function list
1696     * is valid.
1697     */
1698    inh boolean InstFunctionArgument.argumentDefinedTypeValid();
1699    eq FAbstractFunctionCall.getChild().argumentDefinedTypeValid()           = true;
1700    eq FTranspose.getOriginalArg().argumentDefinedTypeValid()                = false;
1701    eq FSymmetric.getOriginalArg().argumentDefinedTypeValid()                = false;
1702    eq FMinMaxExp.getOriginalArg().argumentDefinedTypeValid()                = false;
1703    eq FReductionExp.getOriginalArg().argumentDefinedTypeValid()             = false;
1704    eq FHomotopyExp.getOriginalArg().argumentDefinedTypeValid()              = false;
1705    eq FSemiLinearExp.getOriginalArg().argumentDefinedTypeValid()            = false;
1706    eq FNdimsExp.getOriginalArg().argumentDefinedTypeValid()                 = false;
1707    eq FInfArgsFunctionCall.getOriginalArg().argumentDefinedTypeValid()      = false;
1708    eq FEnumIntegerExp.getOriginalArg().argumentDefinedTypeValid()           = false;
1709    eq FVectUnaryBuiltIn.getOriginalArg().argumentDefinedTypeValid()         = false;
1710    eq FDimensionConvert.getOriginalArg().argumentDefinedTypeValid()         = false;
1711    eq FEventGenExp.getOriginalArg().argumentDefinedTypeValid()              = false;
1712    eq FCardinality.getOriginalArg().argumentDefinedTypeValid()              = false;
1713    eq FDecouple.getOriginalArg().argumentDefinedTypeValid()                 = false;
1714    eq FMathematicalFunctionCall.getOriginalArg().argumentDefinedTypeValid() = false;
1715    eq FConnectionsOp.getOriginalArg().argumentDefinedTypeValid()            = false;
1716    eq FStringExp.getOriginalArg(int i).argumentDefinedTypeValid()           = i > 0;
1717    eq FConnPotentialRoot.getOriginalArg(int i).argumentDefinedTypeValid()   = i == 1;
1718    eq FSizeExp.getOriginalArg(int i).argumentDefinedTypeValid()             = i != 0;
1719    eq FSmoothExp.getOriginalArg(int i).argumentDefinedTypeValid()           = i == 0;
1720    eq InstPreExp.getOriginalArg().argumentDefinedTypeValid()                = false;
1721    eq FEdgeExp.getOriginalArg().argumentDefinedTypeValid()                  = false;
1722    eq FChangeExp.getOriginalArg().argumentDefinedTypeValid()                = false;
1723    eq FReinit.getOriginalArg().argumentDefinedTypeValid()                   = false;
1724
1725    /**
1726     * Check if the type supplied is valid for this argument.
1727     *
1728     * Only used if argumentDefinedTypeValid() returns <code>false</code>.
1729     */
1730    inh boolean InstFunctionArgument.argumentTypeValid(FType type);
1731    eq FAbstractFunctionCall.getChild().argumentTypeValid(FType type)     = type.isPrimitive();
1732    eq FTranspose.getChild().argumentTypeValid(FType type)                = type.ndims() >= 2 && type.isPrimitive();
1733    eq FSymmetric.getChild().argumentTypeValid(FType type)                = 
1734        type.ndims() == 2 && type.size().get(0) == type.size().get(1);
1735    eq FReductionExp.getChild().argumentTypeValid(FType type)             = type.ndims() > 0 && type.hasAdd();
1736    eq FSemiLinearExp.getChild().argumentTypeValid(FType type)            = type.isNumeric();
1737    eq FAbsExp.getChild().argumentTypeValid(FType type)                   = type.isNumeric();
1738    eq FSignExp.getChild().argumentTypeValid(FType type)                  = type.isNumeric();
1739    eq FEnumIntegerExp.getChild().argumentTypeValid(FType type)           = type.isEnum();
1740    eq FEventGenExp.getChild().argumentTypeValid(FType type)              = type.isNumeric();
1741    eq FMathematicalFunctionCall.getChild().argumentTypeValid(FType type) = type.isNumeric();
1742    eq FMinMaxExp.getOriginalArg(int i).argumentTypeValid(FType type)     = 
1743        hasY() != getOriginalArg(i).getFExp().isArray() && type.isPrimitive();
1744    eq FSmoothExp.getChild().argumentTypeValid(FType type)                = type.onlyContainsReal();
1745    eq FDimensionConvert.getChild().argumentTypeValid(FType type)         = argumentSizeValid(type.size());
1746    eq FCardinality.getChild().argumentTypeValid(FType type)              = 
1747        type.isScalar() && getFExp().isComponentReference(ALLOWED_COMPONENTS);
1748    eq FDecouple.getChild().argumentTypeValid(FType type)                 = true;
1749    eq FConnectionsOp.getOriginalArg(int i).argumentTypeValid(FType type) = 
1750        getOriginalArg(i).getFExp().isComponentReference(ALLOWED_COMPONENTS);
1751    eq FStringExp.getChild().argumentTypeValid(FType type)                = 
1752        type.isScalar() && (type.isReal() || type.isInteger() || type.isBoolean() || type.isEnum());
1753    eq FInfArgsFunctionCall.getChild().argumentTypeValid(FType type)      = true;  // Type checked in FInfArgsFunctionCall.typeCheck()
1754    eq FReinit.getChild().argumentTypeValid(FType type)                   = true;  // Type checked in Reinit.typeCheck()
1755    eq FHomotopyExp.getChild().argumentTypeValid(FType type)              = type.isNumeric();
1756    // All types allowed for these:
1757    eq FNoEventExp.getChild().argumentTypeValid(FType type)               = true;
1758    eq FSizeExp.getChild().argumentTypeValid(FType type)                  = true;
1759
1760    /**
1761     * Description of expexted type of argument.
1762     *
1763     * Should be defined for cases where argumentDefinedTypeValid() returns and
1764     * argumentTypeValid() both return false.
1765     */
1766    inh String InstFunctionArgument.expectedArgumentType();
1767    eq FAbstractFunctionCall.getChild().expectedArgumentType()     = 
1768        "Real, Integer, Boolean, String or enumeration";
1769    eq FTranspose.getChild().expectedArgumentType()                = 
1770        "matrix of Real, Integer, Boolean, String or enumeration";
1771    eq FSymmetric.getChild().expectedArgumentType()                = "square matrix";
1772    eq FReductionExp.getChild().expectedArgumentType()             = "Real array";
1773    eq FEnumIntegerExp.getChild().expectedArgumentType()           = "enumeration";
1774    eq FSemiLinearExp.getChild().expectedArgumentType()            = "Real";
1775    eq FAbsExp.getChild().expectedArgumentType()                   = "Real";
1776    eq FSignExp.getChild().expectedArgumentType()                  = "Real";
1777    eq FEventGenExp.getChild().expectedArgumentType()              = "Real";
1778    eq FMathematicalFunctionCall.getChild().expectedArgumentType() = "Real";
1779    eq FSmoothExp.getChild().expectedArgumentType()                = "Real";
1780    eq FHomotopyExp.getChild().expectedArgumentType()              = "Real";
1781    eq FMinMaxExp.getOriginalArg(int i).expectedArgumentType()     = 
1782        (hasY() ? "scalar" : "array of") + " Real, Integer, Boolean, String or enumeration";
1783    eq FScalarExp.getChild().expectedArgumentType()                = "array with exactly 1 element";
1784    eq FVectorExp.getChild().expectedArgumentType()                = "scalar or vector-shaped array";
1785    eq FMatrixExp.getChild().expectedArgumentType()                = "scalar, vector or matrix-shaped array";
1786    eq FCardinality.getChild().expectedArgumentType()              = "scalar connector";
1787    eq FConnectionsOp.getChild().expectedArgumentType()            = "connector";
1788    eq FStringExp.getChild().expectedArgumentType()                = "scalar Real, Integer, Boolean or enumeration";
1789
1790  protected static final Criteria<InstComponentDecl> FCardinality.ALLOWED_COMPONENTS = new Criteria<InstComponentDecl>() {
1791      public boolean test(InstComponentDecl elem) { return elem.isConnector() || elem.isPrimitive(); }
1792  };
1793
1794  protected static final Criteria<InstComponentDecl> FConnectionsOp.ALLOWED_COMPONENTS = new Criteria<InstComponentDecl>() {
1795      public boolean test(InstComponentDecl elem) { return elem.isOverconstrainedType(); }
1796  };
1797
1798  /**
1799   * Check if this expression is a reference to a component matching specified criteria.
1800   *
1801   * Only valid in instance tree.
1802   */
1803  syn boolean FExp.isComponentReference(Criteria<InstComponentDecl> criteria)  = false;
1804    eq CommonAccessExp.isComponentReference(Criteria<InstComponentDecl> criteria) = getAccess().isComponentReference(criteria);
1805   
1806    syn boolean CommonAccess.isComponentReference(Criteria<InstComponentDecl> criteria) = false;
1807    eq InstAccess.isComponentReference(Criteria<InstComponentDecl> criteria) = 
1808            criteria.test(myInstComponentDecl());
1809
1810  /**
1811   * Check if the given Size is valid for the argument of the operator.
1812   */
1813  syn boolean FDimensionConvert.argumentSizeValid(Size s) {
1814      if (s.isUnknown())
1815          return true;
1816     
1817          boolean[] useDim = new boolean[s.ndims()];
1818          for (int d : dimensionsToKeep())
1819                  if (d < useDim.length)
1820                          useDim[d] = true;
1821          for (int i = 0; i < s.ndims(); i++)
1822                  if (!useDim[i] && s.get(i) != 1)
1823                          return false;
1824          return true;
1825  }
1826
1827    protected static void ASTNode.typeCheckEnabled(ErrorCheckType checkType, FExp enabledExp, FType parentType, boolean eachSet) {
1828        enabledExp.collectErrors(checkType);
1829        if (checkType.checkTypes()) {
1830            if (!enabledExp.type().isBoolean() && !enabledExp.type().isUnknown()) {
1831                enabledExp.error("The type of the enabled expression is not boolean");
1832            } else if (!eachSet && !parentType.dimensionCompatible(enabledExp.type())) { 
1833                if (enabledExp.ndims() == 0) {
1834                    enabledExp.warning("Assuming 'each' for enabled expression");
1835                } else {
1836                    enabledExp.errorUnlessDisabledAnd(parentType.ndims() == enabledExp.ndims(), 
1837                            "Array size mismatch for the enabled attribute, " + 
1838                            "size of component declaration is %s and size of expression is %s", 
1839                            parentType.size(), enabledExp.size());
1840                }
1841            } else if (eachSet) { 
1842                if (parentType.ndims() == 0) {
1843                    enabledExp.warning("The 'each' keyword should not be applied to attributes of scalar components");
1844                } else if (enabledExp.ndims() > 0) {
1845                    enabledExp.error("The enabled attribute is declared 'each' and the expression is not scalar");
1846                }
1847            }
1848        }
1849    }
1850
1851}
1852
1853aspect VariabilityCheck {
1854   
1855    private FExp FPreExp.instanceTreeSource = null;
1856    private FExp FReinit.instanceTreeSource = null;
1857   
1858    public void FExp.setInstanceTreeSource(FExp source) {}
1859   
1860    public void FPreExp.setInstanceTreeSource(FExp source) {
1861        instanceTreeSource = source;
1862    }
1863   
1864    public void FReinit.setInstanceTreeSource(FExp source) {
1865        instanceTreeSource = source;
1866    }
1867   
1868    /**
1869     * Surrounds the FExp <code>exp</code> with pre if it is an non literal exp, else return exp.
1870     *
1871     * Also sets the <code>instanceTreeSource</code> field of the FPreExp (if any)
1872     * to <code>source</code>.
1873     */
1874    public static FExp FPreExp.create(FExp exp, FExp source) {
1875        exp = exp.createFPreExp();
1876        exp.setInstanceTreeSource(source);
1877        return exp;
1878    }
1879   
1880    /**
1881     * Check operations that are allowed on discrete expressions or variables,
1882     * but not on continuous ones or vice-versa.
1883     *
1884     * Also breaks connections back to instance tree that was added during flattening
1885     * to be able to report these errors in the correct place.
1886     */
1887    public void ASTNode.checkDiscreteOperations() {
1888        for (ASTNode n : this)
1889            n.checkDiscreteOperations();
1890    }
1891   
1892    public void FPreExp.checkDiscreteOperations() {
1893        if (instanceTreeSource != null && getFAccess().myFV().isContinuous() && !inWhen() && !inInitialEquationSection()) 
1894            instanceTreeSource.discreteArgError();
1895        instanceTreeSource = null;
1896    }
1897   
1898    public void FReinit.checkDiscreteOperations() {
1899        if (instanceTreeSource != null && !myFV().isContinuous()) 
1900            instanceTreeSource.discreteArgError();
1901        instanceTreeSource = null;
1902    }
1903   
1904    /**
1905     * Generate error for operator that got wrong variability of argument
1906     */
1907    public void FExp.discreteArgError() {
1908        throw new UnsupportedOperationException();
1909    }
1910   
1911    public void InstPreExp.discreteArgError() {
1912        error("Calling built-in operator pre() with a continuous variable access as argument can only be done in when clauses and initial equations");
1913    }
1914   
1915    public void FChangeExp.discreteArgError() {
1916        error("Calling built-in operator change() with a continuous variable access as argument can only be done in when clauses and initial equations");
1917    }
1918   
1919    public void FReinit.discreteArgError() {
1920        error("Built-in operator reinit() must have a continuous variable access as its first argument");
1921    }
1922   
1923}
Note: See TracBrowser for help on using the repository browser.