source: branches/dev-mj-1626/Compiler/ModelicaFlatTree/src/jastadd/TypeAnalysis.jrag @ 13095

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

Merge from trunk

File size: 61.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.ArrayList;
18import java.util.Arrays;
19import java.util.Collection;
20import java.util.Collections;
21import java.util.TreeSet;
22import java.util.Set;
23import java.util.HashSet;
24import java.util.Iterator;
25
26import org.jmodelica.util.collections.ParallelIterable;
27
28
29aspect FlatTypeInterface {
30
31    public interface CommonType {
32        public CommonType cell(Index index);
33        public CommonType expand(Size size);
34        public CommonType contract(int ndims);
35        public CommonType scalarType();
36        public CommonType arrayType(Size size);
37        public CommonType sizedType(Size size);
38        public boolean typeCompatible(CommonType type);
39        public boolean typeCompatible(CommonType type, boolean unknownAllowed);
40        public boolean isUnknown();
41        public Size size();
42        public int ndims();
43        public FType asFType();
44       
45        public boolean isBoolean();
46        public boolean isInteger();
47        public boolean isReal();
48        public boolean isNumeric();
49        public boolean isString();
50        public boolean isEnum();
51        public boolean isRecord();
52        public boolean isFunction();
53        public boolean isExternalObject();
54    }
55   
56    FType implements CommonType;
57    syn FType FType.asFType() = this;
58    syn FType FType.expand(Size size) = sizedType(size().expand(size));
59    syn FType FType.contract(int ndims) = sizedType(size().contractLeft(ndims));
60   
61    public class ArrayType<T extends HasType> implements CommonType {
62       
63        private Indices indices;
64        private Map<Index, T> cells;
65        private CommonType scalarType;
66       
67        public ArrayType(Indices indices, Map<Index, T> cells, CommonType scalarType) {
68            this.indices = indices;
69            this.cells = cells;
70            this.scalarType = scalarType;
71        }
72       
73        public CommonType expand(Size size) {
74            Map<Index, T> expandedCells = new HashMap<>();
75            for (Map.Entry<Index, T> entry : cells.entrySet()) {
76                for (Index index : Indices.create(size)) {
77                    expandedCells.put(index.expand(entry.getKey()).fix(), entry.getValue());
78                }
79            }
80            cells = expandedCells;
81            indices = Indices.create(indices.size().expand(size));
82            return this;
83        }
84       
85        public CommonType contract(int ndims) {
86            int ndimsDiff = ndims() - ndims;
87            Indices newIndices = Indices.create(size().contractLeft(ndims));
88            Map<Index, T> newCells = new HashMap<>();
89           
90            for (Index index : indices) {
91                newCells.put(index.subIndex(ndimsDiff), cells.get(index));
92            }
93            return new ArrayType(newIndices, newCells, scalarType);
94        }
95       
96        public CommonType cell(Index index) {
97            if (indices.isValid(index)) {
98                return cells.get(index).type().scalarType();
99            }
100            if (index.ndims() == 0) {
101                return this;
102            }
103            return FUnknownType.SCALAR;
104        }
105       
106        public CommonType scalarType() {
107            return scalarType;
108        }
109       
110        public CommonType arrayType(Size size) {
111            return scalarType().arrayType(size); // Not perfect, but will do for now.
112        }
113       
114        public CommonType sizedType(Size size) {
115            return size.ndims() > 0 ? arrayType(size) : scalarType();
116        }
117       
118        public boolean typeCompatible(CommonType type) {
119            return typeCompatible(type, false);
120        }
121       
122        public boolean typeCompatible(CommonType type, boolean unknownAllowed) {
123            if (!indices.size().equivalent(type.size(), unknownAllowed)) {
124                return false;
125            }
126            for (Index i : indices) {
127                if (!cell(i).typeCompatible(type.cell(i))) {
128                    return false;
129                }
130            }
131            return true;
132        }
133       
134        public boolean isUnknown() {
135            for (T cell : cells.values()) {
136                if (cell.type().isUnknown()) {
137                    return true;
138                }
139            }
140            return false;
141        }
142       
143        public Size size() {
144            return indices.size();
145        }
146       
147        public int ndims() {
148            return indices.ndims();
149        }
150       
151        public FType asFType() {
152            return scalarType().asFType().sizedType(size());
153        };
154       
155        public String toString() {
156            return scalarType.toString() + (ndims() > 0 ? size() : "");
157        };
158       
159        public boolean isBoolean() { return scalarType().isBoolean(); }
160        public boolean isInteger() { return scalarType().isInteger(); }
161        public boolean isReal() { return scalarType().isReal(); }
162        public boolean isNumeric() { return scalarType().isNumeric(); }
163        public boolean isString() { return scalarType().isString(); }
164        public boolean isEnum() { return scalarType().isEnum(); }
165        public boolean isRecord() { return scalarType().isRecord(); }
166        public boolean isFunction() { return scalarType().isFunction(); }
167        public boolean isExternalObject() { return scalarType().isExternalObject(); }
168    }
169
170}
171
172aspect FlatTypeAnalysis {
173
174    syn lazy FType FAbstractVariable.type();
175    eq FGlobalVariable.type()           = getFType();
176    eq FRealVariable.type()             = fRealType(size());
177    eq FIntegerVariable.type()          = fIntegerType(size());
178    eq FBooleanVariable.type()          = fBooleanType(size());
179    eq FStringVariable.type()           = fStringType(size());
180    eq FEnumVariable.type()             = getType();
181    eq FRecordVariable.type()           = getType();
182    eq FDerivativeVariable.type()       = fRealScalarType();
183    eq FFunctionVariable.type()         = getType();
184    eq UnknownFVariable.type()          = fUnknownType();
185    eq FExternalObjectVariable.type()   = getType();
186
187    syn FType FRealVariable.stateSelectType() = myFClass().stateSelectType();
188    syn lazy FType FClass.stateSelectType() {
189        FEnumType type = new FEnumType(Size.SCALAR, "StateSelect", new List());
190        for (String el : new String[] { "never", "avoid", "default", "prefer", "always" }) 
191            type.addFEnumLiteralType(new FEnumLiteralType(Size.SCALAR, el));
192        return type;
193    }
194   
195    syn FType FRecordVariable.getType() = myFRecordDecl().type().sizedType(size());
196
197    syn FType FEnumVariable.getType() = myFEnumDecl().type().sizedType(size());
198
199    syn nta FType FExternalObjectVariable.getType() = getConstructor().myFCallable().myOutputs().get(0).type().sizedType(size());
200
201
202    public FExternalStmt FAlgorithm.getFExternalStmt() {
203        for (FStatement stmt : getFStatements())
204            if (stmt instanceof FExternalStmt)
205                return (FExternalStmt) stmt;
206        return null;
207    }
208
209    inh CommonVariableDecl FExternalStmt.myConstructorOutput();
210    eq Root.getChild().myConstructorOutput() = null;
211    eq FFunctionDecl.getChild().myConstructorOutput()     = myOutputs().get(0);
212    eq InstFullClassDecl.getChild().myConstructorOutput() = myOutputs().get(0);
213
214    public ArrayList<FExp> FExternalStmt.myConstructorArgs() {
215        ArrayList<FExp> res = new ArrayList<FExp>();
216        for (FExp arg : getArgs()) {
217            if (arg.isConstructorTypeArg()) {
218                res.add(arg);
219            }
220        }
221        return res;
222    }
223
224    syn boolean FExp.isConstructorTypeArg() = false;
225    eq CommonAccessExp.isConstructorTypeArg()     = !myCommonVarDecl().isOutput();
226    eq FSizeExp.isConstructorTypeArg()      = true;
227
228    // TODO: Reduce code duplication here
229    syn lazy FRecordType FRecordDecl.type() {
230        FRecordType type = new FRecordType(Size.SCALAR, name(), new List(), myFClass());
231        for (FVariable fv : getFVariables()) 
232            type.addComponent(new FRecordComponentType(fv.name(), (FType) fv.type().fullCopy()));
233        return type;
234    }
235
236    public class FRecordComponentType implements Comparable<FRecordComponentType> {
237        public int compareTo(FRecordComponentType other) {
238            return getName().compareTo(other.getName());
239        }
240    }
241
242    syn lazy FType FEnumDecl.type() {
243        FEnumType type = new FEnumType(Size.SCALAR, name(), new List());
244        for (FEnumLiteral el : enumLiterals()) 
245            type.addFEnumLiteralType(new FEnumLiteralType(Size.SCALAR, el.name()));
246        return type;
247    }
248
249    syn FType FEnumLiteral.type() = myFEnumDecl().type();
250    eq FEnumLitExp.type()     = hasType() ? getType() : myFEnumDecl().type();
251
252
253    public FType FExp.promoteTypeForFExpList(Iterable<FExp> exps) {
254        Iterator<FExp> it = exps.iterator();
255        if (!it.hasNext())
256            return fUnknownType();
257        FType tot = it.next().type();
258        while (it.hasNext())
259            tot = tot.typePromotion(it.next().type());
260        return tot;
261    }
262
263    syn lazy FType FAbstractEquation.type() = fUnknownType();
264    eq FEquation.type() = getRight().type().typePromotion(getLeft().type());
265
266    syn lazy FType FStatement.type() = fUnknownType();
267    eq FAssignStmt.type()            = getRight().type().typePromotion(getLeft().type());
268    eq FFunctionCallStmt.type()      = getCall().type();
269    eq FInitArrayStmt.type()         = getType();
270   
271    syn FType FInitArrayStmt.getType() {
272        if (getShallow() && getNumSize() > 0) {
273            MutableSize ms = new MutableSize(getNumSize());
274            for (FExp size : getSizes()) {
275                ms.append(size);
276            }
277            return getFAccessExp().myFV().type().arrayType(ms);
278        } else {
279            return getFAccessExp().myFV().type().treeCopy();
280        }
281    }
282   
283    /**
284     * True if this equation exclusively equates real types.
285     */
286    syn boolean FAbstractEquation.isReal() = isReal(true);
287
288    /**
289     * True if this equation exclusively equates non-real types.
290     */
291    syn boolean FAbstractEquation.isNonReal() = !isReal(false);
292
293    /**
294     * True if this equation equates at least one real type and one non real type.
295     */
296    syn boolean FAbstractEquation.isMixed() {
297        return !isReal(true) && isReal(false);
298    }
299
300    syn boolean FAbstractEquation.isReal(boolean exclusive) {
301        boolean res = exclusive;
302        for (FVariable fv : referencedFVariablesInLHS()) {
303            boolean fvRes = fv.isReal();
304            res = exclusive ? res && fvRes : res || fvRes;
305        }
306        return res;
307    }
308    eq FEquation.isReal(boolean exclusive) = type().isReal();
309
310    eq FIfWhenElseEquation.isReal(boolean exclusive) {
311        boolean res = exclusive;
312        for (FAbstractEquation equation : getFAbstractEquations()) {
313            boolean fvRes = equation.isReal(exclusive);
314            res = exclusive ? res && fvRes : res || fvRes;
315        }
316        return res;
317    }
318    eq FIfEquation.isReal(boolean exclusive) {
319        return super.isReal(exclusive) && (!exclusive || (!hasElse() || getElse().isReal()));
320    }
321
322    syn boolean FRelExp.isRealComparison()    = comparisonType().isReal();
323    syn boolean FRelExp.isIntegerComparison() = comparisonType().isInteger();
324    syn boolean FRelExp.isBooleanComparison() = comparisonType().isBoolean();
325    syn boolean FRelExp.isStringComparison()  = comparisonType().isString();
326
327    syn FType FRelExp.comparisonType() = getRight().type().numericPromotion(getLeft().type());
328
329    syn FType FExp.type() = fUnknownType();
330   
331    eq FNoExp.type() = fNoType();
332   
333    syn lazy FType FAbstractArrayExp.type() = super.type();
334
335    eq FUnaryExp.type()   = 
336        shouldUseOverloadedOperator() ? getFExp().type().matchOverloadedOperatorType(null, operatorName()) : getFExp().type();
337    eq FArtmBinExp.type() = getLeft().type().looseNumericPromotion(getRight().type(), false, operatorName());
338    eq FSubExp.type()     = getLeft().type().numericPromotion(getRight().type(), false, operatorName());
339    eq FNegExp.type()     = getFExp().type().hasNeg() ? super.type() : fUnknownType();
340
341    eq FDotAddExp.type() {
342        FType t = rawType();
343        return t.hasAdd() ? t : fUnknownType();
344    }
345
346    syn FType FDotAddExp.rawType() = getLeft().type().looseTypePromotion(getRight().type(), false, operatorName());
347    eq FAddExp.rawType()           = getLeft().type().typePromotion(getRight().type(), false, operatorName());
348
349    eq FUnaryBuiltIn.type() = getFExp().type();
350    eq FEdgeExp.type()      = fBooleanType(size());
351    eq FChangeExp.type()    = fBooleanType(size());
352    eq FLoadResource.type() = fStringScalarType();
353
354    eq FDimensionConvert.type() = getFExp().type().sizedType(size());
355
356    eq FDotDivExp.type() {
357        FType t = super.type();
358        if (t.isInteger())
359            t = fRealType(t.size());
360        return t;
361    }
362
363    eq FDivExp.type() {
364        if (getRight().type().isScalar() || shouldUseOverloadedOperator())
365            return super.type();
366        else
367            return fUnknownType();
368    }
369
370    eq FMulExp.type() {
371        FType res = null;
372        if (ndims() < 0)
373            return fUnknownType();
374        if (isElementWise())
375            return super.type();
376        Size left = getLeft().size();
377        Size right = getRight().size();
378        if (shouldUseOverloadedOperator()) {
379            res = getLeft().type().matchOverloadedOperatorType(getRight().type(), operatorName());
380            if (!res.isUnknown())
381                return res;
382        }
383        if (!left.equivalentDim(right, inFunction(), left.ndims() - 1, 0))
384            return fUnknownType();
385        if (left.ndims() > 2 || right.ndims() > 2)
386            return fUnknownType();
387        if (left.ndims() == 1 && right.ndims() > 0 && shouldUseOverloadedOperator())
388            return fUnknownType();
389        res = getLeft().type().scalarNumericPromotion(getRight().type(), operatorName());
390        if (left.ndims() == 1 && right.ndims() == 1) 
391            return res;
392        else
393            return res.arrayType(size());
394    }
395
396    eq FDotPowExp.type() {
397        FType t = getLeft().type().looseNumericPromotion(getRight().type());
398        return (t.isUnknown() || shouldUseOverloadedOperator()) ? t : fRealType(t.size());
399    }
400
401    eq FPowExp.type() {
402        if (shouldUseOverloadedOperator()) {
403            FType res = getLeft().type().matchOverloadedOperatorType(getRight().type(), operatorName());
404            if (!res.isUnknown())
405                return res;
406        }
407        if (getRight().type().isUnknown() || getRight().ndims() != 0)
408            return fUnknownType();
409        if (getLeft().ndims() == 0)
410            return super.type();
411        // Left operand is not scalar, must be matrix exponentiation
412        if (ndims() != 2 || size().get(0) != size().get(1))
413            return fUnknownType();
414        if (!getRight().type().isInteger() || !getRight().isConstantExp())
415            return fUnknownType();
416        if (getRight().ceval().intValue() < 0)
417            return fUnknownType();
418        return fRealArrayType(size());
419    }
420
421    eq FEnumIntegerExp.type() = getFExp().type().isEnumScalar() ? fIntegerScalarType() : fUnknownType();
422    eq FStringExp.type()      = fStringScalarType();
423
424    eq FEventGenExp.type()    = fRealType(size());
425    eq FIntegerFuncExp.type() = fIntegerType(size());
426    eq FBinEventGenExp.type() = argType();
427
428    eq FHomotopyExp.type()    = getActual().type();
429    eq FSemiLinearExp.type()  = fRealType(size());
430
431    // Should be safe, FDSDerExp are introduced by index reduction and represent
432    // derivatives which always are continuous and real typed.
433    eq FDSDerExp.type()       = FRealType.SCALAR;
434
435    syn FType FEventGenExp.argType() = getX().type();
436    eq FBinEventGenExp.argType() = 
437            getX().type().scalarType().numericPromotion(getY().type().scalarType()).sizedType(size());
438
439    eq FRecordConstructor.type() = getType();
440
441    syn FType FRecordConstructor.getType() = getRecord().recordType().forConstructor(getArgs());
442
443    public FType FRecordType.forConstructor(List<? extends HasType> args) {
444        FRecordType frt = treeCopy();
445        for (int i = 0; i < frt.getNumComponent(); i++) {
446            frt.updateComponent(args.getChild(i).type(), i);
447        }
448        return frt;
449    }
450   
451    private void FRecordType.updateComponent(FType type, int i) {
452        if (getComponent(i).getFType().isRecord() && type.isRecord()) {
453            getComponent(i).setFType(type.treeCopy());
454        } else {
455            getComponent(i).getFType().transferSizes(type);
456        }
457    }
458
459    public interface HasType {
460        public FType type();
461    }
462
463    FExp implements HasType;
464    InstFunctionArgument implements HasType;
465    InstComponentDecl implements HasType;
466
467    syn FType InstFunctionArgument.type() = getFExp().type();
468
469    syn FRecordType FRecordAccess.recordType() = myFRecordDecl().type();
470
471    eq FTranspose.type() = 
472        getFExp().ndims() < 2 ? 
473            fUnknownType() : 
474            getFExp().type().arrayType(size());
475
476    eq FSymmetric.type() = getFExp().ndims() == 2 && getFExp().size().get(0) == getFExp().size().get(1) ?
477        getFExp().type() : 
478        fUnknownType();
479
480    eq FCross.type() = getX().type().numericPromotion(getY().type());
481    eq FSkew.type()  = getFExp().type().arrayType(size());
482
483    eq FOuterProduct.type() { 
484        if (getX().ndims() != 1 || getY().ndims() != 1)
485            return fUnknownType();
486        FType scalar = getX().type().scalarType().numericPromotion(getY().type().scalarType());
487        return scalar.sizedType(size());
488    }
489
490    eq FNdimsExp.type() = fIntegerScalarType();
491
492    eq FSizeExp.type() {
493        if (hasDim()) {
494            if (!getDim().variability().parameterOrLess())
495                return fUnknownType();
496            if (!getDim().type().isIntegerScalar())
497                return fUnknownType();
498            int dim = dimension();
499            if (dim < 0 || dim > getFExp().ndims() - 1)
500                return fUnknownType();
501            return fIntegerScalarType();
502        } else {
503            return fIntegerArrayType(size());
504        }
505    }
506
507    eq FMinMaxExp.type() = 
508        hasY() ? 
509            getX().type().scalarType().typePromotion(getY().type().scalarType()) : 
510            getX().type().scalarType();
511
512    eq FRangeExp.type() {
513        FType tot = promoteTypeForFExpList(getFExps());
514        if (!tot.isScalar() || !tot.canBeRange() || (!tot.isNumeric() && hasStep()))
515                return fUnknownType();
516        return tot.arrayType(size());
517    }
518
519    syn boolean FType.canBeRange() = false;
520    eq FIntegerType.canBeRange()   = true;
521    eq FRealType.canBeRange()      = true;
522    eq FBooleanType.canBeRange()   = true;
523    eq FEnumType.canBeRange()      = true;
524
525    eq FDeferExp.type() = getFExp().type();
526
527    eq FLinspace.type() {
528        // TODO: check that endpoints are real
529        if (hasValidN() || inFunction()) {
530            return fRealArrayType(size());
531        } else {
532            return fUnknownType();
533        }
534    }
535
536    syn boolean FLinspace.hasValidN() {
537        if (getN().variability().parameterOrLess()) {
538            try {
539                CValue val = getN().ceval();
540                return val.hasIntValue() && val.intValue() >= 2;
541            } catch (ConstantEvaluationException e) {}
542        }
543        return false;
544    }
545
546    eq FRelExp.type() {
547        if (shouldUseOverloadedOperator()) 
548            return getLeft().type().matchOverloadedOperatorType(getRight().type(), operatorName());
549        FType left = getLeft().type();
550        if (left.equivalentTo(getRight().type()) && left.isScalar()) {
551            if (left.isRecord()) {
552                return left;
553            } else {
554                return fBooleanScalarType();
555            }
556        }
557        else
558            return fUnknownType();
559    }
560    eq FLogBinExp.type() {
561        if (shouldUseOverloadedOperator()) 
562            return getLeft().type().matchOverloadedOperatorType(getRight().type(), operatorName());
563        FType left = getLeft().type();
564        if (left.typeCompatible(getRight().type()) && left.isBoolean())
565            return left;
566        else
567            return fUnknownType();
568    }
569    eq FNotExp.type() = shouldUseOverloadedOperator() ? 
570            getFExp().type().matchOverloadedOperatorType(null, operatorName()) : 
571            (getFExp().type().isBoolean() ? getFExp().type() : fUnknownType());
572
573    eq FIfExp.type() {
574        FType thenType = getThenExp().type();
575        FType elseType = getElseExp().type();
576        if (!getIfExp().isOKTestExp() || thenType.ndims() != elseType.ndims())
577            return fUnknownType();
578       
579        return fypeSelection(thenType, elseType);
580    }
581
582    syn FType FIfExp.fypeSelection(FType thenType, FType elseType) = 
583        thenType.typePromotion(elseType, inFunction(), null);
584
585    syn FType InstIfExp.fypeSelection(FType thenType, FType elseType) {
586        if (thenType.equivalentTo(elseType))
587            return thenType.typePromotion(elseType);
588       
589        if (isParameterIf()) {
590            try {
591                FExp sel = cevalSelectExp();
592                FType scalar = thenType.scalarType();
593                scalar = scalar.typePromotion(elseType.scalarType());
594                return scalar.arrayType(sel.size());
595            } catch (ConstantEvaluationException e) {
596            }
597        }
598       
599        // Test doesn't seem to be parameter expression - require sizes to be same
600        return thenType.typePromotion(elseType, inFunction(), null);
601    }
602
603    syn boolean InstIfExp.isParameterIf() = getIfExp().variability().parameterOrLess();
604
605    syn boolean FExp.isOKTestExp() = type().isBoolean() && type().isScalar();
606
607    eq FRealLitExp.type()     = fRealScalarType();
608    eq FIntegerLitExp.type()  = fIntegerScalarType();
609    eq FBooleanLitExp.type()  = fBooleanScalarType();
610    eq FStringLitExp.type()   = fStringScalarType();
611    eq FTimeExp.type()        = fRealScalarType();
612    eq FAbstractDerExp.type() = fRealType(size());
613    eq FInStreamEpsExp.type() = fRealScalarType();
614
615    eq FVectUnaryBuiltIn.type() = scalarType().sizedType(size());
616
617    eq FExInStream.type() = fRealType(size());
618
619    eq FSmoothExp.type() = getFExp().type();
620
621    eq FConnectionsOp.type() = fNoType();
622    eq FConnBoolOp.type()    = fBooleanScalarType();
623
624    eq FCardinality.type() = fIntegerScalarType();
625    eq FDecouple.type()    = getFExp().type();
626
627    eq FTerminate.type() = fNoType();
628    eq FReinit.type() = fNoType();
629    eq FAssert.type() = getTest().type(); /* For scalarizing record type asserts. Needed for TestExternalRecordCeval */
630    eq FGetInstanceName.type() = fStringScalarType();
631
632    eq FDelayExp.type() = getFExp().type();
633    eq FSpatialDistExp.type() = getIn0().type();
634
635    eq FIndicatorExp.type() = FRealType.SCALAR;
636
637    /**
638     * The scalar type of the expression.
639     *
640     * Base implementation assumes same as argument.
641     */
642    syn FType FVectUnaryBuiltIn.scalarType() = getFExp().type().scalarType();
643    eq FSignExp.scalarType() = fIntegerScalarType();
644
645    // TODO: expand to handle boolean and enum index
646    eq FEndExp.type() = fIntegerScalarType();
647
648    eq FSampleExp.type() {
649        for (FExp ch : childFExps())
650            if (!ch.variability().parameterOrLess())
651                return fUnknownType();
652        return fBooleanScalarType();
653    }
654
655    eq FSimulationStateBuiltIn.type() = fBooleanScalarType();
656
657    /**
658     * Check if this FArray is on the form "{exp for i in exp}".
659     */
660    syn boolean FArray.isIterArray() = getNumFExp() == 1 && getFExp(0).isIterExp();
661
662    /**
663     * Check if this FExp is on the form "exp for i in exp".
664     */
665    syn boolean FExp.isIterExp() = false;
666    eq FIterExp.isIterExp()      = true;
667
668    /**
669     * Returns the expression being iterated over in an FIterExp child.
670     *        Only valid if {@link #isIterExp()} returns <code>true</code>.
671     */
672    public FExp FReductionExp.iterExp() { return ((FIterExp) getFExp()).getFExp(); }
673
674    eq FArray.type() {
675        if (isIterArray())
676            return getFExp(0).type();
677       
678        // Check that the types of the elements are consistent
679        int n = getNumFExp();
680        FType t = n > 0 ? getFExp(0).type() : fUnknownType();
681        boolean func = inFunction();
682        for (int i = 1; i < n && !t.isUnknown(); i++) {
683            t = mergeArrayCellTypes(t, getFExp(i).type(), func);
684        }
685        return t.arrayType(size());
686    }
687   
688    syn FType FArray.mergeArrayCellTypes(FType t1, FType t2, boolean func) {
689        if (t1.isNoType()) {
690            return t2;
691        } else if (t2.isNoType()) {
692            return t1;
693        } else {
694            return t1.typePromotion(t2, func, null);
695        }
696    }
697   
698    syn boolean FExp.isNoExp() = false;
699    eq FNoExp.isNoExp() = true;
700   
701    syn boolean FType.isNoType() = false;
702    eq FNoType.isNoType() = true;
703
704    eq FAbstractCat.type() {
705        if (!dimensionIsOk())
706            return fUnknownType();
707        int dim = dimension();
708       
709        FType t = getFExp(0).type().scalarType();
710        Size s = getFExp(0).size().promote(ndimsForArg(0));
711        boolean func = inFunction();
712        for (int i = 1; i < getNumFExp(); i++) {
713            // Check that the types of the elements are consistent
714            FType t2 = getFExp(i).type().scalarType();
715            if (!t.equivalentTo(t2, func)) 
716                return fUnknownType();
717            if (t.isNumeric()) 
718                t = t.numericPromotion(t2, func, null);
719           
720            // Check that the sizes are consistent
721            Size s2 = getFExp(i).size().promote(ndimsForArg(i));
722            if (!s.equivalentExcept(s2, func, dim))
723                return fUnknownType();
724        }
725        return t.arrayType(size());
726    }
727
728    syn int FAbstractCat.ndimsForArg(int i) = ndims();
729    eq FCatExp.ndimsForArg(int i)           = getFExp(i).ndims();
730
731    eq FReductionExp.type() {
732        if (getFExp().isIterExp())
733            return iterExp().type();
734        return getFExp().type().scalarType();
735    }
736
737    eq FIterExp.type() {
738        if (ndims() < 0)
739            return fUnknownType();
740        return getFExp().type().arrayType(size());
741    }
742
743    eq FIdentity.type() {
744        if (!getFExp().variability().parameterOrLess() && !inFunction())
745            return fUnknownType();
746        return fIntegerArrayType(size());
747    }
748    eq FDiagonal.type() = getFExp().type().arrayType(size());
749
750    eq FSubscriptedExp.type() = getFExp().type().sizedType(size());
751    eq FComponentExp.type()   = getFExp().type().componentType(getName());
752
753    eq FArrayDimAsArgsExp.type() = (size().ndims() > 0) ? fIntegerArrayType(size()) : fUnknownType();
754    eq FFillExp.type()           = (size().ndims() > 0) ? getFillExp().type().arrayType(size()) : fUnknownType();
755
756    eq CommonAccessExp.type() = getAccess().type(); // When converting to the new type interface, this should compute
757                                                    // type for every element in getArray() for arrays of records.
758   
759    eq FDSRefExp.type() = getOrg().type();
760
761    syn FType CommonAccess.type();
762   
763    eq FAccess.type() = myFV().type().sizedType(size());
764
765    eq InstAccess.type() {
766        if (myInstComponentDecl().isAssignable()) {
767            InstAssignable ip = (InstAssignable) myInstComponentDecl();
768            if (ip.type().isRecord()) {
769                CommonType t = ip.commonType();
770                int ndimsWithoutFAS = ndims() + (hasFArraySubscripts() ? getFArraySubscripts().ndims() - getFArraySubscripts().accessNdims() : 0);
771                // Using expand here is insufficient for record arrays where the elements have different sizes.
772                // Very difficult to fix here, should be fixed in CommonAccessExp.type() instead.
773                t = t.expand(size().contractRight(ndimsWithoutFAS - ip.ndims()));
774                try {
775                    t = t.cell(accessIndex()).asFType();
776                } catch (ConstantEvaluationException e) {
777                    t = ndims() > 0 ? t.arrayType(size()) : t.scalarType();
778                }
779                return t.asFType();
780            } else {
781                return ip.type().sizedType(size());
782            }
783        } else if (myInstClassDecl().isEnum()) {
784            return myInstClassDecl().enumType();
785        } else if (myInstClassDecl().isBoolean()) {
786            return myInstClassDecl().fBooleanType(size());
787        } else {
788            return fUnknownType();
789        }
790        }
791
792    eq FIgnoredBuiltIn.type() = fUnknownType();
793
794    inh FType FFunctionCallLeft.type();
795    eq FFunctionCallEquation.getLeft(int i).type() = getCall().typeOfOutput(i);
796    eq FFunctionCallStmt.getLeft(int i).type()     = getCall().typeOfOutput(i);
797
798        syn FType FAbstractFunctionCall.typeOfOutput(int i) = typeOutput(i) ? type() : fUnknownType();
799    eq FFunctionCall.typeOfOutput(int i)                = getFType().component(i);
800    eq FPartialFunctionCall.typeOfOutput(int i)         = getFType();
801    eq InstFunctionCall.typeOfOutput(int i)             = getFType().component(i);
802    eq InstPartialFunctionCall.typeOfOutput(int i)      = getFType();
803   
804    syn boolean FAbstractFunctionCall.typeOutput(int i) = i == 0;
805    eq FSpatialDistExp.typeOutput(int i)                = i == 1 || super.typeOutput(i);
806   
807    syn FType FType.component(int i)  = fUnknownType();
808    eq FFunctionType.component(int i) = nullUnknown(getOutput(i)).vectorized(getSize());
809   
810    syn FType FType.nullUnknown(FRecordComponentType t) = t == null ? fUnknownType() : t.getFType();
811   
812    syn FType FType.vectorized(Size s) {
813        if (s == Size.SCALAR) {
814            return this;
815        }
816        MutableSize s2 = s.mutableClone();
817        s2.append(getSize());
818        return sizedType(s2);
819    }
820   
821    eq FFunctionCall.type()    = hasOutputs() ? typeOfOutput(0) : fNoType();
822    eq FMathematicalFunctionCall.type() = fRealType(size());
823    eq FPartialFunctionCall.type() {
824        List<FRecordComponentType> inputs = new List<FRecordComponentType>();
825        Set<String> s = new HashSet<String>();
826        for (CommonAccess u : getArgNames()) {
827            s.add(u.name());
828        }
829        for (CommonVariableDecl cvd : myFCallable().myInputs()) {
830            if (!s.contains(cvd.name()))
831                inputs.add(new FRecordComponentType(cvd.name(), cvd.type()));
832        }
833        List<FRecordComponentType> outputs = new List<FRecordComponentType>();
834        for (CommonVariableDecl cvd : myFCallable().myOutputs()) {
835            outputs.add(new FRecordComponentType(cvd.name(), cvd.type()));
836        }
837        return new FFunctionType(size(), name(), inputs, outputs, myFClass());
838    }
839
840    // TODO: expand to handle boolean and enum index (see #3597)
841    syn FType FSubscript.type();
842    syn lazy FType FColonSubscript.type() = fIntegerArrayType(size());
843    eq FIntegerSubscript.type() = fIntegerScalarType();
844    eq FExpSubscript.type()     = getFExp().type();
845    public FType IntegerSubscript.type() { return FIntegerType.SCALAR; }
846
847    public FType FType.lookupHierarchical(FAccess name, int part, int last) {
848        return this;
849    }
850   
851    public FType FRecordType.lookupHierarchical(FAccess name, int part, int last) {
852        if (part == last) {
853            return this;
854        }
855        FAbstractVariable res = null;
856        String pn = name.partName(part);
857        for (FRecordComponentType frct : getComponents())
858            if (frct.getName().equals(pn))
859                return frct.getFType().lookupHierarchical(name, part + 1, last);
860        return null;
861    }
862   
863    syn FType FType.cell(Index index) {
864        if (index.ndims() == 0) {
865            return this;
866        }
867        if (indices().isValid(index)) {
868            return scalarType();
869        }
870        return FUnknownType.SCALAR;
871    }
872
873
874}
875
876
877aspect TypePromotion {
878
879    /**
880     * Combine two types to the broadest common denominator.
881     *
882     * If types are not compatible, the unknown type is returned.
883     * An integer type combined with a real type results in a real type.
884     */
885    syn FType FType.typePromotion(FType type) = typePromotion(type, false, null);
886
887    /**
888     * Combine two types to the broadest common denominator.
889     *
890     * If scalar types are not compatible or if both are array and sizes
891     * doesn't match, the unknown type is returned.
892     * An integer type combined with a real type results in a real type.
893     */
894    syn FType FType.looseTypePromotion(FType type) = looseTypePromotion(type, false, null);
895
896    /**
897     * Combine two types to the broadest common numeric denominator.
898     *
899     * If either is non-numeric or if sizes doesn't match,
900     * the unknown type is returned.
901     * An integer type combined with a real type results in a real type.
902     */
903    syn FType FType.numericPromotion(FType type) = numericPromotion(type, false, null);
904
905    /**
906     * Combine two types to the broadest common numeric denominator.
907     *
908     * If either is non-numeric or if both are array and sizes doesn't match,
909     * the unknown type is returned.
910     * An integer type combined with a real type results in a real type.
911     */
912    syn FType FType.looseNumericPromotion(FType type) = looseNumericPromotion(type, false, null);
913
914    /**
915     * Combine two types to the broadest common denominator.
916     *
917     * If types are not compatible, the unknown type is returned.
918     * An integer type combined with a real type results in a real type.
919     * If <code>allowUnknown</code> is <code>true</code>, then unknown
920     * lengths are considered equal to any length. If non-<code>null</code>,
921     * <code>operator</code> defines what operator to use in operator
922     * overloading.
923     */
924    syn FType FType.typePromotion(FType type, boolean allowUnknown, String operator) {
925        if ((isOperatorRecord() || type.isOperatorRecord()) && operator != null) {
926            return matchOverloadedOperatorType(type, operator, false, allowUnknown);
927        } else if (isNumeric()) {
928            return numericPromotion(type, allowUnknown, operator);
929        } else if (!equivalentTo(type, allowUnknown)) {
930            if (isRecord()) {
931                return mergeRecordType(type);
932            }
933            return fUnknownType();
934        } else {
935            return this;
936        }
937    }
938   
939    syn FType FType.mergeRecordType(FType other) {
940        FType res = treeCopy();
941        res.setSize(getSize().mergeRecordType(other.getSize()));
942        return res;
943    }
944    eq FRecordType.mergeRecordType(FType other) {
945        if (ndims() != other.ndims() || !other.isRecord()) {
946            return fUnknownType();
947        }
948        FRecordType rec = (FRecordType) other;
949        if (getNumComponent() != rec.getNumComponent()) {
950            return fUnknownType();
951        }
952        Map<String, FRecordComponentType> comps = new HashMap<>();
953        Iterable<FRecordComponentType[]> iter = new ParallelIterable(
954                new FRecordComponentType[2], true, componentTreeSet(), rec.componentTreeSet());
955        for (FRecordComponentType[] frct : iter) {
956            if (frct[0] == null || frct[1] == null || 
957                    !frct[0].getName().equals(frct[1].getName())) {
958                return fUnknownType();
959            }
960            FType comp = frct[0].getFType().mergeRecordType(frct[1].getFType());
961            if (comp.isUnknown()) {
962                return fUnknownType();
963            }
964            String name = frct[0].getName();
965            comps.put(name, new FRecordComponentType(name, comp));
966        }
967       
968        List<FRecordComponentType> compList = new List<>();
969        for (FRecordComponentType comp : getComponents()) {
970            compList.add(comps.get(comp.getName()));
971        }
972       
973        Size size = getSize().mergeRecordType(other.getSize());
974        return new FRecordType(size, getName(), compList, getFClass());
975    }
976   
977    syn TreeSet<FRecordComponentType> FRecordType.componentTreeSet() {
978        TreeSet<FRecordComponentType> res = new TreeSet<FRecordComponentType>();
979        for (FRecordComponentType frct : getComponents()) {
980            res.add(frct);
981        }
982        return res;
983    }
984   
985    public Size Size.mergeRecordType(Size other) {
986        if (ndims() == 0) {
987            return this;
988        }
989        int[] merged = new int[ndims()];
990        for (int i = 0; i < ndims(); i++) {
991            int s = get(i);
992            if (s != other.get(i)) {
993                s = Size.UNKNOWN;
994            }
995            merged[i] = s;
996        }
997        return new Size(merged);
998    }
999   
1000    /**
1001     * Combine two types to the broadest common denominator.
1002     *
1003     * If scalar types are not compatible or if both are array and sizes
1004     * doesn't match, the unknown type is returned.
1005     * An integer type combined with a real type results in a real type.
1006     * If <code>allowUnknown</code> is <code>true</code>, then unknown
1007     * lengths are considered equal to any length. If non-<code>null</code>,
1008     * <code>operator</code> defines what operator to use in operator
1009     * overloading.
1010     */
1011    syn FType FType.looseTypePromotion(FType type, boolean allowUnknown, String operator) {
1012        if ((isOperatorRecord() || type.isOperatorRecord()) && operator != null) {
1013            return matchOverloadedOperatorType(type, operator, true, allowUnknown);
1014        } else if (looslyDimensionCompatible(type, allowUnknown)) {
1015            FType scalar = scalarType().typePromotion(type.scalarType(), false, operator);
1016            return scalar.sizedType(isScalar() ? type.size() : size());
1017        } else {
1018            return fUnknownType();
1019        }
1020    }
1021
1022    /**
1023     * Combine two types to the broadest common numeric denominator.
1024     *
1025     * If either is non-numeric or if sizes doesn't match,
1026     * the unknown type is returned. Operator records with suitable
1027     * overloaded operators are considered numeric for this purpose.
1028     * An integer type combined with a real type results in a real type.
1029     * If <code>allowUnknown</code> is <code>true</code>, then unknown
1030     * lengths are considered equal to any length. If non-<code>null</code>,
1031     * <code>operator</code> defines what operator to use in operator
1032     * overloading.
1033     */
1034    syn FType FType.numericPromotion(FType type, boolean allowUnknown, String operator) {
1035        if (isOperatorRecord() || type.isOperatorRecord()) {
1036            return matchOverloadedOperatorType(type, operator, false, allowUnknown);
1037        } else if (dimensionCompatible(type, allowUnknown)) {
1038            return scalarNumericPromotion(type, operator).arrayType(size());
1039        } else {
1040            return fUnknownType();
1041        }
1042    }
1043
1044    /**
1045     * Combine two types to the broadest common numeric denominator.
1046     *
1047     * If either is non-numeric or if both are array and sizes doesn't match,
1048     * the unknown type is returned. Operator records with suitable
1049     * overloaded operators are considered numeric for this purpose.
1050     * An integer type combined with a real type results in a real type.
1051     * If <code>allowUnknown</code> is <code>true</code>, then unknown
1052     * lengths are considered equal to any length. If non-<code>null</code>,
1053     * <code>operator</code> defines what operator to use in operator
1054     * overloading.
1055     */
1056    syn FType FType.looseNumericPromotion(FType type, boolean allowUnknown, String operator) {
1057        if (isOperatorRecord() || type.isOperatorRecord()) {
1058            return matchOverloadedOperatorType(type, operator, true, allowUnknown);
1059        } else if (looslyDimensionCompatible(type, allowUnknown)) {
1060            return scalarNumericPromotion(type, operator).sizedType(isScalar() ? type.size() : size());
1061        } else {
1062            return fUnknownType();
1063        }
1064    }
1065
1066    /**
1067     * Combine two types to the broadest common scalar numeric denominator.
1068     *
1069     * If either is non-numeric, the unknown type is returned.
1070     * An integer type combined with a real type results in a real type.
1071     * If non-<code>null</code>, <code>operator</code> defines what operator
1072     * to use in operator overloading.
1073     */
1074    syn FType FType.scalarNumericPromotion(FType type, String operator) = 
1075        scalarType().matchOverloadedOperatorType(type.scalarType(), operator, false, false);
1076    eq FPrimitiveNumericType.scalarNumericPromotion(FType type, String operator) {
1077        if (isOperatorRecord() || type.isOperatorRecord()) {
1078            return scalarType().matchOverloadedOperatorType(type.scalarType(), operator, false, false);
1079        } else if (type.isNumeric()) 
1080            return isReal() ? scalarType() : type.scalarType();
1081        else 
1082            return super.scalarNumericPromotion(type, operator);
1083    }
1084    eq FArrayType.scalarNumericPromotion(FType type, String operator) {
1085        return scalarType().scalarNumericPromotion(type.scalarType(), operator);
1086    }
1087
1088}
1089
1090
1091aspect FTypeCompatibility {
1092
1093    syn boolean FType.typeCompatible(CommonType type) = typeCompatible(type, false);
1094
1095    syn boolean FType.typeCompatible(CommonType type, boolean allowUnknown) = false;
1096    eq FRealType.typeCompatible(CommonType type, boolean allowUnknown) = 
1097        (type.isReal() || type.isInteger()) && dimensionCompatible(type, allowUnknown);
1098    eq FIntegerType.typeCompatible(CommonType type, boolean allowUnknown) = 
1099        type.isInteger() && dimensionCompatible(type, allowUnknown);
1100    eq FBooleanType.typeCompatible(CommonType type, boolean allowUnknown) = 
1101        type.isBoolean() && dimensionCompatible(type, allowUnknown);
1102    eq FStringType.typeCompatible(CommonType type, boolean allowUnknown) = 
1103        type.isString() && dimensionCompatible(type, allowUnknown);
1104    eq FRecordType.typeCompatible(CommonType type, boolean allowUnknown) = 
1105        typeMatches(type, true, allowUnknown);
1106    eq FEnumType.typeCompatible(CommonType type, boolean allowUnknown) = 
1107        typeMatches(type, true, allowUnknown);
1108    eq FExternalObjectType.typeCompatible(CommonType type, boolean allowUnknown) = 
1109        typeMatches(type, true, allowUnknown);
1110    eq FFunctionType.typeCompatible(CommonType type, boolean allowUnknown) =
1111        typeMatches(type, true, allowUnknown);
1112   
1113    syn boolean FType.typeMatches(CommonType type, boolean compatible, boolean allowUnknown) =
1114        compatible ? typeCompatible(type, allowUnknown) : equivalentTo(type, allowUnknown);
1115    eq FRecordType.typeMatches(CommonType type, boolean compatible, boolean allowUnknown) {
1116        if (!dimensionCompatible(type, allowUnknown) || !type.isRecord())
1117            return false;
1118        FRecordType rec = (FRecordType) type;
1119        if (getNumComponent() != rec.getNumComponent())
1120            return false;
1121        return typeMatches(componentTreeSet(), rec.componentTreeSet(), compatible, true);
1122    }
1123    eq FExternalObjectType.typeMatches(CommonType type, boolean compatible, boolean allowUnknown) {
1124        if (!dimensionCompatible(type, allowUnknown) || !type.isExternalObject())
1125            return false;
1126        FExternalObjectType obj = (FExternalObjectType) type;
1127        if (getName().equals(obj.getName())) 
1128            return true;
1129        return false;
1130    }
1131    eq FEnumType.typeMatches(CommonType type, boolean compatible, boolean allowUnknown) {
1132        if (!dimensionCompatible(type, allowUnknown) || !type.isEnum())
1133            return false;
1134        FEnumType enu = (FEnumType) type;
1135        if (getName().equals(enu.getName())) 
1136            return true;
1137        int n1 = getNumFEnumLiteralType(), n2 = enu.getNumFEnumLiteralType();
1138        if (n1 != n2)
1139            return n1 == 0 || n2 == 0;
1140        Iterator<FEnumLiteralType> it = enu.getFEnumLiteralTypes().iterator();
1141        for (FEnumLiteralType elt : getFEnumLiteralTypes())
1142            if (!elt.getName().equals(it.next().getName()))
1143                return false;
1144        return true;
1145    }
1146    eq FFunctionType.typeMatches(CommonType type, boolean compatible, boolean allowUnknown) {
1147        if (!dimensionCompatible(type, allowUnknown) || !type.isFunction())
1148            return false;
1149        FFunctionType obj = (FFunctionType) type;
1150        if (!compatible && !getName().equals(obj.getName())) 
1151            return false;
1152        if (getNumInput() != obj.getNumInput())
1153            return false;
1154        if (getNumOutput() > obj.getNumOutput())
1155            return false;
1156       
1157        return typeMatches(getInputs(), obj.getInputs(), compatible, allowUnknown) &&
1158                typeMatches(getOutputs(), obj.getOutputs(), compatible, allowUnknown);
1159    }
1160   
1161    syn boolean FType.typeMatches(Iterable<FRecordComponentType> comps1, Iterable<FRecordComponentType> comps2, 
1162            boolean compatible, boolean allowUnknown) {
1163        Iterator<FRecordComponentType> i1 = comps1.iterator();
1164        Iterator<FRecordComponentType> i2 = comps2.iterator();
1165        while (i1.hasNext()) {
1166            if (!i1.next().typeMatches(i2.next(), compatible, allowUnknown))
1167                return false;
1168        }
1169        return true;
1170    }
1171   
1172    syn boolean FRecordComponentType.typeMatches(FRecordComponentType type, boolean compatible, boolean allowUnknown) {
1173        return getName().equals(type.getName()) && getFType().typeMatches(type.getFType(), compatible, allowUnknown);
1174    }
1175   
1176    syn boolean FType.dimensionCompatible(CommonType type) = dimensionCompatible(type, false);
1177    syn boolean FType.dimensionCompatible(CommonType type, boolean allowUnknown) = 
1178        size().equivalent(type.size(), allowUnknown);
1179
1180    syn boolean FType.looslyDimensionCompatible(FType type, boolean allowUnknown) = 
1181        isScalar() || type.isScalar() || dimensionCompatible(type, allowUnknown);
1182
1183    /**
1184     * Check if types are compatible except for array lengths, i.e. if the scalar types and the number of array
1185     * dimensions are the same.
1186     */
1187    syn boolean FType.typeCompatibleExceptLengths(FType type) = 
1188        ndims() == type.ndims() && scalarType().typeCompatible(type.scalarType());
1189
1190}
1191
1192
1193aspect FTypeEquivalent {
1194
1195    /* The function equivalentTo is used in equation type checking where equations like
1196     * x=0
1197     * where x is declared as Real is ok.
1198     */
1199    syn boolean FType.equivalentTo(CommonType type) = equivalentTo(type, false);
1200
1201    syn boolean FType.equivalentTo(CommonType type, boolean allowUnknown) = false;
1202    eq FPrimitiveNumericType.equivalentTo(CommonType type, boolean allowUnknown) = 
1203        type.isNumeric() && dimensionCompatible(type, allowUnknown);
1204    eq FBooleanType.equivalentTo(CommonType type, boolean allowUnknown) = 
1205        type.isBoolean() && dimensionCompatible(type, allowUnknown);
1206    eq FStringType.equivalentTo(CommonType type, boolean allowUnknown) = 
1207        type.isString() && dimensionCompatible(type, allowUnknown);
1208    eq FRecordType.equivalentTo(CommonType type, boolean allowUnknown) = 
1209        typeMatches(type, false, allowUnknown);
1210    eq FEnumType.equivalentTo(CommonType type, boolean allowUnknown) = 
1211        typeMatches(type, false, allowUnknown);
1212    eq FExternalObjectType.equivalentTo(CommonType type, boolean allowUnknown) = 
1213        typeMatches(type, false, allowUnknown);
1214    eq FFunctionType.equivalentTo(CommonType type, boolean allowUnknown) =
1215        typeMatches(type, false, allowUnknown);
1216
1217    /**
1218     * Check if types are equivalent except for array lengths, i.e. if the scalar types and the number of array
1219     * dimensions are the same.
1220     */
1221    syn boolean FType.equivalentExceptLengths(FType type) = 
1222        ndims() == type.ndims() && scalarType().equivalentTo(type.scalarType());
1223
1224}
1225
1226
1227aspect BuiltInFlatTypes {
1228
1229    public static final FRealType    FRealType.SCALAR                   = new FRealType(Size.SCALAR);
1230    public static final FIntegerType FIntegerType.SCALAR                = new FIntegerType(Size.SCALAR);
1231    public static final FBooleanType FBooleanType.SCALAR                = new FBooleanType(Size.SCALAR);
1232    public static final FStringType  FStringType.SCALAR                 = new FStringType(Size.SCALAR);
1233    public static final FUnknownType FUnknownType.SCALAR                = new FUnknownType(Size.SCALAR);
1234    public static final FNoType      FNoType.SCALAR                     = new FNoType(Size.SCALAR);
1235
1236    syn boolean FType.isReal()                  = false;
1237    eq FArrayType.isReal()                      = getFPrimitiveType().isReal();
1238    eq FRealType.isReal()                       = true;
1239    syn boolean FType.isInteger()               = false;
1240    eq FArrayType.isInteger()                   = getFPrimitiveType().isInteger();
1241    eq FIntegerType.isInteger()                 = true;
1242    syn boolean FType.isBoolean()               = false;
1243    eq FArrayType.isBoolean()                   = getFPrimitiveType().isBoolean();
1244    eq FBooleanType.isBoolean()                 = true;
1245    syn boolean FType.isString()                = false;
1246    eq FArrayType.isString()                    = getFPrimitiveType().isString();
1247    eq FStringType.isString()                   = true;
1248    syn boolean FType.isRecord()                = false;
1249    eq FRecordType.isRecord()                   = true;
1250    syn boolean FType.isOperatorRecord()        = false;
1251    eq FOperatorRecordType.isOperatorRecord()   = true;
1252    syn boolean FType.isEnum()                  = false;
1253    eq FArrayType.isEnum()                      = getFPrimitiveType().isEnum();
1254    eq FEnumType.isEnum()                       = true;
1255    syn boolean FType.isExternalObject()        = false;
1256    eq FExternalObjectType.isExternalObject()   = true;
1257    syn boolean FType.isFunction()              = false;
1258    eq FFunctionType.isFunction()               = true;
1259
1260    syn boolean FType.isArray()  = getSize() != Size.SCALAR;
1261    syn boolean FType.isScalar() = getSize() == Size.SCALAR;
1262
1263    syn boolean FType.isNumeric()        = false;
1264    eq FArrayType.isNumeric()            = getFPrimitiveType().isNumeric();
1265    eq FPrimitiveNumericType.isNumeric() = true;
1266
1267    syn boolean FType.isPrimitive() = false;
1268    eq FPrimitiveType.isPrimitive() = true;
1269
1270    syn boolean FType.isRealScalar()            = isScalar() && isReal();
1271    syn boolean FType.isRealArray()             = isReal() && isArray();
1272    syn boolean FType.isIntegerScalar()         = isScalar() && isInteger();
1273    syn boolean FType.isIntegerArray()          = isInteger() && isArray();
1274    syn boolean FType.isBooleanScalar()         = isScalar() && isBoolean();
1275    syn boolean FType.isBooleanArray()          = isBoolean() && isArray();
1276    syn boolean FType.isStringScalar()          = isScalar() && isString();
1277    syn boolean FType.isStringArray()           = isString() && isArray();
1278    syn boolean FType.isRecordScalar()          = isScalar() && isRecord();
1279    syn boolean FType.isRecordArray()           = isRecord() && isArray();
1280    syn boolean FType.isEnumScalar()            = isScalar() && isEnum();
1281    syn boolean FType.isEnumArray()             = isEnum() && isArray();
1282    syn boolean FType.isNumericScalar()         = isScalar() && isNumeric();
1283    syn boolean FType.isNumericArray()          = isNumeric() && isArray();
1284    syn boolean FType.isPrimitiveScalar()       = isScalar() && isPrimitive();
1285    syn boolean FType.isPrimitiveArray()        = isPrimitive() && isArray();
1286    syn boolean FType.isExternalObjectScalar()  = isScalar() && isExternalObject();
1287    syn boolean FType.isExternalObjectArray()   = isExternalObject() && isArray();
1288
1289    syn boolean FType.isType(FType type);
1290    eq FArrayType.isType(FType type) = getFPrimitiveType().isType(type);
1291    eq FCellType.isType(FType type) {
1292        throw new UnsupportedOperationException("isType() is not supported for class type " + getClass().getSimpleName());
1293    }
1294    eq FRealType.isType(FType type) = type.isReal();
1295    eq FIntegerType.isType(FType type) = type.isInteger();
1296    eq FBooleanType.isType(FType type) = type.isBoolean();
1297    eq FStringType.isType(FType type) = type.isString();
1298    eq FRecordType.isType(FType type) = type.isRecord();
1299    eq FOperatorRecordType.isType(FType type) = type.isOperatorRecord();
1300    eq FEnumType.isType(FType type) = type.isEnum();
1301    eq FExternalObjectType.isType(FType type) = type.isExternalObject();
1302    eq FFunctionType.isType(FType type) = type.isFunction();
1303
1304    public abstract boolean FType.containsType(FType type);
1305    syn boolean FArrayType.containsType(FType type) = getFPrimitiveType().containsType(type);
1306    syn boolean FCellType.containsType(FType type) = isType(type);
1307    eq FRecordType.containsType(FType type) {
1308        for (FRecordComponentType component : getComponents())
1309            if (component.getFType().containsType(type))
1310                return true;
1311        return false;
1312    }
1313
1314    syn boolean FType.containsReal()           = containsType(FRealType.SCALAR);
1315    syn boolean FType.containsInteger()        = containsType(FIntegerType.SCALAR);
1316    syn boolean FType.containsBoolean()        = containsType(FBooleanType.SCALAR);
1317
1318    // For type comparisons ending with "or records of such types"
1319    public abstract class FType {
1320        protected static interface TypeChecker {
1321            public boolean check(FType t);
1322        }
1323       
1324        private static final TypeChecker IS_REAL = new TypeChecker() {
1325            public boolean check(FType t) { return t.isReal(); }
1326        };
1327    }
1328
1329    protected boolean FType.recursiveTypeCheck(TypeChecker ch) {
1330        return ch.check(this);
1331    }
1332
1333    protected boolean FRecordType.recursiveTypeCheck(TypeChecker ch) {
1334        for (FRecordComponentType c : getComponents())
1335            if (!c.getFType().recursiveTypeCheck(ch))
1336                return false;
1337        return true;
1338    }
1339
1340    syn boolean FType.onlyContainsReal() = recursiveTypeCheck(IS_REAL);
1341
1342    syn boolean FType.hasAdd()        = false;
1343    eq FPrimitiveNumericType.hasAdd() = true;
1344    eq FStringType.hasAdd()           = true;
1345
1346    syn boolean FType.hasNeg()        = false;
1347    eq FPrimitiveNumericType.hasNeg() = true;
1348
1349    syn boolean FType.canBeIndex() = false;
1350    eq FIntegerType.canBeIndex()   = true;
1351    eq FBooleanType.canBeIndex()   = true;
1352    eq FEnumType.canBeIndex()      = true;
1353
1354    syn int FType.ndims() = getSize().ndims();
1355
1356    syn Size FType.size() = getSize();
1357
1358    syn FType FType.sizedType(Size s) = 
1359        (s == Size.SCALAR) ? scalarType() : arrayType(s);
1360
1361    syn FType FType.scalarType() {
1362        if (isScalar() && !isFunction())
1363            return this;
1364        FType copy = (FType) fullCopy();
1365        copy.setSize(Size.SCALAR);
1366        return copy;
1367    }
1368    eq FArrayType.scalarType()          = getFPrimitiveType();
1369    eq FUnknownType.scalarType()        = this;
1370    eq FNoType.scalarType()             = this;
1371    eq FRealType.scalarType()           = FRealType.SCALAR;
1372    eq FIntegerType.scalarType()        = FIntegerType.SCALAR;
1373    eq FBooleanType.scalarType()        = FBooleanType.SCALAR;
1374    eq FStringType.scalarType()         = FStringType.SCALAR;
1375
1376    syn boolean FType.isUnknown() = false;
1377    eq FUnknownType.isUnknown()   = true;
1378
1379    syn FType ASTNode.fUnknownType() = FUnknownType.SCALAR;
1380    syn FType ASTNode.fNoType()      = FNoType.SCALAR;
1381
1382    syn FPrimitiveType ASTNode.fRealScalarType()            = FRealType.SCALAR;
1383    syn FPrimitiveType ASTNode.fIntegerScalarType()         = FIntegerType.SCALAR;
1384    syn FPrimitiveType ASTNode.fBooleanScalarType()         = FBooleanType.SCALAR;
1385    syn FPrimitiveType ASTNode.fStringScalarType()          = FStringType.SCALAR;
1386
1387    syn FPrimitiveType ASTNode.fRealArrayType(Size size)            = new FRealType(size);
1388    syn FPrimitiveType ASTNode.fIntegerArrayType(Size size)         = new FIntegerType(size);
1389    syn FPrimitiveType ASTNode.fBooleanArrayType(Size size)         = new FBooleanType(size);
1390    syn FPrimitiveType ASTNode.fStringArrayType(Size size)          = new FStringType(size);
1391
1392    syn FPrimitiveType ASTNode.fRealType(Size size) = 
1393        (size == Size.SCALAR) ? fRealScalarType() : fRealArrayType(size);
1394    syn FPrimitiveType ASTNode.fIntegerType(Size size) = 
1395        (size == Size.SCALAR) ? fIntegerScalarType() : fIntegerArrayType(size);
1396    syn FPrimitiveType ASTNode.fBooleanType(Size size) = 
1397        (size == Size.SCALAR) ? fBooleanScalarType() : fBooleanArrayType(size);
1398    syn FPrimitiveType ASTNode.fStringType(Size size) = 
1399        (size == Size.SCALAR) ? fStringScalarType() : fStringArrayType(size);
1400
1401    syn FType FType.arrayType(Size size) {
1402        if (size == size())
1403            return this;
1404        FType copy = (FType) fullCopy();
1405        copy.setSize(size);
1406        return copy;
1407    }
1408    eq FArrayType.arrayType(Size size) = getFPrimitiveType().arrayType(size);
1409    eq FUnknownType.arrayType(Size size)        = this;
1410
1411    /**
1412     * Create a literal with the zero value for this type, if applicable.
1413     *
1414     * For operator records, a record constructor call is created.
1415     */
1416     public FExp FType.sizedZeroLiteral() {
1417         if (isArray()) {
1418             return size().fillDimsOfExp(createArrayZeroLiteralNoSize());
1419         } else {
1420             return zeroLiteral();
1421         }
1422     }
1423
1424     /**
1425      * Create a literal with the zero value for the scalar version of this type, if applicable.
1426      *
1427      * For operator records, a record constructor call is created.
1428      */
1429    public FExp FType.zeroLiteral()        { return new FNoExp(); }
1430    public FExp FRealType.zeroLiteral()    { return new FRealLitExp(0.0); }
1431    public FExp FIntegerType.zeroLiteral() { return new FIntegerLitExp(0); }
1432    public FExp FStringType.zeroLiteral()  { return new FStringLitExp(""); }
1433    public FExp FBooleanType.zeroLiteral() { return new FBooleanLitExpFalse(); }
1434    public FExp FEnumType.zeroLiteral()    { return new FEnumLitExp(this, 1); }
1435    public FExp FRecordType.zeroLiteral()  {
1436        if (getFClass() != null) {
1437            List<FExp> args = new List<FExp>();
1438            for (FRecordComponentType t : getComponents()) {
1439                args.add(t.getFType().zeroLiteral());
1440            }
1441            return new FRecordConstructor(new FRecordAccess(getName()), args);
1442        } else {
1443            List<InstFunctionArgument> args = new List<InstFunctionArgument>();
1444            int i = 1;
1445            for (FRecordComponentType t : getComponents()) {
1446                args.add(new InstPositionalArgument(t.getFType().zeroLiteral(), i++));
1447            }
1448            return new InstRecordConstructor(new InstGlobalAccess(InstAccess.fromName(getName())), args);
1449        }
1450    }
1451
1452    /**
1453     * Create a fill or zeros expression, as appropriate to describe a zero-value
1454     * literal of this type, without the size filled in.
1455     */
1456   protected FArrayDimAsArgsExp FType.createArrayZeroLiteralNoSize() {
1457       return new FFillExp(new List<FExp>(), zeroLiteral());
1458   }
1459   protected FArrayDimAsArgsExp FPrimitiveNumericType.createArrayZeroLiteralNoSize() {
1460       return new FZeros();
1461   }
1462
1463    /**
1464     * Create a literal with the given integer value for this type, if applicable.
1465     */
1466   public FExp FType.createSizedLiteral(int v) {
1467       if (isArray()) {
1468           return size().fillDimsOfExp(createArrayLiteralNoSize(v));
1469       } else {
1470           return createLiteral(v);
1471       }
1472   }
1473
1474   /**
1475    * Create a literal with the given integer value for the scalar version of this type, if applicable.
1476    */
1477    public FExp FType.createLiteral(int v)        { throw new UnsupportedOperationException(); }
1478    public FExp FIntegerType.createLiteral(int v) { return new FIntegerLitExp(v); }
1479    public FExp FRealType.createLiteral(int v)    { return new FRealLitExp(v); }
1480    public FExp FEnumType.createLiteral(int v)    { return new FEnumLitExp(this, v); }
1481    public FExp FBooleanType.createLiteral(int v) { 
1482        return (v == 1 || v == 2) ? FBooleanLitExp.create(v == 2) : null; 
1483    }
1484
1485    /**
1486     * Create a fill, zeros or ones expression, as appropriate for the given integer value,
1487     * without the size filled in.
1488     */
1489   protected FArrayDimAsArgsExp FType.createArrayLiteralNoSize(int v) {
1490       return new FFillExp(new List<FExp>(), createLiteral(v));
1491   }
1492   protected FArrayDimAsArgsExp FPrimitiveNumericType.createArrayLiteralNoSize(int v) {
1493       switch (v) {
1494       case 0:
1495           return new FZeros();
1496       case 1:
1497           return new FOnes();
1498       default:
1499           return super.createArrayLiteralNoSize(v);
1500       }
1501   }
1502
1503}
1504
1505
1506aspect ExternalObjectTypes {
1507   
1508    public interface FCallable {
1509        public boolean isConstructor();
1510        public boolean isDestructor();
1511    }
1512
1513    syn boolean FExp.isConstructorCall()    = false;
1514    eq InstFunctionCall.isConstructorCall() = getName().myInstClassDecl().isConstructor();
1515    eq FFunctionCall.isConstructorCall()    = myFCallable().isConstructor();
1516    syn boolean FExp.isDestructorCall()     = false;
1517    eq InstFunctionCall.isDestructorCall()  = getName().myInstClassDecl().isDestructor();
1518    eq FFunctionCall.isDestructorCall()     = myFCallable().isDestructor();
1519
1520    syn boolean InstClassDecl.isDestructor()      = name().equals("destructor") && inExternalObject();
1521    syn boolean FFunctionDecl.isDestructor()      = false;
1522    syn boolean FFunctionVariable.isDestructor()  = false;
1523    eq FDestructorDecl.isDestructor()             = true;
1524    syn boolean InstClassDecl.isConstructor()     = name().equals("constructor") && inExternalObject();
1525    syn boolean FFunctionDecl.isConstructor()     = false;
1526    syn boolean FFunctionVariable.isConstructor() = false;
1527    eq FConstructorDecl.isConstructor()           = true;
1528
1529    syn boolean FExternalObjectVariable.hasConstructorCall() {
1530        if (hasBindingExp()) {
1531            return getBindingExp().isConstructorCall();
1532        } else {
1533            for (FAccessExp use : uses()) {
1534                if (use.isConstructorAssign()) {
1535                    return true;
1536                }
1537            }
1538            return false;
1539        }
1540       
1541    }
1542
1543    inh boolean CommonAccessExp.isConstructorAssign();
1544    eq BaseNode.getChild().isConstructorAssign() = false;
1545    eq FEquation.getLeft().isConstructorAssign() = getRight().isConstructorCall();
1546
1547}
1548
1549aspect AliasTypes {
1550
1551    syn boolean FType.typeAliasCompatible(FType type) = typeCompatible(type);
1552    eq FRealType.typeAliasCompatible(FType type)      = type.isReal() && typeCompatible(type);
1553
1554}
Note: See TracBrowser for help on using the repository browser.