source: trunk/Compiler/ModelicaFrontEnd/src/jastadd/errorcheck/ErrorCheck.jrag @ 13103

Last change on this file since 13103 was 13103, checked in by Jonathan Kämpe, 5 months ago

#5766 Merging refactoring from dev-mo-merge. Parameterized blt creation, top level connector prefixes in flat tree, and many smaller changes.

File size: 68.9 KB
Line 
1/*
2    Copyright (C) 2009 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.Collection;
18import java.util.Collections;
19import java.util.LinkedList;
20import java.util.ArrayList;
21import java.util.Map;
22
23import org.jmodelica.util.collections.ListMap;
24import org.jmodelica.util.collections.LinkedHashListMap;
25import org.jmodelica.api.problemHandling.Problem;
26import org.jmodelica.api.problemHandling.ProblemSeverity;
27import org.jmodelica.api.problemHandling.ProblemKind;
28import org.jmodelica.util.problemHandling.ReporterNode;
29import org.jmodelica.util.problemHandling.ProblemOptionsProvider;
30import org.jmodelica.util.ErrorCheckType;
31
32
33
34/**
35 * Interface for handling semantic errors.
36 * $see Root#setErrorHandler(IErrorHandler)
37 */
38public interface IErrorHandler {
39    /**
40     * Called when a semantic error is found.
41     *
42     * @param s error message.
43     * @param n the node the error originated from.
44     * @see ASTNode#error(String)
45     */
46    public void error(String s, ASTNode n);
47   
48    /**
49     * Called when a compiler compliance error is found.
50     *
51     * These errors are generated when compiling code that is legal Modelica,
52     * but uses features that aren't implemented. Compliance errors are ignored
53     * by test cases (except ComplianceErrorTestCase).
54     * 
55     * @param s error message.
56     * @param n the node the error originated from.
57     * @see ASTNode#compliance(String)
58     */
59    public void compliance(String s, ASTNode n);
60   
61    /**
62     * Called when a warning is issued during semantic error checking.
63     *
64     * @param s warning message.
65     * @param n the node the warning originated from.
66     * @see ASTNode#warning(String)
67     */
68    public void warning(String s, ASTNode n);
69   
70    /**
71     * Called when a problem is generated though other means than the other methods in this interface,
72     * typically during parsing of library files.
73     *
74     * @param p   the problem
75     * @see ASTNode#problem(Problem)
76     */
77    public void problem(Problem p);
78}
79
80/**
81 * Default implementation of {@link IErrorHandler}.
82 * 
83 * Collects a list of {@link Problem} for all found errors.
84 */
85public class DefaultErrorHandler implements IErrorHandler {
86    protected boolean haltOnWarning;
87   
88    public DefaultErrorHandler() {
89        this(false);
90    }
91
92    public DefaultErrorHandler(boolean haltOnWarning) {
93        this.haltOnWarning = haltOnWarning;
94    }
95
96    /**
97     * Creates a new {@link Problem} and adds it to root.errors, ignoring duplicates.
98     *
99     * @param s error message.
100     * @param n the node the error originated from.
101     */
102    public void error(String s, ASTNode n) {
103        problem(s, n, ProblemSeverity.ERROR, ProblemKind.SEMANTIC);
104    }
105
106    /**
107     * Creates a new {@link Problem} with kind COMPLIANCE
108     *        and adds it to root.errors, ignoring duplicates.
109     *
110     * @param s error message.
111     * @param n the node the error originated from.
112     */
113    public void compliance(String s, ASTNode n) {
114        problem(s, n, ProblemSeverity.ERROR, ProblemKind.COMPLIANCE);
115    }
116
117    /**
118     * Creates a new {@link Problem} and adds it to root.warnings, ignoring duplicates.
119     *
120     * @param s warning message.
121     * @param n the node the warning originated from.
122     */
123    public void warning(String s, ASTNode n) {
124        problem(s, n, ProblemSeverity.WARNING, ProblemKind.OTHER);
125    }
126
127    /**
128     * Called when a problem is generated though other means than the other methods in this class,
129     * typically during parsing of library files.
130     *
131     * @param p   the problem
132     * @see ASTNode#problem(Problem)
133     */
134    public void problem(Problem p) {
135        ArrayList<Problem> list = (p.severity() == ProblemSeverity.WARNING) ? warnings : errors;
136        int pos = list.indexOf(p);
137        if (pos == -1)
138            list.add(p);
139        else
140            list.get(pos).merge(p);
141    }
142
143    protected void problem(String message, ASTNode node, ProblemSeverity severity, ProblemKind kind) {
144        problem(Problem.createProblem(null, node, severity, kind, message));
145    }
146}
147
148/**
149 * Error handler that generates warnings for compliance errors,
150 *        delegating to another error handler.
151 */
152public class ComplianceWarnErrorHandler implements IErrorHandler {
153
154    private IErrorHandler delegate;
155
156    public ComplianceWarnErrorHandler(IErrorHandler delegate) {
157        if (delegate instanceof ComplianceWarnErrorHandler)
158            delegate = ((ComplianceWarnErrorHandler) delegate).delegate;
159        this.delegate = delegate;
160    }
161
162    /**
163     * Delegates to wrapped error handler.
164     *
165     * @param s error message.
166     * @param n the node the error originated from.
167     */
168    public void error(String s, ASTNode n) {
169        delegate.error(s, n);
170    }
171
172    /**
173     * Delegates to warning() in wrapped error handler.
174     *
175     * @param s error message.
176     * @param n the node the error originated from.
177     */
178    public void compliance(String s, ASTNode n) {
179        delegate.warning(s, n);
180    }
181
182    /**
183     * Delegates to wrapped error handler.
184     *
185     * @param s warning message.
186     * @param n the node the warning originated from.
187     */
188    public void warning(String s, ASTNode n) {
189        delegate.warning(s, n);
190    }
191
192    /**
193     * Called when a problem is generated though other means than the other methods in this class,
194     * typically during parsing of library files.
195     *
196     * @param p   the problem
197     * @see ASTNode#problem(Problem)
198     */
199    public void problem(Problem p) {
200        delegate.problem(p);
201    }
202}
203
204aspect ErrorCheck {
205   
206    syn ProblemOptionsProvider ASTNode.myProblemOptionsProvider() = root().getUtilInterface();
207   
208    public class UtilInterface implements ProblemOptionsProvider {
209        private Set<String> warningsFilterCache = null;
210        public boolean filterThisWarning(String identifier) {
211            if (warningsFilterCache == null) {
212                warningsFilterCache = new HashSet<String>();
213                for (String s : getOptionRegistry().getStringOption("filter_warnings").split(",")) {
214                    warningsFilterCache.add(s);
215                }
216            }
217            return warningsFilterCache.contains(identifier);
218        }
219    }
220       
221        public void ASTNode.breakOnErrors() throws CompilerException {
222                root().getErrorHandler().breakOnErrors();
223        }
224       
225        public void IErrorHandler.breakOnErrors() throws CompilerException;
226       
227        public void ComplianceWarnErrorHandler.breakOnErrors() throws CompilerException {
228            delegate.breakOnErrors();
229        }
230       
231        public void DefaultErrorHandler.breakOnErrors() throws CompilerException {
232                if (!errors.isEmpty() || (haltOnWarning && !warnings.isEmpty())) {
233                    java.util.Collections.sort(errors);
234                    java.util.Collections.sort(warnings);
235                   
236                    ArrayList<Problem> problems = new ArrayList<Problem>();
237                    problems.addAll(warnings);
238                    problems.addAll(errors);
239                   
240                    errors.clear();
241                    warnings.clear();
242                   
243                    throw new CompilerException(problems);
244                }
245        }
246       
247    public ArrayList<Problem> ASTNode.collectWarnings() {
248        return root().getErrorHandler().collectWarnings();
249    }
250   
251    public ArrayList<Problem> IErrorHandler.collectWarnings();
252   
253    public ArrayList<Problem> ComplianceWarnErrorHandler.collectWarnings() {
254        return delegate.collectWarnings();
255    }
256   
257    public ArrayList<Problem> DefaultErrorHandler.collectWarnings() {
258                breakOnErrors();
259            java.util.Collections.sort(warnings);
260            ArrayList<Problem> problems = new ArrayList<Problem>(warnings);
261            warnings.clear();
262            return problems;
263        }
264
265    public ArrayList<Problem> DefaultErrorHandler.errors = new ArrayList<Problem>();
266    public ArrayList<Problem> DefaultErrorHandler.warnings = new ArrayList<Problem>();
267
268  private IErrorHandler Root.errorHandler = null;
269 
270  /**
271   * Get the handler for semantic errors.
272   * @see IErrorHandler
273   */
274  public IErrorHandler Root.getErrorHandler() {
275          return getUtilInterface().getErrorHandler();
276  }
277
278  ASTNode implements ReporterNode;
279 
280  public void ASTNode.reportProblem(Problem problem) {
281      root().getErrorHandler().problem(problem);
282  }
283
284 
285 
286  /**
287   * Register an error. Delegates to an {@link IErrorHandler}.
288   * @param s   the error message.
289   */
290  @Deprecated
291  public void ASTNode.error(String s) {
292          root().getErrorHandler().error(s, this);
293  }
294 
295  /**
296   * Register an error. Delegates to an {@link IErrorHandler}.
297   *
298   * Builds error message using <code>format</code> as format string.
299   */
300  @Deprecated
301  public void ASTNode.error(String format, Object... args) {
302          error(String.format(format, args));
303  }
304
305  /**
306   * Register a compliance error. Delegates to an {@link IErrorHandler}.
307   * @param s   the error message.
308   */
309  @Deprecated
310  public void ASTNode.compliance(String s) {
311          root().getErrorHandler().compliance(s, this);
312  }
313 
314  /**
315   * Register a compliance error. Delegates to an {@link IErrorHandler}.
316   *
317   * Builds error message using <code>format</code> as format string.
318   */
319  @Deprecated
320  public void ASTNode.compliance(String format, Object... args) {
321          compliance(String.format(format, args));
322  }
323
324  /**
325   * Register a warning. Delegates to an {@link IErrorHandler}.
326   * @param s   the warning message.
327   */
328  @Deprecated
329  public void ASTNode.warning(String s) {
330          root().getErrorHandler().warning(s, this);
331  }
332 
333  /**
334   * Register a warning. Delegates to an {@link IErrorHandler}.
335   *
336   * Builds warning message using <code>format</code> as format string.
337   */
338  @Deprecated
339  public void ASTNode.warning(String format, Object... args) {
340          warning(String.format(format, args));
341  }
342
343  /**
344   * Register a problem. Delegates to an {@link IErrorHandler}.
345   * @param p   the problem.
346   */
347  @Deprecated
348  public void ASTNode.problem(Problem p) {
349      root().getErrorHandler().problem(p);
350  }
351
352    /**
353     * If this node is in a disabled conditional component, generate a warning, otherwise generate an error.
354     */
355    @Deprecated
356    public void ASTNode.errorUnlessDisabled(String s) {
357        if (inDisabledComponent()) {
358            warning("Found error in disabled conditional:\n  " + s);
359        } else {
360            error(s);
361        }
362    }
363
364    /**
365     * If this node is in a disabled conditional component, generate a warning, otherwise generate an error.
366     */
367    @Deprecated
368    public void ASTNode.errorUnlessDisabled(String format, Object... args) {
369        if (inDisabledComponent()) {
370            warning("Found error in disabled conditional:\n  " + format, args);
371        } else {
372            error(format, args);
373        }
374    }
375
376    /**
377     * If this node is in a disabled conditional component and <code>condition</code> is <code>true</code>,
378     * generate a warning, otherwise generate an error.
379     */
380    @Deprecated
381    public void ASTNode.errorUnlessDisabledAnd(boolean condition, String s) {
382        if (condition) {
383            errorUnlessDisabled(s);
384        } else {
385            error(s);
386        }
387    }
388
389    /**
390     * If this node is in a disabled conditional component and <code>condition</code> is <code>true</code>,
391     * generate a warning, otherwise generate an error.
392     */
393    @Deprecated
394    public void ASTNode.errorUnlessDisabledAnd(boolean condition, String format, Object... args) {
395        if (condition) {
396            errorUnlessDisabled(format, args);
397        } else {
398            error(format, args);
399        }
400    }
401
402    public interface LockBranch {
403        /**
404         * Lock the closest surrounding if-equation or if-expression that have only
405         * parameter-expression tests and evaluates to another branch. Returns true
406         * if any such if exists.
407         *
408         * Any parameters used in the test will be marked as structural.
409         *
410         * NB: This has side-effects, despite being an inherited attribute.
411         *
412         * @param checkType  the type of error checks to use when marking structural parameters
413         */
414        public boolean lockBranch(ErrorCheckType checkType);
415    }
416    FExp implements LockBranch;
417    FExpSubscript implements LockBranch;
418    FAbstractEquation implements LockBranch;
419    FStatement implements LockBranch;
420    FIfWhenClause implements LockBranch;
421    InstComponentDecl implements LockBranch;
422    InstFunctionArgument implements LockBranch;
423    FFunctionCallLeft implements LockBranch;
424    InstValueModification implements LockBranch;
425
426    // TODO: find way to move side-effects out from inh attr
427    inh boolean FExp.lockBranch(ErrorCheckType checkType);
428    inh boolean FExpSubscript.lockBranch(ErrorCheckType checkType);
429    inh boolean FIntegerSubscript.lockBranch(ErrorCheckType checkType);
430    inh boolean FAbstractEquation.lockBranch(ErrorCheckType checkType);
431    inh boolean FStatement.lockBranch(ErrorCheckType checkType);
432    inh boolean FIfWhenClause.lockBranch(ErrorCheckType checkType);
433    inh boolean InstComponentDecl.lockBranch(ErrorCheckType checkType);
434    inh boolean InstFunctionArgument.lockBranch(ErrorCheckType checkType); 
435    inh boolean FFunctionCallLeft.lockBranch(ErrorCheckType checkType);
436    inh boolean InstValueModification.lockBranch(ErrorCheckType checkType);
437    eq FIfExp.getThenExp().lockBranch(ErrorCheckType checkType)                = lockMyBranch(this, checkType, getIfExp(), true);
438    eq FIfExp.getElseExp().lockBranch(ErrorCheckType checkType)                = lockMyBranch(this, checkType, getIfExp(), false);
439    eq FIfEquation.getFAbstractEquation().lockBranch(ErrorCheckType checkType) = lockMyBranch(this, checkType, getTest(), true);
440    eq FIfEquation.getElse().lockBranch(ErrorCheckType checkType)              = lockMyBranch(this, checkType, getTest(), false);
441    eq FIfClause.getFStatement().lockBranch(ErrorCheckType checkType)          = lockMyBranch(this, checkType, getTest(), true);
442    eq FIfStmt.getFIfWhenClause(int i).lockBranch(ErrorCheckType checkType)    = lockMyBranch(checkType, i);
443    eq FIfStmt.getElseStmt().lockBranch(ErrorCheckType checkType)              = lockMyBranch(checkType, getNumFIfWhenClause());
444    eq FConnectClause.getConnector1().lockBranch(ErrorCheckType checkType)     = getConnector2().isDisabled() || lockBranch(checkType);
445    eq FConnectClause.getConnector2().lockBranch(ErrorCheckType checkType)     = getConnector1().isDisabled() || lockBranch(checkType);
446    eq InstComponentDecl.getChild().lockBranch(ErrorCheckType checkType)       = isDisabled() || lockBranch(checkType);
447    eq InstValueModification.getFExp().lockBranch(ErrorCheckType checkType)    = isSuperseded() || lockBranch(checkType);
448    eq InstClassDecl.getChild().lockBranch(ErrorCheckType checkType)           = false;
449    eq InstRoot.getChild().lockBranch(ErrorCheckType checkType)                = false;
450    eq FClass.getChild().lockBranch(ErrorCheckType checkType)                  = false;
451
452    /**
453     * Lock branch on src and return true if it only has parameter-expression
454     * tests that evaluates to use the branch not indicated by <code>then</code>.
455     * Otherwise delegates to {@link LockBranch#lockBranch(ErrorCheckType)}.
456     *
457     * Any parameters used in the test will be marked as structural.
458     *
459     * @param checkType  the type of error checks to use when marking structural parameters
460     * @param then       if true, we are trying to remove the then-branch, otherwise the else-branch
461     */
462    public static boolean ASTNode.lockMyBranch(LockBranch src, ErrorCheckType checkType, FExp exp, boolean then) {
463        if (exp.lockable(then)) {
464            exp.markAsStructuralParameter(checkType);
465            return true;
466        } else {
467            return src.lockBranch(checkType);
468        }
469    }
470
471    syn boolean FExp.lockable(boolean then) {
472        if (variability().fixedParameterOrLess()) {
473            try {
474                CValue val = ceval();
475                return val.hasBooleanValue() && val.booleanValue() != then;
476            } catch (ConstantEvaluationException e) {}
477        }
478        return false;
479    }
480
481    public boolean FIfStmt.lockMyBranch(ErrorCheckType checkType, int i) {
482        if (i == 0) {
483            return lockBranch(checkType);
484        } else {
485            FIfWhenClause next = getFIfWhenClause(i - 1);
486            return lockMyBranch(next, checkType, next.getTest(), false);
487        }
488    }
489
490    syn boolean InstValueModification.isSuperseded() {
491        InstComponentDecl cmp = myTargetComponent();
492        InstModification mod = cmp.isAttribute() ? 
493                parentTargetComponent().totalMergedEnvironment().find(cmp.name()) : 
494                cmp.myInstValueMod();
495        return mod != null && mod != this;
496    }
497
498    inh InstComponentDecl InstModification.myTargetComponent();
499    eq InstComponentModification.getChild().myTargetComponent() = getName().myInstComponentDecl();
500    eq InstComponentDecl.getChild().myTargetComponent()         = this;
501    eq InstNode.getChild().myTargetComponent()                  = unknownInstComponentDecl();
502    eq InstRecordConstructor.getChild().myTargetComponent()     = unknownInstComponentDecl();
503
504    inh InstComponentDecl InstModification.parentTargetComponent();
505    eq InstComponentModification.getChild().parentTargetComponent() = myTargetComponent();
506    eq InstNode.getChild().parentTargetComponent()                  = unknownInstComponentDecl();
507    eq InstRecordConstructor.getChild().parentTargetComponent()     = unknownInstComponentDecl();
508
509    syn boolean InstComponentDecl.isAttribute() = false;
510    eq InstBuiltIn.isAttribute()                = true;
511
512
513    /**
514     * Class used by the error check to delegate to different error checks.
515     * This class is subtyped for each error check with different
516     * implementations of check() that delegate to corresponding check.
517     */
518    public abstract class ErrorChecker {
519        private final String name;
520
521        public ErrorChecker(String name) {
522            this.name = name;
523        }
524
525        /**
526         * Do check on ASTNode node with the ErrorCheckType check type.
527         */
528        public abstract void check(ASTNode node, ErrorCheckType checkType);
529
530        @Override
531        public String toString() {
532            return name;
533        }
534    }
535
536    /**
537     * A list that contains all error checkers. New error checkers are added
538     * dynamically during static evaluation by calling addErrorChecker().
539     *
540     * @see ASTNode.addErrorChecker(ErrorChecker)
541     */
542    private static Collection<ErrorChecker> ASTNode.ERROR_CHECKERS;
543
544    /**
545     * Add ErrorChecker checker to the list of error checkers. This method
546     * should only be called from ASTnode in order to ensure that the checker
547     * is added corretly during static evaluation, hence the private
548     * visibility.
549     */
550    private static ErrorChecker ASTNode.addErrorChecker(ErrorChecker checker) {
551        if (ERROR_CHECKERS == null)
552            ERROR_CHECKERS = new ArrayList<ErrorChecker>();
553        ERROR_CHECKERS.add(checker);
554        return checker;
555    }
556
557    /**
558     * A helper method that calls all registered error checkers.
559     *
560     * @see ASTNode#addErrorChecker(ErrorChecker)
561     */
562    protected void ASTNode.allChecks(ErrorCheckType checkType) {
563        for (ErrorChecker checker : ERROR_CHECKERS)
564            checker.check(this, checkType);
565    }
566
567    public void ASTNode.collectErrors(ErrorCheckType checkType) {
568        for (ASTNode n : this) {
569            n.collectErrors(checkType);
570        }
571        allChecks(checkType);
572    }
573
574
575    public void ASTNode.errorCheck(ErrorCheckType checkType) {
576        collectErrors(checkType);
577        breakOnErrors();
578    }
579
580    inh InstComponentDecl ASTNode.errorEnclosingComponent();
581    eq InstNode.getChild().errorEnclosingComponent() = containingInstComponent();
582    eq InstComponentDecl.getChild().errorEnclosingComponent() {
583        if (isRedeclared() || getSrcComponentDecl().hasRedeclare()) {
584            return errorEnclosingComponent();
585        } else {
586            return containingInstComponent();
587        }
588    }
589    eq InstComponentDecl.getFAbstractEquation().errorEnclosingComponent() = this;
590   
591    eq FlatRoot.getChild().errorEnclosingComponent() = null;
592    eq InstRoot.getChild().errorEnclosingComponent() = null;
593    eq SourceRoot.getChild().errorEnclosingComponent() = null;
594
595    syn String ASTNode.errorComponentName() {
596        InstComponentDecl parent = errorEnclosingComponent();
597        return parent == null ? null : parent.getFAccess().scalarName(false);
598    }
599
600}
601
602aspect InstanceErrorCheck {
603
604 // Error checking in instance tree
605
606    // We don't want to error check an entire model, just the classes
607    //   that are used.
608    public void InstProgramRoot.checkErrorsInInstClass(String className, ErrorCheckType checkType) throws ModelicaClassNotFoundException {
609        InstClassDecl icd = instantiateModel(className);
610        if (!icd.isUnknown()) {
611            icd.errorCheck(checkType);
612        } else {
613            throw new ModelicaClassNotFoundException(className);
614        }
615    }
616
617    public void InstClassDecl.checkErrorsInModelInstance(String className, ErrorCheckType checkType) throws ModelicaClassNotFoundException {
618        errorCheck(checkType);
619    }
620
621    public void UnknownInstClassDecl.checkErrorsInModelInstance(String className, ErrorCheckType checkType) throws ModelicaClassNotFoundException {
622        throw new ModelicaClassNotFoundException(className);
623    }
624
625        protected boolean BaseNode.errorChecked = false;
626
627        public void ASTNode.resetCollectErrors() {
628                for (ASTNode n : noTransform())
629                        n.resetCollectErrors();
630        }
631       
632        public static void ASTNode.resetCollectErrorsOn(ASTNode n) {
633                if (n != null)
634                        n.resetCollectErrors();
635        }
636       
637        public void BaseNode.resetCollectErrors() {
638                errorChecked = false;
639                super.resetCollectErrors();
640        }
641       
642        public void InstNode.resetCollectErrors() {
643                super.resetCollectErrors();
644                resetCollectErrorsOn(getInstComponentDeclListNoTransform());
645                resetCollectErrorsOn(getInstClassDeclListNoTransform());
646                resetCollectErrorsOn(getInstExtendsListNoTransform());
647                resetCollectErrorsOn(getInstImportListNoTransform());
648                resetCollectErrorsOn(getRedeclaredInstClassDeclListNoTransform());
649                resetCollectErrorsOn(getFAbstractEquationListNoTransform());
650        }
651
652  public void InstNode.collectErrors(ErrorCheckType checkType) {
653    if (!errorChecked) {
654      errorChecked = true;
655          allChecks(checkType);
656          for (InstNode n : getInstComponentDecls()) 
657                  n.collectErrors(checkType);
658          for (InstNode n : getInstExtendss()) 
659                  n.collectErrors(checkType);
660          for (FAbstractEquation e : getFAbstractEquations()) 
661                  e.collectErrors(checkType);
662        }
663  }
664   
665    /**
666     * Check if this node is in an InstComponentDecl.
667     */
668    inh boolean InstExtends.inInstComponent();
669    inh boolean InstClassRedeclare.inInstComponent();
670    eq InstComponentDecl.getChild().inInstComponent() = true;
671    eq InstClassDecl.getChild().inInstComponent()     = false;
672    eq InstRoot.getChild().inInstComponent()          = false;
673    eq FlatRoot.getChild().inInstComponent()          = false;
674    eq InstRecordConstructor.getChild().inInstComponent() = false;
675   
676   
677    public void FExp.collectErrors(ErrorCheckType checkType) {
678        if (!errorChecked) {
679            errorChecked = true;
680            super.collectErrors(checkType);
681        }
682    }
683
684    public void InstBaseClassDecl.collectErrors(ErrorCheckType checkType) {
685        if (!errorChecked) {
686            super.collectErrors(checkType);
687            errorChecked = true;
688            for (InstImport ii : getInstImports()) {
689                ii.collectErrors(checkType);
690            }
691            if (hasInstConstrainingClass()) {
692                getInstConstrainingClass().collectErrors(checkType);
693            }
694            getInstRestriction().collectErrors(checkType);
695            getInstGeneratedInners().collectErrors(checkType);
696            if (getSrcBaseClassDecl() instanceof SrcFullClassDecl) {
697                SrcFullClassDecl fcd = (SrcFullClassDecl) getSrcBaseClassDecl();
698                if (!(fcd.getName().getID().equals(fcd.getSrcEndDecl().getEndID()))) {
699                    error("The declaration and end names of a class should be the same");
700                }
701            }
702        }
703    }
704
705    public void InstFullClassDecl.collectErrors(ErrorCheckType checkType) {
706        if (!errorChecked) {
707            super.collectErrors(checkType);
708            errorChecked = true;
709            getInstExternalOpt().collectErrors(checkType);
710            if (isFunction())
711                errorCheckDerivativeAnnotations(checkType);
712        }
713    }
714
715    public void InstGeneratedInner.collectErrors(ErrorCheckType checkType) {
716        if (!checkType.allowBadGeneratedInner()) {
717            boolean error = false;
718            if (getMatchingNonInner() != null) {
719                getCopiedOuter().error("Can not generate missing inner declaration for %s, due to presence of component with same name on top level", name());
720                error = true;
721            } else {
722                ListMap<String, String> map = new LinkedHashListMap<String, String>();
723                for (InstComponentDecl outer : outers) {
724                    if (outer.myInstClass().isPartial()) {
725                        outer.error("Can not generate missing inner declaration for outer component %s of partial type %s", 
726                                outer.name(), outer.myInstClass().qualifiedName());
727                        error = true;
728                    } else {
729                        map.add(outer.myInstClass().actualInstClass().qualifiedName(), outer.qualifiedName());
730                    }
731                }
732                if (map.size() > 1) {
733                    StringBuilder sb = new StringBuilder();
734                    java.util.List<String> classes = new ArrayList<String>(map.keySet());
735                    Collections.sort(classes);
736                    for (String cls : classes) {
737                        java.util.List<String> components = map.get(cls);
738                        Collections.sort(components);
739                        for (String outer : components) {
740                            sb.append("\n    ");
741                            sb.append(cls);
742                            sb.append(" ");
743                            sb.append(outer);
744                        }
745                    }
746                    getCopiedOuter().error("Can't generate missing inner declaration for %s, due to the outer declarations being of different types: %s", name(), sb);
747                    error = true;
748                }
749            }
750            if (!error) {
751                getInstComponentDecl().collectErrors(checkType);
752                // TODO: Use missingInnerMessage annotation
753                getCopiedOuter().warning("Generated missing inner declaration for '%s'", getCopiedOuter());
754            }
755        }
756    }
757
758    public void InstBaseClassDecl.errorCheckDerivativeAnnotations(ErrorCheckType checkType) {
759        errorCheckDerivativeAnnotations(checkType, findDerivativeAnnotationRoot());
760    }
761   
762    private void InstBaseClassDecl.errorCheckDerivativeAnnotations(ErrorCheckType checkType, AnnotationNode src) {
763        for (AnnotationNode derivative : src.allFor("derivative")) {
764            errorCheckDerivativeAnnotation(checkType, derivative);
765        }
766    }
767
768    private void InstBaseClassDecl.errorCheckDerivativeAnnotation(ErrorCheckType checkType, AnnotationNode derivative) {
769        // TODO: Check that inputs/outputs of target function matches this function
770        if (!derivative.isValue()) {
771            derivative.ast().error("Function name is missing in derivative annotation declaration");
772            return;
773        }
774        InstLookupResult<InstClassDecl> lr = derivative.lookupInstClass();
775        if (lr == null) {
776            derivative.ast().error("Invalid derivative function reference");
777            return;
778        }
779        if (lr.isProblem()) {
780            lr.problem(derivative.ast(), "function", derivative.valueToString());
781            if (lr.isError()) {
782                return;
783            }
784        }
785        InstClassDecl icd = lr.target().actualInstClass();
786        if (!icd.isFunction()) {
787            derivative.ast().error("The class " + derivative.valueToString() + " is not a function");
788            return;
789        }
790        icd.collectErrors(checkType);
791        boolean orderFound = false;
792        Set<String> noAndZeroDerivatives = new HashSet<String>();
793        for (AnnotationNode attrNode : derivative) {
794            if ("noDerivative".equals(attrNode.name()) || "zeroDerivative".equals(attrNode.name())) {
795                InstLookupResult<InstComponentDecl> res = attrNode.lookupInstComponent();
796                if (res == null) {
797                    attrNode.ast().error("Expecting variable reference for " + attrNode.name() + " annotation");
798                    continue;
799                }
800                String varName = attrNode.valueToString();
801                if (!noAndZeroDerivatives.add(varName))
802                    attrNode.ast().error("Multiple noDerivative or zeroDerivative declarations for " + varName);
803                if (res.isNotFound())
804                    attrNode.ast().error("Unable to find " + varName);
805                else if (!res.target().isInput())
806                    attrNode.ast().error(attrNode.name() + " annotation may only reference input variables");
807            } else if ("order".equals(attrNode.name())) {
808                if (orderFound)
809                    attrNode.ast().error("Multiple declarations of the order attribute");
810                orderFound = true;
811                if (!attrNode.isIntegerValue())
812                    attrNode.ast().error("Expecting integer typed expression for order attribute");
813                else if (attrNode.integer() < 1)
814                    attrNode.ast().error("Order attribute must be greater or equal to one");
815            }
816        }
817    }
818
819    public void InstSimpleShortClassDecl.collectErrors(ErrorCheckType checkType) {
820        if (!errorChecked) {
821            errorChecked = true;
822            getTarget().collectErrors(checkType);
823            actualInstClass().collectErrors(checkType);
824        }
825    }
826
827    public void InstLibNode.collectErrors(ErrorCheckType checkType) {
828        if (!errorChecked) {
829            errorChecked = true;
830            actualInstClass().collectErrors(checkType);
831        }
832    }
833
834    public void InstImport.collectErrors(ErrorCheckType checkType) {
835        if (!errorChecked) {
836            errorChecked = true;
837            getPackageName().collectErrors(checkType);
838        }
839    }
840
841    public void InstComponentDecl.collectErrors(ErrorCheckType checkType) {
842        if (!errorChecked) {
843            errorChecked = true;
844            if (checkType.checkForRecursiveStructure() && isRecursed()) {
845                error("Recursive class structure");
846            } else if (isOuter()) {
847                myInnerInstComponentDecl().collectErrors(checkType);
848            } else {
849                collectErrorsInClassName(checkType);
850                myInstClass().checkRestriction(checkType);
851                if (hasFArraySubscripts()) {
852                    getFArraySubscripts().collectErrors(checkType);
853                }
854                if (hasConditionalAttribute())  {
855                    getConditionalAttribute().collectErrors(checkType);
856                }
857                if (!isDisabled() && hasInstModification()) {
858                    getInstModification().collectErrors(checkType);
859                }
860                for (InstModification im : getMergedEnvironment()) {
861                    im.checkModificationNames(checkType);
862                }
863               
864                if (shouldCheckInto(checkType)) {
865                    errorChecked = false;
866                    super.collectErrors(checkType);
867                    if (hasInstConstrainingComponent()) {
868                        getInstConstrainingComponent().collectErrors(checkType);
869                    }
870                }
871            }
872        }
873    }
874
875    /**
876     * Check that the restriction of this class is fulfilled.
877     */
878    public void InstClassDecl.checkRestriction(ErrorCheckType checkType) {}
879    public void InstBaseClassDecl.checkRestriction(ErrorCheckType checkType) {
880        if (!errorChecked)
881            getInstRestriction().collectErrors(checkType);
882    }
883
884    public void InstModification.checkModificationNames(ErrorCheckType checkType) {}
885   
886    public void InstElementModification.checkModificationNames(ErrorCheckType checkType) {
887        getName().allChecks(checkType);
888    }
889       
890        syn boolean InstComponentDecl.shouldCheckInto(ErrorCheckType checkType) =
891                !isDisabled() || checkType.checkInactiveComponents() || myOptions().getBooleanOption("check_inactive_contitionals");
892
893        public void InstComponentDecl.collectErrorsInClassName(ErrorCheckType checkType) {
894                getClassName().collectErrors(checkType);
895        }       
896       
897        public void InstArrayComponentDecl.collectErrorsInClassName(ErrorCheckType checkType) {
898                // TODO: use correct class name instead of "ArrayDecl" so that name lookup suceeds instead?
899                //       need that for other things as well, but is there problems with it?
900        }       
901       
902        public void FExp.checkConstantExpression(ErrorCheckType checkType, String varKind, String varName) {
903                boolean failed = false;
904        ConstantEvaluationException eres = null;
905                try {
906                        if (isCircular()) {
907                            if (!checkType.allowConstantNoValue())
908                                error("Could not evaluate binding expression for %s '%s' due to circularity: '%s'", 
909                                                varKind, varName, prettyPrint(""));
910                        } else {
911                                CValue val = ceval();
912                                if (val.isPartlyUnknown()) {
913                                        if (val.isUnsupported()) {
914                                                compliance("Constant evaluation not supported for expression(s) directly or indirectly " + 
915                                                                "used by the binding expression for %s '%s': '%s'", varKind, varName, prettyPrint(""));
916                                        } else {
917                                                failed = true;
918                                        }
919                                }
920                        }
921                } catch (ConstantEvaluationNotReadyException e) {
922                        // Will be evaluatable later, ignore for now
923                } catch (ConstantEvaluationException e) {
924                        failed = true;
925                        eres = e;
926                }
927                if (failed && !checkType.allowConstantNoValue()) {
928                        error("Could not evaluate binding expression for %s '%s': '%s'%s", varKind, varName, prettyPrint(""),
929                                eres == null ? "" : eres.getModelicaStackTrace());
930                }
931        }
932
933    public static final SimpleProblemProducer ASTNode.PARAMETER_MISSING_BINDING_EXPRESSION =
934            new SimpleWarningProducer("PARAMETER_MISSING_BINDING_EXPRESSION", ProblemKind.SEMANTIC, "The parameter %s does not have a binding expression");
935    public static final SimpleProblemProducer ASTNode.CONSTANT_MISSING_BINDING_EXPRESSION =
936            new SimpleWarningProducer("CONSTANT_MISSING_BINDING_EXPRESSION", ProblemKind.SEMANTIC, "The constant %s does not have a binding expression");
937   
938    public void InstAssignable.collectErrors(ErrorCheckType checkType) {
939        //log.debug(toString());
940        if (!errorChecked) {
941            super.collectErrors(checkType);
942            errorChecked = true;
943           
944            // Check modification
945            if (hasInstValueMod()) {
946                myInstValueMod().collectErrors(checkType);
947            }
948           
949            if (hasBindingFExp()) {
950                // Check if the binding expression of constants can be evaluated
951                FExp bexp = getBindingFExp();
952                if (isConstant() && !bexp.type().isUnknown()) {
953                    bexp.checkConstantExpression(checkType, "constant", qualifiedName());
954                } else if (isParameter() && bexp.isCircular()) {
955                    bexp.error("Circularity in binding expression of parameter: %s = %s", qualifiedName(), bexp);
956                }
957            } else {
958                // Warn if constant or parameter does not have a binding expression (start is used)
959                if (variability().fixedParameterOrLess() && !isForIndex() && !isRecord() && 
960                        !hasParentRecordWithBindingExp() && !inFunction()) {
961                    if (isParameter()) {
962                        PARAMETER_MISSING_BINDING_EXPRESSION.invoke(this, qualifiedName());
963                    } else {
964                        CONSTANT_MISSING_BINDING_EXPRESSION.invoke(this, qualifiedName());
965                    }
966                }
967            }
968           
969            TypePrefixVariability v = variability().combine();
970            // Mark parameters with Evaluate=true as structural
971            if (v.parameterVariability() && isEvalAnnotated(true)) {
972                if (v.fixedParameterOrLess()) {
973                    forceVariability(checkType, Variability.STRUCTPARAMETER);
974                } else {
975                    warning("Evaluate annotation is ignored for parameters with fixed=false");
976                }
977            }
978           
979            // Check array indices
980            getClassName().collectErrors(checkType);
981            getLocalFArraySubscriptsOpt().collectErrors(checkType);
982           
983            // Check attributes for primitive variables
984            checkAttributes(checkType);
985        }
986    }
987   
988        public void InstAssignable.checkAttributes(ErrorCheckType checkType) {}
989
990    public static final SimpleProblemProducer ASTNode.START_VALUE_NOT_PARAMETER =
991            new SimpleErrorProducer("START_VALUE_NOT_PARAMETER", ProblemKind.SEMANTIC,
992                    "Variability of binding expression for attribute '%s' is not less"
993                    + " than or equal to parameter variability: %s");
994    public static final SimpleProblemProducer ASTNode.START_VALUE_INITIAL_PARAMETER =
995            new SimpleWarningProducer("START_VALUE_INITIAL_PARAMETER", ProblemKind.COMPLIANCE,
996                    "Variability of binding expression for attribute '%s' is "
997                    + " initial parameter variability: %s");
998
999        public void InstPrimitive.checkAttributes(ErrorCheckType checkType) {
1000                // Check if the expressions of the attributes can be evaluated
1001                // Note that this check has to be done locally in the
1002                // context of an InstAssignable node in order to avoid
1003                // evaluation of all value modifications also for non
1004                // parameters.
1005                for (InstModification im : totalMergedEnvironment()) {
1006                        // Only check attributes, value modifications are checked above
1007                        if (im instanceof InstComponentModification) {
1008                                InstComponentModification icm = (InstComponentModification)im;
1009                                if (icm.hasInstModification() && icm.getInstModification().hasInstValueMod()) {
1010                                        FExp val_mod = icm.getInstModification().instValueMod();
1011                    if (val_mod.variability().lessOrEqual(Variability.CONSTANT)) {
1012                        val_mod.checkConstantExpression(checkType, "attribute", icm.getName().name());
1013                    } else if (!val_mod.variability().parameterOrLess()) {
1014                        ASTNode.START_VALUE_NOT_PARAMETER.invoke(val_mod, icm.getName().name(), val_mod);
1015                    }
1016                                }
1017                        }
1018                }
1019        }
1020
1021       
1022    public static final SimpleProblemProducer ASTNode.UNSPECIFIED_ENUM_COMPONENT =
1023            new SimpleErrorProducer("UNSPECIFIED_ENUM_COMPONENT", ProblemKind.SEMANTIC,
1024                    "Components of unspecified enumerations are not allowed in simulation models:\n %s");
1025
1026        syn boolean InstNode.isUnspecifiedEnum() = false;
1027        eq InstPrimitive.isUnspecifiedEnum() = myInstClass().isUnspecifiedEnum();
1028        eq InstClassDecl.isUnspecifiedEnum() = enumLiterals().isEmpty();
1029
1030        public void InstExtends.collectErrors(ErrorCheckType checkType) {
1031            if (!errorChecked) {
1032                if (checkType.checkForRecursiveStructure() && isRecursed()) {
1033                        error("Recursive class structure");
1034                        errorChecked = true;
1035                } else {
1036                                super.collectErrors(checkType);
1037                        errorChecked = true;
1038                                getClassName().collectErrors(checkType);
1039                                if (hasInstClassModification() && shouldCheckModification())
1040                                        getInstClassModification().collectErrors(checkType);
1041                        }
1042            }
1043        }
1044       
1045        // Normally the class modifications in an InstExtendsShortClass
1046        // does not need to be checked, since they are checked in InstShortClassDecl.
1047        // This is not the case if the short class decl references
1048        // an primitive variable, however, and in this case the
1049        // class modification needs to be checked for errors.
1050        syn boolean InstExtends.shouldCheckModification()           = true;
1051        eq InstExtendsShortClass.shouldCheckModification()          = extendsPrimitive();
1052        eq InstReplacingExtendsShortClass.shouldCheckModification() = extendsPrimitive();
1053
1054    public void InstShortClassDecl.collectErrors(ErrorCheckType checkType) {
1055        if (!errorChecked) {
1056          super.collectErrors(checkType);
1057          errorChecked = true;
1058          // The localInstModifications should only be checked if
1059          // the node is not a InstReplacingShortClassDecl. This
1060          // is accomplished by the method collectInstModificationErrors.
1061          collectInstModificationErrors(checkType);
1062          if (hasInstConstrainingClass()) {
1063                getInstConstrainingClass().collectErrors(checkType);
1064          }
1065        }
1066    }
1067
1068        public void InstShortClassDecl.collectInstModificationErrors(ErrorCheckType checkType) {
1069                for (InstModification mod : localInstModifications())
1070                        mod.collectErrors(checkType);
1071    }
1072    public void InstReplacingShortClassDecl.collectInstModificationErrors(ErrorCheckType checkType) { }
1073
1074        public void InstReplacingShortClassDecl.collectErrors(ErrorCheckType checkType) {
1075            if (!errorChecked) {
1076                  super.collectErrors(checkType);
1077          errorChecked = true;
1078                  getOriginalInstClass().collectErrors(checkType);
1079                }
1080               
1081        }
1082
1083        public void InstReplacingFullClassDecl.collectErrors(ErrorCheckType checkType) {
1084            if (!errorChecked) {
1085                  super.collectErrors(checkType);
1086          errorChecked = true;
1087                  getOriginalInstClass().collectErrors(checkType);
1088                }
1089        }
1090
1091        public void InstBuiltIn.collectErrors(ErrorCheckType checkType) {}
1092
1093        public void InstComponentRedeclare.collectErrors(ErrorCheckType checkType) {
1094                if (!errorChecked) {
1095                    super.collectErrors(checkType);
1096                    errorChecked = true;
1097                }
1098        }
1099       
1100        public void InstNode.checkRedeclares(ErrorCheckType checkType) {
1101            if (!errorChecked) {
1102                for (InstNode n : getInstComponentDecls()) 
1103                    n.checkRedeclares(checkType);
1104                for (InstNode n : getInstExtendss()) 
1105                    n.checkRedeclares(checkType);
1106              }
1107        }
1108
1109    public void InstReplacingRecord.checkRedeclares(ErrorCheckType checkType) {
1110        super.checkRedeclares(checkType);
1111        if (shouldCheckInto(checkType))
1112            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1113    }
1114
1115    public void InstReplacingComposite.checkRedeclares(ErrorCheckType checkType) {
1116        super.checkRedeclares(checkType);
1117        if (shouldCheckInto(checkType))
1118            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1119    }
1120
1121    public void InstReplacingPrimitive.checkRedeclares(ErrorCheckType checkType) {
1122        super.checkRedeclares(checkType);
1123        if (shouldCheckInto(checkType))
1124            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1125    }
1126
1127    public void InstReplacingExpandableConnectorDecl.checkRedeclares(ErrorCheckType checkType) {
1128        super.checkRedeclares(checkType);
1129        if (shouldCheckInto(checkType))
1130            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1131    }
1132
1133    public void InstComponentDecl.checkRedeclares(ErrorCheckType checkType) {
1134        if (!isDisabled() && hasInstModification()) {
1135            getInstModification().collectErrors(checkType);
1136        }
1137        if (shouldCheckInto(checkType)) {
1138            if (hasInstConstrainingComponent()) {
1139                getInstConstrainingComponent().collectErrors(checkType);
1140            }
1141            super.checkRedeclares(checkType);
1142        }
1143    }
1144
1145        public void InstClassRedeclare.collectErrors(ErrorCheckType checkType) {
1146                super.collectErrors(checkType);
1147//              if (!inInstComponent())
1148//                      getInstClassDecl().collectErrors(checkType);   
1149        }
1150
1151        public void InstValueModification.collectErrors(ErrorCheckType checkType) {
1152                getFExp().collectErrors(checkType);
1153        }
1154
1155    public void InstDot.collectErrors(ErrorCheckType checkType) {
1156        for (InstAccess ia : getInstAccesss()) {
1157            ia.collectErrors(checkType);
1158            if (ia.isUnknown() || !ia.myInstComponentDecl().shouldCheckInto(checkType))
1159                break;
1160        }
1161        allChecks(checkType);
1162    }
1163
1164        public void InstClassAccess.collectErrors(ErrorCheckType checkType) {
1165            nameCheck(checkType);
1166        }
1167
1168        public void InstComponentAccess.collectErrors(ErrorCheckType checkType) {
1169            super.collectErrors(checkType);
1170            if (!myInstComponentDecl().isUnknown() && !isModificationName())
1171                myInstComponentDecl().collectErrors(checkType);
1172        }
1173       
1174        public void InstComponentArrayAccess.collectErrors(ErrorCheckType checkType) {
1175            super.collectErrors(checkType);
1176            if (!myInstComponentDecl().isUnknown() && !isModificationName())
1177                myInstComponentDecl().collectErrors(checkType);
1178        }
1179       
1180        inh boolean InstAccess.isModificationName();
1181        eq InstNamedModification.getName().isModificationName() = true;
1182        eq BaseNode.getChild().isModificationName()             = false;
1183
1184        inh boolean FArraySubscripts.myAccessExists();
1185        eq Root.getChild().myAccessExists()       = false;
1186        eq InstAccess.getChild().myAccessExists() = !myInstComponentDecl().isUnknown();
1187       
1188        public void FArrayExpSubscripts.collectErrors(ErrorCheckType checkType) {
1189                // Should this check be in the access instead?
1190        int ndims = myDims();
1191                if (getNumFSubscript() > ndims && !isInstComponentSize() && myAccessExists()) {
1192                        // TODO: shouldn't this check for to few as well? (no [] or all dimensions given)
1193                        error("Too many array subscripts for access: " + getNumFSubscript() + 
1194                    " subscripts given, component has " + ndims + " dimensions");
1195                        allChecks(checkType);
1196                        for (int i = 0; i < ndims; i++)
1197                                getFSubscript(i).collectErrors(checkType);
1198                } else {
1199                super.collectErrors(checkType);
1200                }
1201        }
1202
1203    public void FArrayLitSubscripts.collectErrors(ErrorCheckType checkType) {
1204        /*
1205         * Does not need implementation since error checking is done in the instance tree,
1206         * and there should not be any FArrayLitSubscripts there.
1207         * The reason for that is information necessary for error printing (line and column number)
1208         * is not saved in FArrayLitSubscripts.
1209         */
1210        throw new UnsupportedOperationException();
1211    }
1212
1213    public void FBreakStmt.collectErrors(ErrorCheckType checkType) {
1214        if (enclosingLoop() == null)
1215            error("Break statement must be inside while- or for-statement");
1216    }
1217
1218        /**
1219         * Check if class has exactly one algorithm section or external function declaration.
1220         */
1221    public boolean InstClassDecl.isCompleteFunction() {
1222        return (numFAlgorithm() == 1);
1223    }
1224    syn boolean InstPartialFunction.isCompleteFunction() = true;
1225   
1226        syn boolean InstClassDecl.hasInstExternal() = false;
1227       
1228        syn int InstClassDecl.numInstExternal() {
1229                int n = hasInstExternal() ? 1 : 0;
1230                for (InstExtends ie : getInstExtendss())
1231                        n += ie.myInstClass().numInstExternal();
1232                return n;
1233        }
1234    eq InstSimpleShortClassDecl.numInstExternal() = actualInstClass().numInstExternal();
1235    eq InstLibNode.numInstExternal()              = actualInstClass().numInstExternal();
1236       
1237        syn int InstClassDecl.numFAlgorithm() {
1238                int n = 0;
1239                for (FAbstractEquation e : getFAbstractEquations())
1240                        if (e instanceof FAlgorithm)
1241                                n++;
1242                for (InstExtends ie : getInstExtendss())
1243                        n += ie.myInstClass().numFAlgorithm();
1244                return n;
1245        }
1246    eq InstSimpleShortClassDecl.numFAlgorithm() = actualInstClass().numFAlgorithm();
1247    eq InstLibNode.numFAlgorithm()              = actualInstClass().numFAlgorithm();
1248       
1249        public void InstExternalObject.collectErrors(ErrorCheckType checkType) {
1250            if (!errorChecked) {
1251                        super.collectErrors(checkType);
1252                errorChecked = true;
1253                if (!inFunction()) {
1254                if (!hasBindingExp() && !checkType.allowExternalObjectMissingBindingExpression()) {
1255                    ASTNode.EXTERNAL_OBJECT_MISSING_BINDING_EXPRESSION.invoke(this, name());
1256                }
1257                        getDestructorCall().collectErrors(checkType);
1258                        myInstClass().collectErrors(checkType);
1259                }
1260            }
1261        }
1262
1263    public static final SimpleProblemProducer ASTNode.EXTERNAL_OBJECT_MISSING_BINDING_EXPRESSION =
1264            new SimpleErrorProducer("EXTERNAL_OBJECT_MISSING_BINDING_EXPRESSION", ProblemKind.SEMANTIC,
1265                    "The external object '%s' does not have a binding expression");
1266
1267        public void InstForClauseE.collectErrors(ErrorCheckType checkType) {
1268            collectErrorsInFor(getFAbstractEquationList(), getInstForIndexList(), checkType);
1269        }
1270       
1271        public void InstForStmt.collectErrors(ErrorCheckType checkType) {
1272            collectErrorsInFor(getForStmtList(), getInstForIndexList(), checkType);
1273        }
1274       
1275        public void FIterExp.collectErrors(ErrorCheckType checkType) {
1276            collectErrorsInFor(getFExp(), getForIndexList(), checkType);
1277        }
1278
1279    public void ASTNode.collectErrorsInFor(
1280            ASTNode iterChild, List<? extends CommonForIndex> indices, ErrorCheckType checkType) {
1281        // Do the checks on the for itself
1282        allChecks(checkType);
1283       
1284        // Check everything except iterChild once
1285        for (ASTNode ch : this)
1286            if (ch != iterChild)
1287                ch.collectErrors(checkType);
1288       
1289        // Check array bounds in iterChild for each index combination
1290        try {
1291            indices.getChild(0).collectErrorsForAllIndices(indices, 0, iterChild, checkType);
1292        } catch (ConstantEvaluationException e) {
1293            // Do "general" check if we are unable to determine the indices.
1294            // Will probably give incorrect errors. However this is better than
1295            // passing along errors and then crash!
1296            iterChild.collectErrors(checkType);
1297        }
1298    }
1299
1300    /**
1301     * Collect errors in iterated node for all combinations of for indices.
1302     *
1303     * @param indices    the list of indices too loop for
1304     * @param i          next index to handle
1305     * @param child      the iterated node to check
1306     * @param checkType  type of check to perform
1307     */
1308    public void CommonForIndex.collectErrorsForAllIndices(
1309            List<? extends CommonForIndex> indices, int i, ASTNode child, ErrorCheckType checkType) {
1310        throw new UnsupportedOperationException();
1311    }
1312
1313    @Override
1314    public void InstForIndex.collectErrorsForAllIndices(
1315        List<? extends CommonForIndex> indices, int i, ASTNode child, ErrorCheckType checkType) {
1316        InstComponentDecl var = getInstPrimitive();
1317        CValue oldVal = var.evaluationValue;
1318        boolean last = i == indices.getNumChild() - 1;
1319        CommonForIndex next = last ? null : indices.getChild(i + 1);
1320        CValueArray ivals = getFExp().ceval().array();
1321
1322        if (ivals.hasUnknowns()) {
1323            return;
1324        }
1325
1326        for (Index j : ivals.indices()) {
1327            var.setLocalCachedEvaluationValue(ivals.getCell(j));
1328            if (last) {
1329                child.resetCollectErrors();
1330                child.collectErrors(checkType);
1331                child.flushAllRecursive();
1332            } else {
1333                next.collectErrorsForAllIndices(indices, i + 1, child, checkType);
1334            }
1335        }
1336        var.setLocalCachedEvaluationValue(oldVal);
1337    }
1338
1339    @Override
1340    public void InstForIndexNoExp.collectErrorsForAllIndices(
1341            List<? extends CommonForIndex> indices, int i, ASTNode child, ErrorCheckType checkType) {
1342        if (hasFExp()) {
1343            super.collectErrorsForAllIndices(indices, i, child, checkType);
1344        }
1345    }
1346
1347
1348    public void InstFunctionCall.collectErrors(ErrorCheckType checkType) {
1349        if (!errorChecked) {
1350            errorChecked = true;
1351            // Check that the function exists
1352            InstLookupResult<InstCallable> lp = getName().myInstLookupCallable();
1353            InstCallable func = getName().myInstCallable();
1354            String name = getName().name();
1355            if (lp.isError()) {
1356                // Function does not exist or can't be used due to constraining classes, or name refers to component
1357                getName().generateClassLookupProblems(lp, this);
1358            } else if (!func.isCallable()) {
1359                if (func.isExternalObject()) {
1360                    // Something is be wrong with constructor
1361                    name += ".constructor";
1362                    func = func.asInstClassDecl().myConstructor();
1363                    if (func.isUnknown()) {
1364                        // The constructor does not exist
1365                        error("Cannot find function declaration for " + name + "()");
1366                    }
1367                    // Constructor exists, but is not a function - delegate to default case
1368                }
1369                if (!func.isUnknown()) {
1370                    // Not a function
1371                    error("The class " + name + " is not a function");
1372                }
1373            } else if (!func.isRecord() && !func.isCompleteFunction()) {
1374                // TODO: add check if function is partial?
1375                if (!checkType.allowIncompleteReplaceableFunc() || !canBeReplacedForMe(func.asInstNode()))
1376                    error("Calling function " + getName().name() + 
1377                            "(): can only call functions that have one algorithm section or external function specification");
1378            } else {
1379                // Function exists, check everything
1380                errorChecked = false; // super also uses errorChecked
1381                super.collectErrors(checkType);
1382               
1383                // We need to check the function definition as well.
1384                func.collectErrors(checkType);
1385               
1386                // Check if there were any unbindable args
1387                boolean pos = true;
1388                String desc = functionCallDecription();
1389                for (InstFunctionArgument arg : unbindableArgs) 
1390                    pos = arg.generateUnbindableError(desc, pos);
1391            }
1392        }
1393    }
1394   
1395    public static final SimpleProblemProducer ASTNode.NEGATIVE_SIZE_FILL =
1396        new SimpleErrorProducer("NEGATIVE_SIZE_FILL", ProblemKind.SEMANTIC, "The dimension arguments of the fill()"
1397                + " operator may not be negative");
1398   
1399    public void FFillExp.collectErrors(ErrorCheckType checkType) {
1400        super.collectErrors(checkType);
1401        if (!lockBranch(checkType)) {
1402            for (FExp exp : getFExps()) {
1403                if (exp.variability().knownParameterOrLess()) {
1404                    try {
1405                        CValue val = exp.ceval();
1406                            if (val.hasIntValue()) {
1407                            int d = val.intValue();
1408                            if (d < 0) {
1409                                NEGATIVE_SIZE_FILL.invoke(exp);
1410                            }
1411                        }
1412                    } catch (ConstantEvaluationException e) {}
1413                }
1414            }
1415        }
1416    }
1417   
1418    /**
1419     * Can <code>node</code> be replaced as seen from here?
1420     */
1421    inh boolean ASTNode.canBeReplacedForMe(InstNode node);
1422    eq InstNode.getChild().canBeReplacedForMe(InstNode node) = node.canBeReplacedFor(this);
1423    eq Root.getChild().canBeReplacedForMe(InstNode node)     = false;
1424
1425    /**
1426     * Is this node or any node before the closest common ancestor with <code>source</code> replaceable?
1427     */
1428    syn boolean InstNode.canBeReplacedFor(InstNode source)        = canBeReplacedIn(commonAncestor(source));
1429    eq InstSimpleShortClassDecl.canBeReplacedFor(InstNode source) = 
1430        super.canBeReplacedFor(source) || myTargetInstClassDecl().canBeReplacedFor(source);
1431    eq InstShortClassDecl.canBeReplacedFor(InstNode source)       = 
1432        super.canBeReplacedFor(source) || myTargetInstClassDecl().canBeReplacedFor(source);
1433
1434    /**
1435     * Is this class or any class before <code>ancestor</code> replaceable?
1436     */
1437    syn boolean InstNode.canBeReplacedIn(InstNode ancestor) = isReplaceable() || canBeReplacedInHelper(ancestor);
1438
1439    /**
1440     * Helper method for {@link #canBeReplacedIn(InstNode)} - use it instead.
1441     */
1442    inh boolean InstNode.canBeReplacedInHelper(InstNode ancestor);
1443    eq InstNode.getChild().canBeReplacedInHelper(InstNode ancestor) = (ancestor != this) && canBeReplacedIn(ancestor);
1444    eq Root.getChild().canBeReplacedInHelper(InstNode ancestor)     = false;
1445
1446        public void FInfArgsFunctionCall.collectErrors(ErrorCheckType checkType) {
1447                super.collectErrors(checkType);
1448                if (unbindableArgs != null) {
1449                    boolean pos = true;
1450                    String desc = functionCallDecription();
1451                    for (InstFunctionArgument arg : unbindableArgs) 
1452                        pos = arg.generateUnbindableError(desc, pos);
1453                }
1454        }
1455       
1456        syn String FAbstractFunctionCall.functionCallDecription() = "Calling function " + name() + "()";
1457        eq FRecordConstructor.functionCallDecription()    = "Record constructor for " + name();
1458    eq InstRecordConstructor.functionCallDecription() = "Record constructor for " + name();
1459        eq InstFunctionCall.functionCallDecription()      = getName().myInstClassDecl().isRecord() ? 
1460                        "Record constructor for " + name() : super.functionCallDecription();
1461    eq InstPartialFunctionCall.functionCallDecription() = "Creating functional input argument " + name() + "()";
1462         
1463        public boolean InstFunctionArgument.generateUnbindableError(String desc, boolean genForPos) {
1464                return genForPos;
1465        }
1466         
1467        public boolean InstPositionalArgument.generateUnbindableError(String desc, boolean genForPos) {
1468                if (genForPos)
1469                        error(desc + ": too many positional arguments");
1470                return false;
1471        }
1472         
1473        public boolean InstNamedArgument.generateUnbindableError(String desc, boolean genForPos) {
1474                error(desc + ": no input matching named argument " + getName().name() + " found");
1475                return genForPos;
1476        }
1477       
1478        public void FBuiltInFunctionCall.collectErrors(ErrorCheckType checkType) {
1479            if (!errorChecked) {
1480                super.collectErrors(checkType);
1481                errorChecked = true;
1482                getOriginalArgs().collectErrors(checkType);
1483            }
1484        }
1485       
1486        public void FUnsupportedBuiltIn.collectErrors(ErrorCheckType checkType) {
1487                // Don't check arguments
1488                allChecks(checkType);
1489        }
1490 
1491        public void InstNamedArgument.collectErrors(ErrorCheckType checkType) {
1492                // TODO: This way, the FExp for each argument to a built-in function is checked twice - fix that
1493            if (!errorChecked) {
1494                        allChecks(checkType);
1495                        getFExp().collectErrors(checkType);
1496            }
1497        }
1498
1499    /**
1500     * Check if this node is in a recursive structure.
1501     */
1502    syn boolean InstNode       .isRecursed() = false;
1503    eq InstComponentDecl       .isRecursed() = !isPrimitive() && isWithin(myInstClass());
1504    eq InstExtends             .isRecursed() = isWithin(myInstClass());
1505    eq InstSimpleShortClassDecl.isRecursed() = isWithin(actualInstClass());
1506    eq InstArrayComponentDecl  .isRecursed() = instComponentDecl().isRecursed();
1507
1508        // TODO: check if we realy need this in addition to isRecursed()
1509        /**
1510         * Check if extends tree is recursive.
1511         */
1512        public boolean InstExtends.isRecursive() {
1513                if (recursiveCache == RECURSIVE_UNKNOWN)
1514                        calcIsRecursive(new HashSet<InstNode>());
1515                return recursiveCache == RECURSIVE_YES;
1516        }
1517
1518    /**
1519     * Check if extends tree is recursive.
1520     */
1521    public boolean InstSimpleShortClassDecl.isRecursive() {
1522        if (recursiveCache == RECURSIVE_UNKNOWN)
1523            calcIsRecursive(new HashSet<InstNode>());
1524        return recursiveCache == RECURSIVE_YES;
1525    }
1526
1527    /**
1528     * Check if extends tree is recursive.
1529     */
1530    public boolean InstLibNode.isRecursive() {
1531        if (recursiveCache == RECURSIVE_UNKNOWN)
1532            calcIsRecursive(new HashSet<InstNode>());
1533        return recursiveCache == RECURSIVE_YES;
1534    }
1535
1536        /**
1537         * Examine extends tree to find recursive extends nodes.
1538         */
1539        public void InstExtends.calcIsRecursive(HashSet<InstNode> visited) {
1540                recursiveCache = visited.contains(this) ? RECURSIVE_YES : RECURSIVE_NO;
1541                visited.add(this);
1542                if (recursiveCache == RECURSIVE_NO) 
1543                        myInstClass().calcIsRecursive(visited);
1544        }
1545       
1546        /**
1547         * Examine extends tree to find recursive extends nodes.
1548         */
1549        public void InstClassDecl.calcIsRecursive(HashSet<InstNode> visited) {
1550                for (InstExtends ie : getInstExtendss())
1551                        ie.calcIsRecursive(visited);
1552        }
1553
1554    public void InstSimpleShortClassDecl.calcIsRecursive(HashSet<InstNode> visited) {
1555        recursiveCache = visited.contains(this) ? RECURSIVE_YES : RECURSIVE_NO;
1556        visited.add(this);
1557        if (recursiveCache == RECURSIVE_NO) 
1558            myTargetInstClassDecl().calcIsRecursive(visited);
1559        // Can't use actualInstClass() here, since it uses isRecursive()
1560    }
1561
1562    public void InstLibNode.calcIsRecursive(HashSet<InstNode> visited) {
1563        recursiveCache = visited.contains(this) ? RECURSIVE_YES : RECURSIVE_NO;
1564        visited.add(this);
1565        if (recursiveCache == RECURSIVE_NO) 
1566            resolveLib().calcIsRecursive(visited);
1567        // Can't use actualInstClass() here, since it uses isRecursive()
1568    }
1569
1570    private byte InstExtends.recursiveCache              = RECURSIVE_UNKNOWN;
1571    private byte InstSimpleShortClassDecl.recursiveCache = RECURSIVE_UNKNOWN;
1572    private byte InstLibNode.recursiveCache              = RECURSIVE_UNKNOWN;
1573    protected static final byte InstNode.RECURSIVE_UNKNOWN = 0;
1574    protected static final byte InstNode.RECURSIVE_YES     = 1;
1575    protected static final byte InstNode.RECURSIVE_NO      = 2;
1576
1577    /**
1578     * Check if <code>icd</code> is an ancestor of this node or any ancestor is an
1579     *        instance of <code>icd</code>.
1580     */
1581    inh boolean InstComponentDecl.isWithin(InstClassDecl icd);
1582    inh boolean InstExtends      .isWithin(InstClassDecl icd);
1583    inh boolean InstClassDecl    .isWithin(InstClassDecl icd);
1584    eq InstNode         .getChild().isWithin(InstClassDecl icd) = isOfInstClassDecl(icd);
1585    eq InstComponentDecl.getChild().isWithin(InstClassDecl icd) = isOfInstClassDecl(icd) || isWithin(icd);
1586    eq InstClassDecl    .getChild().isWithin(InstClassDecl icd) = icd == this || (!isFunction() && isWithin(icd));
1587    eq InstExtends      .getChild().isWithin(InstClassDecl icd) = isOfInstClassDecl(icd) || isWithin(icd);
1588    eq Root             .getChild().isWithin(InstClassDecl icd) = false;
1589
1590        /**
1591         * Check if this node is equal to or an instance of <code>icd</code>.
1592         */
1593        syn boolean InstNode.isOfInstClassDecl(InstClassDecl icd) = false;
1594        eq InstClassDecl.isOfInstClassDecl(InstClassDecl icd)     = icd == this;
1595        eq InstComponentDecl.isOfInstClassDecl(InstClassDecl icd) = icd == myInstClass() && !icd.isUnknown();
1596        eq InstExtends.isOfInstClassDecl(InstClassDecl icd)       = icd == myInstClass() && !icd.isUnknown();
1597       
1598}
1599
1600aspect AssertEval {
1601
1602    public class FClass {
1603        /**
1604         * Evaluate asserts with constant tests, generate errors for failing ones,
1605         * and eliminate all evaluated assets.
1606         */
1607        public class evaluateAsserts extends Transformation {
1608            public void perform() {
1609                List<FAbstractEquation> l = new List<FAbstractEquation>();
1610                for (FAbstractEquation eqn : getFAbstractEquations()) {
1611                    if (!eqn.evaluateAsserts(true)) {
1612                        l.add(eqn);
1613                    }
1614                }
1615                setFAbstractEquationList(l);
1616            }
1617        }
1618    }
1619   
1620    /**
1621     * Try to evaluate asserts with constant tests in this equation.
1622     *
1623     * If this equation should be removed, returns true.
1624     */
1625    public boolean FAbstractEquation.evaluateAsserts(boolean inClause) {
1626        return false;
1627    }
1628   
1629    @Override
1630    public boolean FFunctionCallEquation.evaluateAsserts(boolean inClause) {
1631        return getCall().evaluateAsserts(inClause);
1632    }
1633   
1634    @Override
1635    public boolean FIfWhenEquation.evaluateAsserts(boolean inClause) {
1636        boolean res = super.evaluateAsserts(inClause);
1637        if (hasElse())
1638            res = res && getElse().evaluateAsserts(inClause);
1639        return res;
1640    }
1641   
1642    @Override
1643    public boolean FIfWhenElseEquation.evaluateAsserts(boolean inClause) {
1644        boolean res = true;
1645        List<FAbstractEquation> l = new List<FAbstractEquation>();
1646        for (FAbstractEquation eqn : getFAbstractEquations()) {
1647            if (!eqn.evaluateAsserts(false)) {
1648                l.add(eqn);
1649                res = false;
1650            }
1651        }
1652        setFAbstractEquationList(l);
1653        return res;
1654    }
1655   
1656    /**
1657     * If this call is an assert with constant test, try to evaluate it.
1658     *
1659     * @return  <code>true</code> if this is an assert that should be removed
1660     */
1661    public boolean FAbstractFunctionCall.evaluateAsserts(boolean error) {
1662        return false;
1663    }
1664   
1665    private static final String FAssert.LEVEL_ERROR = "error";
1666   
1667    public boolean FAssert.evaluateAsserts(boolean error) {
1668        boolean remove = false;
1669        if (getTest().isConstantExp()) {
1670            try {
1671                CValue cVal = getTest().ceval();
1672                if (cVal.hasBooleanValue()) {
1673                    boolean val = cVal.booleanValue();
1674                    if (error && !val) {
1675                        CValue cMsg = getMsg().ceval();
1676                        CValue cLevel = hasLevel() ? getLevel().ceval() : null;
1677                        if (cMsg.hasStringValue() && (cLevel == null || cLevel.hasStringValue())) {
1678                            String msg = "Assertion failed: " + cMsg.stringValue();
1679                            if (cLevel == null || cLevel.stringValue().equals(LEVEL_ERROR)) {
1680                                error(msg);
1681                            } else {
1682                                warning(msg);
1683                            }
1684                        }
1685                    }
1686                    remove = val || error;
1687                }
1688            } catch (ConstantEvaluationException e) {}
1689        }
1690        return remove;
1691    }
1692   
1693}
1694
1695aspect ScalarizationErrorCheck {
1696   
1697    /**
1698     * Check errors that can only be detected after scalarization.
1699     */
1700    public void FClass.scalarizationErrorCheck() {
1701        checkMultipleReinit();
1702        breakOnErrors();
1703    }
1704   
1705    /**
1706     * Check for variables with multiple reinits.
1707     */
1708    public void FClass.checkMultipleReinit() {
1709        Map<FAbstractVariable, Set<FReinit>> reinitMap = collectReinits();
1710        for (FAbstractVariable fv : reinitMap.keySet()) {
1711            if (!fv.isUnknown()) {
1712                Set<FWhenEquation> whens = new HashSet<FWhenEquation>();
1713                Set<FReinit> reinits = reinitMap.get(fv);
1714                for (FReinit r : reinits) {
1715                    FWhenEquation w = r.myWhen();
1716                    if (!whens.contains(w))
1717                        whens.add(w);
1718                }
1719                if (whens.size() > 1) {
1720                    StringBuilder buf = new StringBuilder();
1721                    buf.append("The variable ");
1722                    buf.append(fv.name());
1723                    buf.append(" is assigned in reinit() clauses in more than one when clause:\n");
1724                    for (FReinit r : reinits) {
1725                        buf.append("    ");
1726                        buf.append(r);
1727                        buf.append(";\n");
1728                    }
1729                    fv.error(buf.toString());
1730                }
1731            }
1732        }
1733    }
1734   
1735    /**
1736     * Find all reinits and map them to the assigned variable.
1737     */
1738    syn boolean FVariable.isReinit() = myFClass().collectReinits().keySet().contains(this);
1739   
1740    syn lazy Map<FAbstractVariable,Set<FReinit>> FClass.collectReinits() {
1741        Map<FAbstractVariable,Set<FReinit>> reinitMap = new HashMap<FAbstractVariable,Set<FReinit>>();
1742        for (FAbstractEquation e : getFAbstractEquations())
1743            e.collectReinits(reinitMap);
1744        return reinitMap;
1745    }
1746   
1747    public void FAbstractEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {}
1748   
1749    public void FIfWhenEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1750        if (isWhen()) {
1751                super.collectReinits(map);
1752                if (hasElse())
1753                    getElse().collectReinits(map);
1754        }
1755    }
1756   
1757    public void FIfWhenElseEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1758        for (FAbstractEquation e : getFAbstractEquations())
1759            e.collectReinits(map);
1760    }
1761   
1762    public void FFunctionCallEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1763        getCall().collectReinits(map);
1764    }
1765   
1766    public void FExp.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {}
1767   
1768    public void FReinit.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1769        Set<FReinit> set = map.get(myFV());
1770        if (set == null) {
1771            set = new LinkedHashSet<FReinit>();
1772            map.put(myFV(), set);
1773        }
1774        set.add(this);
1775    }
1776   
1777    /**
1778     * Get the when-equation that this reinit() belongs to.
1779     *
1780     * Gets the top when in a when-elsewhen-else.
1781     * Not valid after when-equations have been converted to if-equations.
1782     */
1783    inh FWhenEquation FReinit.myWhen();
1784    eq FWhenEquation.getChild().myWhen() = findTopWhen(this);
1785    eq FClass.getChild().myWhen()        = null;
1786    eq InstNode.getChild().myWhen()      = null;
1787   
1788    /**
1789     * Find the top when-equation in a when-elsewhen-else equation.
1790     *
1791     * @param last  will be returned if parent is not a when equation
1792     */
1793    inh FWhenEquation FWhenEquation.findTopWhen(FWhenEquation last);
1794    eq FWhenEquation.getChild().findTopWhen(FWhenEquation last) = findTopWhen(this);
1795    eq FClass.getChild().findTopWhen(FWhenEquation last)        = last;
1796    eq InstNode.getChild().findTopWhen(FWhenEquation last)      = last;
1797   
1798}
Note: See TracBrowser for help on using the repository browser.