source: trunk/Compiler/ModelicaFrontEnd/src/jastadd/errorcheck/TypeCheck.jrag @ 13396

Last change on this file since 13396 was 13396, checked in by molsson, 3 months ago

#5813 Merging branches/dev-mo-2263-merge to trunk.

File size: 93.4 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 (hasZeroNominal(owner, cell)) {
378                        NOMINAL_ELEMENT_EQUAL_TO_ZERO.invoke(this, owner.kindDescription(), owner.name(), valMod, i);
379                        break;
380                    }
381                }
382            } else {
383                if (hasZeroNominal(owner, val)) {
384                    NOMINAL_EQUAL_TO_ZERO.invoke(this, owner.kindDescription(), owner.name(), valMod);
385                }
386            }
387        } catch (ConstantEvaluationException e) {}
388    }
389   
390    public boolean InstComponentModification.hasZeroNominal(InstNode owner, CValue val) {
391        return val.hasRealValue() && val.realValue() == 0.0;
392    }
393
394    syn String InstNode.kindDescription() {
395        throw new UnsupportedOperationException();
396    }
397    eq InstComponentDecl.kindDescription() = myInstClass().kindDescription() + " instance";
398    eq InstPrimitive.kindDescription()     = "variable";
399    eq InstBaseClassDecl.kindDescription() = getInstRestriction().toString();
400
401    public static final SimpleProblemProducer ASTNode.EACH_APPLIED_ON_SCALAR =
402            new SimpleWarningProducer("EACH_APPLIED_ON_SCALAR", ProblemKind.SEMANTIC,
403                    "The 'each' keyword should not be applied to a modification of a scalar component: %s");
404
405    public void InstArgument.typeCheck(ErrorCheckType checkType) {
406        if (checkType.checkTypes() && getEach() && expectedSizeFromParent() == Size.SCALAR) 
407            EACH_APPLIED_ON_SCALAR.invoke(this, this);
408    }
409
410    inh boolean InstNode.isInRedeclareMod();
411    eq InstElementRedeclare.getChild().isInRedeclareMod() = true;
412    eq InstClassDecl.getChild().isInRedeclareMod()        = false;
413    eq InstRoot.getChild().isInRedeclareMod()             = false;
414    eq Root.getChild().isInRedeclareMod()                 = false;
415
416    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_EQUATION =
417            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_EQUATION", ProblemKind.SEMANTIC,
418                    "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");
419    public static final SimpleProblemProducer ASTNode.TYPE_MISMATCH_IN_EQUATION =
420            new SimpleErrorProducer("TYPE_MISMATCH_IN_EQUATION", ProblemKind.SEMANTIC,
421                    "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");
422
423    public void FEquation.typeCheck(ErrorCheckType checkType) {
424        typeCheckLocalIteration(checkType);
425        FType left = getLeft().type();
426        FType right = getRight().type();
427        if (!left.isUnknown() && !right.isUnknown()) {
428            if (!left.equivalentTo(right)) {
429                if (left.equivalentExceptLengths(right)) {
430                    if (!lockBranch(checkType))
431                        ARRAY_SIZE_MISMATCH_IN_EQUATION.invoke(this, left.size(), right.size());
432                } else {
433                    TYPE_MISMATCH_IN_EQUATION.invoke(this, left, right);
434                }
435            }
436        }
437    }
438
439    public static final SimpleProblemProducer ASTNode.CONNECT_WITH_INVALID_TYPE =
440            new SimpleErrorProducer("CONNECT_WITH_INVALID_TYPE", ProblemKind.SEMANTIC,
441                    "Connecting to an instance of a non-connector type is not allowed");
442    public static final SimpleProblemProducer ASTNode.CONNECT_EXPANDABLE_AND_NON_EXPANDABLE_TYPE =
443            new SimpleErrorProducer("CONNECT_EXPANDABLE_AND_NON_EXPANDABLE_TYPE", ProblemKind.SEMANTIC,
444                    "Connecting an expandable connector to a non-expandable connector is not allowed");
445    public static final SimpleProblemProducer ASTNode.TYPE_MISMATCH_IN_CONNECT =
446            new SimpleErrorProducer("TYPE_MISMATCH_IN_CONNECT", ProblemKind.SEMANTIC,
447                    "Types of connected components do not match");
448    public static final ErrorProducerUnlessDisabled ASTNode.ARRAY_SIZE_MISMATCH_IN_CONNECT =
449            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_CONNECT", ProblemKind.SEMANTIC,
450                    "Sizes do not match in connection, size of '%s' is %s and size of '%s' is %s");
451    public static final ErrorProducerUnlessDisabled ASTNode.EXPANDABLE_ARRAY_SIZE_MISMATCH_IN_CONNECT =
452            new ErrorProducerUnlessDisabled("ARRAY_SIZE_MISMATCH_IN_CONNECT", ProblemKind.SEMANTIC,
453                    "Sizes do not match in connection, size of the part of '%s' referring to " + 
454                    "the expandable connector is %s and size of '%s' is %s");
455
456    public void FConnectClause.typeCheck(ErrorCheckType checkType) {
457        InstAccess left  = getConnector1();
458        InstAccess right = getConnector2();
459        boolean checkTypes = !isDisabled();
460        boolean isExpandable = false;
461        boolean expandableSame = true;
462        for (InstAccess access = left; access != null; access = (access == left) ? right : null) {
463            if (access.isExpandableConnectorPart()) {
464                isExpandable = true;
465            } else if (!access.isUnknown() && !access.myInstComponentDecl().myInstClass().isUnknown()) {
466                if (!access.myInstComponentDecl().isConnector())
467                    CONNECT_WITH_INVALID_TYPE.invoke(access);
468            } else {
469                checkTypes = false;
470            }
471            if (access.myInstComponentDecl().isExpandableConnector()) {
472                expandableSame = !expandableSame;
473            }
474        }
475        if (!expandableSame && !left.isUnknown() && !right.isUnknown()) {
476            CONNECT_EXPANDABLE_AND_NON_EXPANDABLE_TYPE.invoke(this);
477        }
478        if (checkTypes) { 
479            if (isExpandable) {
480                boolean leftUnknown = left.isExpandableConnectorPart();
481                InstAccess known   = leftUnknown ? right : left;
482                InstAccess unknown = leftUnknown ? left  : right;
483                Size knownSize   = known.size();
484                Size unknownSize = unknown.findExpandableConnectorPart().partSize();
485                if (knownSize.ndims() < unknownSize.ndims() || 
486                        !unknownSize.equivalent(knownSize.contractRight(unknownSize.ndims()), false)) {
487                    EXPANDABLE_ARRAY_SIZE_MISMATCH_IN_CONNECT.invoke(
488                            this, unknown, unknownSize, known, knownSize);
489                }
490            } else {
491                InstComponentDecl leftComp  = left.lookupEvaluatingIndices();
492                InstComponentDecl rightComp = right.lookupEvaluatingIndices();
493                if (!leftComp.connectableTo(rightComp)) {
494                    TYPE_MISMATCH_IN_CONNECT.invoke(this);
495                } else if (!left.size().equivalent(right.size(), false)) {
496                    ARRAY_SIZE_MISMATCH_IN_CONNECT.invokeWithCondition(this, left.ndims() == right.ndims(), 
497                            left, left.size(), right, right.size());
498                }
499            }
500        }
501    }
502
503    // Generic typeCheck(checkType) that calls typeError() if type is unknown and no FExp child has unknown type
504    public void FExp.typeCheck(ErrorCheckType checkType) {
505        if (generateTypeError()) {
506            for (FExp exp : childFExps())
507                if (exp.type().isUnknown())
508                    return;
509            typeError(checkType);
510        } else {
511            size().markAsStructuralParameter(checkType, this);
512        }
513    }
514
515    syn boolean FExp.generateTypeError() = type().isUnknown();
516    eq FEqRelExp.generateTypeError()     = super.generateTypeError() ||
517            (getLeft().type().isReal() || getRight().type().isReal()) && !inFunction();
518
519    public void InstIfExp.typeCheck(ErrorCheckType checkType) {
520        if (!generateTypeError()) {
521            if (!getThenExp().type().equivalentTo(getElseExp().type())) {
522                getIfExp().markAsStructuralParameter(checkType);
523            }
524        }
525        super.typeCheck(checkType);
526    }
527
528    public void Size.markAsStructuralParameter(ErrorCheckType checkType, FExp src) {}
529   
530    public void MutableSize.markAsStructuralParameter(ErrorCheckType checkType, FExp src) {
531        if (src.errorCheckUnknownSize()) {
532            for (int i = 0; i < ndims(); i++) {
533                FExp exp = exps[i];
534                if (exp != null) {
535                    exp.markAsStructuralParameter(checkType);
536                    if (!exp.variability().parameterOrLess()) {
537                        ASTNode.NON_PARAMETER_SIZE_IN_EXPRESSION.invoke(src, exp, i, src);
538                    }
539                }
540            }
541        }
542    }
543   
544    syn boolean FExp.errorCheckUnknownSize() = !inFunction() && !allowUnknownSize();
545
546    syn boolean BaseNode.allowUnknownSize()    = false;
547    eq InstFunctionArgument.allowUnknownSize() = inAllowUnknownSize();
548    eq FIfExp.allowUnknownSize()               = inAllowUnknownSize();
549    eq InstFunctionCall.allowUnknownSize()     = inAllowUnknownSize() && callsExternal();
550    eq FFunctionCall.allowUnknownSize()        = inAllowUnknownSize() && callsExternal();
551
552    inh boolean BaseNode.inAllowUnknownSize();
553    eq BaseNode.getChild().inAllowUnknownSize()         = allowUnknownSize();
554    eq InstFunctionCall.getChild().inAllowUnknownSize() = allowUnknownSize() || callsExternal();
555    eq FFunctionCall.getChild().inAllowUnknownSize()    = allowUnknownSize() || callsExternal();
556
557    class FFunctionDecl {
558        public enum FunctionType {
559            EXTERNAL, LOADRESOURCE;
560           
561            public boolean containsFunction(CommonCallable cc) {
562                if (this == EXTERNAL) {
563                    return cc.isExternalFunction();
564                } else {
565                    return cc.containsFunction(this);
566                }
567            }
568        }
569    }
570
571    interface CommonCallable {
572        public boolean containsFunction(FFunctionDecl.FunctionType ft);
573        public boolean isExternalFunction();
574    }
575
576    syn boolean InstPartialFunction.isExternalFunction() = false;
577    syn boolean FFunctionVariable.isExternalFunction()   = false;
578
579    syn boolean InstClassDecl.isExternalFunction() = findFunctionExternal() != null;
580    syn boolean FFunctionDecl.isExternalFunction() = getFirstExternalStmt() != null;
581
582    syn FExternalStmt FFunctionDecl.getFirstExternalStmt() {
583        for (FStatement stmt : getFAlgorithm().getFStatements()) {
584            if (stmt.isExternalStatement()) {
585                return (FExternalStmt) stmt;
586            }
587        }
588        return null;
589    }
590
591    syn boolean FStatement.isExternalStatement() = false;
592    eq FExternalStmt.isExternalStatement()       = true;
593
594    syn boolean FAbstractFunctionCall.callsExternal() =
595            FFunctionDecl.FunctionType.EXTERNAL.containsFunction(myCommonCallable());
596    syn boolean FAbstractFunctionCall.callsLoadResource() =
597            FFunctionDecl.FunctionType.LOADRESOURCE.containsFunction(myCommonCallable());
598   
599    syn boolean ASTNode.containsFunction(FFunctionDecl.FunctionType ft) {
600        for (ASTNode n : this) {
601            if (n.containsFunction(ft)) {
602                return true;
603            }
604        }
605        return false;
606    }
607
608    @Override
609    syn boolean InstNode.containsFunction(FFunctionDecl.FunctionType ft) =
610        getInstComponentDecls().containsFunction(ft) || getInstExtendss().containsFunction(ft) ||
611        getFAbstractEquations().containsFunction(ft) || super.containsFunction(ft);
612
613    eq InstComponentDecl.containsFunction(FFunctionDecl.FunctionType ft) =
614        (hasInstModification() && getInstModification().containsFunction(ft)) ||  super.containsFunction(ft);
615    eq InstValueModification.containsFunction(FFunctionDecl.FunctionType ft) =
616        getFExp().containsFunction(ft) ||  super.containsFunction(ft);
617    eq InstFunctionCall.containsFunction(FFunctionDecl.FunctionType ft) =
618        ft.containsFunction(myCommonCallable()) || super.containsFunction(ft);
619    eq FFunctionCall.containsFunction(FFunctionDecl.FunctionType ft) =
620        ft.containsFunction(myCommonCallable()) || super.containsFunction(ft);
621    eq FLoadResource.containsFunction(FFunctionDecl.FunctionType ft) =
622        ft == FFunctionDecl.FunctionType.LOADRESOURCE || super.containsFunction(ft);
623
624    syn lazy boolean InstClassDecl.containsFunction(FFunctionDecl.FunctionType ft) circular [false] =
625            super.containsFunction(ft);
626    eq InstSimpleShortClassDecl.containsFunction(FFunctionDecl.FunctionType ft) =
627            actualInstClass().containsFunction(ft);
628
629    public static final ErrorProducerUnlessDisabled ASTNode.NON_PARAMETER_SIZE_IN_EXPRESSION =
630            new ErrorProducerUnlessDisabled("NON_PARAMETER_SIZE_IN_EXPRESSION", ProblemKind.COMPLIANCE,
631                    "Non-parameter expression sizes not supported, '%s', dimension %d in '%s'");
632   
633    @Override
634    public void FLoadResource.typeCheck(ErrorCheckType checkType) {
635        super.complianceCheck(checkType);
636        getFExp().forceVariability(checkType, Variability.LOADRESOURCEPARAMETER);
637    }
638   
639    public class FExp {
640        public static class ExpTypeErrorProducer extends AbstractErrorProducerUnlessDisabled<ReporterNode> {
641            private final String message;
642           
643            public ExpTypeErrorProducer(String identifier, String message) {
644                super(identifier, ProblemKind.SEMANTIC);
645                this.message = message;
646            }
647
648            public void invoke(FExp node) {
649                StringBuilder buf = new StringBuilder(message);
650                buf.append(": ");
651                buf.append(node);
652                for (FExp exp : node.childFExps()) {
653                    buf.append("\n    type of '");
654                    buf.append(exp);
655                    buf.append("' is ");
656                    buf.append(exp.type());
657                }
658                super.invokeWithCondition(node, node.typeErrorOnlyLengths(), buf.toString());
659            }
660
661            @Override
662            public String description() {
663                return message;
664            }
665           
666        }
667    }
668   
669    public static final FExp.ExpTypeErrorProducer ASTNode.TYPE_MISMATCH_IN_EXPRESSION =
670            new FExp.ExpTypeErrorProducer("TYPE_MISMATCH_IN_EXPRESSION", "Type error in expression");
671
672    public void FExp.typeError(ErrorCheckType checkType) {
673        TYPE_MISMATCH_IN_EXPRESSION.invoke(this);
674    }
675
676    /**
677     * Check if type error is only in array lengths.
678     */
679    syn boolean FExp.typeErrorOnlyLengths() = typeErrorOnlyLengths(childFExps());
680    eq FAbstractCat.typeErrorOnlyLengths()  = typeErrorOnlyLengths(getFExps());
681
682    /**
683     * Check if type error is only in array lengths.
684     */
685    syn boolean FExp.typeErrorOnlyLengths(Iterable<FExp> exps) {
686        FType first = null;
687        for (FExp exp : exps) {
688            FType cur = exp.type();
689            if (first == null) {
690                first = cur;
691            } else if (!first.equivalentExceptLengths(cur)) {
692                return false;
693            }
694        }
695        return true;
696    }
697
698    /**
699     * The message to use in default type error.
700     */
701    public static final FExp.ExpTypeErrorProducer ASTNode.EQUALITY_COMPARISON_OF_REAL =
702            new FExp.ExpTypeErrorProducer("EQUALITY_COMPARISON_OF_REALS", "Equality comparisons do not allow real operands");
703
704    @Override
705    public void FEqRelExp.typeError(ErrorCheckType checkType) {
706        if ((getLeft().type().isRealScalar() || getRight().type().isRealScalar()) && !inFunction()) {
707            EQUALITY_COMPARISON_OF_REAL.invoke(this);
708        } else {
709            super.typeError(checkType);
710        }
711    }
712   
713    public void FLinspace.typeCheck(ErrorCheckType checkType) {
714        getN().markAsStructuralParameter(checkType);
715        super.typeCheck(checkType);
716    }
717
718    public void FLinspace.typeError(ErrorCheckType checkType) {
719        boolean badVar = !getN().variability().fixedParameterOrLess();
720        boolean canCeval = getN().canCeval();
721        boolean badVal = canCeval && getN().ceval().intValue() < 2;
722        if (badVar || !canCeval || badVal) {
723            String msg = "Third argument of linspace() must be a scalar parameter Integer expression that is greater than 1";
724            if (badVar)
725                error("%s\n    '%s' is of %s variability", msg, getN(), getN().variability().toStringLiteral());
726            else if (canCeval && !lockBranch(checkType))
727                errorUnlessDisabled("%s\n    '%s' evaluates to %d", msg, getN(), getN().ceval().intValue());
728        } else {
729            super.typeError(checkType);
730        }
731    }
732
733    public void FIdentity.typeCheck(ErrorCheckType checkType) {
734        getFExp().markAsStructuralParameter(checkType);
735        super.typeCheck(checkType);
736    }
737
738    public void FIdentity.typeError(ErrorCheckType checkType) {
739        if (!getFExp().variability().parameterOrLess()) 
740            error("Argument of identity() must be a scalar parameter Integer expression\n    '%s' is of %s variability", 
741                    getFExp(), getFExp().variability().toStringLiteral());
742        else 
743            super.typeError(checkType);
744    }
745
746    public void FSizeExp.typeCheck(ErrorCheckType checkType) {
747        if (hasDim())
748            getDim().markAsStructuralParameter(checkType);
749        super.typeCheck(checkType);
750    }
751
752    public void FSizeExp.typeError(ErrorCheckType checkType) {
753        if (hasDim()) {
754            if (!getDim().type().isIntegerScalar())
755                return;  // Error is reported for argument in this case
756            boolean badVar = !getDim().variability().parameterOrLess();
757            boolean canCeval = getDim().canCeval();
758            int dimVal = canCeval ? getDim().ceval().intValue() : 1;
759            int ndims = getFExp().ndims();
760            if (badVar || !canCeval || dimVal < 1 || dimVal > ndims) {
761                String msg = "Second argument of size() must be a scalar parameter Integer expression that evaluates to a valid dimension of the first argument";
762                if (badVar)
763                    error("%s\n    '%s' is of %s variability", msg, getDim(), getDim().variability().toStringLiteral());
764                else if (canCeval)
765                    error("%s\n    '%s' evaluates to %d, and '%s' has %d dimensions", msg, getDim(), dimVal, getFExp(), ndims);
766                return;
767            }
768        }
769        super.typeError(checkType);
770    }
771
772    public void CommonAccess.typeError(ErrorCheckType checkType, CommonAccessExp access) {
773        access.typeError(checkType);
774    }
775
776    public void InstAccess.typeError(ErrorCheckType checkType, CommonAccessExp access) {
777        typeError();
778    }
779
780  public void InstAccess.typeError() {
781          typeError(this);
782  }
783 
784  protected void InstAccess.typeError(InstAccess top) {}
785 
786  protected void InstDot.typeError(InstAccess top) {
787          getLastInstAccess().typeError(top);
788  }
789 
790  protected void InstGlobalAccess.typeError(InstAccess top) {
791          getInstAccess().typeError(top);
792  }
793 
794    protected void InstComponentAccess.typeError(InstAccess top) {
795        InstComponentDecl icd = myInstComponentDecl();
796        if (!icd.isUnknown() && !icd.myInstClass().isUnknown() && !inCardinality()) {
797            if (!icd.isPrimitive() && !icd.isRecord()) {
798                top.error("Accesses to composite components other than records are not allowed: " + top.name());
799            }
800        }
801    }
802 
803    protected void InstComponentArrayAccess.typeError(InstAccess top) {
804        InstComponentDecl icd = myInstComponentDecl();
805        if (!icd.isUnknown() && !icd.myInstClass().isUnknown() && !inCardinality()) {
806            if (!icd.isPrimitive() && !icd.isRecord()) {
807                top.error("Accesses to composite components other than records are not allowed: " + top.name());
808            }
809        }
810    }
811 
812  inh boolean InstAccess.inCardinality();
813  eq FCardinality.getChild().inCardinality() = true;
814  eq InstNode.getChild().inCardinality()     = false;
815  eq Root.getChild().inCardinality()         = false;
816 
817  protected void InstClassAccess.typeError(InstAccess top) {
818          if (!isComponentSizeClass() || !isInstComponentSize())
819                  top.error("Illegal access to class in expression: " + top.name());
820  }
821
822    public void InstIfExp.typeError(ErrorCheckType checkType) {
823        FType thenType = getThenExp().type();
824        FType elseType = getElseExp().type();
825        FType scalar = thenType.scalarType().typePromotion(elseType.scalarType());
826       
827        if (!getIfExp().isOKTestExp()) {
828            getIfExp().error("Test expression of if expression must be scalar boolean:\n    %s is %s", 
829                    getIfExp(), getIfExp().type());
830        } else if (thenType.ndims() != elseType.ndims()) { 
831            error("Branches of if expression have different number of array dimensions:" + 
832                    "\n    %s has size %s\n    %s has size %s", 
833                getThenExp(), thenType.size(), getElseExp(), elseType.size());
834        } else if (scalar.isUnknown()) { 
835            error("Branches of if expression have incompatible types:\n    %s is %s\n    %s is %s", 
836                    getThenExp(), thenType, getElseExp(), elseType);
837        } else {
838            if (isParameterIf())
839                error("Failed to evaluate test expression in if expression with branches that have different array sizes:" + 
840                        "\n    %s has size %s\n    %s has size %s", 
841                    getThenExp(), thenType.size(), getElseExp(), elseType.size());
842            else
843                error("If expression with branches that have different array sizes must have parameter test expression:" + 
844                        "\n    %s has size %s\n    %s has size %s", 
845                    getThenExp(), thenType.size(), getElseExp(), elseType.size());
846        }
847    }
848
849  public void CommonAccessExp.typeCheck(ErrorCheckType checkType) {
850          if (!getAccess().isUnknown() && type().isUnknown() && !accessToModelOK())
851            getAccess().typeError(checkType, this);
852  }
853 
854        public void CommonAccess.typeCheck(ErrorCheckType checkType) {
855        if (inAlgorithm() && useIsAssignment()) {
856            if (variability().constantVariability()) {
857                error("Assignments to constants is not allowed in algorithms");
858            } else if (variability().parameterVariability()) {
859                if (isForIndex()) {
860                    error("Can not assign a value to a for loop index");
861                } else if (!(myFAlgorithm().getType().isInitial())) {
862                    error("Assignments to parameters in algorithms is only allowed in initial algorithms");
863                }
864            }
865        }
866        }
867
868  inh boolean CommonAccessExp.accessToModelOK();
869  inh boolean InstFunctionArgument.accessToModelOK();
870  eq BaseNode.getChild().accessToModelOK()             = false;
871  eq InstFunctionArgument.getChild().accessToModelOK() = accessToModelOK();
872  eq FSizeExp.getFExp().accessToModelOK()              = true;
873  eq FSizeExp.getOriginalArg(int i).accessToModelOK()  = i == 0;
874 
875 
876  /**
877   * Check if the FExp of this FIterExp must be scalar.
878   */
879  inh boolean FIterExp.iterExpMustBeScalar();
880  eq FExp.getChild().iterExpMustBeScalar()       = false;
881  eq Root.getChild().iterExpMustBeScalar()       = false;
882  eq InstNode.getChild().iterExpMustBeScalar()   = false;
883  eq FMinMaxExp.getChild().iterExpMustBeScalar() = true;
884  // TODO: Add product() when it is implemented
885
886    public void FMulExp.typeCheck(ErrorCheckType checkType) {
887        if (type().isOperatorRecord() && !isElementWise()) {
888            int left = getLeft().size().get(1);
889            int inner = (left == Size.UNKNOWN) ? getRight().size().get(0) : left;
890            if ((inner == 0 || inner == Size.UNKNOWN) && type().matchOverloadedZero() == null) 
891                error("Matrix multiplication of operator records with an inner dimension of 0 or : requires that an '0' operator is defined");
892        }
893        super.typeCheck(checkType);
894    }
895
896  public void FIterExp.typeCheck(ErrorCheckType checkType) {
897          super.typeCheck(checkType);
898          if (iterExpMustBeScalar() && !getFExp().type().isUnknown() && getFExp().ndims() != 0)
899                  error("The expression of a reduction-expression must be scalar, except for sum(): " +
900                                  getFExp() + " has " + getFExp().ndims() + " dimension(s)");
901  }
902 
903  public void FStreamBuiltIn.typeCheck(ErrorCheckType checkType) {
904          if (!getFExp().isAccessToStream()) 
905                  error("Argument of " + builtInName() + "() must be a stream variable");
906  }
907 
908  /**
909   * Returns true if this expression can be interpreted as some form of Access
910   */
911  syn boolean FExp.isAccessLikeExp() = false;
912  eq CommonAccessExp.isAccessLikeExp() = true;
913 
914  syn boolean FExp.isAccess()  = false;
915  eq CommonAccessExp.isAccess()= true;
916  syn CommonAccess FExp.asCommonAccess() {
917          throw new UnsupportedOperationException("asCommonAccess() is not supported for class type " + getClass().getSimpleName());
918  }
919  eq CommonAccessExp.asCommonAccess() = getAccess();
920 
921  syn CommonAccessExp FExp.asCommonAccessExp() {
922      throw new UnsupportedOperationException("asCommonAccessExp() is not supported for class type " + getClass().getSimpleName());
923  }
924  eq CommonAccessExp.asCommonAccessExp() = this;
925 
926  syn FAccessExp FExp.asFAccessExp() {
927      throw new UnsupportedOperationException("asFAccessExp() is not supported for class type " + getClass().getSimpleName());
928  }
929  eq FAccessExp.asFAccessExp() = this;
930 
931    syn boolean CommonAccess.isInstAccess() = false;
932    eq InstAccess.isInstAccess() = true;
933    syn boolean CommonAccess.isFAccess() = false;
934    eq FAccess.isFAccess() = true;
935   
936        syn InstAccess FExp.asInstAccess() {
937                throw new UnsupportedOperationException();
938        }
939    eq InstAccessExp.asInstAccess() = getInstAccess();
940   
941    syn InstAccess CommonAccess.asInstAccess() {
942        throw new UnsupportedOperationException();
943    }
944    eq InstAccess.asInstAccess() = this;
945   
946  syn boolean FExp.isAccessToStream()  = false;
947  eq CommonAccessExp.isAccessToStream()= getAccess().isAccessToStream();
948 
949  syn boolean CommonAccess.isAccessToStream() = false;
950  eq InstAccess.isAccessToStream()      = myInstComponentDecl().isStream();
951
952
953
954    public void InstForIndexWithExp.typeCheck(ErrorCheckType checkType) {
955        if (!getFExp().type().isUnknown() && getFExp().ndims() != 1)
956            error("The expression of for index " + name() + " must be a vector expression: " + 
957                    getFExp() + " has " + getFExp().ndims() + " dimension(s)");
958    }
959
960    public void InstForIndexNoExp.typeCheck(ErrorCheckType checkType) {
961        hasFExp(); // Force eval of NTA, thus checking if a consistent range can be computed
962    }
963
964    public static final SimpleErrorProducer InstForIndexNoExp.IMPLICIT_FOR_RANGE_NON_INTEGER =
965            new SimpleErrorProducer("IMPLICIT_FOR_RANGE_NON_INTEGER", ProblemKind.COMPLIANCE, 
966                    "Non-integer for iteration range not supported");
967
968    public static final SimpleErrorProducer InstForIndexNoExp.IMPLICIT_FOR_RANGE_INCONSISTENT =
969            new SimpleErrorProducer("IMPLICIT_FOR_RANGE_INCONSISTENT", ProblemKind.SEMANTIC, 
970                    "For index with implicit iteration range used for inconsistent sizes, here used for size %s and earlier for size %s");
971
972    public static final SimpleErrorProducer InstForIndexNoExp.IMPLICIT_FOR_RANGE_NOT_USED =
973            new SimpleErrorProducer("IMPLICIT_FOR_RANGE_NOT_USED", ProblemKind.SEMANTIC, 
974                    "For index with implicit iteration range must be used as array index");
975
976    // TODO: handle other index types than integer (see #3597)
977    syn lazy Opt<FExp> InstForIndexNoExp.getFExpOpt() {
978        Size size = null;
979        for (FExpSubscript use : mySubscriptUses()) {
980            Size useSize = use.mySize();
981            if (!use.myIndexType().isInteger()) {
982                IMPLICIT_FOR_RANGE_NON_INTEGER.invoke(use);
983            }
984            if (size == null) {
985                size = useSize;
986            } else if (!size.equivalent(useSize, true)) {
987                IMPLICIT_FOR_RANGE_INCONSISTENT.invoke(use, useSize, size);
988            }
989        }
990        if (size == null) {
991            IMPLICIT_FOR_RANGE_NOT_USED.invoke(this);
992            return new Opt();
993        } else {
994            return new Opt(size.createRangeExp(0));
995        }
996    }
997
998    syn lazy Collection<FExpSubscript> InstForIndexNoExp.mySubscriptUses() = mySubscriptUses(name());
999
1000    inh Collection<FExpSubscript> InstForIndexNoExp.mySubscriptUses(String name);
1001    eq InstForClauseE.getInstForIndex(int i).mySubscriptUses(String name) = collectSubscriptUses(name);
1002    eq InstForStmt.getInstForIndex(int i).mySubscriptUses(String name)    = collectSubscriptUses(name);
1003    eq FIterExp.getForIndex(int i).mySubscriptUses(String name)           = collectSubscriptUses(name);
1004
1005    public Collection<FExpSubscript> ASTNode.collectSubscriptUses(String name) {
1006        Collection<FExpSubscript> nodes = new ArrayList<FExpSubscript>();
1007        collectSubscriptUses(name, nodes);
1008        return nodes;
1009    }
1010
1011    public void ASTNode.collectSubscriptUses(String name, Collection<FExpSubscript> nodes) {
1012        for (ASTNode n : this) {
1013            n.collectSubscriptUses(name, nodes);
1014        }
1015    }
1016
1017    public void FExpSubscript.collectSubscriptUses(String name, Collection<FExpSubscript> nodes) {
1018        if (getFExp().isIdentifier(name)) {
1019            nodes.add(this);
1020        }
1021        super.collectSubscriptUses(name, nodes);
1022    }
1023
1024    public void FSubscript.typeCheckAsSize(ErrorCheckType checkType) {
1025        if (ndims() > 0) { 
1026            error("Array size must be scalar expression: " + toString());
1027        } else if (!type().isUnknown() && !canBeComponentSize()) {
1028            error("Array size must be Integer expression, Boolean type or enumeration type: " + toString());
1029        } else if (!inFunction()) { 
1030            if (!variability().parameterOrLess()) {
1031                error("Array size must be constant or parameter: " + toString());
1032            } else if (!checkType.allowIncompleteSizes()) {
1033                boolean ok = false;
1034                try {
1035                    ok = ceval().hasIntValue();
1036                } catch (ConstantEvaluationException e) {}
1037                if (!ok) {
1038                    error("Could not evaluate array size expression: " + toString());
1039                }
1040            }
1041        }
1042    }
1043
1044    public void FSubscript.typeCheckAsIndex(ErrorCheckType checkType) {
1045        if (ndims() > 1) { 
1046            error("Array index must be scalar or vector expression: " + toString());
1047        } else if (!type().isUnknown() && !type().canBeIndex()) {
1048            error("Array index must be Integer, Boolean, or enumeration expression: " + toString());
1049        } else if (!type().isUnknown() && !myIndexType().isUnknown() && !type().scalarType().typeCompatible(myIndexType())) {
1050            error("Expected array index of type '%s' found '%s'", myIndexType().name(), type().scalarType().name());
1051        } else if (!inFunction()) { 
1052            if (!variability().fixedParameterOrLess()) {
1053                warning("Variable array index in equation can result in slow simulation time");
1054            } else if (!inUnknownAccess() && !mySize().isUnknown()) {
1055                typeCheckOutOfBounds(checkType);
1056            }
1057        }
1058    }
1059
1060    public void FSubscript.typeCheckOutOfBounds(ErrorCheckType checkType) {}
1061    public void FExpSubscript.typeCheckOutOfBounds(ErrorCheckType checkType) {
1062        if (!reportedOutOfBound) {
1063            // Check array bounds
1064            // TODO: Perform bounds check in functions if index has parameter variability or lower?
1065            try {
1066                int max = mySize().get(0);
1067                for (FExp e : getFExp().getArray().iterable()) {
1068                    CValue cval = e.ceval();
1069                    if (cval.hasIntValue()) {
1070                        int i = cval.intValue();
1071                        if (i < 1 || i > max) {
1072                            if (!lockBranch(checkType)) {
1073                                errorUnlessDisabled("Array index out of bounds: " + i + 
1074                                                    ", index expression: " + getFExp());
1075                            }
1076                            reportedOutOfBound = true;
1077                            return;
1078                        }
1079                    }
1080                }
1081            } catch (ConstantEvaluationException e) {
1082            }
1083        }
1084    }
1085    public void FIntegerSubscript.typeCheckOutOfBounds(ErrorCheckType checkType) {
1086        int max = mySize().get(0);
1087        int i = getValue();
1088        if ((i < 1 || i > max) && !lockBranch(checkType)) {
1089            errorUnlessDisabled("Array index out of bounds: " + i +
1090                                ", index expression: " + i);
1091        }
1092    }
1093
1094  syn FType FSubscript.myIndexType() = myIndexType(myDim());
1095  inh FType FSubscript.myIndexType(int i);
1096  eq Root.getChild().myIndexType(int i)                                = fUnknownType();
1097  eq InstArrayAccess.getFArraySubscripts().myIndexType(int i)          = fUnknownType();
1098  eq InstComponentArrayAccess.getFArraySubscripts().myIndexType(int i) = myInstComponentDecl().myIndexType(i);
1099 
1100  syn FType InstComponentDecl.myIndexType(int dim) {
1101    FArraySubscripts fas = getFArraySubscripts();
1102    if (fas != null && fas.ndims() > dim)
1103        return fas.subscript(dim).type().scalarType();
1104    else
1105        return fUnknownType();
1106  }
1107 
1108  private boolean FExpSubscript.reportedOutOfBound = false;
1109 
1110 
1111  public void FSubscript.typeCheck(ErrorCheckType checkType) {
1112          if (isInstComponentSize()) 
1113                  typeCheckAsSize(checkType);
1114          else 
1115                  typeCheckAsIndex(checkType);
1116  }
1117  public void FColonSubscript.typeCheck(ErrorCheckType checkType) {}
1118 
1119  inh boolean FSubscript.inUnknownAccess();
1120  eq FAccess.getChild().inUnknownAccess()                  = myFV().isUnknown();
1121  eq InstScalarAccess.getChild().inUnknownAccess()        = myInstComponentDecl().isUnknown();
1122  eq InstArrayAccess.getChild().inUnknownAccess()         = myInstComponentDecl().isUnknown();
1123  eq FlatRoot.getChild().inUnknownAccess()                = false;
1124  eq InstRoot.getChild().inUnknownAccess()                = false;
1125 
1126  syn boolean FExp.canBeComponentSize()  = type().isInteger();
1127  eq CommonAccessExp.canBeComponentSize()= type().isInteger() || getAccess().isComponentSizeClass();
1128 
1129  syn boolean FSubscript.canBeComponentSize() = true;
1130  eq FExpSubscript.canBeComponentSize() = getFExp().canBeComponentSize();
1131 
1132  syn boolean CommonAccess.isComponentSizeClass() = false;
1133  eq InstAccess.isComponentSizeClass()      = myInstClassDecl().isComponentSizeClass();
1134 
1135  syn boolean InstClassDecl.isComponentSizeClass() = isBoolean();
1136  eq InstEnumClassDecl.isComponentSizeClass()      = true;
1137 
1138  inh boolean InstClassAccess.isInstComponentSize();
1139    inh boolean CommonAccessExp.isInstComponentSize();
1140  inh boolean FSubscript.isInstComponentSize();
1141  inh boolean FArraySubscripts.isInstComponentSize();
1142  eq InstComponentDecl.getLocalFArraySubscripts().isInstComponentSize() = true;
1143  eq InstComponentDecl.getFArraySubscripts().isInstComponentSize()      = true;
1144  eq InstShortClassDecl.getFArraySubscripts().isInstComponentSize()     = true;
1145  eq InstAccess.getChild().isInstComponentSize()                        = false;
1146  eq FAccessPart.getChild().isInstComponentSize()                       = false;
1147  eq CommonAccess.getChild().isInstComponentSize()                      = false;
1148  eq FExp.getChild().isInstComponentSize()                              = false;
1149    eq CommonAccessExp.getChild().isInstComponentSize()                 = isInstComponentSize();
1150  eq FAbstractVariable.getChild().isInstComponentSize()                 = false;
1151  eq FAbstractEquation.getChild().isInstComponentSize()                 = false;
1152  eq InstNode.getChild().isInstComponentSize()                          = false;
1153  eq FlatRoot.getChild().isInstComponentSize()                          = false;
1154       
1155  syn boolean FArraySubscripts.isFullSize() = false;
1156  eq FArrayExpSubscripts.isFullSize() {
1157      for (FSubscript fs : getFSubscripts())
1158          if (!fs.isColon())
1159              return false;
1160      return true;
1161  }
1162
1163  public void FAssignStmt.typeCheck(ErrorCheckType checkType) {
1164      FType left = getLeft().type();
1165      FType right = getRight().type();
1166      if (!left.isUnknown() && !right.isUnknown()) {
1167          if (!left.typeCompatible(right, true)) {
1168              if (left.typeCompatibleExceptLengths(right)) {
1169                  // TODO: should use lockBranch() here as well, but no support yet
1170                  errorUnlessDisabled("The array sizes of right and left hand side of assignment are not compatible, " + 
1171                          "size of left-hand side is %s, and size of right-hand side is %s", left.size(), right.size());
1172              } else {
1173                  error("The right and left expression types of assignment are not compatible, " + 
1174                          "type of left-hand side is %s, and type of right-hand side is %s", left, right);
1175              }
1176          }
1177      }
1178  }
1179
1180  public void FWhileStmt.typeCheck(ErrorCheckType checkType) {
1181          if (!getTest().type().isUnknown() && !fBooleanScalarType().typeCompatible(getTest().type())) 
1182                  error("Type of test expression of while statement is not Boolean");
1183  }
1184 
1185  public void FIfClause.typeCheck(ErrorCheckType checkType) {
1186          if (!getTest().type().isUnknown() && !fBooleanScalarType().typeCompatible(getTest().type())) 
1187                  error("Type of test expression of if statement is not Boolean");
1188  }
1189 
1190  public void FWhenClause.typeCheck(ErrorCheckType checkType) {
1191          if (!getTest().type().isUnknown()) {
1192                  if (!getTest().type().isBoolean() || getTest().type().ndims() > 1) 
1193                          error("Test expression of when statement isn't Boolean scalar or vector expression");
1194          }
1195  }
1196 
1197  public void FIfEquation.typeCheck(ErrorCheckType checkType) {
1198          if (!getTest().type().isUnknown() && !fBooleanScalarType().typeCompatible(getTest().type())) 
1199                  error("Type of test expression of if equation is not Boolean");
1200  }
1201 
1202  public void FWhenEquation.typeCheck(ErrorCheckType checkType) {
1203          if (!getTest().type().isUnknown()) {
1204                  if (!getTest().type().isBoolean() || getTest().type().ndims() > 1) 
1205                          error("Test expression of when equation isn't Boolean scalar or vector expression");
1206          }
1207  }
1208
1209
1210    public void FInfArgsFunctionCall.typeCheck(ErrorCheckType checkType) {
1211        super.typeCheck(checkType);
1212        if (numArgs() < minNumArgs()) 
1213            error("Too few arguments to " + builtInName() + "(), must have at least " + minNumArgs());
1214        else 
1215            typeCheckFExps();
1216    }
1217
1218  public void FInfArgsFunctionCall.typeCheckFExps() {
1219      for (FExp exp : getFExps()) {
1220          if (!exp.type().isUnknown() && !typeOfArgIsOK(exp)) {
1221              exp.error("Argument of " + builtInName() + "() is not " + 
1222                          getOKArgTypeString() + ": " + exp);
1223          } else if (!variabilityOfArgIsOK(exp)) {
1224                  exp.error("Argument of " + builtInName() + "() does not have " + 
1225                                  getOKArgVariabilityString() + " variability: " + exp);
1226          }
1227      }
1228  }
1229 
1230  public void FCatExp.typeCheckFExps() {
1231          if (!getDim().type().isUnknown() && !fIntegerScalarType().typeCompatible(getDim().type()))
1232                  error("Dimension argument of cat() is not compatible with Integer: " + getDim());
1233          else if (!getDim().type().isUnknown() && !getDim().variability().parameterOrLess())
1234                  error("Dimension argument of cat() does not have constant variability: " + getDim());
1235  }
1236
1237    public void FAbstractCat.typeCheckFExps() {}
1238
1239    public void FAbstractCat.typeError(ErrorCheckType checkType) {
1240        errorUnlessDisabledAnd(typeErrorOnlyLengths(), "Types do not match in array concatenation");
1241    }
1242
1243    syn int FInfArgsFunctionCall.minNumArgs() = 1;
1244    eq FFillExp.minNumArgs()                  = 2;
1245    eq FCatExp.minNumArgs()                   = 2;
1246    eq FAbstractCat.minNumArgs()              = 0;
1247
1248    syn int FInfArgsFunctionCall.numArgs() = getNumFExp();
1249    eq FFillExp.numArgs()                  = getNumFExp() + 1;
1250    eq FCatExp.numArgs()                   = getNumFExp() + 1;
1251
1252  syn boolean FInfArgsFunctionCall.typeOfArgIsOK(FExp exp)        = 
1253                  fIntegerScalarType().typeCompatible(exp.type());
1254  syn boolean FInfArgsFunctionCall.variabilityOfArgIsOK(FExp exp) = 
1255                  exp.variability().parameterOrLess() || inFunction();
1256  syn String FInfArgsFunctionCall.getOKArgTypeString()            = 
1257                  "compatible with Integer";
1258  syn String FInfArgsFunctionCall.getOKArgVariabilityString()     = 
1259                  "constant or parameter";
1260
1261    public void FBuiltInFunctionCall.typeCheck(ErrorCheckType checkType) {
1262        if (checkTypeAsExpression()) {
1263            super.typeCheck(checkType);
1264        } else if (!inFunction()) {
1265            size().markAsStructuralParameter(checkType, this);
1266        }
1267        int n = builtInNumOutput();
1268        if (myLefts().size() > n)
1269            error("Too many components assigned from function call: " + builtInName() + 
1270                    "() has " + n + " output(s)");
1271        else if (!isFunctionCallClause() && n == 0)
1272            error("Function " + builtInName() + "() has no outputs, but is used in expression");
1273    }
1274
1275    public void FSemiLinearExp.typeCheck(ErrorCheckType checkType) {
1276        checkVectorizedSizes(checkType);
1277        super.typeCheck(checkType);
1278    }
1279
1280    public void FBuiltInFunctionCall.checkVectorizedSizes(ErrorCheckType checkType) {
1281        if (isArray()) {
1282            boolean error = false;
1283            boolean onlyLengths = true;
1284            Size s = size();
1285            for (FExp arg : myArgs()) {
1286                if (arg.isArray() && !arg.size().equivalent(s, false)) {
1287                    error = true;
1288                    if (arg.ndims() != s.ndims()) {
1289                        onlyLengths = false;
1290                        break;
1291                    }
1292                }
1293            }
1294            if (error) {
1295                errorUnlessDisabledAnd(onlyLengths, 
1296                        "Mismatching sizes in %s. All non-scalar arguments need matching sizes", builtInName());
1297            }
1298        }
1299    }
1300
1301  public void FEnumIntegerExp.typeCheck(ErrorCheckType checkType) {
1302          super.typeCheck(checkType);
1303          // We can't define the function Integer in PredefinedTypes.jrag - conflict with the type Integer
1304          int n = getNumOriginalArg();
1305          if (n != 1)
1306                  error("Calling function Integer(): too " + (n > 1 ? "many" : "few") + " arguments");
1307  }
1308 
1309  public void InstPreExp.typeCheck(ErrorCheckType checkType) {
1310        if (!getFExp().isAccess()) {
1311                error("Calling function pre(): argument must be variable access");
1312                return;
1313        }
1314  }
1315 
1316  public void FAssert.typeCheck(ErrorCheckType checkType) {
1317          if (hasLevel()) {
1318                  if (getLevel().isParameterExp())
1319                          getLevel().markAsStructuralParameter(checkType);
1320                  else if (!getLevel().isConstantExp())
1321                          error("Level of assert() must be constant or parameter");
1322          }
1323  }
1324 
1325  public void FReinit.typeCheck(ErrorCheckType checkType) {
1326      if (!getVar().isAccess() || !getVar().type().isReal())
1327          error("First argument to reinit() must be an access to a Real variable");
1328      else if (!getVar().type().typeCompatible(getFExp().type()))
1329          error("Arguments to reinit() must be of compatible types");
1330  }
1331
1332    public void FEdgeExp.typeCheck(ErrorCheckType checkType) {
1333        if (!getFExp().isAccess() || !getFExp().type().isBoolean()) 
1334            error("Calling function edge(): argument must be a boolean variable access");
1335    }
1336
1337    public void FChangeExp.typeCheck(ErrorCheckType checkType) {
1338        if (!getFExp().isAccess()) 
1339            error("Calling function change(): argument must be a variable access");
1340    }
1341
1342    public static final SimpleProblemProducer ASTNode.DELAY_MAX_NOT_PARAMETER =
1343            new SimpleErrorProducer("DELAY_MAX_NOT_PARAMETER", ProblemKind.SEMANTIC,
1344                    "Calling function delay(): third argument must be of parameter variability: %s");
1345    public static final SimpleProblemProducer ASTNode.DELAY_NOT_PARAMETER =
1346            new SimpleErrorProducer("DELAY_NOT_PARAMETER", ProblemKind.SEMANTIC,
1347                    "Calling function delay(): second argument must be of parameter variability when third argument is not given: %s");
1348    public static final SimpleProblemProducer ASTNode.DELAY_OVER_MAX =
1349            new SimpleErrorProducer("DELAY_OVER_MAX", ProblemKind.SEMANTIC,
1350                    "Calling function delay(): second argument may not be larger than third argument: %s = %s > %s = %s");
1351    public static final SimpleProblemProducer ASTNode.DELAY_NEGATIVE =
1352            new SimpleErrorProducer("DELAY_NEGATIVE", ProblemKind.SEMANTIC,
1353                    "Calling function delay(): second argument may not be negative: %s = %s < 0");
1354    public static final SimpleProblemProducer ASTNode.DELAY_MAX_NEGATIVE =
1355            new SimpleErrorProducer("DELAY_MAX_NEGATIVE", ProblemKind.SEMANTIC,
1356                    "Calling function delay(): third argument may not be negative: %s = %s < 0");
1357
1358    public void FDelayExp.typeCheck(ErrorCheckType checkType) {
1359        boolean max = hasMax();
1360        boolean maxParam = max ? getMax().variability().parameterOrLess() : false;
1361        boolean delayParam = getDelay().variability().parameterOrLess();
1362        if (max && !maxParam) {
1363            DELAY_MAX_NOT_PARAMETER.invoke(this, getMax());
1364        }
1365        if (!max && !delayParam) {
1366            DELAY_NOT_PARAMETER.invoke(this, getDelay());
1367        }
1368       
1369        double maxVal = 0.0;
1370        boolean maxEval = false;
1371        if (max && maxParam) {
1372            try {
1373                CValue maxCVal = getMax().ceval();
1374                if (maxCVal.hasRealValue()) {
1375                    maxVal = maxCVal.realValue();
1376                    maxEval = true;
1377                }
1378            } catch (ConstantEvaluationException e) {}
1379        }
1380        double delayVal = 0.0;
1381        boolean delayEval = false;
1382        if (delayParam) {
1383            try {
1384                CValue delayCVal = getDelay().ceval();
1385                if (delayCVal.hasRealValue()) {
1386                    delayVal = delayCVal.realValue();
1387                    delayEval = true;
1388                }
1389            } catch (ConstantEvaluationException e) {}
1390        }
1391        if (maxEval && delayEval && delayVal > maxVal) {
1392            DELAY_OVER_MAX.invoke(this, getDelay(), delayVal, getMax(), maxVal);
1393        }
1394        if (delayEval && delayVal < 0.0) {
1395            DELAY_NEGATIVE.invoke(this, getDelay(), delayVal);
1396        }
1397        if (maxEval && maxVal < 0.0) {
1398            DELAY_MAX_NEGATIVE.invoke(this, getMax(), maxVal);
1399        }
1400    }
1401   
1402    public void FSpatialDistExp.typeCheck(ErrorCheckType checkType) {
1403        super.typeCheck(checkType);
1404        if (!getIn0().size().equivalent(getIn1().size(), true)) {
1405            errorUnlessDisabledAnd(getIn0().ndims() == getIn1().ndims(), 
1406                    "Calling function spatialDistribution(): first and second arguments 'in0' and 'in1' needs equivalent sizes");
1407        }
1408        if (isArray()) {
1409            if (getX().isArray()) {
1410                error("Calling function spatialDistribution(): third argument 'x' cannot be vectorized");
1411            }
1412            if (getPositiveVelocity().isArray()) {
1413                error("Calling function spatialDistribution(): fourth argument 'positiveVelocity' cannot be vectorized");
1414            }
1415        }
1416    }
1417
1418        public void FStringExp.typeCheck(ErrorCheckType checkType) {
1419                FType valueType = getValue().type();
1420                if (hasSignificantDigits() && !valueType.isReal())
1421                        error("Calling function String(): named argument significantDigits can only be used when first argument is real");
1422                if (hasFormat() && (hasMinimumLength() || hasLeftJustified() || hasSignificantDigits()))
1423                        error("Calling function String(): named argument format can not be used together with minimumLength, leftJustified or significantDigits");
1424        }
1425
1426    public void FHomotopyExp.typeCheck(ErrorCheckType checkType) {
1427        checkVectorizedSizes(checkType);
1428        super.typeCheck(checkType);
1429    }
1430 
1431    public void InstDerExp.typeCheck(ErrorCheckType checkType) {
1432        FType type = getFExp().type();
1433        if (!(type.isReal() && (type.isScalar() || type.isArray())))
1434            error("Only real typed expressions are allowed in der() operator");
1435    }
1436 
1437  syn int FBuiltInFunctionCall.builtInNumOutput() = 1;
1438  eq FIgnoredBuiltIn.builtInNumOutput() = 0;
1439  eq FConnectionsOp.builtInNumOutput()  = 0;
1440  eq FConnBoolOp.builtInNumOutput()     = 1;
1441  eq FSpatialDistExp.builtInNumOutput() = 2;
1442 
1443  syn boolean FBuiltInFunctionCall.checkTypeAsExpression() = false;
1444  eq FSizeExp.checkTypeAsExpression()     = true;
1445  eq FMinMaxExp.checkTypeAsExpression()   = true;
1446  eq FIdentity.checkTypeAsExpression()    = true;
1447  eq FLinspace.checkTypeAsExpression()    = true;
1448  eq FAbstractCat.checkTypeAsExpression() = dimensionIsOk();
1449 
1450  syn boolean FAbstractCat.dimensionIsOk(); 
1451  eq FCatExp.dimensionIsOk()    = getDim().isConstantExp() && 
1452                                                                  getDim().type().isInteger() && getDim().type().isScalar();
1453  eq FMatrix.dimensionIsOk()    = true;
1454  eq FMatrixRow.dimensionIsOk() = true;
1455    public static final SimpleProblemProducer ASTNode.CANNOT_INFER_ARRAY_SIZE_OF_OUTPUT =
1456            new SimpleErrorProducer("CANNOT_INFER_ARRAY_SIZE_OF_OUTPUT", ProblemKind.SEMANTIC,
1457                    "Could not evaluate array size of output %s");
1458
1459    public void InstFunctionCall.typeCheck(ErrorCheckType checkType) {
1460        if (!isFunctionCallClause() && !getName().myInstClassDecl().isRecord() && !hasOutputs() && !isPartialFunctionCall()) {
1461            error("Function " + getName().name() + "() has no outputs, but is used in expression");
1462        } else if (!isFunctionCallClause() && size().isUnknown() && errorCheckUnknownSize()) {
1463            CANNOT_INFER_ARRAY_SIZE_OF_OUTPUT.invoke(this, expOutput().name());
1464        }
1465        if (myCallOutputs().size() < myLefts().size()) {
1466            error("Too many components assigned from function call: " + getName().name() + 
1467                    "() has " + myCallOutputs().size() + " output(s)");
1468        }
1469        if (isFunctionCallClause()) {
1470            for (int i = 0, n = myCallOutputs().size(); i < n; i++) {
1471                sizeOfOutput(i).markAsStructuralParameter(checkType, this);
1472            }
1473        } else {
1474            size().markAsStructuralParameter(checkType, this);
1475        }
1476        if (callsLoadResource()) {
1477            getArgs().forceUsesVariability(checkType, Variability.LOADRESOURCEPARAMETER);
1478        }
1479    }
1480 
1481  syn boolean InstFunctionCall.isPartialFunctionCall() = false;
1482  eq InstPartialFunctionCall.isPartialFunctionCall()   = true;
1483
1484    public void FFunctionCallLeft.typeCheck(ErrorCheckType checkType) {
1485        if (hasFExp() && !myOutput().isUnknown() && !type().isUnknown()) { // Avoid duplicate error
1486            if (getFExp() instanceof CommonAccessExp) {  // Should never be false - add check?
1487                CommonAccess use = getFExp().asCommonAccess();
1488                FType useType = getFExp().type();
1489                FType outType = type();
1490                if (!use.isForIndex()) {
1491                    if (outType.size().isUnknown() && !inFunction() && !checkType.allowIncompleteSizes()) {
1492                        error(functionCallDecription() + ": could not evaluate array size of output " + 
1493                                myOutput().name());
1494                    } else if (!useType.typeCompatible(outType, true)) {
1495                        if (useType.typeCompatibleExceptLengths(outType)) {
1496                            if (!lockBranch(checkType))
1497                                errorUnlessDisabled("%s: component %s is of size %s and output %s is of size %s - they are not compatible", 
1498                                        functionCallDecription(), use.name(), useType.size(), myOutput().name(), outType.size());
1499                        } else {
1500                            error("%s: component %s is of type %s and output %s is of type %s - they are not compatible", 
1501                                    functionCallDecription(), use.name(), useType, myOutput().name(), outType);
1502                        }
1503                    }
1504                }
1505            }
1506        }
1507    }
1508
1509  inh String InstFunctionArgument.functionCallDecription();
1510  inh String FFunctionCallLeft.functionCallDecription();
1511  eq FAbstractFunctionCall.getChild().functionCallDecription() = functionCallDecription();
1512  eq FFunctionCallEquation.getChild().functionCallDecription() = getCall().functionCallDecription();
1513  eq FFunctionCallStmt.getChild().functionCallDecription()     = getCall().functionCallDecription();
1514
1515    public void InstFunctionArgument.typeCheck(ErrorCheckType checkType) {
1516        boolean typeOk = true;
1517        FType type = getFExp().type();
1518        if (!type.isUnknown()) {
1519            if (argumentDefinedTypeValid()) {
1520                FType boundType = getBoundInput().type();
1521                if (!boundType.isUnknown()) {
1522                    if (isVectorized()) 
1523                        boundType = boundType.sizedType(boundType.size().expand(vectorizedSize()));
1524                    else if (isDestructorArgument())
1525                        boundType = boundType.sizedType(type.size());
1526                    typeOk = boundType.typeCompatible(type, true) || lockBranch(checkType);
1527                }
1528            } else {
1529                typeOk = argumentTypeValid(type);
1530            }
1531        }
1532        if (!typeOk) {
1533            String msg = argumentTypeError();
1534            if (msg != null) {
1535                error(msg);
1536            } else {
1537                error("%s: types of %s and input %s are not compatible\n" + 
1538                      "    type of '%s' is %s\n" + 
1539                      "    expected type is %s", 
1540                      functionCallDecription(), argumentDesc(), getBoundInput().name(), 
1541                      getFExp(), getFExp().type(),
1542                      argumentDefinedTypeValid() ? getBoundInput().type().toString() : expectedArgumentType());
1543            }
1544        }
1545    }
1546
1547    public void InstBadArgument.typeCheck(ErrorCheckType checkType) {
1548        if (!isDestructorArgument()) {
1549            error(functionCallDecription() + ": " + errorString() + " " + getBoundInput().name());
1550        }
1551    }
1552
1553    syn String InstBadArgument.errorString();
1554    eq InstMissingArgument      .errorString() = "missing argument for required input";
1555    eq InstUnknownArgument      .errorString() = "could not resolve argument for required input";
1556    eq InstMultipleBoundArgument.errorString() = "multiple arguments matches input";
1557
1558    inh boolean InstFunctionArgument.isDestructorArgument();
1559    eq InstFunctionCall.getArg().isDestructorArgument() = isDestructorCall();
1560    eq BaseNode.getChild().isDestructorArgument()       = false;
1561
1562    inh boolean InstClassDecl.inExternalObject();
1563    eq InstNode.getChild().inExternalObject()              = isExternalObject();
1564    eq InstRecordConstructor.getChild().inExternalObject() = false;
1565
1566    public void InstDefaultArgument.typeCheck(ErrorCheckType checkType) {}
1567
1568  inh String InstFunctionArgument.argumentTypeError();
1569  eq FExp.getChild().argumentTypeError()         = null;
1570  eq FCardinality.getChild().argumentTypeError() = "The argument of cardinality() must be a scalar reference to a connector";
1571
1572    syn String InstFunctionArgument.argumentDesc() = null;
1573    eq InstPositionalArgument.argumentDesc() = "positional argument " + (getPos() + 1);
1574    eq InstNamedArgument.argumentDesc()      = "named argument " + getName();
1575
1576  public void InstAccess.typeCheck(ErrorCheckType checkType) {
1577      if (myInstComponentDecl().size().isUndefined() && myInstComponentDecl().inFunction() && 
1578              !myInstComponentDecl().isInput() && !checkType.allowIncompleteSizes()) {
1579          compliance("Using variables with undefined size is not supported");
1580      }
1581      super.typeCheck(checkType);
1582  }
1583 
1584  public boolean Size.isUndefined() {
1585      for (int i = 0; i < ndims(); i++) {
1586          if (size[i] == Size.UNKNOWN) {
1587              return true;
1588          }
1589      }
1590      return false;
1591  }
1592
1593  public boolean MutableSize.isUndefined() {
1594      for (int i = 0; i < ndims(); i++) {
1595          if (size[i] == Size.UNKNOWN && (exps[i] == null || exps[i] instanceof FColonSizeExp)) {
1596              return true;
1597          }
1598      }
1599      return false;
1600  }
1601
1602    public static final SimpleProblemProducer ASTNode.ACCESS_TO_CLASS_THROUGH_COMPONENT = 
1603            new SimpleErrorProducer("ACCESS_TO_CLASS_THROUGH_COMPONENT", ProblemKind.SEMANTIC,
1604                    "Can not access non-function class through component access: '%s'");
1605    public static final SimpleProblemProducer ASTNode.ACCESS_TO_FUNCTION_THROUGH_MULTIPLE_COMPONENTS = 
1606            new SimpleErrorProducer("ACCESS_TO_FUNCTION_THROUGH_MULTIPLE_COMPONENTS", ProblemKind.SEMANTIC,
1607                    "Can not access function through component unless only the first part of the name is a component: '%s'");
1608    public static final SimpleProblemProducer ASTNode.ACCESS_TO_FUNCTION_THROUGH_ARRAY_COMPONENT = 
1609            new SimpleErrorProducer("ACCESS_TO_FUNCTION_THROUGH_ARRAY_COMPONENT", ProblemKind.SEMANTIC,
1610                    "Can not access function through array component access: '%s'");
1611
1612    public void InstDot.typeCheck(ErrorCheckType checkType) {
1613        // Check for accesses to component in class or class in component
1614        int n = getNumInstAccess();
1615        for (int i = 0; i < n - 1; i++)
1616            getInstAccess(i).checkMixedAccessLeft(getInstAccess(i + 1), this, checkType, i, n);
1617        super.typeCheck(checkType);
1618    }
1619
1620    public void InstAccess.checkMixedAccessLeft(
1621            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {}
1622
1623    public void InstClassAccess.checkMixedAccessLeft(
1624            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {
1625        right.checkMixedAccessRight(myInstClassDecl(), top, checkType, i, n);
1626    }
1627
1628    public void InstComponentAccess.checkMixedAccessLeft(
1629            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {
1630        right.checkMixedAccessRight(myInstComponentDecl(), top, checkType, isArray(), i, n);
1631    }
1632
1633    public void InstComponentArrayAccess.checkMixedAccessLeft(
1634            InstAccess right, InstAccess top, ErrorCheckType checkType, int i, int n) {
1635        right.checkMixedAccessRight(myInstComponentDecl(), top, checkType, true, i, n);
1636    }
1637
1638    public void InstAccess.checkMixedAccessRight(
1639            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {}
1640
1641    public void InstComponentAccess.checkMixedAccessRight(
1642            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {
1643        checkMixedAccessRightForComponent(left, top, checkType, i, n);
1644    }
1645
1646    public void InstComponentArrayAccess.checkMixedAccessRight(
1647            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {
1648        checkMixedAccessRightForComponent(left, top, checkType, i, n);
1649    }
1650
1651    public void InstAccess.checkMixedAccessRightForComponent(
1652            InstClassDecl left, InstAccess top, ErrorCheckType checkType, int i, int n) {
1653        String type = null;
1654        if (left.extendsEnum()) {
1655            if (!myInstComponentDecl().isEnumLiteral())
1656                type = "attribute of primitive with dot notation";
1657        } else if (left.isPackage()) {
1658            left.checkRestriction(checkType);
1659        } else if (!left.isOkPackage()) {
1660            type = "component in non-package class";
1661        }
1662        if (type != null) {
1663            top.error("Can not access " + type + ": " + top.name());
1664        }
1665    }
1666
1667    public void InstAccess.checkMixedAccessRight(
1668            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {}
1669
1670    public void InstClassAccess.checkMixedAccessRight(
1671            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1672        if (!top.myInstClassDecl().isFunction()) {
1673            ACCESS_TO_CLASS_THROUGH_COMPONENT.invoke(top, top);
1674        } else if (i != 0) {
1675            ACCESS_TO_FUNCTION_THROUGH_MULTIPLE_COMPONENTS.invoke(top, top);
1676        } else if (array) {
1677            ACCESS_TO_FUNCTION_THROUGH_ARRAY_COMPONENT.invoke(top, top);
1678         }
1679    }
1680
1681    public void InstComponentAccess.checkMixedAccessRight(
1682            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1683        checkMixedAccessRightForComponent(left, top, checkType, array, i, n);
1684    }
1685
1686    public void InstComponentArrayAccess.checkMixedAccessRight(
1687            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1688        checkMixedAccessRightForComponent(left, top, checkType, array, i, n);
1689    }
1690
1691    public void InstAccess.checkMixedAccessRightForComponent(
1692            InstComponentDecl left, InstAccess top, ErrorCheckType checkType, boolean array, int i, int n) {
1693        if (left.isPrimitive()) {
1694            top.error("Can not access attribute of primitive with dot notation: " + top.name());
1695        }
1696    }
1697
1698    /**
1699     * Check if the type defined for this argument in the built-in function list
1700     * is valid.
1701     */
1702    inh boolean InstFunctionArgument.argumentDefinedTypeValid();
1703    eq FAbstractFunctionCall.getChild().argumentDefinedTypeValid()           = true;
1704    eq FTranspose.getOriginalArg().argumentDefinedTypeValid()                = false;
1705    eq FSymmetric.getOriginalArg().argumentDefinedTypeValid()                = false;
1706    eq FMinMaxExp.getOriginalArg().argumentDefinedTypeValid()                = false;
1707    eq FReductionExp.getOriginalArg().argumentDefinedTypeValid()             = false;
1708    eq FHomotopyExp.getOriginalArg().argumentDefinedTypeValid()              = false;
1709    eq FSemiLinearExp.getOriginalArg().argumentDefinedTypeValid()            = false;
1710    eq FNdimsExp.getOriginalArg().argumentDefinedTypeValid()                 = false;
1711    eq FInfArgsFunctionCall.getOriginalArg().argumentDefinedTypeValid()      = false;
1712    eq FEnumIntegerExp.getOriginalArg().argumentDefinedTypeValid()           = false;
1713    eq FVectUnaryBuiltIn.getOriginalArg().argumentDefinedTypeValid()         = false;
1714    eq FDimensionConvert.getOriginalArg().argumentDefinedTypeValid()         = false;
1715    eq FEventGenExp.getOriginalArg().argumentDefinedTypeValid()              = false;
1716    eq FCardinality.getOriginalArg().argumentDefinedTypeValid()              = false;
1717    eq FDecouple.getOriginalArg().argumentDefinedTypeValid()                 = false;
1718    eq FMathematicalFunctionCall.getOriginalArg().argumentDefinedTypeValid() = false;
1719    eq FConnectionsOp.getOriginalArg().argumentDefinedTypeValid()            = false;
1720    eq FStringExp.getOriginalArg(int i).argumentDefinedTypeValid()           = i > 0;
1721    eq FConnPotentialRoot.getOriginalArg(int i).argumentDefinedTypeValid()   = i == 1;
1722    eq FSizeExp.getOriginalArg(int i).argumentDefinedTypeValid()             = i != 0;
1723    eq FSmoothExp.getOriginalArg(int i).argumentDefinedTypeValid()           = i == 0;
1724    eq InstPreExp.getOriginalArg().argumentDefinedTypeValid()                = false;
1725    eq FEdgeExp.getOriginalArg().argumentDefinedTypeValid()                  = false;
1726    eq FChangeExp.getOriginalArg().argumentDefinedTypeValid()                = false;
1727    eq FReinit.getOriginalArg().argumentDefinedTypeValid()                   = false;
1728
1729    /**
1730     * Check if the type supplied is valid for this argument.
1731     *
1732     * Only used if argumentDefinedTypeValid() returns <code>false</code>.
1733     */
1734    inh boolean InstFunctionArgument.argumentTypeValid(FType type);
1735    eq FAbstractFunctionCall.getChild().argumentTypeValid(FType type)     = type.isPrimitive();
1736    eq FTranspose.getChild().argumentTypeValid(FType type)                = type.ndims() >= 2 && type.isPrimitive();
1737    eq FSymmetric.getChild().argumentTypeValid(FType type)                = 
1738        type.ndims() == 2 && type.size().get(0) == type.size().get(1);
1739    eq FReductionExp.getChild().argumentTypeValid(FType type)             = type.ndims() > 0 && type.hasAdd();
1740    eq FSemiLinearExp.getChild().argumentTypeValid(FType type)            = type.isNumeric();
1741    eq FAbsExp.getChild().argumentTypeValid(FType type)                   = type.isNumeric();
1742    eq FSignExp.getChild().argumentTypeValid(FType type)                  = type.isNumeric();
1743    eq FEnumIntegerExp.getChild().argumentTypeValid(FType type)           = type.isEnum();
1744    eq FEventGenExp.getChild().argumentTypeValid(FType type)              = type.isNumeric();
1745    eq FMathematicalFunctionCall.getChild().argumentTypeValid(FType type) = type.isNumeric();
1746    eq FMinMaxExp.getOriginalArg(int i).argumentTypeValid(FType type)     = 
1747        hasY() != getOriginalArg(i).getFExp().isArray() && type.isPrimitive();
1748    eq FSmoothExp.getChild().argumentTypeValid(FType type)                = type.onlyContainsReal();
1749    eq FDimensionConvert.getChild().argumentTypeValid(FType type)         = argumentSizeValid(type.size());
1750    eq FCardinality.getChild().argumentTypeValid(FType type)              = 
1751        type.isScalar() && getFExp().isComponentReference(ALLOWED_COMPONENTS);
1752    eq FDecouple.getChild().argumentTypeValid(FType type)                 = true;
1753    eq FConnectionsOp.getOriginalArg(int i).argumentTypeValid(FType type) = 
1754        getOriginalArg(i).getFExp().isComponentReference(ALLOWED_COMPONENTS);
1755    eq FStringExp.getChild().argumentTypeValid(FType type)                = 
1756        type.isScalar() && (type.isReal() || type.isInteger() || type.isBoolean() || type.isEnum());
1757    eq FInfArgsFunctionCall.getChild().argumentTypeValid(FType type)      = true;  // Type checked in FInfArgsFunctionCall.typeCheck()
1758    eq FReinit.getChild().argumentTypeValid(FType type)                   = true;  // Type checked in Reinit.typeCheck()
1759    eq FHomotopyExp.getChild().argumentTypeValid(FType type)              = type.isNumeric();
1760    // All types allowed for these:
1761    eq FNoEventExp.getChild().argumentTypeValid(FType type)               = true;
1762    eq FSizeExp.getChild().argumentTypeValid(FType type)                  = true;
1763
1764    /**
1765     * Description of expexted type of argument.
1766     *
1767     * Should be defined for cases where argumentDefinedTypeValid() returns and
1768     * argumentTypeValid() both return false.
1769     */
1770    inh String InstFunctionArgument.expectedArgumentType();
1771    eq FAbstractFunctionCall.getChild().expectedArgumentType()     = 
1772        "Real, Integer, Boolean, String or enumeration";
1773    eq FTranspose.getChild().expectedArgumentType()                = 
1774        "matrix of Real, Integer, Boolean, String or enumeration";
1775    eq FSymmetric.getChild().expectedArgumentType()                = "square matrix";
1776    eq FReductionExp.getChild().expectedArgumentType()             = "Real array";
1777    eq FEnumIntegerExp.getChild().expectedArgumentType()           = "enumeration";
1778    eq FSemiLinearExp.getChild().expectedArgumentType()            = "Real";
1779    eq FAbsExp.getChild().expectedArgumentType()                   = "Real";
1780    eq FSignExp.getChild().expectedArgumentType()                  = "Real";
1781    eq FEventGenExp.getChild().expectedArgumentType()              = "Real";
1782    eq FMathematicalFunctionCall.getChild().expectedArgumentType() = "Real";
1783    eq FSmoothExp.getChild().expectedArgumentType()                = "Real";
1784    eq FHomotopyExp.getChild().expectedArgumentType()              = "Real";
1785    eq FMinMaxExp.getOriginalArg(int i).expectedArgumentType()     = 
1786        (hasY() ? "scalar" : "array of") + " Real, Integer, Boolean, String or enumeration";
1787    eq FScalarExp.getChild().expectedArgumentType()                = "array with exactly 1 element";
1788    eq FVectorExp.getChild().expectedArgumentType()                = "scalar or vector-shaped array";
1789    eq FMatrixExp.getChild().expectedArgumentType()                = "scalar, vector or matrix-shaped array";
1790    eq FCardinality.getChild().expectedArgumentType()              = "scalar connector";
1791    eq FConnectionsOp.getChild().expectedArgumentType()            = "connector";
1792    eq FStringExp.getChild().expectedArgumentType()                = "scalar Real, Integer, Boolean or enumeration";
1793
1794  protected static final Criteria<InstComponentDecl> FCardinality.ALLOWED_COMPONENTS = new Criteria<InstComponentDecl>() {
1795      public boolean test(InstComponentDecl elem) { return elem.isConnector() || elem.isPrimitive(); }
1796  };
1797
1798  protected static final Criteria<InstComponentDecl> FConnectionsOp.ALLOWED_COMPONENTS = new Criteria<InstComponentDecl>() {
1799      public boolean test(InstComponentDecl elem) { return elem.isOverconstrainedType(); }
1800  };
1801
1802  /**
1803   * Check if this expression is a reference to a component matching specified criteria.
1804   *
1805   * Only valid in instance tree.
1806   */
1807  syn boolean FExp.isComponentReference(Criteria<InstComponentDecl> criteria)  = false;
1808    eq CommonAccessExp.isComponentReference(Criteria<InstComponentDecl> criteria) = getAccess().isComponentReference(criteria);
1809   
1810    syn boolean CommonAccess.isComponentReference(Criteria<InstComponentDecl> criteria) = false;
1811    eq InstAccess.isComponentReference(Criteria<InstComponentDecl> criteria) = 
1812            criteria.test(myInstComponentDecl());
1813
1814  /**
1815   * Check if the given Size is valid for the argument of the operator.
1816   */
1817  syn boolean FDimensionConvert.argumentSizeValid(Size s) {
1818      if (s.isUnknown())
1819          return true;
1820     
1821          boolean[] useDim = new boolean[s.ndims()];
1822          for (int d : dimensionsToKeep())
1823                  if (d < useDim.length)
1824                          useDim[d] = true;
1825          for (int i = 0; i < s.ndims(); i++)
1826                  if (!useDim[i] && s.get(i) != 1)
1827                          return false;
1828          return true;
1829  }
1830
1831    protected static void ASTNode.typeCheckEnabled(ErrorCheckType checkType, FExp enabledExp, FType parentType, boolean eachSet) {
1832        enabledExp.collectErrors(checkType);
1833        if (checkType.checkTypes()) {
1834            if (!enabledExp.type().isBoolean() && !enabledExp.type().isUnknown()) {
1835                enabledExp.error("The type of the enabled expression is not boolean");
1836            } else if (!eachSet && !parentType.dimensionCompatible(enabledExp.type())) { 
1837                if (enabledExp.ndims() == 0) {
1838                    enabledExp.warning("Assuming 'each' for enabled expression");
1839                } else {
1840                    enabledExp.errorUnlessDisabledAnd(parentType.ndims() == enabledExp.ndims(), 
1841                            "Array size mismatch for the enabled attribute, " + 
1842                            "size of component declaration is %s and size of expression is %s", 
1843                            parentType.size(), enabledExp.size());
1844                }
1845            } else if (eachSet) { 
1846                if (parentType.ndims() == 0) {
1847                    enabledExp.warning("The 'each' keyword should not be applied to attributes of scalar components");
1848                } else if (enabledExp.ndims() > 0) {
1849                    enabledExp.error("The enabled attribute is declared 'each' and the expression is not scalar");
1850                }
1851            }
1852        }
1853    }
1854
1855}
1856
1857aspect VariabilityCheck {
1858   
1859    private FExp FPreExp.instanceTreeSource = null;
1860    private FExp FReinit.instanceTreeSource = null;
1861   
1862    public void FExp.setInstanceTreeSource(FExp source) {}
1863   
1864    public void FPreExp.setInstanceTreeSource(FExp source) {
1865        instanceTreeSource = source;
1866    }
1867   
1868    public void FReinit.setInstanceTreeSource(FExp source) {
1869        instanceTreeSource = source;
1870    }
1871   
1872    /**
1873     * Surrounds the FExp <code>exp</code> with pre if it is an non literal exp, else return exp.
1874     *
1875     * Also sets the <code>instanceTreeSource</code> field of the FPreExp (if any)
1876     * to <code>source</code>.
1877     */
1878    public static FExp FPreExp.create(FExp exp, FExp source) {
1879        exp = exp.createFPreExp();
1880        exp.setInstanceTreeSource(source);
1881        return exp;
1882    }
1883   
1884    /**
1885     * Check operations that are allowed on discrete expressions or variables,
1886     * but not on continuous ones or vice-versa.
1887     *
1888     * Also breaks connections back to instance tree that was added during flattening
1889     * to be able to report these errors in the correct place.
1890     */
1891    public void ASTNode.checkDiscreteOperations() {
1892        for (ASTNode n : this)
1893            n.checkDiscreteOperations();
1894    }
1895   
1896    public void FPreExp.checkDiscreteOperations() {
1897        if (instanceTreeSource != null && getFAccess().myFV().isContinuous() && !inWhen() && !inInitialEquationSection()) 
1898            instanceTreeSource.discreteArgError();
1899        instanceTreeSource = null;
1900    }
1901   
1902    public void FReinit.checkDiscreteOperations() {
1903        if (instanceTreeSource != null && !myFV().isContinuous()) 
1904            instanceTreeSource.discreteArgError();
1905        instanceTreeSource = null;
1906    }
1907   
1908    /**
1909     * Generate error for operator that got wrong variability of argument
1910     */
1911    public void FExp.discreteArgError() {
1912        throw new UnsupportedOperationException();
1913    }
1914   
1915    public void InstPreExp.discreteArgError() {
1916        error("Calling built-in operator pre() with a continuous variable access as argument can only be done in when clauses and initial equations");
1917    }
1918   
1919    public void FChangeExp.discreteArgError() {
1920        error("Calling built-in operator change() with a continuous variable access as argument can only be done in when clauses and initial equations");
1921    }
1922   
1923    public void FReinit.discreteArgError() {
1924        error("Built-in operator reinit() must have a continuous variable access as its first argument");
1925    }
1926   
1927}
Note: See TracBrowser for help on using the repository browser.