source: branches/dev-mo-2617/Compiler/ModelicaFrontEnd/src/jastadd/errorcheck/ErrorCheck.jrag @ 13911

Last change on this file since 13911 was 13911, checked in by molsson, 7 weeks ago

#5843 Conditional attribute requires variability fixed parameter or less.

File size: 69.6 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                    FExp cond = getConditionalAttribute();
856                    cond.collectErrors(checkType);
857                    if (!cond.type().isUnknown()) {
858                        if (!cond.type().isScalar()) {
859                            NON_SCALAR_CONDITIONAL_GUARD.invoke(this);
860                        }
861                        if (!cond.type().isBoolean()) {
862                            NON_BOOLEAN_CONDITIONAL_GUARD.invoke(this);
863                        }
864                        if (!cond.variability().fixedParameterOrLess()) {
865                            NON_FIXED_CONDITIONAL_GUARD.invoke(this);
866                        } else {
867                            cond.markAsStructuralParameter(checkType);
868                        }
869                    }
870                }
871                if (!isDisabled() && hasInstModification()) {
872                    getInstModification().collectErrors(checkType);
873                }
874                for (InstModification im : getMergedEnvironment()) {
875                    im.checkModificationNames(checkType);
876                }
877               
878                if (shouldCheckInto(checkType)) {
879                    errorChecked = false;
880                    super.collectErrors(checkType);
881                    if (hasInstConstrainingComponent()) {
882                        getInstConstrainingComponent().collectErrors(checkType);
883                    }
884                }
885            }
886        }
887    }
888
889    /**
890     * Check that the restriction of this class is fulfilled.
891     */
892    public void InstClassDecl.checkRestriction(ErrorCheckType checkType) {}
893    public void InstBaseClassDecl.checkRestriction(ErrorCheckType checkType) {
894        if (!errorChecked)
895            getInstRestriction().collectErrors(checkType);
896    }
897
898    public void InstModification.checkModificationNames(ErrorCheckType checkType) {}
899   
900    public void InstElementModification.checkModificationNames(ErrorCheckType checkType) {
901        getName().allChecks(checkType);
902    }
903       
904        syn boolean InstComponentDecl.shouldCheckInto(ErrorCheckType checkType) =
905                !isDisabled() || checkType.checkInactiveComponents() || myOptions().getBooleanOption("check_inactive_contitionals");
906
907        public void InstComponentDecl.collectErrorsInClassName(ErrorCheckType checkType) {
908                getClassName().collectErrors(checkType);
909        }       
910       
911        public void InstArrayComponentDecl.collectErrorsInClassName(ErrorCheckType checkType) {
912                // TODO: use correct class name instead of "ArrayDecl" so that name lookup suceeds instead?
913                //       need that for other things as well, but is there problems with it?
914        }       
915       
916        public void FExp.checkConstantExpression(ErrorCheckType checkType, String varKind, String varName) {
917                boolean failed = false;
918        ConstantEvaluationException eres = null;
919                try {
920                        if (isCircular()) {
921                            if (!checkType.allowConstantNoValue())
922                                error("Could not evaluate binding expression for %s '%s' due to circularity: '%s'", 
923                                                varKind, varName, prettyPrint(""));
924                        } else {
925                                CValue val = ceval();
926                                if (val.isPartlyUnknown()) {
927                                        if (val.isUnsupported()) {
928                                                compliance("Constant evaluation not supported for expression(s) directly or indirectly " + 
929                                                                "used by the binding expression for %s '%s': '%s'", varKind, varName, prettyPrint(""));
930                                        } else {
931                                                failed = true;
932                                        }
933                                }
934                        }
935                } catch (ConstantEvaluationNotReadyException e) {
936                        // Will be evaluatable later, ignore for now
937                } catch (ConstantEvaluationException e) {
938                        failed = true;
939                        eres = e;
940                }
941                if (failed && !checkType.allowConstantNoValue()) {
942                        error("Could not evaluate binding expression for %s '%s': '%s'%s", varKind, varName, prettyPrint(""),
943                                eres == null ? "" : eres.getModelicaStackTrace());
944                }
945        }
946
947    public static final SimpleProblemProducer ASTNode.PARAMETER_MISSING_BINDING_EXPRESSION =
948            new SimpleWarningProducer("PARAMETER_MISSING_BINDING_EXPRESSION", ProblemKind.SEMANTIC, "The parameter %s does not have a binding expression");
949    public static final SimpleProblemProducer ASTNode.CONSTANT_MISSING_BINDING_EXPRESSION =
950            new SimpleWarningProducer("CONSTANT_MISSING_BINDING_EXPRESSION", ProblemKind.SEMANTIC, "The constant %s does not have a binding expression");
951   
952    public void InstAssignable.collectErrors(ErrorCheckType checkType) {
953        //log.debug(toString());
954        if (!errorChecked) {
955            super.collectErrors(checkType);
956            errorChecked = true;
957           
958            // Check modification
959            if (hasInstValueMod()) {
960                myInstValueMod().collectErrors(checkType);
961            }
962           
963            if (hasBindingFExp()) {
964                // Check if the binding expression of constants can be evaluated
965                FExp bexp = getBindingFExp();
966                if (isConstant() && !bexp.type().isUnknown()) {
967                    bexp.checkConstantExpression(checkType, "constant", qualifiedName());
968                } else if (isParameter() && bexp.isCircular()) {
969                    bexp.error("Circularity in binding expression of parameter: %s = %s", qualifiedName(), bexp);
970                }
971            } else {
972                // Warn if constant or parameter does not have a binding expression (start is used)
973                if (variability().fixedParameterOrLess() && !isForIndex() && !isRecord() && 
974                        !hasParentRecordWithBindingExp() && !inFunction()) {
975                    if (isParameter()) {
976                        PARAMETER_MISSING_BINDING_EXPRESSION.invoke(this, qualifiedName());
977                    } else {
978                        CONSTANT_MISSING_BINDING_EXPRESSION.invoke(this, qualifiedName());
979                    }
980                }
981            }
982           
983            TypePrefixVariability v = variability().combine();
984            // Mark parameters with Evaluate=true as structural
985            if (v.parameterVariability() && isEvalAnnotated(true)) {
986                if (v.fixedParameterOrLess()) {
987                    forceVariability(checkType, Variability.STRUCTPARAMETER);
988                } else {
989                    warning("Evaluate annotation is ignored for parameters with fixed=false");
990                }
991            }
992           
993            // Check array indices
994            getClassName().collectErrors(checkType);
995            getLocalFArraySubscriptsOpt().collectErrors(checkType);
996           
997            // Check attributes for primitive variables
998            checkAttributes(checkType);
999        }
1000    }
1001   
1002        public void InstAssignable.checkAttributes(ErrorCheckType checkType) {}
1003
1004    public static final SimpleProblemProducer ASTNode.START_VALUE_NOT_PARAMETER =
1005            new SimpleErrorProducer("START_VALUE_NOT_PARAMETER", ProblemKind.SEMANTIC,
1006                    "Variability of binding expression for attribute '%s' is not less"
1007                    + " than or equal to parameter variability: %s");
1008    public static final SimpleProblemProducer ASTNode.START_VALUE_INITIAL_PARAMETER =
1009            new SimpleWarningProducer("START_VALUE_INITIAL_PARAMETER", ProblemKind.COMPLIANCE,
1010                    "Variability of binding expression for attribute '%s' is "
1011                    + " initial parameter variability: %s");
1012
1013        public void InstPrimitive.checkAttributes(ErrorCheckType checkType) {
1014                // Check if the expressions of the attributes can be evaluated
1015                // Note that this check has to be done locally in the
1016                // context of an InstAssignable node in order to avoid
1017                // evaluation of all value modifications also for non
1018                // parameters.
1019                for (InstModification im : totalMergedEnvironment()) {
1020                        // Only check attributes, value modifications are checked above
1021                        if (im instanceof InstComponentModification) {
1022                                InstComponentModification icm = (InstComponentModification)im;
1023                                if (icm.hasInstModification() && icm.getInstModification().hasInstValueMod()) {
1024                                        FExp val_mod = icm.getInstModification().instValueMod();
1025                    if (val_mod.variability().lessOrEqual(Variability.CONSTANT)) {
1026                        val_mod.checkConstantExpression(checkType, "attribute", icm.getName().name());
1027                    } else if (!val_mod.variability().parameterOrLess()) {
1028                        ASTNode.START_VALUE_NOT_PARAMETER.invoke(val_mod, icm.getName().name(), val_mod);
1029                    }
1030                                }
1031                        }
1032                }
1033        }
1034
1035       
1036    public static final SimpleProblemProducer ASTNode.UNSPECIFIED_ENUM_COMPONENT =
1037            new SimpleErrorProducer("UNSPECIFIED_ENUM_COMPONENT", ProblemKind.SEMANTIC,
1038                    "Components of unspecified enumerations are not allowed in simulation models:\n %s");
1039
1040        syn boolean InstNode.isUnspecifiedEnum() = false;
1041        eq InstPrimitive.isUnspecifiedEnum() = myInstClass().isUnspecifiedEnum();
1042        eq InstClassDecl.isUnspecifiedEnum() = enumLiterals().isEmpty();
1043
1044        public void InstExtends.collectErrors(ErrorCheckType checkType) {
1045            if (!errorChecked) {
1046                if (checkType.checkForRecursiveStructure() && isRecursed()) {
1047                        error("Recursive class structure");
1048                        errorChecked = true;
1049                } else {
1050                                super.collectErrors(checkType);
1051                        errorChecked = true;
1052                                getClassName().collectErrors(checkType);
1053                                if (hasInstClassModification() && shouldCheckModification())
1054                                        getInstClassModification().collectErrors(checkType);
1055                        }
1056            }
1057        }
1058       
1059        // Normally the class modifications in an InstExtendsShortClass
1060        // does not need to be checked, since they are checked in InstShortClassDecl.
1061        // This is not the case if the short class decl references
1062        // an primitive variable, however, and in this case the
1063        // class modification needs to be checked for errors.
1064        syn boolean InstExtends.shouldCheckModification()           = true;
1065        eq InstExtendsShortClass.shouldCheckModification()          = extendsPrimitive();
1066        eq InstReplacingExtendsShortClass.shouldCheckModification() = extendsPrimitive();
1067
1068    public void InstShortClassDecl.collectErrors(ErrorCheckType checkType) {
1069        if (!errorChecked) {
1070          super.collectErrors(checkType);
1071          errorChecked = true;
1072          // The localInstModifications should only be checked if
1073          // the node is not a InstReplacingShortClassDecl. This
1074          // is accomplished by the method collectInstModificationErrors.
1075          collectInstModificationErrors(checkType);
1076          if (hasInstConstrainingClass()) {
1077                getInstConstrainingClass().collectErrors(checkType);
1078          }
1079        }
1080    }
1081
1082        public void InstShortClassDecl.collectInstModificationErrors(ErrorCheckType checkType) {
1083                for (InstModification mod : localInstModifications())
1084                        mod.collectErrors(checkType);
1085    }
1086    public void InstReplacingShortClassDecl.collectInstModificationErrors(ErrorCheckType checkType) { }
1087
1088        public void InstReplacingShortClassDecl.collectErrors(ErrorCheckType checkType) {
1089            if (!errorChecked) {
1090                  super.collectErrors(checkType);
1091          errorChecked = true;
1092                  getOriginalInstClass().collectErrors(checkType);
1093                }
1094               
1095        }
1096
1097        public void InstReplacingFullClassDecl.collectErrors(ErrorCheckType checkType) {
1098            if (!errorChecked) {
1099                  super.collectErrors(checkType);
1100          errorChecked = true;
1101                  getOriginalInstClass().collectErrors(checkType);
1102                }
1103        }
1104
1105        public void InstBuiltIn.collectErrors(ErrorCheckType checkType) {}
1106
1107        public void InstComponentRedeclare.collectErrors(ErrorCheckType checkType) {
1108                if (!errorChecked) {
1109                    super.collectErrors(checkType);
1110                    errorChecked = true;
1111                }
1112        }
1113       
1114        public void InstNode.checkRedeclares(ErrorCheckType checkType) {
1115            if (!errorChecked) {
1116                for (InstNode n : getInstComponentDecls()) 
1117                    n.checkRedeclares(checkType);
1118                for (InstNode n : getInstExtendss()) 
1119                    n.checkRedeclares(checkType);
1120              }
1121        }
1122
1123    public void InstReplacingRecord.checkRedeclares(ErrorCheckType checkType) {
1124        super.checkRedeclares(checkType);
1125        if (shouldCheckInto(checkType))
1126            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1127    }
1128
1129    public void InstReplacingComposite.checkRedeclares(ErrorCheckType checkType) {
1130        super.checkRedeclares(checkType);
1131        if (shouldCheckInto(checkType))
1132            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1133    }
1134
1135    public void InstReplacingPrimitive.checkRedeclares(ErrorCheckType checkType) {
1136        super.checkRedeclares(checkType);
1137        if (shouldCheckInto(checkType))
1138            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1139    }
1140
1141    public void InstReplacingExpandableConnectorDecl.checkRedeclares(ErrorCheckType checkType) {
1142        super.checkRedeclares(checkType);
1143        if (shouldCheckInto(checkType))
1144            typeCheckReplacingComponent(getOriginalInstComponent(), checkType);
1145    }
1146
1147    public void InstComponentDecl.checkRedeclares(ErrorCheckType checkType) {
1148        if (!isDisabled() && hasInstModification()) {
1149            getInstModification().collectErrors(checkType);
1150        }
1151        if (shouldCheckInto(checkType)) {
1152            if (hasInstConstrainingComponent()) {
1153                getInstConstrainingComponent().collectErrors(checkType);
1154            }
1155            super.checkRedeclares(checkType);
1156        }
1157    }
1158
1159        public void InstClassRedeclare.collectErrors(ErrorCheckType checkType) {
1160                super.collectErrors(checkType);
1161//              if (!inInstComponent())
1162//                      getInstClassDecl().collectErrors(checkType);   
1163        }
1164
1165        public void InstValueModification.collectErrors(ErrorCheckType checkType) {
1166                getFExp().collectErrors(checkType);
1167        }
1168
1169    public void InstDot.collectErrors(ErrorCheckType checkType) {
1170        for (InstAccess ia : getInstAccesss()) {
1171            ia.collectErrors(checkType);
1172            if (ia.isUnknown() || !ia.myInstComponentDecl().shouldCheckInto(checkType))
1173                break;
1174        }
1175        allChecks(checkType);
1176    }
1177
1178        public void InstClassAccess.collectErrors(ErrorCheckType checkType) {
1179            nameCheck(checkType);
1180        }
1181
1182        public void InstComponentAccess.collectErrors(ErrorCheckType checkType) {
1183            super.collectErrors(checkType);
1184            if (!myInstComponentDecl().isUnknown() && !isModificationName())
1185                myInstComponentDecl().collectErrors(checkType);
1186        }
1187       
1188        public void InstComponentArrayAccess.collectErrors(ErrorCheckType checkType) {
1189            super.collectErrors(checkType);
1190            if (!myInstComponentDecl().isUnknown() && !isModificationName())
1191                myInstComponentDecl().collectErrors(checkType);
1192        }
1193       
1194        inh boolean InstAccess.isModificationName();
1195        eq InstNamedModification.getName().isModificationName() = true;
1196        eq BaseNode.getChild().isModificationName()             = false;
1197
1198        inh boolean FArraySubscripts.myAccessExists();
1199        eq Root.getChild().myAccessExists()       = false;
1200        eq InstAccess.getChild().myAccessExists() = !myInstComponentDecl().isUnknown();
1201       
1202        public void FArrayExpSubscripts.collectErrors(ErrorCheckType checkType) {
1203                // Should this check be in the access instead?
1204        int ndims = myDims();
1205                if (getNumFSubscript() > ndims && !isInstComponentSize() && myAccessExists()) {
1206                        // TODO: shouldn't this check for to few as well? (no [] or all dimensions given)
1207                        error("Too many array subscripts for access: " + getNumFSubscript() + 
1208                    " subscripts given, component has " + ndims + " dimensions");
1209                        allChecks(checkType);
1210                        for (int i = 0; i < ndims; i++)
1211                                getFSubscript(i).collectErrors(checkType);
1212                } else {
1213                super.collectErrors(checkType);
1214                }
1215        }
1216
1217    public void FArrayLitSubscripts.collectErrors(ErrorCheckType checkType) {
1218        /*
1219         * Does not need implementation since error checking is done in the instance tree,
1220         * and there should not be any FArrayLitSubscripts there.
1221         * The reason for that is information necessary for error printing (line and column number)
1222         * is not saved in FArrayLitSubscripts.
1223         */
1224        throw new UnsupportedOperationException();
1225    }
1226
1227    public void FBreakStmt.collectErrors(ErrorCheckType checkType) {
1228        if (enclosingLoop() == null)
1229            error("Break statement must be inside while- or for-statement");
1230    }
1231
1232        /**
1233         * Check if class has exactly one algorithm section or external function declaration.
1234         */
1235    public boolean InstClassDecl.isCompleteFunction() {
1236        return (numFAlgorithm() == 1);
1237    }
1238    syn boolean InstPartialFunction.isCompleteFunction() = true;
1239   
1240        syn boolean InstClassDecl.hasInstExternal() = false;
1241       
1242        syn int InstClassDecl.numInstExternal() {
1243                int n = hasInstExternal() ? 1 : 0;
1244                for (InstExtends ie : getInstExtendss())
1245                        n += ie.myInstClass().numInstExternal();
1246                return n;
1247        }
1248    eq InstSimpleShortClassDecl.numInstExternal() = actualInstClass().numInstExternal();
1249    eq InstLibNode.numInstExternal()              = actualInstClass().numInstExternal();
1250       
1251        syn int InstClassDecl.numFAlgorithm() {
1252                int n = 0;
1253                for (FAbstractEquation e : getFAbstractEquations())
1254                        if (e instanceof FAlgorithm)
1255                                n++;
1256                for (InstExtends ie : getInstExtendss())
1257                        n += ie.myInstClass().numFAlgorithm();
1258                return n;
1259        }
1260    eq InstSimpleShortClassDecl.numFAlgorithm() = actualInstClass().numFAlgorithm();
1261    eq InstLibNode.numFAlgorithm()              = actualInstClass().numFAlgorithm();
1262       
1263        public void InstExternalObject.collectErrors(ErrorCheckType checkType) {
1264            if (!errorChecked) {
1265                        super.collectErrors(checkType);
1266                errorChecked = true;
1267                if (!inFunction()) {
1268                if (!hasBindingExp() && !checkType.allowExternalObjectMissingBindingExpression()) {
1269                    ASTNode.EXTERNAL_OBJECT_MISSING_BINDING_EXPRESSION.invoke(this, name());
1270                }
1271                        getDestructorCall().collectErrors(checkType);
1272                        myInstClass().collectErrors(checkType);
1273                }
1274            }
1275        }
1276
1277    public static final SimpleProblemProducer ASTNode.EXTERNAL_OBJECT_MISSING_BINDING_EXPRESSION =
1278            new SimpleErrorProducer("EXTERNAL_OBJECT_MISSING_BINDING_EXPRESSION", ProblemKind.SEMANTIC,
1279                    "The external object '%s' does not have a binding expression");
1280
1281        public void InstForClauseE.collectErrors(ErrorCheckType checkType) {
1282            collectErrorsInFor(getFAbstractEquationList(), getInstForIndexList(), checkType);
1283        }
1284       
1285        public void InstForStmt.collectErrors(ErrorCheckType checkType) {
1286            collectErrorsInFor(getForStmtList(), getInstForIndexList(), checkType);
1287        }
1288       
1289        public void FIterExp.collectErrors(ErrorCheckType checkType) {
1290            collectErrorsInFor(getFExp(), getForIndexList(), checkType);
1291        }
1292
1293    public void ASTNode.collectErrorsInFor(
1294            ASTNode iterChild, List<? extends CommonForIndex> indices, ErrorCheckType checkType) {
1295        // Do the checks on the for itself
1296        allChecks(checkType);
1297       
1298        // Check everything except iterChild once
1299        for (ASTNode ch : this)
1300            if (ch != iterChild)
1301                ch.collectErrors(checkType);
1302       
1303        // Check array bounds in iterChild for each index combination
1304        try {
1305            indices.getChild(0).collectErrorsForAllIndices(indices, 0, iterChild, checkType);
1306        } catch (ConstantEvaluationException e) {
1307            // Do "general" check if we are unable to determine the indices.
1308            // Will probably give incorrect errors. However this is better than
1309            // passing along errors and then crash!
1310            iterChild.collectErrors(checkType);
1311        }
1312    }
1313
1314    /**
1315     * Collect errors in iterated node for all combinations of for indices.
1316     *
1317     * @param indices    the list of indices too loop for
1318     * @param i          next index to handle
1319     * @param child      the iterated node to check
1320     * @param checkType  type of check to perform
1321     */
1322    public void CommonForIndex.collectErrorsForAllIndices(
1323            List<? extends CommonForIndex> indices, int i, ASTNode child, ErrorCheckType checkType) {
1324        throw new UnsupportedOperationException();
1325    }
1326
1327    @Override
1328    public void InstForIndex.collectErrorsForAllIndices(
1329        List<? extends CommonForIndex> indices, int i, ASTNode child, ErrorCheckType checkType) {
1330        InstComponentDecl var = getInstPrimitive();
1331        CValue oldVal = var.evaluationValue;
1332        boolean last = i == indices.getNumChild() - 1;
1333        CommonForIndex next = last ? null : indices.getChild(i + 1);
1334        CValueArray ivals = getFExp().ceval().array();
1335
1336        if (ivals.hasUnknowns()) {
1337            return;
1338        }
1339
1340        for (Index j : ivals.indices()) {
1341            var.setLocalCachedEvaluationValue(ivals.getCell(j));
1342            if (last) {
1343                child.resetCollectErrors();
1344                child.collectErrors(checkType);
1345                child.flushAllRecursive();
1346            } else {
1347                next.collectErrorsForAllIndices(indices, i + 1, child, checkType);
1348            }
1349        }
1350        var.setLocalCachedEvaluationValue(oldVal);
1351    }
1352
1353    @Override
1354    public void InstForIndexNoExp.collectErrorsForAllIndices(
1355            List<? extends CommonForIndex> indices, int i, ASTNode child, ErrorCheckType checkType) {
1356        if (hasFExp()) {
1357            super.collectErrorsForAllIndices(indices, i, child, checkType);
1358        }
1359    }
1360
1361
1362    public void InstFunctionCall.collectErrors(ErrorCheckType checkType) {
1363        if (!errorChecked) {
1364            errorChecked = true;
1365            // Check that the function exists
1366            InstLookupResult<InstCallable> lp = getName().myInstLookupCallable();
1367            InstCallable func = getName().myInstCallable();
1368            String name = getName().name();
1369            if (lp.isError()) {
1370                // Function does not exist or can't be used due to constraining classes, or name refers to component
1371                getName().generateClassLookupProblems(lp, this);
1372            } else if (!func.isCallable()) {
1373                if (func.isExternalObject()) {
1374                    // Something is be wrong with constructor
1375                    name += ".constructor";
1376                    func = func.asInstClassDecl().myConstructor();
1377                    if (func.isUnknown()) {
1378                        // The constructor does not exist
1379                        error("Cannot find function declaration for " + name + "()");
1380                    }
1381                    // Constructor exists, but is not a function - delegate to default case
1382                }
1383                if (!func.isUnknown()) {
1384                    // Not a function
1385                    error("The class " + name + " is not a function");
1386                }
1387            } else if (!func.isRecord() && !func.isCompleteFunction()) {
1388                // TODO: add check if function is partial?
1389                if (!checkType.allowIncompleteReplaceableFunc() || !canBeReplacedForMe(func.asInstNode()))
1390                    error("Calling function " + getName().name() + 
1391                            "(): can only call functions that have one algorithm section or external function specification");
1392            } else {
1393                // Function exists, check everything
1394                errorChecked = false; // super also uses errorChecked
1395                super.collectErrors(checkType);
1396               
1397                // We need to check the function definition as well.
1398                func.collectErrors(checkType);
1399               
1400                // Check if there were any unbindable args
1401                boolean pos = true;
1402                String desc = functionCallDecription();
1403                for (InstFunctionArgument arg : unbindableArgs) 
1404                    pos = arg.generateUnbindableError(desc, pos);
1405            }
1406        }
1407    }
1408   
1409    public static final SimpleProblemProducer ASTNode.NEGATIVE_SIZE_FILL =
1410        new SimpleErrorProducer("NEGATIVE_SIZE_FILL", ProblemKind.SEMANTIC, "The dimension arguments of the fill()"
1411                + " operator may not be negative");
1412   
1413    public void FFillExp.collectErrors(ErrorCheckType checkType) {
1414        super.collectErrors(checkType);
1415        if (!lockBranch(checkType)) {
1416            for (FExp exp : getFExps()) {
1417                if (exp.variability().knownParameterOrLess()) {
1418                    try {
1419                        CValue val = exp.ceval();
1420                            if (val.hasIntValue()) {
1421                            int d = val.intValue();
1422                            if (d < 0) {
1423                                NEGATIVE_SIZE_FILL.invoke(exp);
1424                            }
1425                        }
1426                    } catch (ConstantEvaluationException e) {}
1427                }
1428            }
1429        }
1430    }
1431   
1432    /**
1433     * Can <code>node</code> be replaced as seen from here?
1434     */
1435    inh boolean ASTNode.canBeReplacedForMe(InstNode node);
1436    eq InstNode.getChild().canBeReplacedForMe(InstNode node) = node.canBeReplacedFor(this);
1437    eq Root.getChild().canBeReplacedForMe(InstNode node)     = false;
1438
1439    /**
1440     * Is this node or any node before the closest common ancestor with <code>source</code> replaceable?
1441     */
1442    syn boolean InstNode.canBeReplacedFor(InstNode source)        = canBeReplacedIn(commonAncestor(source));
1443    eq InstSimpleShortClassDecl.canBeReplacedFor(InstNode source) = 
1444        super.canBeReplacedFor(source) || myTargetInstClassDecl().canBeReplacedFor(source);
1445    eq InstShortClassDecl.canBeReplacedFor(InstNode source)       = 
1446        super.canBeReplacedFor(source) || myTargetInstClassDecl().canBeReplacedFor(source);
1447
1448    /**
1449     * Is this class or any class before <code>ancestor</code> replaceable?
1450     */
1451    syn boolean InstNode.canBeReplacedIn(InstNode ancestor) = isReplaceable() || canBeReplacedInHelper(ancestor);
1452
1453    /**
1454     * Helper method for {@link #canBeReplacedIn(InstNode)} - use it instead.
1455     */
1456    inh boolean InstNode.canBeReplacedInHelper(InstNode ancestor);
1457    eq InstNode.getChild().canBeReplacedInHelper(InstNode ancestor) = (ancestor != this) && canBeReplacedIn(ancestor);
1458    eq Root.getChild().canBeReplacedInHelper(InstNode ancestor)     = false;
1459
1460        public void FInfArgsFunctionCall.collectErrors(ErrorCheckType checkType) {
1461                super.collectErrors(checkType);
1462                if (unbindableArgs != null) {
1463                    boolean pos = true;
1464                    String desc = functionCallDecription();
1465                    for (InstFunctionArgument arg : unbindableArgs) 
1466                        pos = arg.generateUnbindableError(desc, pos);
1467                }
1468        }
1469       
1470        syn String FAbstractFunctionCall.functionCallDecription() = "Calling function " + name() + "()";
1471        eq FRecordConstructor.functionCallDecription()    = "Record constructor for " + name();
1472    eq InstRecordConstructor.functionCallDecription() = "Record constructor for " + name();
1473        eq InstFunctionCall.functionCallDecription()      = getName().myInstClassDecl().isRecord() ? 
1474                        "Record constructor for " + name() : super.functionCallDecription();
1475    eq InstPartialFunctionCall.functionCallDecription() = "Creating functional input argument " + name() + "()";
1476         
1477        public boolean InstFunctionArgument.generateUnbindableError(String desc, boolean genForPos) {
1478                return genForPos;
1479        }
1480         
1481        public boolean InstPositionalArgument.generateUnbindableError(String desc, boolean genForPos) {
1482                if (genForPos)
1483                        error(desc + ": too many positional arguments");
1484                return false;
1485        }
1486         
1487        public boolean InstNamedArgument.generateUnbindableError(String desc, boolean genForPos) {
1488                error(desc + ": no input matching named argument " + getName().name() + " found");
1489                return genForPos;
1490        }
1491       
1492        public void FBuiltInFunctionCall.collectErrors(ErrorCheckType checkType) {
1493            if (!errorChecked) {
1494                super.collectErrors(checkType);
1495                errorChecked = true;
1496                getOriginalArgs().collectErrors(checkType);
1497            }
1498        }
1499       
1500        public void FUnsupportedBuiltIn.collectErrors(ErrorCheckType checkType) {
1501                // Don't check arguments
1502                allChecks(checkType);
1503        }
1504 
1505        public void InstNamedArgument.collectErrors(ErrorCheckType checkType) {
1506                // TODO: This way, the FExp for each argument to a built-in function is checked twice - fix that
1507            if (!errorChecked) {
1508                        allChecks(checkType);
1509                        getFExp().collectErrors(checkType);
1510            }
1511        }
1512
1513    /**
1514     * Check if this node is in a recursive structure.
1515     */
1516    syn boolean InstNode       .isRecursed() = false;
1517    eq InstComponentDecl       .isRecursed() = !isPrimitive() && isWithin(myInstClass());
1518    eq InstExtends             .isRecursed() = isWithin(myInstClass());
1519    eq InstSimpleShortClassDecl.isRecursed() = isWithin(actualInstClass());
1520    eq InstArrayComponentDecl  .isRecursed() = instComponentDecl().isRecursed();
1521
1522        // TODO: check if we realy need this in addition to isRecursed()
1523        /**
1524         * Check if extends tree is recursive.
1525         */
1526        public boolean InstExtends.isRecursive() {
1527                if (recursiveCache == RECURSIVE_UNKNOWN)
1528                        calcIsRecursive(new HashSet<InstNode>());
1529                return recursiveCache == RECURSIVE_YES;
1530        }
1531
1532    /**
1533     * Check if extends tree is recursive.
1534     */
1535    public boolean InstSimpleShortClassDecl.isRecursive() {
1536        if (recursiveCache == RECURSIVE_UNKNOWN)
1537            calcIsRecursive(new HashSet<InstNode>());
1538        return recursiveCache == RECURSIVE_YES;
1539    }
1540
1541    /**
1542     * Check if extends tree is recursive.
1543     */
1544    public boolean InstLibNode.isRecursive() {
1545        if (recursiveCache == RECURSIVE_UNKNOWN)
1546            calcIsRecursive(new HashSet<InstNode>());
1547        return recursiveCache == RECURSIVE_YES;
1548    }
1549
1550        /**
1551         * Examine extends tree to find recursive extends nodes.
1552         */
1553        public void InstExtends.calcIsRecursive(HashSet<InstNode> visited) {
1554                recursiveCache = visited.contains(this) ? RECURSIVE_YES : RECURSIVE_NO;
1555                visited.add(this);
1556                if (recursiveCache == RECURSIVE_NO) 
1557                        myInstClass().calcIsRecursive(visited);
1558        }
1559       
1560        /**
1561         * Examine extends tree to find recursive extends nodes.
1562         */
1563        public void InstClassDecl.calcIsRecursive(HashSet<InstNode> visited) {
1564                for (InstExtends ie : getInstExtendss())
1565                        ie.calcIsRecursive(visited);
1566        }
1567
1568    public void InstSimpleShortClassDecl.calcIsRecursive(HashSet<InstNode> visited) {
1569        recursiveCache = visited.contains(this) ? RECURSIVE_YES : RECURSIVE_NO;
1570        visited.add(this);
1571        if (recursiveCache == RECURSIVE_NO) 
1572            myTargetInstClassDecl().calcIsRecursive(visited);
1573        // Can't use actualInstClass() here, since it uses isRecursive()
1574    }
1575
1576    public void InstLibNode.calcIsRecursive(HashSet<InstNode> visited) {
1577        recursiveCache = visited.contains(this) ? RECURSIVE_YES : RECURSIVE_NO;
1578        visited.add(this);
1579        if (recursiveCache == RECURSIVE_NO) 
1580            resolveLib().calcIsRecursive(visited);
1581        // Can't use actualInstClass() here, since it uses isRecursive()
1582    }
1583
1584    private byte InstExtends.recursiveCache              = RECURSIVE_UNKNOWN;
1585    private byte InstSimpleShortClassDecl.recursiveCache = RECURSIVE_UNKNOWN;
1586    private byte InstLibNode.recursiveCache              = RECURSIVE_UNKNOWN;
1587    protected static final byte InstNode.RECURSIVE_UNKNOWN = 0;
1588    protected static final byte InstNode.RECURSIVE_YES     = 1;
1589    protected static final byte InstNode.RECURSIVE_NO      = 2;
1590
1591    /**
1592     * Check if <code>icd</code> is an ancestor of this node or any ancestor is an
1593     *        instance of <code>icd</code>.
1594     */
1595    inh boolean InstComponentDecl.isWithin(InstClassDecl icd);
1596    inh boolean InstExtends      .isWithin(InstClassDecl icd);
1597    inh boolean InstClassDecl    .isWithin(InstClassDecl icd);
1598    eq InstNode         .getChild().isWithin(InstClassDecl icd) = isOfInstClassDecl(icd);
1599    eq InstComponentDecl.getChild().isWithin(InstClassDecl icd) = isOfInstClassDecl(icd) || isWithin(icd);
1600    eq InstClassDecl    .getChild().isWithin(InstClassDecl icd) = icd == this || (!isFunction() && isWithin(icd));
1601    eq InstExtends      .getChild().isWithin(InstClassDecl icd) = isOfInstClassDecl(icd) || isWithin(icd);
1602    eq Root             .getChild().isWithin(InstClassDecl icd) = false;
1603
1604        /**
1605         * Check if this node is equal to or an instance of <code>icd</code>.
1606         */
1607        syn boolean InstNode.isOfInstClassDecl(InstClassDecl icd) = false;
1608        eq InstClassDecl.isOfInstClassDecl(InstClassDecl icd)     = icd == this;
1609        eq InstComponentDecl.isOfInstClassDecl(InstClassDecl icd) = icd == myInstClass() && !icd.isUnknown();
1610        eq InstExtends.isOfInstClassDecl(InstClassDecl icd)       = icd == myInstClass() && !icd.isUnknown();
1611       
1612}
1613
1614aspect AssertEval {
1615
1616    public class FClass {
1617        /**
1618         * Evaluate asserts with constant tests, generate errors for failing ones,
1619         * and eliminate all evaluated assets.
1620         */
1621        public class evaluateAsserts extends Transformation {
1622            public void perform() {
1623                List<FAbstractEquation> l = new List<FAbstractEquation>();
1624                for (FAbstractEquation eqn : getFAbstractEquations()) {
1625                    if (!eqn.evaluateAsserts(true)) {
1626                        l.add(eqn);
1627                    }
1628                }
1629                setFAbstractEquationList(l);
1630            }
1631        }
1632    }
1633   
1634    /**
1635     * Try to evaluate asserts with constant tests in this equation.
1636     *
1637     * If this equation should be removed, returns true.
1638     */
1639    public boolean FAbstractEquation.evaluateAsserts(boolean inClause) {
1640        return false;
1641    }
1642   
1643    @Override
1644    public boolean FFunctionCallEquation.evaluateAsserts(boolean inClause) {
1645        return getCall().evaluateAsserts(inClause);
1646    }
1647   
1648    @Override
1649    public boolean FIfWhenEquation.evaluateAsserts(boolean inClause) {
1650        boolean res = super.evaluateAsserts(inClause);
1651        if (hasElse())
1652            res = res && getElse().evaluateAsserts(inClause);
1653        return res;
1654    }
1655   
1656    @Override
1657    public boolean FIfWhenElseEquation.evaluateAsserts(boolean inClause) {
1658        boolean res = true;
1659        List<FAbstractEquation> l = new List<FAbstractEquation>();
1660        for (FAbstractEquation eqn : getFAbstractEquations()) {
1661            if (!eqn.evaluateAsserts(false)) {
1662                l.add(eqn);
1663                res = false;
1664            }
1665        }
1666        setFAbstractEquationList(l);
1667        return res;
1668    }
1669   
1670    /**
1671     * If this call is an assert with constant test, try to evaluate it.
1672     *
1673     * @return  <code>true</code> if this is an assert that should be removed
1674     */
1675    public boolean FAbstractFunctionCall.evaluateAsserts(boolean error) {
1676        return false;
1677    }
1678   
1679    private static final String FAssert.LEVEL_ERROR = "error";
1680   
1681    public boolean FAssert.evaluateAsserts(boolean error) {
1682        boolean remove = false;
1683        if (getTest().isConstantExp()) {
1684            try {
1685                CValue cVal = getTest().ceval();
1686                if (cVal.hasBooleanValue()) {
1687                    boolean val = cVal.booleanValue();
1688                    if (error && !val) {
1689                        CValue cMsg = getMsg().ceval();
1690                        CValue cLevel = hasLevel() ? getLevel().ceval() : null;
1691                        if (cMsg.hasStringValue() && (cLevel == null || cLevel.hasStringValue())) {
1692                            String msg = "Assertion failed: " + cMsg.stringValue();
1693                            if (cLevel == null || cLevel.stringValue().equals(LEVEL_ERROR)) {
1694                                error(msg);
1695                            } else {
1696                                warning(msg);
1697                            }
1698                        }
1699                    }
1700                    remove = val || error;
1701                }
1702            } catch (ConstantEvaluationException e) {}
1703        }
1704        return remove;
1705    }
1706   
1707}
1708
1709aspect ScalarizationErrorCheck {
1710   
1711    /**
1712     * Check errors that can only be detected after scalarization.
1713     */
1714    public void FClass.scalarizationErrorCheck() {
1715        checkMultipleReinit();
1716        breakOnErrors();
1717    }
1718   
1719    /**
1720     * Check for variables with multiple reinits.
1721     */
1722    public void FClass.checkMultipleReinit() {
1723        Map<FAbstractVariable, Set<FReinit>> reinitMap = collectReinits();
1724        for (FAbstractVariable fv : reinitMap.keySet()) {
1725            if (!fv.isUnknown()) {
1726                Set<FWhenEquation> whens = new HashSet<FWhenEquation>();
1727                Set<FReinit> reinits = reinitMap.get(fv);
1728                for (FReinit r : reinits) {
1729                    FWhenEquation w = r.myWhen();
1730                    if (!whens.contains(w))
1731                        whens.add(w);
1732                }
1733                if (whens.size() > 1) {
1734                    StringBuilder buf = new StringBuilder();
1735                    buf.append("The variable ");
1736                    buf.append(fv.name());
1737                    buf.append(" is assigned in reinit() clauses in more than one when clause:\n");
1738                    for (FReinit r : reinits) {
1739                        buf.append("    ");
1740                        buf.append(r);
1741                        buf.append(";\n");
1742                    }
1743                    fv.error(buf.toString());
1744                }
1745            }
1746        }
1747    }
1748   
1749    /**
1750     * Find all reinits and map them to the assigned variable.
1751     */
1752    syn boolean FVariable.isReinit() = myFClass().collectReinits().keySet().contains(this);
1753   
1754    syn lazy Map<FAbstractVariable,Set<FReinit>> FClass.collectReinits() {
1755        Map<FAbstractVariable,Set<FReinit>> reinitMap = new HashMap<FAbstractVariable,Set<FReinit>>();
1756        for (FAbstractEquation e : getFAbstractEquations())
1757            e.collectReinits(reinitMap);
1758        return reinitMap;
1759    }
1760   
1761    public void FAbstractEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {}
1762   
1763    public void FIfWhenEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1764        if (isWhen()) {
1765                super.collectReinits(map);
1766                if (hasElse())
1767                    getElse().collectReinits(map);
1768        }
1769    }
1770   
1771    public void FIfWhenElseEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1772        for (FAbstractEquation e : getFAbstractEquations())
1773            e.collectReinits(map);
1774    }
1775   
1776    public void FFunctionCallEquation.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1777        getCall().collectReinits(map);
1778    }
1779   
1780    public void FExp.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {}
1781   
1782    public void FReinit.collectReinits(Map<FAbstractVariable,Set<FReinit>> map) {
1783        Set<FReinit> set = map.get(myFV());
1784        if (set == null) {
1785            set = new LinkedHashSet<FReinit>();
1786            map.put(myFV(), set);
1787        }
1788        set.add(this);
1789    }
1790   
1791    /**
1792     * Get the when-equation that this reinit() belongs to.
1793     *
1794     * Gets the top when in a when-elsewhen-else.
1795     * Not valid after when-equations have been converted to if-equations.
1796     */
1797    inh FWhenEquation FReinit.myWhen();
1798    eq FWhenEquation.getChild().myWhen() = findTopWhen(this);
1799    eq FClass.getChild().myWhen()        = null;
1800    eq InstNode.getChild().myWhen()      = null;
1801   
1802    /**
1803     * Find the top when-equation in a when-elsewhen-else equation.
1804     *
1805     * @param last  will be returned if parent is not a when equation
1806     */
1807    inh FWhenEquation FWhenEquation.findTopWhen(FWhenEquation last);
1808    eq FWhenEquation.getChild().findTopWhen(FWhenEquation last) = findTopWhen(this);
1809    eq FClass.getChild().findTopWhen(FWhenEquation last)        = last;
1810    eq InstNode.getChild().findTopWhen(FWhenEquation last)      = last;
1811   
1812}
Note: See TracBrowser for help on using the repository browser.