Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 82.7% 633 / 0 / 765
Functions: -% 0 / 1 / 1
Branches: 74.1% 401 / 0 / 541

OMCompiler/Compiler/NFFrontEnd/NFFunction.mo
Line Branch Exec Source
1 /*
2 * This file is part of OpenModelica.
3 *
4 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
5 * c/o Linköpings universitet, Department of Computer and Information Science,
6 * SE-58183 Linköping, Sweden.
7 *
8 * All rights reserved.
9 *
10 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
11 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
12 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
13 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
14 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
15 *
16 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
17 * Public License (OSMC-PL) are obtained from OSMC, either from the above
18 * address, from the URLs:
19 * http://www.openmodelica.org or
20 * https://github.com/OpenModelica/ or
21 * http://www.ida.liu.se/projects/OpenModelica,
22 * and in the OpenModelica distribution.
23 *
24 * GNU AGPL version 3 is obtained from:
25 * https://www.gnu.org/licenses/licenses.html#GPL
26 *
27 * This program is distributed WITHOUT ANY WARRANTY; without
28 * even the implied warranty of MERCHANTABILITY or FITNESS
29 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
30 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
31 *
32 * See the full OSMC Public License conditions for more details.
33 *
34 */
35
36 encapsulated package NFFunction
37
38 import Absyn;
39 import AbsynUtil;
40 import Expression = NFExpression;
41 import Pointer;
42 import NFInstNode.InstNode;
43 import Type = NFType;
44 import NFPrefixes.*;
45 import List;
46 import FunctionDerivative = NFFunctionDerivative;
47 import FunctionInverse = NFFunctionInverse;
48 import NFModifier.Modifier;
49 import BaseModelica;
50
51 protected
52 import ErrorExt;
53 import Inst = NFInst;
54 import NFInst.InstSettings;
55 import Binding = NFBinding;
56 import Config;
57 import DAE;
58 import DAEDumpTypes;
59 import Error;
60 import InstBasics;
61 import Class = NFClass;
62 import Component = NFComponent;
63 import Attributes = NFAttributes;
64 import StringUtil;
65 import Typing = NFTyping;
66 import TypeCheck = NFTypeCheck;
67 import Util;
68 import ComponentRef = NFComponentRef;
69 import NFInstNode.CachedData;
70 import Lookup = NFLookup;
71 import ClassTree = NFClassTree.ClassTree;
72 import Prefixes = NFPrefixes;
73 import NFLookupState.LookupState;
74 import Record = NFRecord;
75 import NFTyping.ClassScope;
76 import MatchKind = NFTypeCheck.MatchKind;
77 import Restriction = NFRestriction;
78 import SCode;
79 import Dimension = NFDimension;
80 import Statement = NFStatement;
81 import Sections = NFSections;
82 import Algorithm = NFAlgorithm;
83 import OperatorOverloading = NFOperatorOverloading;
84 import MetaModelica.Dangerous.listReverseInPlace;
85 import Array;
86 import ElementSource;
87 import SCodeUtil;
88 import IOStream;
89 import ComplexType = NFComplexType;
90 import InstContext = NFInstContext;
91 import UnorderedSet;
92 import Graph;
93 import FlatModelicaUtil = NFFlatModelicaUtil;
94 import Subscript = NFSubscript;
95
96 public
97
98 type NamedArg = tuple<String, Expression>;
99
100 uniontype TypedArg
101 record TYPED_ARG
102 Option<String> name;
103 Expression value;
104 Type ty;
105 Variability var;
106 Purity purity;
107 end TYPED_ARG;
108 end TypedArg;
109
110 public
111 type SlotType = enumeration(
112 POSITIONAL "Only accepts positional arguments.",
113 NAMED "Only accepts named argument.",
114 GENERIC "Accepts both positional and named arguments."
115 ) "Determines which type of argument a slot accepts.";
116
117 type SlotEvalStatus = enumeration(NOT_EVALUATED, EVALUATING, EVALUATED);
118
119 uniontype Slot
120 record SLOT
121 InstNode node;
122 SlotType ty;
123 Option<Expression> default;
124 Option<TypedArg> arg;
125 Integer index;
126 SlotEvalStatus evalStatus;
127 end SLOT;
128
129 function positional
130 input Slot slot;
131 output Boolean pos;
132 algorithm
133 pos := match slot.ty
134 case SlotType.POSITIONAL then true;
135 case SlotType.GENERIC then true;
136 else false;
137 end match;
138 end positional;
139
140 function named
141 input Slot slot;
142 output Boolean pos;
143 algorithm
144 pos := match slot.ty
145 case SlotType.NAMED then true;
146 case SlotType.GENERIC then true;
147 else false;
148 end match;
149 end named;
150
151 function name
152 input Slot slot;
153 output String name = InstNode.name(slot.node);
154 end name;
155
156 function hasNode
157 input InstNode node;
158 input Slot slot;
159 output Boolean hasNode = InstNode.refEqual(node, slot.node);
160 end hasNode;
161 end Slot;
162
163 public
164 encapsulated
165 uniontype FunctionMatchKind
166 import Dimension = NFDimension;
167
168 record EXACT "Exact match." end EXACT;
169
170 record CAST "Matched by casting one or more arguments. e.g. Integer to Real" end CAST;
171
172 record GENERIC "Matched with a generic type on one or more arguments e.g. function F<T> input T i; end F; F(1)"
173 end GENERIC;
174
175 record VECTORIZED "Matched by vectorization"
176 list<Dimension> vectDims;
177 // When vectorizing a call exact argument matches are allowed to not be vectorized
178 // Instead they are added to each call as is.
179 // This list represents which args should be vectorized.
180 list<Integer> vectorizedArgs;
181 FunctionMatchKind baseMatch;
182 end VECTORIZED;
183
184 record NOT_COMPATIBLE end NOT_COMPATIBLE;
185
186 function isValid
187 input FunctionMatchKind mk;
188 output Boolean b;
189 algorithm
190 b := match mk
191 case NOT_COMPATIBLE() then false;
192 else true;
193 end match;
194 end isValid;
195
196 function isExact
197 input FunctionMatchKind mk;
198 output Boolean b;
199 algorithm
200 b := match mk
201 case EXACT() then true;
202 else false;
203 end match;
204 end isExact;
205
206 function isVectorized
207 input FunctionMatchKind mk;
208 output Boolean b;
209 algorithm
210 b := match mk
211 case VECTORIZED() then true;
212 else false;
213 end match;
214 end isVectorized;
215
216 function isExactVectorized
217 input FunctionMatchKind mk;
218 output Boolean b;
219 algorithm
220 b := match mk
221 case VECTORIZED(baseMatch = EXACT()) then true;
222 else false;
223 end match;
224 end isExactVectorized;
225
226 end FunctionMatchKind;
227
228 constant FunctionMatchKind EXACT_MATCH = FunctionMatchKind.EXACT();
229 constant FunctionMatchKind CAST_MATCH = FunctionMatchKind.CAST();
230 constant FunctionMatchKind GENERIC_MATCH = FunctionMatchKind.GENERIC();
231 constant FunctionMatchKind NO_MATCH = FunctionMatchKind.NOT_COMPATIBLE();
232
233 encapsulated
234 uniontype MatchedFunction
235 import NFFunction.Function;
236 import NFFunction.TypedArg;
237 import NFFunction.FunctionMatchKind;
238
239 record MATCHED_FUNC
240 Function func;
241 list<TypedArg> args;
242 FunctionMatchKind mk;
243 end MATCHED_FUNC;
244
245 function getExactMatches
246 input list<MatchedFunction> matchedFunctions;
247 output list<MatchedFunction> outFuncs = list(mf for mf guard(FunctionMatchKind.isExact(mf.mk)) in matchedFunctions);
248 end getExactMatches;
249
250 function getExactVectorizedMatches
251 input list<MatchedFunction> matchedFunctions;
252 output list<MatchedFunction> outFuncs =
253 list(mf for mf guard(FunctionMatchKind.isExactVectorized(mf.mk)) in matchedFunctions);
254 end getExactVectorizedMatches;
255
256 function isVectorized
257 input MatchedFunction mf;
258 output Boolean b = FunctionMatchKind.isVectorized(mf.mk);
259 end isVectorized;
260
261 end MatchedFunction;
262
263 type FunctionStatus = enumeration(
264 BUILTIN "A builtin function.",
265 INITIAL "The initial status.",
266 EVALUATED "Constants in the function has been evaluated by EvalConstants.",
267 SIMPLIFIED "The function has been simplified by SimplifyModel.",
268 COLLECTED "The function has been added to the function tree."
269 );
270
271 uniontype Function
272 record FUNCTION
273 Absyn.Path path;
274 NFInstNode.NodeHandle node "Weakly: the function's scope owns it, and its
275 cache holds this function.";
276 list<InstNode> inputs;
277 list<NFInstNode.NodeHandle> outputs "Weakly: a record constructor's output
278 is typed by the constructor node, whose cache holds this function.";
279 list<InstNode> locals;
280 Option<UnorderedSet<InstNode>> interfaceDiffInfo;
281 list<Slot> slots;
282 Type returnType;
283 DAE.FunctionAttributes attributes;
284 list<FunctionDerivative> derivatives;
285 list<Integer> derivedInputs;
286 array<FunctionInverse> inverses;
287 Pointer<FunctionStatus> status;
288 Pointer<Integer> callCounter "Used during function evaluation to limit recursion.";
289 end FUNCTION;
290
291 function new
292 input Absyn.Path path;
293 input InstNode node;
294 input list<SCode.Comment> comments;
295 output Function fn;
296 protected
297 list<InstNode> inputs, outputs, locals;
298 DAE.FunctionAttributes attr;
299 FunctionStatus status;
300 algorithm
301 23764 (inputs, outputs, locals) := collectParams(node);
302 23761 attr := makeAttributes(node, inputs, outputs, comments);
303 // Make sure builtin functions aren't added to the function tree.
304
2/2
✓ Branch 1 taken 17360 times.
✓ Branch 2 taken 6401 times.
23761 status := if isBuiltinAttr(attr) then FunctionStatus.COLLECTED else FunctionStatus.INITIAL;
305
4/4
✓ Branch 0 taken 23215 times.
✓ Branch 1 taken 23761 times.
✓ Branch 2 taken 23215 times.
✓ Branch 3 taken 23761 times.
46976 fn := FUNCTION(path, InstNode.handle(node), inputs,
306 list(InstNode.handle(o) for o in outputs), locals, NONE(), {}, Type.UNKNOWN(),
307 attr, {}, {}, listArray({}), Pointer.create(status), Pointer.create(0));
308 end new;
309
310 function lookupFunctionSimple
311 input String functionName;
312 input InstNode scope;
313 input InstContext.Type context;
314 output ComponentRef functionRef;
315 protected
316 InstNode found_scope;
317 ComponentRef prefix;
318 algorithm
319 1998 (functionRef, found_scope) :=
320 Lookup.lookupFunctionNameSilent(Absyn.CREF_IDENT(functionName, {}), scope, context);
321 1983 prefix := ComponentRef.fromNodeList(InstNode.scopeList(found_scope));
322 1983 functionRef := ComponentRef.append(functionRef, prefix);
323 end lookupFunctionSimple;
324
325 function lookupFunction
326 input Absyn.ComponentRef functionName;
327 input InstNode scope;
328 input InstContext.Type context;
329 input SourceInfo info;
330 output ComponentRef functionRef;
331 protected
332 InstNode found_scope;
333 Absyn.Path functionPath;
334 ComponentRef prefix;
335 Boolean is_class;
336 algorithm
337 try
338 // Make sure the name is a path.
339 109929 functionPath := AbsynUtil.crefToPath(functionName);
340 else
341 ✗ Error.addSourceMessageAndFail(Error.SUBSCRIPTED_FUNCTION_CALL,
342 {Dump.printComponentRefStr(functionName)}, info);
343 end try;
344
345 109929 (functionRef, found_scope) := Lookup.lookupFunctionName(functionName, scope, context, info);
346 // If we found a function class we include the root in the prefix, but if we
347 // instead found a component (i.e. a functional parameter) we don't.
348 109926 is_class := InstNode.isClass(ComponentRef.node(functionRef));
349 109926 prefix := ComponentRef.fromNodeList(InstNode.scopeList(found_scope, includeRoot = is_class));
350 109926 functionRef := ComponentRef.append(functionRef, prefix);
351 end lookupFunction;
352
353 function instFunction
354 input Absyn.ComponentRef functionName;
355 input InstNode scope;
356 input InstContext.Type context;
357 input SourceInfo info;
358 output ComponentRef fn_ref;
359 output InstNode fn_node;
360 output Boolean specialBuiltin;
361 algorithm
362 109929 fn_ref := lookupFunction(functionName, scope, context, info);
363 109926 (fn_ref, fn_node, specialBuiltin) := instFunctionRef(fn_ref, context, info);
364 end instFunction;
365
366 function instFunctionRef
367 input output ComponentRef fn_ref;
368 input InstContext.Type context;
369 input SourceInfo info;
370 output InstNode fn_node;
371 output Boolean specialBuiltin;
372 protected
373 CachedData cache;
374 InstNode parent;
375 algorithm
376 112110 fn_node := InstNode.classScope(ComponentRef.node(fn_ref));
377 112110 cache := InstNode.getFuncCache(fn_node);
378
379 // Check if a cached instantiation of this function already exists.
380 (fn_node, specialBuiltin) := match cache
381 90675 case CachedData.FUNCTION() then (fn_node, cache.specialBuiltin);
382 else
383 algorithm
384
4/4
✓ Branch 2 taken 20347 times.
✓ Branch 3 taken 1088 times.
✓ Branch 5 taken 14701 times.
✓ Branch 6 taken 5646 times.
21435 parent := if InstNode.isRedeclare(ComponentRef.node(fn_ref)) or ComponentRef.isSimple(fn_ref) then
385 InstNode.EMPTY_NODE() else ComponentRef.node(ComponentRef.rest(fn_ref));
386
387
2/2
✓ Branch 1 taken 21255 times.
✓ Branch 2 taken 180 times.
21435 if not InstNode.isComponent(parent) then
388 parent := InstNode.EMPTY_NODE();
389 end if;
390 21435 then
391 instFunction2(ComponentRef.toPath(fn_ref), fn_node, context, info, parent);
392 end match;
393 end instFunctionRef;
394
395 function instFunctionNode
396 "Instantiates the given InstNode as a function."
397 input output InstNode node;
398 input InstContext.Type context;
399 input SourceInfo info;
400 protected
401 CachedData cache;
402 algorithm
403 3554 cache := InstNode.getFuncCache(node);
404
405 () := match cache
406 case CachedData.FUNCTION() then ();
407 else
408 algorithm
409 3034 node := instFunction2(InstNode.fullPath(node), node, context, info);
410 then
411 ();
412 end match;
413 end instFunctionNode;
414
415 function instFunction2
416 input Absyn.Path fnPath;
417 input output InstNode fnNode;
418 input InstContext.Type context;
419 input SourceInfo info;
420 input InstNode parent = InstNode.EMPTY_NODE();
421 output Boolean specialBuiltin;
422 protected
423 SCode.Element def = InstNode.definition(fnNode);
424 algorithm
425 (fnNode, specialBuiltin) := match def
426 local
427 SCode.ClassDef cdef;
428 Function fn;
429 Absyn.ComponentRef cr;
430 InstNode node;
431 list<SCode.Comment> cmts;
432
433 case SCode.CLASS() guard SCodeUtil.isOperatorRecord(def)
434 algorithm
435 14 fnNode := instFunction3(fnNode, context, info);
436 14 fnNode := OperatorOverloading.instConstructor(fnPath, fnNode, context, info);
437 then
438 (fnNode, false);
439
440 case SCode.CLASS() guard SCodeUtil.isRecord(def)
441 algorithm
442 116 fnNode := instFunction3(fnNode, context, info);
443 116 fnNode := Record.instDefaultConstructor(fnPath, fnNode, context, info);
444 then
445 (fnNode, false);
446
447 case SCode.CLASS(restriction = SCode.R_OPERATOR(), classDef = SCode.PARTS())
448 algorithm
449 135 fnNode := instFunction3(fnNode, context, info);
450 135 fnNode := OperatorOverloading.instOperatorFunctions(fnNode, context, info);
451 then
452 (fnNode, false);
453
454 case SCode.CLASS(classDef = cdef as SCode.OVERLOAD())
455 algorithm
456
2/2
✓ Branch 0 taken 870 times.
✓ Branch 1 taken 435 times.
1305 for p in cdef.pathLst loop
457 870 cr := AbsynUtil.pathToCref(p);
458 870 (_,node,specialBuiltin) := instFunction(cr, fnNode, context, info);
459
2/2
✓ Branch 1 taken 870 times.
✓ Branch 2 taken 870 times.
1740 for f in getCachedFuncs(node) loop
460 870 fnNode := InstNode.cacheAddFunc(fnNode, f, specialBuiltin);
461 end for;
462 end for;
463 then
464 (fnNode, false);
465
466 // An enumeration type name used as an operator, create a conversion
467 // operator EnumTypeName(Integer) => EnumTypeName for it.
468 case SCode.CLASS()
469 guard InstNode.isEnumerationType(fnNode)
470 algorithm
471 1 node := makeEnumConversionOp(fnNode);
472 1 node := InstNode.makeRootClass(node, parent);
473 1 (node, cmts) := instFunction3(node, context, info);
474 1 fn := new(fnPath, node, cmts);
475 1 fnNode := InstNode.cacheAddFunc(fnNode, fn, false);
476 then
477 (fnNode, false);
478
479 case SCode.CLASS()
480 algorithm
481
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 23730 times.
23768 if SCodeUtil.isOperator(def) then
482 38 OperatorOverloading.checkOperatorRestrictions(fnNode);
483 end if;
484
485 23768 fnNode := InstNode.makeRootClass(fnNode, parent);
486 23768 (fnNode, cmts) := instFunction3(fnNode, context, info);
487 23763 fn := new(fnPath, fnNode, cmts);
488 23760 specialBuiltin := isSpecialBuiltin(fn);
489 23760 fn.derivatives := FunctionDerivative.instDerivatives(fnNode, fn);
490 23760 fn.inverses := FunctionInverse.instInverses(fnNode, fn);
491 23759 fn.derivedInputs := instPartialDerivedVars(def.classDef, fn.inputs, fn, context, info);
492 23759 fnNode := InstNode.cacheAddFunc(fnNode, fn, specialBuiltin);
493 23759 then
494 (fnNode, specialBuiltin);
495
496 end match;
497 end instFunction2;
498
499 function instFunction3
500 input output InstNode fnNode;
501 input InstContext.Type context;
502 input SourceInfo info;
503 output list<SCode.Comment> cmts;
504 protected
505 SCode.Element def;
506 Integer numError = Error.getNumErrorMessages();
507 InstContext.Type fn_context = InstContext.set(context, NFInstContext.FUNCTION);
508 algorithm
509 try
510 24034 fnNode := Inst.instantiate(fnNode, context = fn_context, instPartial = true);
511 else
512
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1 true := Error.getNumErrorMessages() == numError;
513 ✗ def := InstNode.definition(fnNode);
514 ✗ Error.addSourceMessage(Error.UNKNOWN_ERROR_INST_FUNCTION, {SCodeDump.unparseElementStr(def)}, SCodeUtil.elementInfo(def));
515 ✗ fail();
516 end try;
517
518 // Save the comments from the node before the class tree is flattened and
519 // we lose the comments from the extended classes.
520 24033 cmts := InstNode.getComments(fnNode);
521
522 // Set up an empty function cache to signal that this function is
523 // currently being instantiated, so recursive functions can be handled.
524 24033 InstNode.cacheInitFunc(fnNode);
525 24033 Inst.instExpressions(fnNode, context = context, settings = InstSettings.create());
526 end instFunction3;
527
528 function makeEnumConversionOp
529 "Creates an EnumTypeName(index) conversion operator."
530 input InstNode enumNode;
531 output InstNode fnNode;
532 protected
533 SCode.ClassDef def, fn_def;
534 SCode.Element elem, fn_elem;
535 list<SCode.Element> params;
536 list<SCode.Statement> stmts;
537 SourceInfo info = InstNode.info(enumNode);
538 String enum_name = InstNode.name(enumNode);
539 algorithm
540 1 elem := InstNode.definition(InstNode.resolveInner(Class.lastBaseClass(enumNode)));
541
542 // Construct an SCode definition for the conversion operator.
543 fn_def := match elem
544 case SCode.Element.CLASS(classDef = def as SCode.ClassDef.ENUMERATION())
545 algorithm
546 // function E
547 1 params := {
548 // input Integer index;
549 SCode.Element.COMPONENT("index",
550 SCode.defaultPrefixes,
551 SCode.defaultInputAttr,
552 Absyn.TypeSpec.TPATH(Absyn.Path.IDENT("Integer"), NONE()),
553 SCode.NOMOD(),
554 SCode.noComment,
555 NONE(),
556 info
557 ),
558 // output E value;
559 SCode.Element.COMPONENT("value",
560 SCode.defaultPrefixes,
561 SCode.defaultOutputAttr,
562 Absyn.TypeSpec.TPATH(Absyn.Path.IDENT(enum_name), NONE()),
563 SCode.NOMOD(),
564 SCode.noComment,
565 NONE(),
566 info
567 )
568 };
569 // algorithm
570
4/4
✓ Branch 10 taken 3 times.
✓ Branch 11 taken 1 time.
✓ Branch 12 taken 3 times.
✓ Branch 13 taken 1 time.
4 stmts := {
571 // assert(index >= 1 and index <= size(E, 1),
572 // "Enumeration index '" + String(index) + "' out of bounds in call to E()");
573 SCode.Statement.ALG_ASSERT(
574 Absyn.Exp.LBINARY(
575 Absyn.Exp.RELATION(
576 Absyn.Exp.CREF(Absyn.ComponentRef.CREF_IDENT("index", {})),
577 Absyn.Operator.GREATEREQ(),
578 Absyn.Exp.INTEGER(1)
579 ),
580 Absyn.Operator.AND(),
581 Absyn.Exp.RELATION(
582 Absyn.Exp.CREF(Absyn.ComponentRef.CREF_IDENT("index", {})),
583 Absyn.Operator.LESSEQ(),
584 Absyn.Exp.INTEGER(listLength(def.enumLst))
585 )
586 ),
587 Absyn.Exp.BINARY(
588 Absyn.Exp.STRING("Enumeration index '"),
589 Absyn.Operator.ADD(),
590 Absyn.Exp.BINARY(
591 Absyn.Exp.CALL(
592 Absyn.ComponentRef.CREF_IDENT("String", {}),
593 Absyn.FunctionArgs.FUNCTIONARGS(
594 {Absyn.Exp.CREF(Absyn.ComponentRef.CREF_IDENT("index", {}))},
595 {}
596 ),
597 {}
598 ),
599 Absyn.Operator.ADD(),
600 Absyn.Exp.STRING("' out of bounds in call to " + enum_name + "()")
601 )
602 ),
603 Absyn.Exp.CREF(Absyn.ComponentRef.CREF_QUAL(
604 "AssertionLevel", {}, Absyn.ComponentRef.CREF_IDENT("error", {}))),
605 SCode.noComment,
606 info
607 ),
608 // value := {E.literal1, E.literal2, ...}[index];
609 SCode.Statement.ALG_ASSIGN(
610 Absyn.Exp.CREF(Absyn.ComponentRef.CREF_IDENT("value", {})),
611 Absyn.Exp.SUBSCRIPTED_EXP(
612 Absyn.Exp.ARRAY(
613 list(Absyn.Exp.CREF(Absyn.ComponentRef.CREF_QUAL(enum_name,
614 {}, Absyn.ComponentRef.CREF_IDENT(e.literal, {}))) for
615 e in def.enumLst)
616 ),
617 {Absyn.Subscript.SUBSCRIPT(Absyn.Exp.CREF(Absyn.ComponentRef.CREF_IDENT("index", {})))}
618 ),
619 SCode.noComment,
620 info
621 )
622 };
623 // end E;
624 2 then
625 SCode.ClassDef.PARTS(params, {}, {}, {SCode.AlgorithmSection.ALGORITHM(stmts)}, {}, {}, {}, NONE());
626
627 else
628 fail();
629 end match;
630
631 // Construct an SCode element from the definition. The element name is
632 // prefixed with $ since we use the enumeration type inside the function,
633 // and the lookup would otherwise find the function instead. But the flat
634 // model will show the non-prefixed name since the name of the function is
635 // defined when creating the Function object.
636 2 fn_elem := SCode.Element.CLASS(
637 "$" + enum_name,
638 SCode.defaultPrefixes,
639 SCode.Encapsulated.NOT_ENCAPSULATED(),
640 SCode.Partial.NOT_PARTIAL(),
641 SCode.Restriction.R_FUNCTION(SCode.FunctionRestriction.FR_NORMAL_FUNCTION(Absyn.FunctionPurity.PURE())),
642 fn_def,
643 SCode.Comment.COMMENT(NONE(), SOME("Automatically generated conversion operator for " + enum_name)),
644 info
645 );
646
647 // Create a node from the SCode element.
648 1 fnNode := InstNode.new(fn_elem, InstNode.parentScope(enumNode, ignoreRedeclare=true));
649 end makeEnumConversionOp;
650
651 function getCachedFuncs
652 input InstNode inNode;
653 output list<Function> outFuncs;
654 protected
655 CachedData cache;
656 algorithm
657 88747 cache := InstNode.getFuncCache(InstNode.classScope(inNode));
658 outFuncs := match cache
659 88483 case CachedData.FUNCTION() then cache.funcs;
660 else {};
661 end match;
662 end getCachedFuncs;
663
664 function mapCachedFuncs
665 input InstNode inNode;
666 input MapFn mapFn;
667
668 partial function MapFn
669 input output Function fn;
670 end MapFn;
671 protected
672 InstNode cls_node;
673 CachedData cache;
674 algorithm
675 951 cls_node := InstNode.classScope(inNode);
676 951 cache := InstNode.getFuncCache(cls_node);
677
678 cache := match cache
679 case CachedData.FUNCTION()
680 algorithm
681
5/6
✓ Branch 0 taken 951 times.
✓ Branch 1 taken 951 times.
✓ Branch 2 taken 951 times.
✓ Branch 3 taken 951 times.
✓ Branch 4 taken 951 times.
✗ Branch 5 not taken.
2853 cache.funcs := list(mapFn(fn) for fn in cache.funcs);
682 then
683 cache;
684
685 else fail();
686 end match;
687
688 951 InstNode.setFuncCache(cls_node, cache);
689 end mapCachedFuncs;
690
691 function isEvaluated
692 input Function fn;
693 output Boolean evaluated;
694 algorithm
695 evaluated := match Pointer.access(fn.status)
696 case FunctionStatus.BUILTIN then true;
697 case FunctionStatus.EVALUATED then true;
698 else false;
699 end match;
700 end isEvaluated;
701
702 function markEvaluated
703 input Function fn;
704 algorithm
705
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12185 times.
12185 if Pointer.access(fn.status) <> FunctionStatus.BUILTIN then
706 12185 Pointer.update(fn.status, FunctionStatus.EVALUATED);
707 end if;
708 end markEvaluated;
709
710 function isSimplified
711 input Function fn;
712 output Boolean simplified;
713 algorithm
714 simplified := match Pointer.access(fn.status)
715 case FunctionStatus.BUILTIN then true;
716 case FunctionStatus.SIMPLIFIED then true;
717 else false;
718 end match;
719 end isSimplified;
720
721 function markSimplified
722 input Function fn;
723 algorithm
724
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12185 times.
12185 if Pointer.access(fn.status) <> FunctionStatus.BUILTIN then
725 12185 Pointer.update(fn.status, FunctionStatus.SIMPLIFIED);
726 end if;
727 end markSimplified;
728
729 function isCollected
730 "Returns true if this function has already been added to the function tree
731 (or shouldn't be added, e.g. if it's builtin), otherwise false."
732 input Function fn;
733 output Boolean collected;
734 algorithm
735 collected := match Pointer.access(fn.status)
736 case FunctionStatus.BUILTIN then true;
737 case FunctionStatus.COLLECTED then true;
738 else false;
739 end match;
740 end isCollected;
741
742 function collect
743 "Marks this function as collected for addition to the function tree."
744 input Function fn;
745 algorithm
746 // The pointer might be immutable, check before assigning to it.
747
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11385 times.
11385 if Pointer.access(fn.status) <> FunctionStatus.BUILTIN then
748 11385 Pointer.update(fn.status, FunctionStatus.COLLECTED);
749 end if;
750 end collect;
751
752 function name
753 input Function fn;
754 output Absyn.Path path = fn.path;
755 end name;
756
757 function setName
758 input Absyn.Path name;
759 input output Function fn;
760 algorithm
761 ✗ fn.path := name;
762 end setName;
763
764 function nameConsiderBuiltin
765 "Handles the DAE.mo structure where builtin calls are replaced by their simpler name"
766 input Function fn;
767 output Absyn.Path path;
768 algorithm
769 path := match fn.attributes.isBuiltin
770 local
771 String name;
772 127519 case DAE.FUNCTION_BUILTIN(name=SOME(name)) then Absyn.IDENT(name);
773 166667 case DAE.FUNCTION_BUILTIN() then AbsynUtil.pathLast(fn.path);
774 48878 else fn.path;
775 end match;
776 end nameConsiderBuiltin;
777
778 function nameEqual
779 input Function fn1;
780 input Function fn2;
781 output Boolean equal = AbsynUtil.pathEqual(name(fn1), name(fn2));
782 end nameEqual;
783
784 function nameHash
785 input Function fn;
786 output Integer hash = AbsynUtil.pathHash(name(fn));
787 end nameHash;
788
789 function signatureString
790 "Constructs a signature string for a function, e.g. Real func(Real x, Real y)"
791 input Function fn;
792 input Boolean printTypes = true;
793 output String str;
794 protected
795 Absyn.Path fn_name;
796 String input_str, output_str, var_s;
797 list<String> inputs_strl = {};
798 list<InstNode> inputs = fn.inputs;
799 Component c;
800 Expression def_exp;
801 Type ty;
802 algorithm
803
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 9 times.
30 for s in fn.slots loop
804 input_str := "";
805 21 c := InstNode.component(listHead(inputs));
806 21 inputs := listRest(inputs);
807
808 // Add the default expression if it has any.
809
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 12 times.
21 if isSome(s.default) then
810 9 SOME(def_exp) := s.default;
811 9 input_str := " = " + Expression.toString(def_exp);
812 end if;
813
814 // Add the name from the slot and not the node, since some builtin
815 // functions don't bother using proper names for the nodes.
816 21 input_str := Slot.name(s) + input_str;
817
818 // Add a $ in front of the name if the parameter only takes positional
819 // arguments.
820 input_str := match s.ty
821 5 case SlotType.POSITIONAL then "$" + input_str;
822 else input_str;
823 end match;
824
825 // Add the type if the parameter has been typed.
826
2/4
✓ Branch 0 taken 21 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 21 times.
✗ Branch 4 not taken.
21 if printTypes and Component.isTyped(c) then
827 21 ty := Component.getType(c);
828 21 var_s := Prefixes.unparseVariability(Component.variability(c), ty);
829 21 input_str := var_s + Type.toString(ty) + " " + input_str;
830 end if;
831
832 inputs_strl := input_str :: inputs_strl;
833 end for;
834
835 9 input_str := stringDelimitList(listReverse(inputs_strl), ", ");
836
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
9 output_str := if printTypes and isTyped(fn) then " => " + Type.toString(fn.returnType) else "";
837 9 fn_name := nameConsiderBuiltin(fn);
838 // if isSome(display_name) then Util.getOption(display_name) else fn.path;
839 9 str := AbsynUtil.pathString(fn_name) + "(" + input_str + ")" + output_str;
840 end signatureString;
841
842 function candidateFuncListString
843 input list<Function> fns;
844 output String s = stringDelimitList(list(Function.signatureString(fn, true) for fn in fns), "\n ");
845 end candidateFuncListString;
846
847 function callString
848 "Constructs a string representing a call, for use in error messages."
849 input Function fn;
850 input list<Expression> posArgs;
851 input list<NamedArg> namedArgs;
852 output String str;
853 algorithm
854 ✗ str := stringDelimitList(list(Expression.toString(arg) for arg in posArgs), ", ");
855
856 ✗ if not listEmpty(namedArgs) then
857 ✗ str := str + ", " + stringDelimitList(
858 list(Util.tuple21(arg) + " = " + Expression.toString(Util.tuple22(arg))
859 for arg in namedArgs), ", ");
860 end if;
861
862 ✗ str := AbsynUtil.pathString(fn.path) + "(" + str + ")";
863 end callString;
864
865 function typeString
866 "Constructs a string representing the type of the function, on the form
867 function_name<function>(inputs) => outputs"
868 input Function fn;
869 output String str;
870 protected
871 String inputs, outputs;
872
873 function param_str
874 input InstNode p;
875 output String s = Type.toString(InstNode.getType(p)) + " " + InstNode.name(p);
876 end param_str;
877
878 function param_cell_str
879 input NFInstNode.NodeHandle p;
880 output String s = param_str(InstNode.fromHandle(p));
881 end param_cell_str;
882 algorithm
883 2 inputs := List.toString(fn.inputs, param_str, List.Style.FLAT);
884 2 outputs := List.toString(fn.outputs, param_cell_str, List.Style.FLAT);
885
886
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
2 if listEmpty(fn.outputs) or listLength(fn.outputs) > 1 then
887 ✗ outputs := "(" + outputs + ")";
888 end if;
889
890 2 str := AbsynUtil.pathString(name(fn)) + "<function>(" + inputs + ") => " + outputs;
891 end typeString;
892
893 function toStream
894 input Function fn;
895 input String indent;
896 input output IOStream.IOStream s;
897 protected
898 String fn_name;
899 Option<SCode.Comment> cmt;
900 algorithm
901 ✗ if isDefaultRecordConstructor(fn) then
902 ✗ s := Record.toDeclarationStream(InstNode.fromHandle(fn.node), indent, s);
903 elseif isPartialDerivative(fn) then
904 ✗ fn_name := AbsynUtil.pathString(fn.path);
905 ✗ cmt := SCodeUtil.getElementComment(InstNode.definition(InstNode.fromHandle(fn.node)));
906
907 ✗ s := IOStream.append(s, indent);
908 ✗ s := IOStream.append(s, "function ");
909 ✗ s := IOStream.append(s, fn_name);
910 ✗ s := IOStream.append(s, " = der(");
911 ✗ s := IOStream.append(s, AbsynUtil.pathString(getDerivedFunctionName(fn)));
912 ✗ s := IOStream.append(s, ", ");
913 ✗ s := IOStream.append(s, stringDelimitList(getDerivedInputNames(fn), ", "));
914 ✗ s := IOStream.append(s, DAEDumpTypes.dumpCommentAnnotationStr(cmt));
915 ✗ s := IOStream.append(s, ")");
916 else
917 ✗ fn_name := AbsynUtil.pathString(fn.path);
918 ✗ cmt := SCodeUtil.getElementComment(InstNode.definition(InstNode.fromHandle(fn.node)));
919 ✗ s := IOStream.append(s, indent);
920
921 ✗ if InstNode.isPartial(InstNode.fromHandle(fn.node)) then
922 ✗ s := IOStream.append(s, "partial ");
923 end if;
924
925 ✗ s := IOStream.append(s, "function ");
926 ✗ s := IOStream.append(s, fn_name);
927 ✗ s := IOStream.append(s, DAEDumpTypes.dumpCommentStr(cmt));
928 ✗ s := IOStream.append(s, "\n");
929
930 ✗ for i in fn.inputs loop
931 ✗ s := IOStream.append(s, indent + " ");
932 ✗ s := IOStream.append(s, InstNode.toString(i));
933 ✗ s := IOStream.append(s, ";\n");
934 end for;
935
936 ✗ for o in fn.outputs loop
937 ✗ s := IOStream.append(s, indent + " ");
938 ✗ s := IOStream.append(s, InstNode.toString(InstNode.fromHandle(o)));
939 ✗ s := IOStream.append(s, ";\n");
940 end for;
941
942 ✗ if not listEmpty(fn.locals) then
943 ✗ s := IOStream.append(s, indent);
944 ✗ s := IOStream.append(s, "protected\n");
945
946 ✗ for l in fn.locals loop
947 ✗ s := IOStream.append(s, indent + " ");
948 ✗ s := IOStream.append(s, InstNode.toString(l));
949 ✗ s := IOStream.append(s, ";\n");
950 end for;
951 end if;
952
953 ✗ s := Sections.toStream(InstNode.getSections(InstNode.fromHandle(fn.node)), indent, s);
954 ✗ s := IOStream.append(s, DAEDumpTypes.dumpClassAnnotationStr(cmt));
955 ✗ s := IOStream.append(s, indent);
956 ✗ s := IOStream.append(s, "end ");
957 ✗ s := IOStream.append(s, fn_name);
958 end if;
959 end toStream;
960
961 function toFlatStream
962 input Function fn;
963 input BaseModelica.OutputFormat format;
964 input String indent;
965 input output IOStream.IOStream s;
966 input String overrideName = "";
967 protected
968 String fn_name;
969 SCode.Comment cmt;
970 SCode.Mod annMod;
971 algorithm
972
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
12 if isDefaultRecordConstructor(fn) then
973 ✗ s := Record.toFlatDeclarationStream(InstNode.fromHandle(fn.node), format, indent, s);
974 elseif isPartialDerivative(fn) then
975 ✗ fn_name := if stringEmpty(overrideName) then Util.makeQuotedIdentifier(AbsynUtil.pathString(fn.path)) else overrideName;
976
977 ✗ s := IOStream.append(s, indent);
978 ✗ s := IOStream.append(s, "function ");
979 ✗ s := IOStream.append(s, fn_name);
980 ✗ s := IOStream.append(s, " = der(");
981 ✗ s := IOStream.append(s, Util.makeQuotedIdentifier(AbsynUtil.pathString(getDerivedFunctionName(fn))));
982 ✗ s := IOStream.append(s, ", ");
983 ✗ s := IOStream.append(s, stringDelimitList(getDerivedInputNames(fn), ", "));
984 ✗ s := FlatModelicaUtil.appendCommentOpt(SCodeUtil.getElementComment(InstNode.definition(InstNode.fromHandle(fn.node))),
985 NFFlatModelicaUtil.ElementType.FUNCTION, s);
986 ✗ s := IOStream.append(s, ")");
987 else
988 12 cmt := Util.getOptionOrDefault(SCodeUtil.getElementComment(InstNode.definition(InstNode.fromHandle(fn.node))), SCode.COMMENT(NONE(), NONE()));
989
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 fn_name := if stringEmpty(overrideName) then Util.makeQuotedIdentifier(AbsynUtil.pathString(fn.path)) else overrideName;
990
991 12 s := IOStream.append(s, indent);
992
993
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if InstNode.isPartial(InstNode.fromHandle(fn.node)) then
994 ✗ s := IOStream.append(s, "partial ");
995 end if;
996
997 12 s := IOStream.append(s, "function ");
998 12 s := IOStream.append(s, fn_name);
999 12 s := FlatModelicaUtil.appendCommentString(cmt, s);
1000 12 s := IOStream.append(s, "\n");
1001
1002
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 12 times.
37 for i in fn.inputs loop
1003 25 s := IOStream.append(s, InstNode.toFlatString(i, format, indent + " "));
1004 25 s := IOStream.append(s, ";\n");
1005 end for;
1006
1007
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 12 times.
25 for o in fn.outputs loop
1008 13 s := IOStream.append(s, InstNode.toFlatString(InstNode.fromHandle(o), format, indent + " "));
1009 13 s := IOStream.append(s, ";\n");
1010 end for;
1011
1012
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 11 times.
12 if not listEmpty(fn.locals) then
1013
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 for l in fn.locals loop
1014 1 s := IOStream.append(s, InstNode.toFlatString(l, format, indent + " "));
1015 1 s := IOStream.append(s, ";\n");
1016 end for;
1017 end if;
1018
1019 12 s := Sections.toFlatStream(InstNode.getSections(InstNode.fromHandle(fn.node)), fn.path, format, indent, s);
1020
1021
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 10 times.
12 if isSome(cmt.annotation_) then
1022 2 SOME(SCode.ANNOTATION(modification=annMod)) := cmt.annotation_;
1023 else
1024 annMod := SCode.NOMOD();
1025 end if;
1026 // Generate derivative/inverse annotations from the instantiated model. Paths have changed.
1027 12 annMod := SCodeUtil.filterSubMods(annMod,
1028 function SCodeUtil.removeGivenSubModNames(namesToRemove={"derivative", "inverse"}));
1029
1030
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 12 times.
14 for derivative in listReverse(fn.derivatives) loop
1031 2 annMod := SCodeUtil.prependSubModToMod(FunctionDerivative.toSubMod(derivative), annMod);
1032 end for;
1033
1034
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
24 for i in arrayLength(fn.inverses):-1:1 loop
1035 ✗ annMod := SCodeUtil.prependSubModToMod(FunctionInverse.toSubMod(fn.inverses[i]), annMod);
1036 end for;
1037
1038
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 10 times.
12 if not SCodeUtil.emptyModOrEquality(annMod) then
1039 4 cmt := SCode.COMMENT(SOME(SCode.ANNOTATION(annMod)), NONE());
1040 2 s := FlatModelicaUtil.appendCommentAnnotation(cmt,
1041 NFFlatModelicaUtil.ElementType.FUNCTION, indent + " ", ";\n", s);
1042 end if;
1043
1044 12 s := IOStream.append(s, indent);
1045 12 s := IOStream.append(s, "end ");
1046 12 s := IOStream.append(s, fn_name);
1047 end if;
1048 end toFlatStream;
1049
1050 function toFlatString
1051 input Function fn;
1052 input BaseModelica.OutputFormat format = BaseModelica.defaultFormat;
1053 input String indent = "";
1054 output String str;
1055 protected
1056 IOStream.IOStream s;
1057 algorithm
1058 2 s := IOStream.create(getInstanceName(), IOStream.IOStreamType.LIST());
1059 2 s := toFlatStream(fn, format, indent, s);
1060 2 str := IOStream.string(s);
1061 2 IOStream.delete(s);
1062 end toFlatString;
1063
1064 function instance
1065 input Function fn;
1066 output InstNode node = InstNode.fromHandle(fn.node);
1067 end instance;
1068
1069 function returnType
1070 input Function fn;
1071 output Type ty = fn.returnType;
1072 end returnType;
1073
1074 function setReturnType
1075 input Type ty;
1076 input output Function fn;
1077 algorithm
1078 156 fn.returnType := ty;
1079 end setReturnType;
1080
1081 function getSlots
1082 input Function fn;
1083 output list<Slot> slots = fn.slots;
1084 end getSlots;
1085
1086 function fillArgs
1087 "Matches the given arguments to the slots in a function, and returns the
1088 arguments sorted in the order of the function parameters."
1089 input list<TypedArg> posArgs;
1090 input list<TypedArg> namedArgs;
1091 input Function fn;
1092 input InstContext.Type context;
1093 input SourceInfo info;
1094 input ComponentRef callPrefix = ComponentRef.EMPTY() "See collectArgs.";
1095 output list<TypedArg> args = posArgs;
1096 output Boolean matching;
1097 protected
1098 Slot slot;
1099 list<Slot> slots;
1100 array<Slot> slots_arr;
1101 Integer pos_arg_count, slot_count, index = 1;
1102 algorithm
1103 101938 slots := fn.slots;
1104 101938 pos_arg_count := listLength(posArgs);
1105 101938 slot_count := listLength(slots);
1106
1107
2/2
✓ Branch 0 taken 303 times.
✓ Branch 1 taken 101635 times.
101938 if pos_arg_count > slot_count then
1108 // If we have too many positional arguments it can't possibly match.
1109 303 matching := false;
1110 303 return;
1111 elseif pos_arg_count == slot_count and listEmpty(namedArgs) and List.all(slots, Slot.positional) then
1112 // If we have exactly as many positional arguments as slots, no named arguments, and all slots
1113 // accept positional arguments, then we can just return the list of arguments as it is.
1114 74974 matching := true;
1115 74974 return;
1116 end if;
1117
1118 26661 slots_arr := listArray(slots);
1119
1120
2/2
✓ Branch 0 taken 26674 times.
✓ Branch 1 taken 26661 times.
53335 for arg in args loop
1121 26674 slot := slots_arr[index];
1122
1123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26674 times.
26674 if not Slot.positional(slot) then
1124 // Slot doesn't allow positional arguments (used for some builtin functions).
1125 ✗ matching := false;
1126 ✗ return;
1127 end if;
1128
1129 26674 slot.arg := SOME(arg);
1130 26674 arrayUpdate(slots_arr, index, slot);
1131 26674 index := index + 1;
1132 end for;
1133
1134
2/2
✓ Branch 0 taken 5715 times.
✓ Branch 1 taken 26315 times.
32030 for narg in namedArgs loop
1135 5715 (slots_arr, matching) := fillNamedArg(narg, slots_arr, fn, info);
1136
1137
2/2
✓ Branch 0 taken 346 times.
✓ Branch 1 taken 5369 times.
5715 if not matching then
1138 346 return;
1139 end if;
1140 end for;
1141
1142 26315 (args, matching) := collectArgs(slots_arr, context, info, callPrefix);
1143 end fillArgs;
1144
1145 function fillNamedArg
1146 "Looks up a slot with the given name and tries to fill it with the given
1147 argument expression."
1148 input TypedArg arg;
1149 input output array<Slot> slots;
1150 input Function fn "For error reporting";
1151 input SourceInfo info;
1152 output Boolean matching = true;
1153 protected
1154 Slot s;
1155 String arg_name;
1156 algorithm
1157
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 5789 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5789 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5789 times.
5789 SOME(arg_name) := arg.name;
1158
1159 // Try to find a slot and fill it with the argument expression.
1160 // Positional arguments fill the slots from the start of the array, so
1161 // searching backwards will generally be a bit more efficient.
1162
2/2
✓ Branch 0 taken 5785 times.
✓ Branch 1 taken 4 times.
15468 for i in arrayLength(slots):-1:1 loop
1163 15122 s := slots[i];
1164
1165
4/4
✓ Branch 1 taken 7711 times.
✓ Branch 2 taken 7411 times.
✓ Branch 5 taken 5443 times.
✓ Branch 6 taken 2268 times.
15122 if Slot.name(s) == arg_name then
1166
1/2
✓ Branch 1 taken 5443 times.
✗ Branch 2 not taken.
5443 if not Slot.named(s) then
1167 // Slot doesn't allow named argument (used for some builtin functions).
1168 matching := false;
1169 elseif isNone(s.arg) then
1170 5443 s.arg := SOME(arg);
1171 5443 slots[i] := s;
1172 else
1173 // TODO: Improve the error message, should mention function name.
1174 ✗ Error.addSourceMessage(Error.FUNCTION_SLOT_ALREADY_FILLED,
1175 {arg_name, ""}, info);
1176 matching := false;
1177 end if;
1178
1179 5443 return;
1180 end if;
1181 end for;
1182
1183 // No slot could be found.
1184 matching := false;
1185
1186 // A slot with the given name couldn't be found. This means it doesn't
1187 // exist, or we removed it when handling positional argument. We need to
1188 // search through all slots to be sure.
1189
2/2
✓ Branch 0 taken 893 times.
✓ Branch 1 taken 346 times.
1239 for s in fn.slots loop
1190
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 893 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
893 if arg_name == Slot.name(s) then
1191 // We found a slot, so it must have already been filled.
1192 ✗ Error.addSourceMessage(Error.FUNCTION_SLOT_ALREADY_FILLED,
1193 {arg_name, ""}, info);
1194 ✗ return;
1195 end if;
1196
1197 // No slot could be found, so it doesn't exist.
1198 1786 Error.addSourceMessage(Error.NO_SUCH_INPUT_PARAMETER,
1199 {InstNode.name(instance(fn)), arg_name}, info);
1200 end for;
1201
1202 end fillNamedArg;
1203
1204 function collectArgs
1205 "Collects the arguments from the given slots."
1206 input array<Slot> slots;
1207 input InstContext.Type context;
1208 input SourceInfo info;
1209 input ComponentRef callPrefix = ComponentRef.EMPTY()
1210 "The components the function was looked up via, e.g. cell.obj for
1211 cell.obj.f(x), without the class part of the scope. Default arguments
1212 that refer to these components, e.g. k in f = g(final k = k), do so via
1213 the scope of the function. Give those parts the origins of the prefix
1214 instead, so that the subscripts of enclosing arrays of components are
1215 applied to them when flattening, like to any cref written in the model.";
1216 output list<TypedArg> args = {};
1217 output Boolean matching = true;
1218 protected
1219 Option<Expression> default;
1220 Option<TypedArg> arg;
1221 TypedArg a;
1222 Boolean rebase = ComponentRef.isComponent(callPrefix);
1223 algorithm
1224
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 26315 times.
✓ Branch 3 taken 79123 times.
✓ Branch 4 taken 26315 times.
131753 for s in slots loop
1225
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 79123 times.
79123 SLOT(default = default, arg = arg) := s;
1226
1227 args := matchcontinue arg
1228 // Use the argument from the call if one was given.
1229 case SOME(a) then a :: args;
1230
1231 // Otherwise, try to fill the slot with its default argument.
1232 case _
1233 algorithm
1234 47426 a := fillDefaultSlot(s, slots, context, info);
1235
1236
2/2
✓ Branch 0 taken 818 times.
✓ Branch 1 taken 42154 times.
42972 if rebase then
1237 818 a := rebaseDefaultArg(a, callPrefix, info);
1238 end if;
1239 then
1240 a :: args;
1241
1242 else
1243 algorithm
1244 matching := false;
1245 4454 then
1246 args;
1247 end matchcontinue;
1248 end for;
1249
1250 26315 args := listReverse(args);
1251 end collectArgs;
1252
1253 function rebaseDefaultArg
1254 "See collectArgs."
1255 input output TypedArg arg;
1256 input ComponentRef callPrefix;
1257 input SourceInfo info;
1258 algorithm
1259 1636 arg.value := Expression.map(arg.value, function rebaseScopeExp(prefix = callPrefix));
1260
1261 // A part left in the scope that isn't one of the prefix's components
1262 // wouldn't get subscripts when flattened and would silently refer to the
1263 // wrong element of an array of components, so make sure there is none.
1264
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 818 times.
818 if Expression.contains(arg.value, function hasUnmatchedScopePartExp(prefix = callPrefix)) then
1265 ✗ Error.addInternalError(getInstanceName() + ": default argument " + Expression.toString(arg.value) +
1266 " of " + ComponentRef.toString(callPrefix) + " refers to a component outside the scope of the function", info);
1267 ✗ fail();
1268 end if;
1269 end rebaseDefaultArg;
1270
1271 function rebaseScopeExp
1272 input Expression exp;
1273 input ComponentRef prefix;
1274 output Expression outExp;
1275 algorithm
1276 outExp := match exp
1277 case Expression.CREF()
1278 algorithm
1279 1051 exp.cref := ComponentRef.rebaseScope(exp.cref, prefix);
1280 then
1281 exp;
1282
1283 else exp;
1284 end match;
1285 end rebaseScopeExp;
1286
1287 function hasUnmatchedScopePartExp
1288 input Expression exp;
1289 input ComponentRef prefix;
1290 output Boolean res;
1291 algorithm
1292 res := match exp
1293 1051 case Expression.CREF() then ComponentRef.hasUnmatchedScopePart(exp.cref, prefix);
1294 else false;
1295 end match;
1296 end hasUnmatchedScopePartExp;
1297
1298 function fillDefaultSlot
1299 input Slot slot;
1300 input array<Slot> slots;
1301 input InstContext.Type context;
1302 input SourceInfo info;
1303 output TypedArg outArg;
1304 algorithm
1305 outArg := match slot
1306 // Slot already filled by function argument.
1307 case SLOT(arg = SOME(outArg)) then outArg;
1308
1309 // Slot not filled by function argument, but has default value.
1310 case SLOT(default = SOME(_))
1311 42974 then fillDefaultSlot2(slot, slots, context, info);
1312
1313 // Give an error if no argument was given and there's no default argument.
1314 else
1315 algorithm
1316 8908 Error.addSourceMessage(Error.UNFILLED_SLOT, {Slot.name(slot)}, info);
1317 4454 then
1318 fail();
1319
1320 end match;
1321 end fillDefaultSlot;
1322
1323 function fillDefaultSlot2
1324 input Slot slot;
1325 input array<Slot> slots;
1326 input InstContext.Type context;
1327 input SourceInfo info;
1328 output TypedArg outArg;
1329 algorithm
1330 outArg := match slot.evalStatus
1331 local
1332 Expression exp;
1333 Type ty;
1334 Variability var;
1335 Purity pur;
1336
1337 // An already evaluated slot, return its binding.
1338 case SlotEvalStatus.EVALUATED
1339 ✗ then Util.getOption(slot.arg);
1340
1341 // A slot in the process of being evaluated => cyclic bindings.
1342 case SlotEvalStatus.EVALUATING
1343 algorithm
1344 ✗ Error.addSourceMessage(Error.CYCLIC_DEFAULT_VALUE, {Slot.name(slot)}, info);
1345 ✗ then
1346 fail();
1347
1348 // A slot with a not evaluated binding, evaluate the binding and return it.
1349 case SlotEvalStatus.NOT_EVALUATED
1350 algorithm
1351 42974 slot.evalStatus := SlotEvalStatus.EVALUATING;
1352 42974 arrayUpdate(slots, slot.index, slot);
1353
1354 42974 exp := evaluateSlotExp(Util.getOption(slot.default), slots, context, info);
1355 42974 (exp, ty, var, pur) := Typing.typeExp(exp, context, info);
1356 42974 outArg := TypedArg.TYPED_ARG(NONE(), exp, ty, var, pur);
1357
1358 42974 slot.arg := SOME(outArg);
1359 42974 slot.evalStatus := SlotEvalStatus.EVALUATED;
1360 42974 arrayUpdate(slots, slot.index, slot);
1361 then
1362 outArg;
1363
1364 end match;
1365 end fillDefaultSlot2;
1366
1367 function evaluateSlotExp
1368 input Expression exp;
1369 input array<Slot> slots;
1370 input InstContext.Type context;
1371 input SourceInfo info;
1372 output Expression outExp;
1373 algorithm
1374 42974 outExp := Expression.map(exp,
1375 function evaluateSlotExp_traverser(slots = slots, context = context, info = info));
1376 end evaluateSlotExp;
1377
1378 function evaluateSlotExp_traverser
1379 input Expression exp;
1380 input array<Slot> slots;
1381 input InstContext.Type context;
1382 input SourceInfo info;
1383 output Expression outExp;
1384 algorithm
1385 outExp := match exp
1386 1809 case Expression.CREF() then evaluateSlotCref(exp, slots, context, info);
1387 else exp;
1388 end match;
1389 end evaluateSlotExp_traverser;
1390
1391 function evaluateSlotCref
1392 input output Expression crefExp;
1393 input array<Slot> slots;
1394 input InstContext.Type context;
1395 input SourceInfo info;
1396 protected
1397 ComponentRef cref;
1398 Type cref_ty;
1399 list<ComponentRef> cref_parts;
1400 Option<Slot> slot;
1401 TypedArg arg;
1402 InstNode cref_node;
1403 algorithm
1404
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1809 times.
1809 Expression.CREF(cref = cref, ty = cref_ty) := crefExp;
1405
1406
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1809 times.
1809 if not ComponentRef.isCref(cref) then
1407 ✗ return;
1408 end if;
1409
1410
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1809 times.
1809 cref :: cref_parts := ComponentRef.toListReverse(cref);
1411 1809 cref_node := ComponentRef.node(cref);
1412 1809 slot := lookupSlotInArray(cref_node, slots);
1413
1414
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1809 times.
✓ Branch 2 taken 50 times.
✓ Branch 3 taken 1759 times.
1809 if isSome(slot) then
1415 50 arg := fillDefaultSlot(Util.getOption(slot), slots, context, info);
1416 50 crefExp := arg.value;
1417 50 crefExp := applyCrefSubs(cref, crefExp);
1418
1419
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 for cr in cref_parts loop
1420 ✗ crefExp := Expression.recordElement(ComponentRef.firstName(cr), crefExp);
1421 ✗ crefExp := applyCrefSubs(cref, crefExp);
1422 end for;
1423
1424
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 if Type.isKnown(cref_ty) then
1425 ✗ crefExp := TypeCheck.matchTypes(Expression.typeOf(crefExp), cref_ty, crefExp);
1426 end if;
1427 end if;
1428 end evaluateSlotCref;
1429
1430 function applyCrefSubs
1431 input ComponentRef cref;
1432 input output Expression exp;
1433 protected
1434 list<Subscript> subs;
1435 algorithm
1436 50 subs := ComponentRef.getSubscripts(cref);
1437
1438
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 4 times.
50 if listEmpty(subs) then
1439 46 return;
1440 end if;
1441
1442 try
1443 4 exp := Expression.applySubscripts(subs, exp);
1444 else
1445 // Applying the subscripts might not work if the expression is not typed,
1446 // in that case just create a subscripted expression.
1447 1 exp := Expression.SUBSCRIPTED_EXP(exp, subs, ComponentRef.getSubscriptedType(cref), false);
1448 end try;
1449 end applyCrefSubs;
1450
1451 function lookupSlotInArray
1452 input InstNode node;
1453 input array<Slot> slots;
1454 output Option<Slot> outSlot;
1455 protected
1456 Slot slot;
1457 algorithm
1458 try
1459 1809 slot := Array.getMemberOnTrue(node, slots, Slot.hasNode);
1460 outSlot := SOME(slot);
1461 else
1462 outSlot := NONE();
1463 end try;
1464 end lookupSlotInArray;
1465
1466 function matchArgs
1467 input Function func;
1468 input output list<TypedArg> args;
1469 input SourceInfo info;
1470 input Boolean vectorize = true;
1471 output FunctionMatchKind funcMatchKind = EXACT_MATCH;
1472 protected
1473 Component comp;
1474 list<InstNode> inputs = func.inputs;
1475 InstNode input_node;
1476 Integer arg_idx = 1;
1477 list<TypedArg> checked_args = {};
1478 Expression arg_exp;
1479 Type arg_ty, input_ty, ty;
1480 Variability arg_var;
1481 TypeCheck.MatchKind mk;
1482 Expression vect_arg = Expression.INTEGER(0);
1483 list<Dimension> vect_dims = {};
1484 Boolean matched;
1485 list<Integer> vectorized_args = {};
1486 algorithm
1487
2/2
✓ Branch 0 taken 174322 times.
✓ Branch 1 taken 80389 times.
254711 for arg in args loop
1488 174322 TypedArg.TYPED_ARG(value = arg_exp, ty = arg_ty, var = arg_var) := arg;
1489
1490
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 174322 times.
174322 input_node :: inputs := inputs;
1491 174322 comp := InstNode.component(input_node);
1492
1493 // Check that the variability of the argument and input parameter matches.
1494
2/2
✓ Branch 1 taken 548 times.
✓ Branch 2 taken 173774 times.
174322 if arg_var > Component.variability(comp) then
1495 3288 Error.addSourceMessage(Error.FUNCTION_SLOT_VARIABILITY,
1496 {InstNode.name(input_node), Expression.toString(arg_exp),
1497 AbsynUtil.pathString(Function.name(func)), Prefixes.variabilityString(arg_var),
1498 Prefixes.variabilityString(Component.variability(comp))}, info);
1499 funcMatchKind := NO_MATCH;
1500 548 return;
1501 end if;
1502
1503 // Check if the type of the argument and the input parameter matches exactly.
1504 173774 input_ty := Component.getType(comp);
1505 173774 (arg_exp, ty, mk) := TypeCheck.matchTypes(arg_ty, input_ty, arg_exp, NFTypeCheck.ALLOW_UNKNOWN);
1506 173774 matched := TypeCheck.isValidArgumentMatch(mk);
1507
1508
2/2
✓ Branch 0 taken 19725 times.
✓ Branch 1 taken 154049 times.
173774 if not matched and vectorize then
1509 // If the types don't match, try to vectorize the argument.
1510 19725 (arg_exp, ty, vect_arg, vect_dims, mk) :=
1511 matchArgVectorized(arg_exp, arg_ty, input_ty, vect_arg, vect_dims, info);
1512 vectorized_args := arg_idx :: vectorized_args;
1513 19725 matched := TypeCheck.isValidArgumentMatch(mk);
1514 end if;
1515
1516
2/2
✓ Branch 0 taken 15909 times.
✓ Branch 1 taken 157865 times.
173774 if not matched then
1517 // Print an error if the types match neither exactly nor vectorized.
1518 111363 Error.addSourceMessage(Error.ARG_TYPE_MISMATCH,
1519 {intString(arg_idx), AbsynUtil.pathString(func.path), InstNode.name(input_node),
1520 Expression.toString(arg_exp), Type.toString(arg_ty), Type.toString(input_ty)}, info);
1521 funcMatchKind := NO_MATCH;
1522 15909 return;
1523 end if;
1524
1525 // TODO: This should be a running reduction of the matches. Not just based on the
1526 // last match.
1527
2/2
✓ Branch 1 taken 152731 times.
✓ Branch 2 taken 5134 times.
157865 if TypeCheck.isCastMatch(mk) then
1528 funcMatchKind := CAST_MATCH;
1529 elseif TypeCheck.isGenericMatch(mk) then
1530 funcMatchKind := GENERIC_MATCH;
1531 end if;
1532
1533 157865 checked_args := TypedArg.TYPED_ARG(arg.name, arg_exp, ty, arg_var, arg.purity) :: checked_args;
1534 157865 arg_idx := arg_idx + 1;
1535 end for;
1536
1537
2/2
✓ Branch 0 taken 1521 times.
✓ Branch 1 taken 78868 times.
80389 if not listEmpty(vectorized_args) then
1538 1521 funcMatchKind := FunctionMatchKind.VECTORIZED(vect_dims, listReverse(vectorized_args), funcMatchKind);
1539 end if;
1540
1541 80389 args := listReverse(checked_args);
1542 end matchArgs;
1543
1544 function matchArgVectorized
1545 input output Expression argExp;
1546 input output Type argTy;
1547 input Type inputTy;
1548 input output Expression vectArg;
1549 input output list<Dimension> vectDims;
1550 input SourceInfo info;
1551 output TypeCheck.MatchKind matchKind;
1552 protected
1553 list<Dimension> arg_dims, input_dims, vect_dims, rest_dims;
1554 Type rest_ty;
1555 Integer vect_dims_count;
1556 algorithm
1557 19725 arg_dims := Type.arrayDims(argTy);
1558 19725 input_dims := Type.arrayDims(inputTy);
1559 19725 vect_dims_count := listLength(arg_dims) - listLength(input_dims);
1560
1561 // Only try to vectorize if the argument has more dimensions than the input parameter.
1562
2/2
✓ Branch 0 taken 15779 times.
✓ Branch 1 taken 3946 times.
19725 if vect_dims_count < 1 then
1563 15779 matchKind := MatchKind.NOT_COMPATIBLE;
1564 15779 return;
1565 end if;
1566
1567 3946 (vect_dims, rest_dims) := List.split(arg_dims, vect_dims_count);
1568
1569 // Make sure the vectorization dimensions are consistent.
1570
2/2
✓ Branch 0 taken 1525 times.
✓ Branch 1 taken 2421 times.
3946 if listEmpty(vectDims) then
1571 1525 vectDims := fillUnknownVectorizedDims(vect_dims, argExp);
1572 vectArg := argExp;
1573 elseif not List.isEqualOnTrue(vectDims, vect_dims, Dimension.isEqual) then
1574 ✗ Error.addSourceMessage(Error.VECTORIZE_CALL_DIM_MISMATCH,
1575 {"", Expression.toString(vectArg), "", Expression.toString(argExp),
1576 Dimension.toStringList(vectDims), Dimension.toStringList(vect_dims)}, info);
1577 end if;
1578
1579 // Check that the argument and the input parameter are type compatible when
1580 // the dimensions to vectorize over has been removed from the argument's type.
1581 3946 rest_ty := Type.liftArrayLeftList(Type.arrayElementType(argTy), rest_dims);
1582 3946 (argExp, argTy, matchKind) := TypeCheck.matchTypes(rest_ty, inputTy, argExp);
1583 end matchArgVectorized;
1584
1585 function fillUnknownVectorizedDims
1586 "Helper function to matchArgVectorized. Replaces unknown dimensions in the
1587 list with size(argExp, dimension index), so that vectorized calls involving
1588 unknown dimensions (e.g. in functions) can be handled correctly."
1589 input list<Dimension> dims;
1590 input Expression argExp;
1591 output list<Dimension> outDims = {};
1592 protected
1593 Integer i = 1;
1594 algorithm
1595
2/2
✓ Branch 0 taken 1533 times.
✓ Branch 1 taken 1525 times.
3058 for dim in dims loop
1596
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 1524 times.
1533 if Dimension.isUnknown(dim) then
1597 18 dim := Dimension.EXP(Expression.SIZE(argExp, SOME(Expression.INTEGER(i))), Variability.CONTINUOUS);
1598 end if;
1599
1600 outDims := dim :: outDims;
1601 1533 i := i + 1;
1602 end for;
1603
1604 1525 outDims := listReverseInPlace(outDims);
1605 end fillUnknownVectorizedDims;
1606
1607 function matchFunction
1608 input Function func;
1609 input list<TypedArg> args;
1610 input list<TypedArg> named_args;
1611 input InstContext.Type context;
1612 input SourceInfo info;
1613 input Boolean vectorize = true;
1614 input ComponentRef callPrefix = ComponentRef.EMPTY() "See collectArgs.";
1615 output list<TypedArg> out_args;
1616 output FunctionMatchKind matchKind = NO_MATCH;
1617 protected
1618 Boolean slot_matched;
1619 algorithm
1620 101938 (out_args, slot_matched) := fillArgs(args, named_args, func, context, info, callPrefix);
1621
1622
2/2
✓ Branch 0 taken 5092 times.
✓ Branch 1 taken 96846 times.
101938 if slot_matched then
1623 96846 (out_args, matchKind) := matchArgs(func, out_args, info, vectorize);
1624 end if;
1625 end matchFunction;
1626
1627 function matchFunctions
1628 input list<Function> funcs;
1629 input list<TypedArg> args;
1630 input list<TypedArg> named_args;
1631 input InstContext.Type context;
1632 input SourceInfo info;
1633 input Boolean vectorize = true;
1634 input ComponentRef callPrefix = ComponentRef.EMPTY() "See collectArgs.";
1635 output list<MatchedFunction> matchedFunctions;
1636 protected
1637 list<TypedArg> m_args;
1638 FunctionMatchKind matchKind;
1639 algorithm
1640 matchedFunctions := {};
1641
2/2
✓ Branch 0 taken 101938 times.
✓ Branch 1 taken 80297 times.
182235 for func in funcs loop
1642 101938 (m_args, matchKind) := matchFunction(func, args, named_args, context, info, vectorize, callPrefix);
1643
1644
2/2
✓ Branch 1 taken 80389 times.
✓ Branch 2 taken 21549 times.
101938 if FunctionMatchKind.isValid(matchKind) then
1645 80389 matchedFunctions := MatchedFunction.MATCHED_FUNC(func,m_args,matchKind)::matchedFunctions;
1646 end if;
1647 end for;
1648 end matchFunctions;
1649
1650 function matchFunctionsSilent
1651 input list<Function> funcs;
1652 input list<TypedArg> args;
1653 input list<TypedArg> named_args;
1654 input InstContext.Type context;
1655 input SourceInfo info;
1656 input Boolean vectorize = true;
1657 output list<MatchedFunction> matchedFunctions;
1658 protected
1659 algorithm
1660 1694 ErrorExt.setCheckpoint("NFFunction:matchFunctions");
1661 1694 matchedFunctions := matchFunctions(funcs, args, named_args, context, info, vectorize);
1662 1694 ErrorExt.rollBack("NFFunction:matchFunctions");
1663 end matchFunctionsSilent;
1664
1665 function isTyped
1666 input Function fn;
1667 output Boolean isTyped;
1668 algorithm
1669 isTyped := match fn.returnType
1670 case Type.UNKNOWN() then false;
1671 else true;
1672 end match;
1673 end isTyped;
1674
1675 function typeRefCache
1676 "Returns the function(s) referenced by the given cref, and types them if
1677 they are not already typed."
1678 input ComponentRef functionRef;
1679 input InstContext.Type context = NFInstContext.FUNCTION;
1680 output list<Function> functions;
1681 algorithm
1682 functions := match functionRef
1683 103106 case ComponentRef.CREF() then typeNodeCache(ComponentRef.node(functionRef), context);
1684 else
1685 algorithm
1686 ✗ Error.terminate(getInstanceName() + " got invalid function call reference", sourceInfo());
1687 ✗ then
1688 fail();
1689 end match;
1690 end typeRefCache;
1691
1692 function typeNodeCache
1693 "Returns the function(s) in the cache of the given node, and types them if
1694 they are not already typed."
1695 input InstNode functionNode;
1696 input InstContext.Type context = NFInstContext.FUNCTION;
1697 output list<Function> functions;
1698 protected
1699 InstNode fn_node;
1700 Boolean typed, special;
1701 algorithm
1702 106839 fn_node := InstNode.classScope(functionNode);
1703
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 106839 times.
106839 CachedData.FUNCTION(functions, typed, special) := InstNode.getFuncCache(fn_node);
1704
1705 // Type the function(s) if not already done.
1706
2/2
✓ Branch 0 taken 85755 times.
✓ Branch 1 taken 21084 times.
106839 if not typed then
1707
4/4
✓ Branch 0 taken 21577 times.
✓ Branch 1 taken 21083 times.
✓ Branch 2 taken 21577 times.
✓ Branch 3 taken 21083 times.
42660 functions := list(typeFunctionSignature(f, context) for f in functions);
1708
2/2
✓ Branch 0 taken 18360 times.
✓ Branch 1 taken 2723 times.
39443 InstNode.setFuncCache(fn_node, CachedData.FUNCTION(functions, true, special));
1709
4/4
✓ Branch 0 taken 21576 times.
✓ Branch 1 taken 21076 times.
✓ Branch 2 taken 21576 times.
✓ Branch 3 taken 21076 times.
42652 functions := list(typeFunctionBody(f, context) for f in functions);
1710 21076 InstNode.setFuncCache(fn_node, CachedData.FUNCTION(functions, true, special));
1711 end if;
1712 end typeNodeCache;
1713
1714 function getRefCache
1715 input ComponentRef fnRef;
1716 output list<Function> functions;
1717 protected
1718 InstNode fn_node;
1719 algorithm
1720 1519 fn_node := InstNode.classScope(ComponentRef.node(fnRef));
1721
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1519 times.
1519 CachedData.FUNCTION(funcs = functions) := InstNode.getFuncCache(fn_node);
1722 end getRefCache;
1723
1724 function typeFunction
1725 input output Function fn;
1726 input InstContext.Type context = NFInstContext.FUNCTION;
1727 algorithm
1728 4578 fn := typeFunctionSignature(fn, context);
1729 4578 fn := typeFunctionBody(fn, context);
1730 end typeFunction;
1731
1732 function typeFunctionSignature
1733 "Types a function's parameters and local components."
1734 input output Function fn;
1735 input InstContext.Type context;
1736 protected
1737 InstNode node = InstNode.fromHandle(fn.node);
1738 InstContext.Type fn_context;
1739 algorithm
1740
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26155 times.
26155 if not isTyped(fn) then
1741 26155 fn_context := InstContext.set(context, NFInstContext.FUNCTION);
1742 26155 fn.slots := makeSlots(fn.inputs);
1743
1744 // Type all the components in the function.
1745 26155 Typing.typeClassType(node, NFBinding.EMPTY_BINDING, fn_context, node);
1746 26155 Typing.typeComponents(node, fn_context, preserveDerived = isPartialDerivative(fn));
1747
1748
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 26127 times.
26155 if InstNode.isPartial(node) then
1749 28 ClassTree.applyComponents(Class.classTree(InstNode.getClass(node)), boxFunctionParameter);
1750 end if;
1751
1752 // Make the slots and return type for the function.
1753 26155 checkParamTypes(fn);
1754 26155 checkPartialDerivativeTypes(fn);
1755 26154 fn.returnType := makeReturnType(fn);
1756 end if;
1757 end typeFunctionSignature;
1758
1759 function useFirstElementInBinding
1760 "A function looked up via a component in an array of components, e.g. f in
1761 cell[i].obj.f() with f = g(final k = k), is instantiated once for all
1762 elements of the array, so the default values of its inputs refer to the
1763 array without subscripts, like cell.obj.k. Arguments are always filled in
1764 at the call site, so the default value is only needed for its type, e.g.
1765 the values of constants in a record. Use the first element of the array as
1766 a representative, so that the default value can be evaluated."
1767 input InstNode node;
1768 protected
1769 Component comp = InstNode.component(node);
1770 Binding binding = Component.getBinding(comp);
1771 Binding new_binding;
1772 algorithm
1773
2/2
✓ Branch 1 taken 58383 times.
✓ Branch 2 taken 20899 times.
79282 if Binding.isBound(binding) then
1774 20899 new_binding := Binding.mapExp(binding, useFirstElementInExp);
1775
1776
2/2
✓ Branch 0 taken 19450 times.
✓ Branch 1 taken 1449 times.
20899 if not referenceEq(binding, new_binding) then
1777 1449 comp := Component.setBinding(new_binding, comp);
1778 1449 InstNode.updateComponent(comp, node);
1779 end if;
1780 end if;
1781 end useFirstElementInBinding;
1782
1783 function useFirstElementInExp
1784 input output Expression exp;
1785 protected
1786 ComponentRef cref;
1787 algorithm
1788 () := match exp
1789 case Expression.CREF()
1790 algorithm
1791 1756 cref := useFirstElementInCref(exp.cref);
1792
1793
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1753 times.
1756 if not referenceEq(cref, exp.cref) then
1794 3 exp.cref := cref;
1795 end if;
1796 then
1797 ();
1798
1799 else ();
1800 end match;
1801 end useFirstElementInExp;
1802
1803 function useFirstElementInCref
1804 "Subscripts the array parts of the scope of a cref with index 1. The type of
1805 the parts is kept, like for any other subscripted part of the scope."
1806 input ComponentRef cref;
1807 output ComponentRef outCref;
1808 protected
1809 ComponentRef rest;
1810 Type ty;
1811 algorithm
1812 outCref := match cref
1813 case ComponentRef.CREF()
1814 algorithm
1815 4138 rest := useFirstElementInCref(cref.restCref);
1816 4138 ty := ComponentRef.nodeType(cref);
1817
1818
7/8
✓ Branch 0 taken 2029 times.
✓ Branch 1 taken 2109 times.
✓ Branch 2 taken 2029 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 263 times.
✓ Branch 7 taken 1766 times.
✓ Branch 9 taken 3 times.
✓ Branch 10 taken 260 times.
4138 if cref.origin == NFComponentRef.Origin.SCOPE and listEmpty(cref.subscripts) and
1819 InstNode.isComponent(ComponentRef.node(cref)) and Type.isArray(ty) then
1820
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
9 cref.subscripts := list(Subscript.INDEX(Expression.INTEGER(1)) for i in 1:Type.dimensionCount(ty));
1821 3 cref.restCref := rest;
1822 elseif not referenceEq(rest, cref.restCref) then
1823 6 cref.restCref := rest;
1824 end if;
1825 then
1826 cref;
1827
1828 else cref;
1829 end match;
1830 end useFirstElementInCref;
1831
1832 function typeFunctionBody
1833 "Types the body of a function, along with any component bindings."
1834 input output Function fn;
1835 input InstContext.Type context;
1836 protected
1837 Boolean pure;
1838 DAE.FunctionAttributes attr;
1839 InstContext.Type fn_context;
1840 algorithm
1841 26154 fn_context := InstContext.set(context, NFInstContext.FUNCTION);
1842
1843 // Type the bindings of components in the function.
1844
2/2
✓ Branch 0 taken 79282 times.
✓ Branch 1 taken 26154 times.
105436 for c in fn.inputs loop
1845 79282 Typing.typeComponentBinding(c, fn_context);
1846 79282 useFirstElementInBinding(c);
1847 end for;
1848
1849
2/2
✓ Branch 0 taken 25534 times.
✓ Branch 1 taken 26154 times.
51688 for c in fn.outputs loop
1850 25534 Typing.typeComponentBinding(InstNode.fromHandle(c), fn_context);
1851 end for;
1852
1853
2/2
✓ Branch 0 taken 30617 times.
✓ Branch 1 taken 26154 times.
56771 for c in fn.locals loop
1854 30617 Typing.typeComponentBinding(c, fn_context);
1855 end for;
1856
1857 // Type the algorithm section of the function, if it has one.
1858 26154 Typing.typeFunctionSections(InstNode.fromHandle(fn.node), fn_context);
1859
1860 // Type any derivatives of the function.
1861
2/2
✓ Branch 0 taken 893 times.
✓ Branch 1 taken 26147 times.
27040 for fn_der in fn.derivatives loop
1862 893 FunctionDerivative.typeDerivative(fn_der);
1863 end for;
1864
1865 // Type any inverses of the function.
1866 26147 Array.mapNoCopy(fn.inverses, FunctionInverse.typeInverse);
1867
1868 // If the function is pure, check that it doesn't contain any impure calls.
1869
2/2
✓ Branch 1 taken 25586 times.
✓ Branch 2 taken 561 times.
26147 if not isImpure(fn) then
1870 25586 pure := foldExp(fn, function checkPureCall(fn = fn), true);
1871
1872 // The function does contain impure calls, mark the function as impure.
1873
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 25568 times.
25586 if not pure then
1874 18 attr := fn.attributes;
1875 18 attr.purity := DAE.Purity.IMPURE;
1876 18 fn.attributes := attr;
1877 end if;
1878 end if;
1879
1880 // Sort the local variables based on their dependencies.
1881 26147 fn.locals := sortLocals(fn.locals, InstNode.info(InstNode.fromHandle(fn.node)));
1882 end typeFunctionBody;
1883
1884 function checkPureCall
1885 input Expression exp;
1886 input Function fn;
1887 input output Boolean pure;
1888 algorithm
1889
2/2
✓ Branch 0 taken 147 times.
✓ Branch 1 taken 774366 times.
774513 if not pure then
1890 147 return;
1891 end if;
1892
1893
2/2
✓ Branch 1 taken 774348 times.
✓ Branch 2 taken 18 times.
774366 if Expression.isImpureCall(exp) then
1894 pure := false;
1895
1896
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
18 if Config.languageStandardAtLeast(Config.LanguageStandard._3_3) then
1897 54 Error.addSourceMessage(Error.PURE_FUNCTION_WITH_IMPURE_CALLS,
1898 {AbsynUtil.pathString(Function.name(fn)), Expression.getName(exp)},
1899 InstNode.info(InstNode.fromHandle(fn.node)));
1900 end if;
1901 end if;
1902 end checkPureCall;
1903
1904 function boxFunctionParameter
1905 input InstNode component;
1906 protected
1907 Component comp;
1908 algorithm
1909 56 comp := InstNode.component(component);
1910 56 comp := Component.setType(Type.box(Component.getType(comp)), comp);
1911 56 InstNode.updateComponent(comp, component);
1912 end boxFunctionParameter;
1913
1914 function typePartialApplication
1915 input output Expression exp;
1916 input InstContext.Type context;
1917 input SourceInfo info;
1918 output Type ty;
1919 output Variability variability;
1920 output Purity purity;
1921 protected
1922 ComponentRef fn_ref;
1923 list<Expression> args;
1924 list<String> arg_names;
1925 String arg_name;
1926 Type arg_ty;
1927 Variability arg_var;
1928 Purity arg_pur;
1929 Function fn;
1930 InstContext.Type next_context = InstContext.set(context, NFInstContext.SUBEXPRESSION);
1931 list<Slot> slots;
1932 array<Slot> slots_arr;
1933 TypedArg ty_arg;
1934 algorithm
1935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 Expression.PARTIAL_FUNCTION_APPLICATION(fn = fn_ref, args = args, argNames = arg_names) := exp;
1936 // TODO: Handle overloaded functions?
1937
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 fn :: _ := typeRefCache(fn_ref);
1938 27 slots_arr := listArray(fn.slots);
1939
1940
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 purity := if Function.isImpure(fn) then Purity.IMPURE else Purity.PURE;
1941 variability := Variability.CONSTANT;
1942
1943 // Type the arguments and add them to the slots.
1944
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 27 times.
101 for arg in args loop
1945 74 (arg, arg_ty, arg_var, arg_pur) := Typing.typeExp(arg, next_context, info);
1946
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 74 times.
74 arg_name :: arg_names := arg_names;
1947 74 ty_arg := TypedArg.TYPED_ARG(SOME(arg_name), arg, arg_ty, arg_var, arg_pur);
1948
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
74 (slots_arr, true) := fillNamedArg(ty_arg, slots_arr, fn, info);
1949 74 variability := Prefixes.variabilityMax(variability, arg_var);
1950 74 purity := Prefixes.purityMin(purity, arg_pur);
1951 end for;
1952
1953 27 exp := makePartialApplicationFromSlots(slots_arr, fn, fn_ref, info);
1954 26 ty := Expression.typeOf(exp);
1955 end typePartialApplication;
1956
1957 function makePartialApplicationFromSlots
1958 input array<Slot> slotsArray;
1959 input Function fn;
1960 input ComponentRef fnRef;
1961 input SourceInfo info;
1962 output Expression outExp;
1963 protected
1964 list<InstNode> inputs = {};
1965 list<Slot> slots = {};
1966 list<Expression> args = {};
1967 list<String> arg_names = {};
1968 TypedArg ty_arg;
1969 Expression arg;
1970 TypeCheck.MatchKind mk;
1971 Type fn_ty;
1972 algorithm
1973
2/2
✓ Branch 1 taken 101 times.
✓ Branch 2 taken 26 times.
127 for slot in slotsArray loop
1974
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 101 times.
✓ Branch 2 taken 27 times.
✓ Branch 3 taken 74 times.
101 if isSome(slot.arg) then
1975 // For each slot that's filled, type check the argument and add it to
1976 // the arguments of the partial function application.
1977 74 SOME(ty_arg) := slot.arg;
1978 74 (arg, _, mk) := TypeCheck.matchTypes(ty_arg.ty, InstNode.getType(slot.node), ty_arg.value, NFTypeCheck.ALLOW_UNKNOWN);
1979
1980
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 73 times.
74 if TypeCheck.isIncompatibleMatch(mk) then
1981 6 Error.addSourceMessage(Error.NAMED_ARG_TYPE_MISMATCH,
1982 {AbsynUtil.pathString(name(fn)), Util.getOption(ty_arg.name), Expression.toString(ty_arg.value),
1983 Type.toString(ty_arg.ty), Type.toString(InstNode.getType(slot.node))}, info);
1984 1 fail();
1985 end if;
1986
1987 73 args := Expression.box(arg) :: args;
1988 73 arg_names := Util.getOption(ty_arg.name) :: arg_names;
1989 else
1990 // For each slot that's not filled, add it as an input of the partially evaluated function.
1991 27 inputs := slot.node :: inputs;
1992 slots := slot :: slots;
1993 end if;
1994 end for;
1995
1996 26 fn.inputs := listReverseInPlace(inputs);
1997 fn.slots := listReverseInPlace(slots);
1998 26 fn_ty := Type.FUNCTION(fn, NFType.FunctionType.FUNCTIONAL_VARIABLE);
1999 26 args := listReverseInPlace(args);
2000 26 arg_names := listReverseInPlace(arg_names);
2001 26 outExp := Expression.PARTIAL_FUNCTION_APPLICATION(fnRef, args, arg_names, fn_ty);
2002 end makePartialApplicationFromSlots;
2003
2004 function isBuiltin
2005 input Function fn;
2006 output Boolean isBuiltin = isBuiltinAttr(fn.attributes);
2007 end isBuiltin;
2008
2009 function isBuiltinAttr
2010 input DAE.FunctionAttributes attrs;
2011 output Boolean isBuiltin;
2012 algorithm
2013 isBuiltin := match attrs.isBuiltin
2014 case DAE.FunctionBuiltin.FUNCTION_NOT_BUILTIN() then false;
2015 else true;
2016 end match;
2017 end isBuiltinAttr;
2018
2019 function isSpecialBuiltin
2020 "Returns whether or not a Function refers to a builtin function that doesn't
2021 follow normal Modelica rules and instead needs special handling."
2022 input Function fn;
2023 output Boolean special;
2024 protected
2025 Absyn.Path path;
2026 algorithm
2027
2/2
✓ Branch 1 taken 7454 times.
✓ Branch 2 taken 17782 times.
25236 if not isBuiltin(fn) then
2028 special := false;
2029 else
2030 7454 path := Function.nameConsiderBuiltin(fn);
2031
2032
1/2
✓ Branch 1 taken 7454 times.
✗ Branch 2 not taken.
7454 if not AbsynUtil.pathIsIdent(path) then
2033 special := false;
2034 else
2035 special := match AbsynUtil.pathFirstIdent(path)
2036 // Can have variable number of arguments.
2037 case "array" then true;
2038 case "actualStream" then true;
2039 case "backSample" then true;
2040 case "branch" then true;
2041 case "cardinality" then true;
2042 case "cat" then true;
2043 // Function should not be used in function context.
2044 // argument should be a cref?
2045 case "change" then true;
2046 case "der" then true;
2047 // Function should not be used in function context.
2048 case "edge" then true;
2049 // can have variable number of arguments
2050 case "fill" then true;
2051 case "getInstanceName" then true;
2052 // Always discrete.
2053 case "initial" then true;
2054 case "inStream" then true;
2055 case "isRoot" then true;
2056 // Arguments can be scalar, vector, matrix, 3d array .... basically anything
2057 // We need to make sure size(Arg,i) = 1 for 2 < i <= ndims(Arg).
2058 // return type should always be Matrix.
2059 case "matrix" then true;
2060 // Needs to check that arguments are basic types or enums
2061 // We need to check array inputs as well
2062 case "max" then true;
2063 // Needs to check that arguments are basic types or enums
2064 // We need to check array inputs as well
2065 case "min" then true;
2066 // Argument can have any number of dimensions.
2067 case "ndims" then true;
2068 // Can take any expression as argument.
2069 case "noEvent" then true;
2070 // Needs extra error checking
2071 case "nthRoot" then true;
2072 // can have variable number of arguments
2073 case "ones" then true;
2074 case "potentialRoot" then true;
2075 // Function should not be used in function context.
2076 // argument should be a cref?
2077 case "pre" then true;
2078 // needs unboxing and return type fix.
2079 case "promote" then true;
2080 case "pure" then true;
2081 case "root" then true;
2082 case "rooted" then true;
2083 case "uniqueRoot" then true;
2084 case "uniqueRootIndices" then true;
2085 // We need to make sure size(Arg,i) = 1 for 0 <= i <= ndims(Arg).
2086 // return type should always be scalar.
2087 case "scalar" then true;
2088 // First argument can have any number of dimensions.
2089 case "size" then true;
2090 case "shiftSample" then true;
2091 // Needs to check that second argument is real or array of real or record of reals.
2092 case "smooth" then true;
2093 // May only be used in certain context, and has other special rules.
2094 case "spatialDistribution" then true;
2095 case "subSample" then true;
2096 // needs unboxing and return type fix.
2097 case "superSample" then true;
2098 // unbox args and set return type.
2099 case "symmetric" then true;
2100 // Always discrete.
2101 case "terminal" then true;
2102 // unbox args and set return type (swap the first two dims).
2103 case "transpose" then true;
2104 // We need to construct output diminsion size from the size of elements in the array
2105 // return type should always be vector.
2106 case "vector" then true;
2107 // can have variable number of arguments
2108 case "zeros" then true;
2109 // sample - overloaded for sync features
2110 case "sample" then true;
2111 else false;
2112 end match;
2113 end if;
2114 end if;
2115 end isSpecialBuiltin;
2116
2117 function isSubscriptableBuiltin
2118 input Function fn;
2119 output Boolean scalarBuiltin;
2120 protected
2121 algorithm
2122
2/2
✓ Branch 1 taken 14 times.
✓ Branch 2 taken 82 times.
96 if not isBuiltin(fn) then
2123 scalarBuiltin := false;
2124 else
2125 scalarBuiltin := match AbsynUtil.pathFirstIdent(Function.nameConsiderBuiltin(fn))
2126 case "change" then true;
2127 case "der" then true;
2128 case "pre" then true;
2129 else false;
2130 end match;
2131 end if;
2132 end isSubscriptableBuiltin;
2133
2134 function isImpure
2135 input Function fn;
2136 output Boolean isImpure = fn.attributes.purity == DAE.Purity.IMPURE;
2137 end isImpure;
2138
2139 function isFunctionPointer
2140 input Function fn;
2141 output Boolean isPointer = fn.attributes.isFunctionPointer;
2142 end isFunctionPointer;
2143
2144 function setFunctionPointer
2145 input Boolean isPointer;
2146 input output Function fn;
2147 protected
2148 DAE.FunctionAttributes attr = fn.attributes;
2149 algorithm
2150 ✗ attr.isFunctionPointer := isPointer;
2151 ✗ fn.attributes := attr;
2152 // The whole function should just be this, but it doesn't compile yet.
2153 //fn.attributes.isFunctionPointer := isPointer;
2154 end setFunctionPointer;
2155
2156 function isExternal
2157 input Function fn;
2158 output Boolean isExternal = not InstNode.isEmpty(InstNode.fromHandle(fn.node)) and
2159 Class.isExternalFunction(InstNode.getClass(InstNode.fromHandle(fn.node)));
2160 end isExternal;
2161
2162 function isExternalObjectConstructorOrDestructor
2163 input Function fn;
2164 output Boolean isExternal;
2165 protected
2166 Absyn.Path path;
2167 String lastIdent;
2168 algorithm
2169 10 path := name(fn);
2170 10 lastIdent := AbsynUtil.pathLastIdent(path);
2171 isExternal := false;
2172
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
10 if lastIdent == "constructor" then
2173 ✗ isExternal := Type.isExternalObject(fn.returnType);
2174 elseif lastIdent == "destructor" then
2175 ✗ if listLength(fn.inputs) == 1 then
2176 ✗ isExternal := Type.isExternalObject(Component.getType(InstNode.component(listHead(fn.inputs))));
2177 end if;
2178 end if;
2179 end isExternalObjectConstructorOrDestructor;
2180
2181 function isPartialDerivative
2182 "Returns true if the function is a partial derivative of a function, df = der(f, x)."
2183 input Function fn;
2184 output Boolean res = not listEmpty(fn.derivedInputs);
2185 end isPartialDerivative;
2186
2187 function getDerivedInputNames
2188 "Returns the names of the differentiated inputs in a partial derivative,
2189 df = der(f, x, y, z) => {\"x\", \"y\", \"z\"}"
2190 input Function fn;
2191 output list<String> names = {};
2192 algorithm
2193
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 for i in fn.derivedInputs loop
2194 1 names := InstNode.name(listGet(fn.inputs, i)) :: names;
2195 end for;
2196
2197 1 names := listReverseInPlace(names);
2198 end getDerivedInputNames;
2199
2200 function getDerivedFunctionName
2201 "Returns the name of the derived function in a partial derivative, df = der(f, x) => f"
2202 input Function fn;
2203 output Absyn.Path name = InstNode.fullPath(Class.lastBaseClass(InstNode.fromHandle(fn.node)), ignoreBaseClass = true);
2204 end getDerivedFunctionName;
2205
2206 function inlineBuiltin
2207 input Function fn;
2208 output DAE.InlineType inlineType;
2209 algorithm
2210 inlineType := match fn.attributes.isBuiltin
2211 case DAE.FunctionBuiltin.FUNCTION_BUILTIN_PTR()
2212 then DAE.InlineType.BUILTIN_EARLY_INLINE();
2213 198319 else fn.attributes.inline;
2214 end match;
2215 end inlineBuiltin;
2216
2217 function isDefaultRecordConstructor
2218 input Function fn;
2219 output Boolean isConstructor = Restriction.isRecordConstructor(InstNode.restriction(InstNode.fromHandle(fn.node)));
2220 end isDefaultRecordConstructor;
2221
2222 function isNonDefaultRecordConstructor
2223 input Function fn;
2224 output Boolean b = isNonDefaultRecordConstructorPath(fn.path);
2225 function isNonDefaultRecordConstructorPath
2226 input Absyn.Path path;
2227 output Boolean b;
2228 algorithm
2229 b := match path
2230 case Absyn.Path.QUALIFIED(name = "'constructor'") then true;
2231 47243 case Absyn.Path.QUALIFIED() then isNonDefaultRecordConstructorPath(path.path);
2232 else false;
2233 end match;
2234 end isNonDefaultRecordConstructorPath;
2235 end isNonDefaultRecordConstructor;
2236
2237 function toDAE
2238 input Function fn;
2239 input DAE.FunctionDefinition def;
2240 output DAE.Function daeFn;
2241 protected
2242 SCode.Visibility vis;
2243 Boolean par, impr;
2244 DAE.InlineType ity;
2245 DAE.Type ty;
2246 list<DAE.FunctionDefinition> defs;
2247 list<Integer> unused_inputs;
2248 algorithm
2249 vis := SCode.PUBLIC(); // TODO: Use the actual visibility.
2250 par := false; // TODO: Use the actual partial prefix.
2251 9810 impr := fn.attributes.purity == DAE.Purity.IMPURE;
2252 9810 ity := fn.attributes.inline;
2253 9810 ty := makeDAEType(fn);
2254 9810 unused_inputs := analyseUnusedParameters(fn);
2255
4/4
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 9810 times.
✓ Branch 3 taken 45 times.
✓ Branch 4 taken 9810 times.
19710 defs := list(FunctionInverse.toDAE(fn_inv) for fn_inv in fn.inverses);
2256
4/4
✓ Branch 0 taken 783 times.
✓ Branch 1 taken 9810 times.
✓ Branch 2 taken 783 times.
✓ Branch 3 taken 9810 times.
10593 defs := listAppend(list(FunctionDerivative.toDAE(fn_der) for fn_der in fn.derivatives), defs);
2257 defs := def :: defs;
2258
2/2
✓ Branch 6 taken 9654 times.
✓ Branch 7 taken 156 times.
19464 daeFn := DAE.FUNCTION(fn.path, defs, ty, vis, par, impr, ity, unused_inputs,
2259 ElementSource.createElementSource(InstNode.info(InstNode.fromHandle(fn.node))),
2260 SCodeUtil.getElementComment(InstNode.definition(InstNode.fromHandle(fn.node))));
2261 end toDAE;
2262
2263 function makeDAEType
2264 input Function fn;
2265 input Boolean boxTypes = false;
2266 output DAE.Type outType;
2267 protected
2268 list<DAE.FuncArg> params = {};
2269 String pname;
2270 Type ty;
2271 DAE.Type ptype;
2272 DAE.Const pconst;
2273 DAE.VarParallelism ppar;
2274 Option<DAE.Exp> pdefault;
2275 Component comp;
2276 algorithm
2277
2/2
✓ Branch 0 taken 35951 times.
✓ Branch 1 taken 11553 times.
47504 for param in fn.inputs loop
2278 35951 comp := InstNode.component(param);
2279 35951 pname := InstNode.name(param);
2280 35951 ty := Component.getType(comp);
2281
2/2
✓ Branch 0 taken 172 times.
✓ Branch 1 taken 35779 times.
35951 ptype := Type.toDAE(if boxTypes then Type.box(ty) else ty);
2282 35951 pconst := Prefixes.variabilityToDAEConst(Component.variability(comp));
2283 35951 ppar := Prefixes.parallelismToDAE(Component.parallelism(comp));
2284 35951 pdefault := Util.applyOption(Binding.typedExp(Component.getBinding(comp)), function Expression.toDAE(allowEmpty = false));
2285 35951 params := DAE.FuncArg.FUNCARG(pname, ptype, pconst, ppar, pdefault) :: params;
2286 end for;
2287
2288 11553 params := listReverse(params);
2289
2/2
✓ Branch 1 taken 1575 times.
✓ Branch 2 taken 9978 times.
11553 ty := if isDefaultRecordConstructor(fn) then InstNode.getType(InstNode.fromHandle(fn.node)) else fn.returnType;
2290
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 11487 times.
11553 ty := if boxTypes then Type.box(ty) else ty;
2291 11553 outType := DAE.T_FUNCTION(params, Type.toDAE(ty), fn.attributes, fn.path);
2292 end makeDAEType;
2293
2294 function getSingleBodyExp
2295 "returns the rhs of the function body if its a single assignment, fails otherwise"
2296 input Function fn;
2297 output Expression exp;
2298 protected
2299 list<Statement> body;
2300 algorithm
2301 516 body := getBody(fn);
2302 exp := match body
2303 local
2304 Statement stmt;
2305
2306 516 case {stmt as Statement.ASSIGNMENT()} then stmt.rhs;
2307
2308 else algorithm
2309 ✗ Error.addMessage(Error.INTERNAL_ERROR,{getInstanceName()
2310 + " failed because the body of the function is not a single assignment:\n"
2311 + List.toStringCustom(body, function Statement.toString(indent = "\t"), "", "", "\n", "")});
2312 ✗ then fail();
2313 end match;
2314 end getSingleBodyExp;
2315
2316 function getBody
2317 input Function fn;
2318 output list<Statement> body = getBody2(InstNode.fromHandle(fn.node));
2319 end getBody;
2320
2321 function hasUnboxArgs
2322 "Returns true if the function has the __OpenModelica_UnboxArguments annotation, otherwise false."
2323 input Function fn;
2324 output Boolean res;
2325 algorithm
2326 res := match fn.attributes
2327 case DAE.FunctionAttributes.FUNCTION_ATTRIBUTES(
2328 isBuiltin = DAE.FunctionBuiltin.FUNCTION_BUILTIN(unboxArgs = res)) then res;
2329 else false;
2330 end match;
2331 end hasUnboxArgs;
2332
2333 function hasUnboxArgsAnnotation
2334 input SCode.Comment cmt;
2335 output Boolean res = SCodeUtil.commentHasBooleanNamedAnnotation(cmt, "__OpenModelica_UnboxArguments");
2336 end hasUnboxArgsAnnotation;
2337
2338 function hasOptionalArgument
2339 input SCode.Element component;
2340 output Boolean res = SCodeUtil.hasBooleanNamedAnnotationInComponent(component, "__OpenModelica_optionalArgument");
2341 end hasOptionalArgument;
2342
2343 function mapExp
2344 input output Function fn;
2345 input MapFunc mapFn;
2346 input MapFunc mapFnFields = mapFn "Used for expressions in subcomponents, i.e. record fields";
2347 input Boolean mapParameters = true;
2348 input Boolean mapBody = true;
2349
2350 partial function MapFunc
2351 input output Expression exp;
2352 end MapFunc;
2353 protected
2354 Class cls;
2355 ClassTree ctree;
2356 Sections sections;
2357 algorithm
2358 47116 cls := InstNode.getClass(InstNode.fromHandle(fn.node));
2359
2360
1/2
✓ Branch 0 taken 47116 times.
✗ Branch 1 not taken.
47116 if mapParameters then
2361 47116 ctree := Class.classTree(cls);
2362 47116 ClassTree.applyComponents(ctree,
2363 function mapExpParameter(mapFn = mapFn, mapFnFields = mapFnFields));
2364 47116 fn.returnType := makeReturnType(fn);
2365 end if;
2366
2367
2/2
✓ Branch 0 taken 23116 times.
✓ Branch 1 taken 24000 times.
47116 if mapBody then
2368 24000 sections := Sections.mapExp(Class.getSections(cls), mapFn);
2369 24000 cls := cls.setSections(sections, cls);
2370 24000 InstNode.updateClass(cls, InstNode.fromHandle(fn.node));
2371 end if;
2372 end mapExp;
2373
2374 function mapExpParameter
2375 input InstNode node;
2376 input MapFunc mapFn;
2377 input MapFunc mapFnFields;
2378
2379 partial function MapFunc
2380 input output Expression exp;
2381 end MapFunc;
2382 protected
2383 Component comp;
2384 Binding binding, binding2;
2385 Class cls;
2386 Type ty;
2387 Boolean dirty = false;
2388 algorithm
2389 7138046 comp := InstNode.component(node);
2390 7138046 binding := Component.getBinding(comp);
2391 7138046 binding2 := Binding.mapExpShallow(binding, mapFn);
2392
2393
2/2
✓ Branch 0 taken 53344 times.
✓ Branch 1 taken 7084702 times.
7138046 if not referenceEq(binding, binding2) then
2394 53344 comp := Component.setBinding(binding2, comp);
2395 dirty := true;
2396 end if;
2397
2398 () := match comp
2399 case Component.COMPONENT()
2400 algorithm
2401 641072 ty := Type.mapDims(comp.ty, function Dimension.mapExp(func = mapFn));
2402
2403
2/2
✓ Branch 0 taken 37385 times.
✓ Branch 1 taken 603687 times.
641072 if not referenceEq(ty, comp.ty) then
2404 37385 comp.ty := ty;
2405 dirty := true;
2406 end if;
2407
2408 641072 cls := InstNode.getClass(comp.classInst);
2409 641072 ClassTree.applyComponents(Class.classTree(cls),
2410 function mapExpParameter(mapFn = mapFnFields, mapFnFields = mapFnFields));
2411 then
2412 ();
2413
2414 else ();
2415 end match;
2416
2417
2/2
✓ Branch 0 taken 7052139 times.
✓ Branch 1 taken 85907 times.
7138046 if dirty then
2418 85907 InstNode.updateComponent(comp, node);
2419 end if;
2420 end mapExpParameter;
2421
2422 function mapBody
2423 input output Function fn;
2424 input MapFn mapFn;
2425
2426 partial function MapFn
2427 input output Algorithm alg;
2428 end MapFn;
2429 protected
2430 Class cls;
2431 Sections sections;
2432 algorithm
2433 10931 cls := InstNode.getClass(InstNode.fromHandle(fn.node));
2434 10931 sections := Sections.map(Class.getSections(cls), algFn = mapFn);
2435 10931 cls := cls.setSections(sections, cls);
2436 10931 InstNode.updateClass(cls, InstNode.fromHandle(fn.node));
2437 end mapBody;
2438
2439 function foldExp<ArgT>
2440 input Function fn;
2441 input FoldFunc foldFn;
2442 input output ArgT arg;
2443 input Boolean mapParameters = true;
2444 input Boolean mapBody = true;
2445
2446 partial function FoldFunc
2447 input Expression exp;
2448 input output ArgT arg;
2449 end FoldFunc;
2450 protected
2451 Class cls;
2452 algorithm
2453 35396 cls := InstNode.getClass(InstNode.fromHandle(fn.node));
2454
2455
1/2
✓ Branch 0 taken 35396 times.
✗ Branch 1 not taken.
35396 if mapParameters then
2456 35396 arg := ClassTree.foldComponents(Class.classTree(cls),
2457 function foldExpParameter(foldFn = foldFn), arg);
2458 end if;
2459
2460
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35396 times.
35396 if mapBody then
2461 35396 arg := Sections.foldExp(Class.getSections(cls), foldFn, arg);
2462 end if;
2463 end foldExp;
2464
2465 function foldExpParameter<ArgT>
2466 input InstNode node;
2467 input FoldFunc foldFn;
2468 input output ArgT arg;
2469
2470 partial function FoldFunc
2471 input Expression exp;
2472 input output ArgT arg;
2473 end FoldFunc;
2474 protected
2475 Component comp;
2476 Class cls;
2477 algorithm
2478 3459495 comp := InstNode.component(node);
2479 3459495 arg := Binding.foldExp(Component.getBinding(comp), foldFn, arg);
2480
2481 () := match comp
2482 case Component.COMPONENT()
2483 algorithm
2484 325246 arg := Type.foldDims(comp.ty, function Dimension.foldExp(func = foldFn), arg);
2485 325246 cls := InstNode.getClass(comp.classInst);
2486 325246 arg := ClassTree.foldComponents(Class.classTree(cls),
2487 function foldExpParameter(foldFn = foldFn), arg);
2488 then
2489 ();
2490
2491 else ();
2492 end match;
2493 end foldExpParameter;
2494
2495 function isPartial
2496 input Function fn;
2497 output Boolean isPartial = InstNode.isPartial(InstNode.fromHandle(fn.node));
2498 end isPartial;
2499
2500 function getLocalArguments
2501 input Function fn;
2502 output list<Expression> localArgs = {};
2503 protected
2504 Binding binding;
2505 algorithm
2506 ✗ for l in fn.locals loop
2507 ✗ if InstNode.isComponent(l) then
2508 ✗ binding := Component.getBinding(InstNode.component(l));
2509 ✗ Error.assertion(Binding.hasExp(binding),
2510 getInstanceName() + " got local component without binding", sourceInfo());
2511 ✗ localArgs := Binding.getExp(binding) :: localArgs;
2512 end if;
2513 end for;
2514
2515 ✗ localArgs := listReverseInPlace(localArgs);
2516 end getLocalArguments;
2517
2518 protected
2519 function collectParams
2520 "Sorts all the function parameters as inputs, outputs and locals."
2521 input InstNode node;
2522 input output list<InstNode> inputs = {};
2523 input output list<InstNode> outputs = {};
2524 input output list<InstNode> locals = {};
2525 protected
2526 Class cls;
2527 array<InstNode> comps;
2528 InstNode n;
2529 Boolean check_vis;
2530 algorithm
2531
1/4
✗ Branch 1 not taken.
✓ Branch 2 taken 24044 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
24044 Error.assertion(InstNode.isClass(node) or InstNode.isComponent(node), getInstanceName() + " got non-class/non-component node", sourceInfo());
2532 24044 cls := InstNode.getClass(node);
2533
2534 () := match cls
2535 case Class.INSTANCED_CLASS(elements = ClassTree.FLAT_TREE(components = comps))
2536 algorithm
2537
2/2
✓ Branch 0 taken 22736 times.
✓ Branch 1 taken 1028 times.
122510 for i in arrayLength(comps):-1:1 loop
2538 98749 n := comps[i];
2539 // Base Modelica does not have public/protected.
2540 98749 check_vis := not Flags.getConfigBool(Flags.BASE_MODELICA);
2541
2542 // Sort the components based on their direction.
2543 () := match paramDirection(n, check_vis)
2544 case Direction.INPUT algorithm inputs := n :: inputs; then ();
2545 23215 case Direction.OUTPUT algorithm outputs := n :: outputs; then ();
2546 31521 case Direction.NONE algorithm locals := n :: locals; then ();
2547 end match;
2548 end for;
2549 then
2550 ();
2551
2552 case Class.EXPANDED_DERIVED()
2553 algorithm
2554 280 (inputs, outputs, locals) := collectParams(cls.baseClass);
2555 then
2556 ();
2557
2558 else
2559 algorithm
2560 ✗ Error.terminate(getInstanceName() + " got non-instantiated function " + AbsynUtil.pathString(InstNode.scopePath(node)), sourceInfo());
2561 ✗ then
2562 fail();
2563 end match;
2564 end collectParams;
2565
2566 function paramDirection
2567 input InstNode component;
2568 input Boolean checkVisibility;
2569 output Direction direction;
2570 protected
2571 Component comp;
2572 ConnectorType.Type cty;
2573 InnerOuter io;
2574 Visibility vis;
2575 Variability var;
2576 algorithm
2577 98749 comp := InstNode.component(InstNode.resolveOuter(component));
2578
2579 // Outer components are not instantiated, so check this first to make sure
2580 // it's safe to e.g. fetch the attributes of the component.
2581 98749 io := Component.innerOuter(comp);
2582
2583 // Function components may not be inner/outer.
2584
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 98748 times.
98749 if io <> InnerOuter.NOT_INNER_OUTER then
2585 3 Error.addSourceMessage(Error.INNER_OUTER_FORMAL_PARAMETER,
2586 {Prefixes.innerOuterString(io), InstNode.name(component)},
2587 InstNode.info(InstNode.resolveOuter(component)));
2588 1 fail();
2589 end if;
2590
2591
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 98748 times.
98748 Attributes.ATTRIBUTES(
2592 connectorType = cty,
2593 direction = direction,
2594 variability = var) := Component.getAttributes(comp);
2595
2596 // Function components may not be connectors.
2597
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 98748 times.
98748 if ConnectorType.isFlowOrStream(cty) then
2598 ✗ Error.addSourceMessage(Error.INNER_OUTER_FORMAL_PARAMETER,
2599 {ConnectorType.toString(cty), InstNode.name(component)},
2600 InstNode.info(component));
2601 ✗ fail();
2602 end if;
2603
2604
2/2
✓ Branch 0 taken 98673 times.
✓ Branch 1 taken 75 times.
98748 if checkVisibility then
2605 // Formal parameters must be public, other function variables must be protected.
2606 98673 vis := InstNode.visibility(component);
2607
2608
2/2
✓ Branch 0 taken 67151 times.
✓ Branch 1 taken 31522 times.
98673 if direction <> Direction.NONE then
2609
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 67150 times.
67151 if vis == Visibility.PROTECTED then
2610 2 Error.addSourceMessage(Error.PROTECTED_FORMAL_FUNCTION_VAR,
2611 {InstNode.name(component)}, InstNode.info(component));
2612 1 fail();
2613 end if;
2614 elseif vis == Visibility.PUBLIC then
2615 2 Error.addSourceMessageAsError(Error.NON_FORMAL_PUBLIC_FUNCTION_VAR,
2616 {InstNode.name(component)}, InstNode.info(component));
2617 1 fail();
2618 end if;
2619 end if;
2620 end paramDirection;
2621
2622 function makeSlots
2623 input list<InstNode> inputs;
2624 output list<Slot> slots = {};
2625 protected
2626 Integer index = 1;
2627 algorithm
2628
2/2
✓ Branch 0 taken 79283 times.
✓ Branch 1 taken 26155 times.
105438 for i in inputs loop
2629 79283 slots := makeSlot(i, index) :: slots;
2630 79283 index := index + 1;
2631 end for;
2632
2633 26155 slots := listReverseInPlace(slots);
2634 end makeSlots;
2635
2636 function makeSlot
2637 input InstNode component;
2638 input Integer index;
2639 output Slot slot;
2640 protected
2641 Component comp;
2642 Option<Expression> default;
2643 String name;
2644 algorithm
2645 try
2646 79283 comp := InstNode.component(component);
2647 79283 default := Binding.getExpOpt(Component.getImplicitBinding(comp, InstNode.instanceParent(component)));
2648 79283 name := InstNode.name(component);
2649
2650 // Remove $in_ for OM input output arguments.
2651
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 79283 times.
79283 if StringUtil.startsWith(name, "$in_") then
2652 ✗ name := substring(name, 5, stringLength(name));
2653 end if;
2654
2655 79283 slot := SLOT(component, SlotType.GENERIC, default, NONE(), index, SlotEvalStatus.NOT_EVALUATED);
2656 else
2657 ✗ Error.terminate(getInstanceName() + " got invalid component", sourceInfo());
2658 end try;
2659 end makeSlot;
2660
2661 function hasOMPure
2662 input SCode.Comment cmt;
2663 output Boolean res =
2664 not SCodeUtil.commentHasBooleanNamedAnnotation(cmt, "__OpenModelica_Impure");
2665 end hasOMPure;
2666
2667 function getBuiltinPtr
2668 input SCode.Comment cmt;
2669 output DAE.FunctionBuiltin builtin =
2670 if SCodeUtil.commentHasBooleanNamedAnnotation(cmt, "__OpenModelica_BuiltinPtr") then
2671 DAE.FUNCTION_BUILTIN_PTR() else DAE.FUNCTION_NOT_BUILTIN();
2672 end getBuiltinPtr;
2673
2674 function mergeFunctionAnnotations
2675 "Merges the function's comments from inherited classes."
2676 input list<SCode.Comment> comments;
2677 output SCode.Comment outComment;
2678 protected
2679 Option<String> comment = NONE();
2680 SCode.Mod mod = SCode.NOMOD(), mod2;
2681 algorithm
2682
2/2
✓ Branch 0 taken 38626 times.
✓ Branch 1 taken 23761 times.
62387 for cmt in comments loop
2683
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 38626 times.
✓ Branch 2 taken 24030 times.
✓ Branch 3 taken 14596 times.
38626 if isNone(comment) then
2684 24030 comment := cmt.comment;
2685 end if;
2686
2687 mod := match cmt
2688 case SCode.COMMENT(annotation_ = SOME(SCode.ANNOTATION(modification = mod2)))
2689 34411 then SCodeUtil.mergeModifiers(mod2, mod);
2690 else mod;
2691 end match;
2692 end for;
2693
2694 outComment := match mod
2695 1006 case SCode.NOMOD() then SCode.COMMENT(NONE(), comment);
2696 45510 else SCode.COMMENT(SOME(SCode.ANNOTATION(mod)), comment);
2697 end match;
2698 end mergeFunctionAnnotations;
2699
2700 function makeAttributes
2701 input InstNode node;
2702 input list<InstNode> inputs;
2703 input list<InstNode> outputs;
2704 input list<SCode.Comment> comments;
2705 output DAE.FunctionAttributes attr;
2706 protected
2707 SCode.Element def;
2708 SCode.Restriction res;
2709 SCode.FunctionRestriction fres;
2710 Boolean is_partial;
2711 SCode.Comment cmt;
2712 DAE.Purity purity;
2713 algorithm
2714 23761 def := InstNode.classDefinition(Class.lastBaseClass(node));
2715 23761 res := SCodeUtil.getClassRestriction(def);
2716
2717 23761 Error.assertion(SCodeUtil.isFunctionRestriction(res), getInstanceName() + " got non-function restriction", sourceInfo());
2718
2719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23761 times.
23761 SCode.Restriction.R_FUNCTION(functionRestriction = fres) := res;
2720 23761 is_partial := InstNode.isPartial(node);
2721
2722 23761 cmt := mergeFunctionAnnotations(comments);
2723 23761 purity := InstBasics.getFunctionRestrictionPurity(SCodeUtil.getFunctionRestrictionPurity(fres), cmt, newFrontend = true);
2724
2725 attr := matchcontinue fres
2726 local
2727 Boolean has_unbox_args;
2728 String name;
2729 list<String> in_params, out_params;
2730 DAE.InlineType inline_ty;
2731 Boolean generateEvents;
2732
2733 // External builtin function.
2734 case SCode.FunctionRestriction.FR_EXTERNAL_FUNCTION()
2735 algorithm
2736
4/4
✓ Branch 0 taken 7199 times.
✓ Branch 1 taken 6849 times.
✓ Branch 2 taken 7199 times.
✓ Branch 3 taken 6849 times.
14048 in_params := list(InstNode.name(i) for i in inputs);
2737
4/4
✓ Branch 0 taken 5577 times.
✓ Branch 1 taken 6849 times.
✓ Branch 2 taken 5577 times.
✓ Branch 3 taken 6849 times.
12426 out_params := list(InstNode.name(o) for o in outputs);
2738 6849 name := SCodeUtil.isBuiltinFunction(def, in_params, out_params);
2739 6401 inline_ty := InstBasics.commentIsInlineFunc(cmt);
2740 6401 generateEvents := InstBasics.commentGenerateEvents(cmt);
2741 6401 has_unbox_args := hasUnboxArgsAnnotation(cmt);
2742
4/6
✓ Branch 0 taken 6354 times.
✓ Branch 1 taken 47 times.
✓ Branch 3 taken 6401 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 6401 times.
✗ Branch 6 not taken.
25604 then
2743 DAE.FUNCTION_ATTRIBUTES(inline_ty, generateEvents, purity, is_partial,
2744 DAE.FUNCTION_BUILTIN(SOME(name), has_unbox_args), DAE.FP_NON_PARALLEL(), DAE.NoReturn.RETURNS);
2745
2746 // Parallel function: there are some builtin functions.
2747 case SCode.FunctionRestriction.FR_PARALLEL_FUNCTION()
2748 algorithm
2749 ✗ in_params := list(InstNode.name(i) for i in inputs);
2750 ✗ out_params := list(InstNode.name(o) for o in outputs);
2751 ✗ name := SCodeUtil.isBuiltinFunction(def, in_params, out_params);
2752 ✗ inline_ty := InstBasics.commentIsInlineFunc(cmt);
2753 ✗ generateEvents := InstBasics.commentGenerateEvents(cmt);
2754 ✗ has_unbox_args := hasUnboxArgsAnnotation(cmt);
2755 ✗ then
2756 DAE.FUNCTION_ATTRIBUTES(inline_ty, generateEvents, purity, is_partial,
2757 DAE.FUNCTION_BUILTIN(SOME(name), has_unbox_args), DAE.FP_PARALLEL_FUNCTION(), DAE.NoReturn.RETURNS);
2758
2759 // Parallel function: non-builtin.
2760 case SCode.FunctionRestriction.FR_PARALLEL_FUNCTION()
2761 algorithm
2762 ✗ inline_ty := InstBasics.commentIsInlineFunc(cmt);
2763 ✗ generateEvents := InstBasics.commentGenerateEvents(cmt);
2764 ✗ then
2765 DAE.FUNCTION_ATTRIBUTES(inline_ty, generateEvents, purity, is_partial,
2766 getBuiltinPtr(cmt), DAE.FP_PARALLEL_FUNCTION(), DAE.NoReturn.RETURNS);
2767
2768 // Kernel functions: never builtin and never inlined.
2769 case SCode.FunctionRestriction.FR_KERNEL_FUNCTION()
2770 ✗ then DAE.FUNCTION_ATTRIBUTES(DAE.NO_INLINE(), false, purity, is_partial,
2771 DAE.FUNCTION_NOT_BUILTIN(), DAE.FP_KERNEL_FUNCTION(), DAE.NoReturn.RETURNS);
2772
2773 // Normal function.
2774 else
2775 algorithm
2776 17360 inline_ty := InstBasics.commentIsInlineFunc(cmt);
2777 17360 generateEvents := InstBasics.commentGenerateEvents(cmt);
2778
2779 // Since Modelica 3.3, normal functions are pure by default and external functions are impure.
2780
4/4
✓ Branch 0 taken 16985 times.
✓ Branch 1 taken 375 times.
✓ Branch 3 taken 16431 times.
✓ Branch 4 taken 554 times.
17360 if purity == DAE.Purity.UNDEFINED and Config.languageStandardAtLeast(Config.LanguageStandard._3_3) then
2781
2/2
✓ Branch 1 taken 16300 times.
✓ Branch 2 taken 131 times.
16431 purity := if SCodeUtil.isExternalFunctionRestriction(fres) then DAE.Purity.IMPURE else DAE.Purity.PURE;
2782 end if;
2783
2784
2/2
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 17349 times.
17360 if SCodeUtil.hasNamedExternalCall("ModelicaError", SCodeUtil.getClassDef(def)) then
2785 purity := DAE.Purity.PURE;
2786 end if;
2787
3/4
✓ Branch 1 taken 17289 times.
✓ Branch 2 taken 71 times.
✓ Branch 3 taken 17360 times.
✗ Branch 4 not taken.
52009 then
2788 DAE.FUNCTION_ATTRIBUTES(inline_ty, generateEvents, purity, is_partial, getBuiltinPtr(cmt), DAE.FP_NON_PARALLEL(), DAE.NoReturn.RETURNS);
2789
2790 end matchcontinue;
2791 end makeAttributes;
2792
2793 function checkParamTypes
2794 "Checks that all the function parameters have types which are allowed in a
2795 function."
2796 input Function fn;
2797 algorithm
2798 26155 checkParamTypes2(fn.inputs);
2799
4/4
✓ Branch 0 taken 25535 times.
✓ Branch 1 taken 26155 times.
✓ Branch 2 taken 25535 times.
✓ Branch 3 taken 26155 times.
51690 checkParamTypes2(list(InstNode.fromHandle(o) for o in fn.outputs));
2800 26155 checkParamTypes2(fn.locals);
2801 end checkParamTypes;
2802
2803 function checkParamTypes2
2804 input list<InstNode> params;
2805 protected
2806 Type ty;
2807 algorithm
2808
2/2
✓ Branch 0 taken 135435 times.
✓ Branch 1 taken 78465 times.
213900 for p in params loop
2809 135435 ty := InstNode.getType(p);
2810
2811
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 135435 times.
135435 if not isValidParamType(ty) then
2812 ✗ Error.addSourceMessage(Error.INVALID_FUNCTION_VAR_TYPE,
2813 {Type.toString(ty), InstNode.name(p)}, InstNode.info(p));
2814 ✗ fail();
2815 end if;
2816 end for;
2817 end checkParamTypes2;
2818
2819 function isValidParamType
2820 input Type ty;
2821 output Boolean isValid;
2822 algorithm
2823 isValid := match ty
2824 case Type.INTEGER() then true;
2825 case Type.REAL() then true;
2826 case Type.STRING() then true;
2827 case Type.BOOLEAN() then true;
2828 case Type.CLOCK() then true;
2829 case Type.ENUMERATION() then true;
2830 case Type.POLYMORPHIC() then true;
2831 13814 case Type.ARRAY() then isValidParamType(ty.elementType);
2832 19908 case Type.COMPLEX() then isValidParamState(Type.complexNode(ty));
2833 case Type.FUNCTION() then true;
2834 56 case Type.METABOXED() then isValidParamType(ty.ty);
2835 else false;
2836 end match;
2837 end isValidParamType;
2838
2839 function isValidParamState
2840 input InstNode cls;
2841 output Boolean isValid;
2842 algorithm
2843 isValid := match Class.restriction(InstNode.getClass(cls))
2844 case Restriction.RECORD() then true;
2845 case Restriction.TYPE() then true;
2846 case Restriction.OPERATOR() then true;
2847 case Restriction.FUNCTION() then true;
2848 case Restriction.EXTERNAL_OBJECT() then true;
2849 else false;
2850 end match;
2851 end isValidParamState;
2852
2853 function checkPartialDerivativeTypes
2854 input Function fn;
2855 protected
2856 InstNode node;
2857 Type ty;
2858 algorithm
2859
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 26154 times.
26156 for i in fn.derivedInputs loop
2860 2 node := listGet(fn.inputs, i);
2861 2 ty := InstNode.getType(node);
2862
2863
3/4
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
2 if not (Type.isReal(ty) and Type.isScalar(ty)) then
2864 3 Error.addSourceMessage(Error.PARTIAL_DERIVATIVE_INPUT_INVALID_TYPE,
2865 {InstNode.name(node), AbsynUtil.pathString(getDerivedFunctionName(fn))}, InstNode.info(InstNode.fromHandle(fn.node)));
2866 1 fail();
2867 end if;
2868 end for;
2869 end checkPartialDerivativeTypes;
2870
2871 public function makeReturnType
2872 input Function fn;
2873 output Type returnType;
2874 protected
2875 list<Type> ret_tyl;
2876 algorithm
2877
4/4
✓ Branch 0 taken 74056 times.
✓ Branch 1 taken 73270 times.
✓ Branch 2 taken 74056 times.
✓ Branch 3 taken 73270 times.
147326 ret_tyl := list(InstNode.getType(InstNode.fromHandle(o)) for o in fn.outputs);
2878
2879 returnType := match ret_tyl
2880 case {} then Type.NORETCALL();
2881 case {returnType} then returnType;
2882 2049 else Type.TUPLE(ret_tyl, NONE());
2883 end match;
2884 end makeReturnType;
2885
2886 function getBody2
2887 input InstNode node;
2888 output list<Statement> body;
2889 protected
2890 Algorithm fn_body;
2891 algorithm
2892 body := match InstNode.getSections(node)
2893 case Sections.SECTIONS(algorithms = {}) then {};
2894 60067 case Sections.SECTIONS(algorithms = {fn_body}) then fn_body.statements;
2895 case Sections.EMPTY() then {};
2896 case Sections.EXTERNAL()
2897 algorithm
2898 ✗ Error.terminate(getInstanceName() + " got function with external section (not algorithm section)", sourceInfo());
2899 ✗ then fail();
2900
2901 case Sections.SECTIONS()
2902 algorithm
2903 ✗ Error.terminate(getInstanceName() + " got function with multiple algorithm sections", sourceInfo());
2904 ✗ then fail();
2905
2906 else
2907 algorithm
2908 Error.terminate(getInstanceName() + " got unknown sections", sourceInfo());
2909 ✗ then fail();
2910
2911 end match;
2912 end getBody2;
2913
2914 function hasSingleOrEmptyBody
2915 input Function fn;
2916 output Boolean b = false;
2917 protected
2918 list<Algorithm> algorithms;
2919 algorithm
2920 try
2921 // InstNode.getSections can fail
2922
2/2
✓ Branch 1 taken 51171 times.
✓ Branch 2 taken 27394 times.
78565 if isBuiltin(fn) then return; end if;
2923 b := match InstNode.getSections(InstNode.fromHandle(fn.node))
2924 27255 case Sections.SECTIONS(algorithms = algorithms) then listLength(algorithms) < 2;
2925 case Sections.EMPTY() then true;
2926 else false;
2927 end match;
2928 else
2929 end try;
2930 end hasSingleOrEmptyBody;
2931
2932 function analyseUnusedParameters
2933 input Function fn;
2934 output list<Integer> unusedInputs = {};
2935 protected
2936 list<InstNode> inputs;
2937 Integer index;
2938 algorithm
2939 9810 inputs := foldExp(fn, analyseUnusedParametersExp, fn.inputs);
2940
2941
2/2
✓ Branch 0 taken 1165 times.
✓ Branch 1 taken 9810 times.
10975 for i in inputs loop
2942 1165 index := List.positionOnTrue(fn.inputs, function InstNode.refEqual(node1 = i));
2943 unusedInputs := index :: unusedInputs;
2944 end for;
2945 end analyseUnusedParameters;
2946
2947 function analyseUnusedParametersExp
2948 input Expression exp;
2949 input output list<InstNode> params;
2950 algorithm
2951
2/2
✓ Branch 0 taken 152207 times.
✓ Branch 1 taken 208040 times.
360247 if not listEmpty(params) then
2952 208040 params := Expression.fold(exp, analyseUnusedParametersExp2, params);
2953 end if;
2954 end analyseUnusedParametersExp;
2955
2956 function analyseUnusedParametersExp2
2957 input Expression exp;
2958 input output list<InstNode> params;
2959 algorithm
2960 () := match exp
2961 case Expression.CREF()
2962 algorithm
2963 132181 params := List.deleteMemberOnTrue(exp.cref, params, ComponentRef.containsNode);
2964 then
2965 ();
2966
2967 else ();
2968 end match;
2969 end analyseUnusedParametersExp2;
2970
2971 function sortLocals
2972 "Sorts local components in a function such that they can be initialized in
2973 order, or gives an error if there are mutually dependent components."
2974 input output list<InstNode> locals;
2975 input SourceInfo info;
2976 protected
2977 UnorderedSet<InstNode> locals_set;
2978 list<tuple<InstNode, list<InstNode>>> dep_graph, cycles;
2979 String cycles_str;
2980 algorithm
2981 26147 locals_set := UnorderedSet.fromList(locals, InstNode.hash, InstNode.refEqual);
2982 26147 dep_graph := Graph.buildGraph(locals, getLocalDependencies, locals_set);
2983 26147 (locals, cycles) := Graph.topologicalSort(dep_graph, InstNode.refEqual);
2984
2985
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26147 times.
26147 if not listEmpty(cycles) then
2986 ✗ cycles_str := stringDelimitList(
2987 list(List.toString(cycle, InstNode.name, List.Style.FLAT_CURLY)
2988 for cycle in Graph.findCycles(cycles, InstNode.refEqual)), ", ");
2989
2990 ✗ Error.addSourceMessage(Error.CYCLIC_FUNCTION_COMPONENTS, {cycles_str}, info);
2991 ✗ fail();
2992 end if;
2993 end sortLocals;
2994
2995 function getLocalDependencies
2996 input InstNode node;
2997 input UnorderedSet<InstNode> locals;
2998 output list<InstNode> dependencies;
2999 protected
3000 UnorderedSet<InstNode> deps;
3001 algorithm
3002 // Use a set to store the dependencies to avoid duplicates.
3003 30618 deps := UnorderedSet.new(InstNode.hash, InstNode.refEqual, 1);
3004 30618 deps := getLocalDependencies2(node, locals, deps);
3005
3006 // If we have a record instance with fields that have bindings that refer to
3007 // other fields we'll get a dependency on the record instance itself here.
3008 // But that's actually fine, so remove it to avoid a false cycle being detected.
3009 30618 UnorderedSet.remove(node, deps);
3010
3011 30618 deps := Type.foldDims(InstNode.getType(node),
3012 function getLocalDependenciesDim(locals = locals), deps);
3013
3014 30618 dependencies := UnorderedSet.toList(deps);
3015 end getLocalDependencies;
3016
3017 function getLocalDependencies2
3018 input InstNode node;
3019 input UnorderedSet<InstNode> locals;
3020 input output UnorderedSet<InstNode> dependencies;
3021 protected
3022 Component comp;
3023 Binding binding;
3024 algorithm
3025 66088 comp := InstNode.component(node);
3026 66088 binding := Component.getBinding(comp);
3027
3028
2/2
✓ Branch 1 taken 22283 times.
✓ Branch 2 taken 43805 times.
66088 if Binding.hasExp(binding) then
3029 22283 dependencies := getLocalDependenciesExp(Binding.getExp(binding), locals, dependencies);
3030 elseif Type.isRecord(Component.getType(comp)) then
3031 // If the component is a record instance without a binding, check the
3032 // bindings on the record fields instead.
3033 3141 dependencies := ClassTree.foldComponents(
3034 Class.classTree(InstNode.getClass(node)),
3035 function getLocalDependencies2(locals = locals), dependencies);
3036 end if;
3037 end getLocalDependencies2;
3038
3039 function getLocalDependenciesExp
3040 input Expression exp;
3041 input UnorderedSet<InstNode> locals;
3042 input output UnorderedSet<InstNode> deps;
3043 algorithm
3044 22862 deps := Expression.fold(exp,
3045 function getLocalDependenciesExp2(locals = locals), deps);
3046 end getLocalDependenciesExp;
3047
3048 function getLocalDependenciesExp2
3049 input Expression exp;
3050 input UnorderedSet<InstNode> locals;
3051 input output UnorderedSet<InstNode> deps;
3052 algorithm
3053 () := match exp
3054 local
3055 ComponentRef cr;
3056 InstNode cr_node;
3057
3058 case Expression.CREF()
3059 algorithm
3060 // Get the 'last' part of the cref, i.e. a in a.b.c, in case there are
3061 // e.g. local record instances.
3062 8585 cr := ComponentRef.last(exp.cref);
3063
3064 // Make sure it's something that actually has a node.
3065
1/2
✓ Branch 1 taken 8585 times.
✗ Branch 2 not taken.
8585 if ComponentRef.isCref(cr) then
3066 8585 cr_node := ComponentRef.node(cr);
3067
3068 // Check if the cref refers to a local variable, in that case add it
3069 // to the set of dependencies.
3070
2/2
✓ Branch 1 taken 4098 times.
✓ Branch 2 taken 4487 times.
8585 if UnorderedSet.contains(cr_node, locals) then
3071 4098 UnorderedSet.add(cr_node, deps);
3072 end if;
3073 end if;
3074 then
3075 ();
3076
3077 else ();
3078 end match;
3079 end getLocalDependenciesExp2;
3080
3081 function getLocalDependenciesDim
3082 input Dimension dim;
3083 input UnorderedSet<InstNode> locals;
3084 input output UnorderedSet<InstNode> deps;
3085 algorithm
3086 3605 deps := Dimension.foldExp(dim,
3087 function getLocalDependenciesExp(locals = locals), deps);
3088 end getLocalDependenciesDim;
3089
3090 function getDerivative
3091 "returns the first derivative that fits the interface_map
3092 and returns NONE() if none of them fit."
3093 input Function original;
3094 input UnorderedMap<String, Boolean> interface_map;
3095 output Option<Function> derivative = NONE();
3096 protected
3097 list<FunctionDerivative> derivatives;
3098 algorithm
3099
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 27 times.
74 for func in original.derivatives loop
3100
2/2
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 1 time.
47 if FunctionDerivative.perfectFit(func, interface_map) then
3101 46 derivative := SOME(listHead(getCachedFuncs(InstNode.borrow(func.derivativeFn))));
3102 46 return;
3103 end if;
3104 end for;
3105
3106 // no derivative could be found, set whole map to true
3107 // so that the self-generated function removes as much as possible
3108
2/2
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 27 times.
50 for key in UnorderedMap.keyList(interface_map) loop
3109 23 UnorderedMap.add(key, true, interface_map);
3110 end for;
3111 end getDerivative;
3112
3113 function checkUseBeforeAssign
3114 "Checks if any output or local variables in a function are used
3115 uninitialized and prints warnings for such uses."
3116 input Function fn;
3117 protected
3118 Vector<InstNode> unassigned;
3119 list<Statement> body;
3120 InstNode parent;
3121 list<SourceInfo> sources;
3122 Boolean no_return;
3123 algorithm
3124 // Skip external and builtin functions.
3125
3/4
✓ Branch 1 taken 10070 times.
✓ Branch 2 taken 268 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 10070 times.
10338 if isExternal(fn) or isBuiltin(fn) then
3126 268 return;
3127 end if;
3128
3129 // Check if there are variables being used uninitialized.
3130 10070 unassigned := Vector.new<InstNode>();
3131
4/4
✓ Branch 0 taken 10448 times.
✓ Branch 1 taken 10070 times.
✓ Branch 2 taken 10448 times.
✓ Branch 3 taken 10070 times.
20518 addUnassignedComponents(unassigned, list(InstNode.fromHandle(o) for o in fn.outputs));
3132 10070 addUnassignedComponents(unassigned, fn.locals);
3133
3134 10070 body := getBody(fn);
3135 10070 no_return := checkUseBeforeAssign2(unassigned, body);
3136
3137 // Skip checking for unassigned outputs if the function is known to never return.
3138
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 10040 times.
10070 if no_return then
3139 30 return;
3140 end if;
3141
3142 // Give a warning for any outputs that were not assigned in the function.
3143
2/2
✓ Branch 1 taken 357 times.
✓ Branch 2 taken 10040 times.
10397 for var in Vector.toList(unassigned) loop
3144
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 356 times.
357 if InstNode.isOutput(var) then
3145 1 parent := InstNode.InstNode.parent(var);
3146 1 sources := {InstNode.info(var)};
3147
3148 // If the output is inherited then also give the source location of the
3149 // derived function, since that's likely where the assignment is missing.
3150
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1 if InstNode.isBaseClass(parent) then
3151 ✗ sources := InstNode.info(InstNode.getDerivedNode(parent)) :: sources;
3152 end if;
3153
3154 2 Error.addMultiSourceMessage(Error.UNASSIGNED_FUNCTION_OUTPUT, {InstNode.name(var)}, sources);
3155 end if;
3156 end for;
3157 end checkUseBeforeAssign;
3158
3159 function addUnassignedComponents
3160 input Vector<InstNode> unassigned;
3161 input list<InstNode> variables;
3162 protected
3163 Type ty;
3164 algorithm
3165
2/2
✓ Branch 0 taken 34942 times.
✓ Branch 1 taken 20188 times.
55130 for var in variables loop
3166 34942 ty := InstNode.getType(var);
3167
3168
4/4
✓ Branch 1 taken 26143 times.
✓ Branch 2 taken 8799 times.
✓ Branch 5 taken 22807 times.
✓ Branch 6 taken 3336 times.
34942 if Type.isScalarBuiltin(ty) and not Component.hasBinding(InstNode.component(var)) then
3169 22807 Vector.push(unassigned, var);
3170 end if;
3171 end for;
3172 end addUnassignedComponents;
3173
3174 function checkUseBeforeAssign2
3175 input Vector<InstNode> unassigned;
3176 input list<Statement> statements;
3177 input Option<String> generatedName = NONE() "name of the generated function if checking generated code, where use before assign is an error";
3178 output Boolean noReturn = false "True if an assert/terminate is definitely triggered";
3179 protected
3180 SourceInfo info;
3181 Option<InstNode> shadowed;
3182 Integer index;
3183 algorithm
3184
2/2
✓ Branch 0 taken 90580 times.
✓ Branch 1 taken 19369 times.
109949 for stmt in statements loop
3185 90580 info := Statement.info(stmt);
3186
3187 () := match stmt
3188 case Statement.ASSIGNMENT()
3189 algorithm
3190 83690 checkUseBeforeAssignExp(unassigned, stmt.rhs, info, generatedName);
3191
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 83690 times.
✓ Branch 2 taken 340 times.
✓ Branch 3 taken 83350 times.
84030 markAssignedOutput(unassigned, stmt.lhs, isSome(generatedName));
3192 then
3193 ();
3194
3195 case Statement.FOR()
3196 algorithm
3197
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 396 times.
✓ Branch 2 taken 396 times.
✗ Branch 3 not taken.
396 if isSome(stmt.range) then
3198 396 checkUseBeforeAssignExp(unassigned, Util.getOption(stmt.range), info, generatedName);
3199 end if;
3200
3201 // generated functions are checked by name, so a local with the name of the
3202 // iterator is shadowed inside the loop and must not be matched
3203 shadowed := NONE();
3204
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 396 times.
✓ Branch 2 taken 22 times.
✓ Branch 3 taken 374 times.
396 if isSome(generatedName) then
3205 22 (shadowed, index) := Vector.find(unassigned, function InstNode.nameEqual(node1 = stmt.iterator));
3206
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 21 times.
22 if index > 0 then
3207 1 Vector.remove(unassigned, index);
3208 end if;
3209 end if;
3210
3211 396 checkUseBeforeAssign2(unassigned, stmt.body, generatedName);
3212
3213
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 396 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 395 times.
396 if isSome(shadowed) then
3214 1 Vector.push(unassigned, Util.getOption(shadowed));
3215 end if;
3216 then
3217 ();
3218
3219 case Statement.IF()
3220 algorithm
3221 4183 checkUseBeforeAssignIf(unassigned, stmt.branches, info, generatedName);
3222 then
3223 ();
3224
3225 case Statement.ASSERT()
3226 algorithm
3227
2/2
✓ Branch 1 taken 328 times.
✓ Branch 2 taken 1697 times.
2025 if Expression.isFalse(stmt.condition) then
3228 noReturn := true;
3229 328 return;
3230 end if;
3231
3232 1697 checkUseBeforeAssignExp(unassigned, stmt.condition, info, generatedName);
3233 1697 checkUseBeforeAssignExp(unassigned, stmt.message, info, generatedName);
3234 1697 checkUseBeforeAssignExp(unassigned, stmt.level, info, generatedName);
3235 then
3236 ();
3237
3238 case Statement.TERMINATE()
3239 algorithm
3240 noReturn := true;
3241 ✗ return;
3242 then
3243 ();
3244
3245 case Statement.WHILE()
3246 algorithm
3247 204 checkUseBeforeAssignExp(unassigned, stmt.condition, info, generatedName);
3248 204 checkUseBeforeAssign2(unassigned, stmt.body, generatedName);
3249 then
3250 ();
3251
3252 else ();
3253 end match;
3254 end for;
3255 end checkUseBeforeAssign2;
3256
3257 function markAssignedOutput
3258 input Vector<InstNode> unassigned;
3259 input Expression assignedExp;
3260 input Boolean byName = false "compare nodes by name instead of by reference,
3261 needed for generated functions where the crefs in the body do not share
3262 nodes with the interface (names are unique within a function scope)";
3263 protected
3264 InstNode node;
3265 Integer index;
3266 algorithm
3267 () := match assignedExp
3268 case Expression.CREF()
3269 guard ComponentRef.isCref(assignedExp.cref)
3270 algorithm
3271 84631 node := ComponentRef.node(ComponentRef.last(assignedExp.cref));
3272
3273
2/2
✓ Branch 0 taken 668 times.
✓ Branch 1 taken 83963 times.
84631 if byName then
3274 668 (_, index) := Vector.find(unassigned, function InstNode.nameEqual(node1 = node));
3275 else
3276 83963 (_, index) := Vector.find(unassigned, function InstNode.refEqual(node1 = node));
3277 end if;
3278
3279
2/2
✓ Branch 0 taken 32456 times.
✓ Branch 1 taken 52175 times.
84631 if index > 0 then
3280 32456 Vector.remove(unassigned, index);
3281 end if;
3282 then
3283 ();
3284
3285 case Expression.TUPLE()
3286 algorithm
3287
2/2
✓ Branch 0 taken 1202 times.
✓ Branch 1 taken 546 times.
1748 for e in assignedExp.elements loop
3288 1202 markAssignedOutput(unassigned, e, byName);
3289 end for;
3290 then
3291 ();
3292
3293 else ();
3294 end match;
3295 end markAssignedOutput;
3296
3297 function checkUseBeforeAssignIf
3298 input Vector<InstNode> unassigned;
3299 input list<tuple<Expression, list<Statement>>> branches;
3300 input SourceInfo info;
3301 input Option<String> generatedName = NONE();
3302 protected
3303 Vector<InstNode> unassigned_branch;
3304 list<InstNode> assigned = {};
3305 Integer index;
3306 algorithm
3307
2/2
✓ Branch 0 taken 9003 times.
✓ Branch 1 taken 4183 times.
13186 for b in branches loop
3308 9003 checkUseBeforeAssignExp(unassigned, Util.tuple21(b), info, generatedName);
3309 end for;
3310
3311 // Check each branch separately, then assume that any variables that were
3312 // assigned (or incorrectly used) in at least one branch are assigned after
3313 // the if-statement to avoid false positives.
3314
2/2
✓ Branch 0 taken 9003 times.
✓ Branch 1 taken 4183 times.
13186 for b in branches loop
3315 9003 unassigned_branch := Vector.copy(unassigned);
3316 9003 checkUseBeforeAssign2(unassigned_branch, Util.tuple22(b), generatedName);
3317
3318
2/2
✓ Branch 2 taken 6471 times.
✓ Branch 3 taken 2532 times.
9003 if Vector.size(unassigned) <> Vector.size(unassigned_branch) then
3319 6471 assigned := listAppend(
3320 List.setDifferenceOnTrue(Vector.toList(unassigned), Vector.toList(unassigned_branch), InstNode.refEqual),
3321 assigned);
3322 end if;
3323 end for;
3324
3325
2/2
✓ Branch 0 taken 1320 times.
✓ Branch 1 taken 2863 times.
4183 if not listEmpty(assigned) then
3326 2863 assigned := List.uniqueOnTrue(assigned, InstNode.refEqual);
3327
3328
2/2
✓ Branch 0 taken 9220 times.
✓ Branch 1 taken 2863 times.
12083 for a in assigned loop
3329 9220 (_, index) := Vector.find(unassigned, function InstNode.refEqual(node1 = a));
3330
3331
1/2
✓ Branch 0 taken 9220 times.
✗ Branch 1 not taken.
9220 if index > 0 then
3332 9220 Vector.remove(unassigned, index);
3333 end if;
3334 end for;
3335 end if;
3336 end checkUseBeforeAssignIf;
3337
3338 function checkUseBeforeAssignExp
3339 input Vector<InstNode> unassigned;
3340 input Expression exp;
3341 input SourceInfo info;
3342 input Option<String> generatedName = NONE();
3343 algorithm
3344 98384 Expression.apply(exp,
3345 function checkUseBeforeAssignExp_traverse(unassigned = unassigned, info = info, generatedName = generatedName));
3346 end checkUseBeforeAssignExp;
3347
3348 function checkUseBeforeAssignExp_traverse
3349 input Vector<InstNode> unassigned;
3350 input Expression exp;
3351 input SourceInfo info;
3352 input Option<String> generatedName;
3353 protected
3354 Integer index;
3355 InstNode node;
3356 String fn_name;
3357 algorithm
3358 () := match exp
3359 case Expression.CREF()
3360 guard ComponentRef.isCref(exp.cref)
3361 algorithm
3362 454036 node := ComponentRef.node(ComponentRef.last(exp.cref));
3363
3364
3/4
✗ Branch 0 not taken.
✓ Branch 1 taken 454036 times.
✓ Branch 2 taken 449744 times.
✓ Branch 3 taken 4292 times.
454036 if isSome(generatedName) then
3365 4292 (_, index) := Vector.find(unassigned, function InstNode.nameEqual(node1 = node));
3366 else
3367 449744 (_, index) := Vector.find(unassigned, function InstNode.refEqual(node1 = node));
3368 end if;
3369
3370
2/2
✓ Branch 0 taken 185 times.
✓ Branch 1 taken 453851 times.
454036 if index > 0 then
3371 185 Vector.remove(unassigned, index);
3372
3373 () := match generatedName
3374 case SOME(fn_name)
3375 algorithm
3376 // in generated functions a use before assign is provably an
3377 // error per the specification, and also a compiler bug
3378 ✗ Error.addSourceMessage(Error.GENERATED_FUNCTION_USE_BEFORE_ASSIGN,
3379 {InstNode.name(node), fn_name}, info);
3380 ✗ then
3381 fail();
3382
3383 else
3384 algorithm
3385 370 Error.addStrictMessage(Error.WARNING_DEF_USE, {InstNode.name(node)}, info);
3386 then
3387 ();
3388 end match;
3389 end if;
3390 then
3391 ();
3392
3393 else ();
3394 end match;
3395 end checkUseBeforeAssignExp_traverse;
3396
3397 function checkUseBeforeAssignGenerated
3398 "Use before assign check for compiler-generated functions (e.g. symbolic
3399 derivatives), which never pass through typeFunctionBody. Provably
3400 unassigned uses and outputs are reported as errors; variables not provably
3401 assigned on every path are returned for the caller to initialize."
3402 input Function fn;
3403 output list<InstNode> uninitialized = {} "scalar variables not proven to always be assigned";
3404 protected
3405 Vector<InstNode> unassigned, not_proven;
3406 list<Statement> body;
3407 String fn_name;
3408 Integer index;
3409 algorithm
3410
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 24 times.
24 if isExternal(fn) or isBuiltin(fn) then
3411 ✗ return;
3412 end if;
3413
3414 24 body := getBody(fn);
3415
3416
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if listEmpty(body) then
3417 ✗ return;
3418 end if;
3419
3420 24 fn_name := AbsynUtil.pathString(name(fn));
3421 24 unassigned := Vector.new<InstNode>();
3422
4/4
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 24 times.
56 addUnassignedComponents(unassigned, list(InstNode.fromHandle(o) for o in fn.outputs));
3423 24 addUnassignedComponents(unassigned, fn.locals);
3424 24 not_proven := Vector.copy(unassigned);
3425
3426 // optimistic pass: leaves variables assigned on no path
3427 24 checkUseBeforeAssign2(unassigned, body, SOME(fn_name));
3428 // pessimistic pass: leaves variables not assigned on every path
3429 24 markProvenAssigned(not_proven, body);
3430
3431
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 24 times.
28 for var in Vector.toList(unassigned) loop
3432
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 3 times.
4 if InstNode.isOutput(var) then
3433
1/2
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
1 if Type.isDiscrete(InstNode.getType(var)) then
3434 // the derivative of a discrete output (e.g. an Integer error code) is zero
3435 uninitialized := var :: uninitialized;
3436 else
3437 ✗ Error.addSourceMessage(Error.GENERATED_FUNCTION_UNASSIGNED_OUTPUT,
3438 {InstNode.name(var), fn_name}, InstNode.info(var));
3439 ✗ fail();
3440 end if;
3441 end if;
3442 end for;
3443
3444 // assigned on some but not all paths: caller must initialize them
3445
2/2
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 24 times.
96 for var in Vector.toList(not_proven) loop
3446 72 (_, index) := Vector.find(unassigned, function InstNode.nameEqual(node1 = var));
3447
3448
2/2
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 4 times.
72 if index <= 0 then
3449 uninitialized := var :: uninitialized;
3450 end if;
3451 end for;
3452 end checkUseBeforeAssignGenerated;
3453
3454 function initializeUninitialized
3455 "Prepends '<var> := 0' statements for generated-function variables that
3456 could not be proven to be assigned before use, with a warning."
3457 input output Sections sections;
3458 input list<InstNode> variables;
3459 input String fn_name;
3460 protected
3461 list<Statement> inits = {};
3462 Type ty;
3463 Expression zero;
3464 Algorithm alg;
3465 list<Algorithm> rest;
3466 algorithm
3467 () := match sections
3468 case Sections.SECTIONS(algorithms = alg :: rest) algorithm
3469
2/2
✓ Branch 1 taken 69 times.
✓ Branch 2 taken 6 times.
75 for var in listReverse(variables) loop
3470 69 ty := InstNode.getType(var);
3471 zero := match ty
3472 case Type.STRING() then Expression.STRING("");
3473 69 else Expression.makeZero(ty);
3474 end match;
3475 207 Error.addSourceMessage(Error.GENERATED_FUNCTION_DEFAULT_INIT,
3476 {InstNode.name(var), fn_name, Expression.toString(zero)}, InstNode.info(var));
3477 69 inits := Statement.ASSIGNMENT(Expression.fromCref(ComponentRef.fromNode(var, ty)), zero, ty, alg.source) :: inits;
3478 end for;
3479 6 alg.statements := listAppend(inits, alg.statements);
3480 6 sections.algorithms := alg :: rest;
3481 then ();
3482 else ();
3483 end match;
3484 end initializeUninitialized;
3485
3486 function markProvenAssigned
3487 "pessimistic assignment analysis: removes variables from the vector only
3488 if they are provably assigned on every possible execution path. For and
3489 while loops may execute zero times, so assignments in them prove nothing."
3490 input Vector<InstNode> unassigned;
3491 input list<Statement> statements;
3492 algorithm
3493
5/5
✓ Branch 0 taken 294 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 334 times.
✓ Branch 4 taken 57 times.
391 for stmt in statements loop
3494 () := match stmt
3495 case Statement.ASSIGNMENT()
3496 algorithm
3497 294 markAssignedOutput(unassigned, stmt.lhs, true);
3498 then
3499 ();
3500
3501 case Statement.IF()
3502 algorithm
3503 17 markProvenAssignedIf(unassigned, stmt.branches);
3504 then
3505 ();
3506
3507 else ();
3508 end match;
3509 end for;
3510 end markProvenAssigned;
3511
3512 function markProvenAssignedIf
3513 "removes variables that are assigned in every branch of an if-statement,
3514 which requires an else branch for the branches to cover all paths"
3515 input Vector<InstNode> unassigned;
3516 input list<tuple<Expression, list<Statement>>> branches;
3517 protected
3518 Boolean has_else = false;
3519 Vector<InstNode> unassigned_branch;
3520 list<InstNode> still_unassigned = {};
3521 Integer index;
3522 algorithm
3523
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 17 times.
50 for b in branches loop
3524 // only an else branch (condition is literally true) at the end makes
3525 // the branches cover every execution path
3526 33 has_else := Expression.isTrue(Util.tuple21(b));
3527 33 unassigned_branch := Vector.copy(unassigned);
3528 33 markProvenAssigned(unassigned_branch, Util.tuple22(b));
3529 33 still_unassigned := listAppend(Vector.toList(unassigned_branch), still_unassigned);
3530 end for;
3531
3532
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 11 times.
17 if has_else then
3533
2/2
✓ Branch 1 taken 212 times.
✓ Branch 2 taken 11 times.
223 for node in Vector.toList(unassigned) loop
3534
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 182 times.
212 if not List.isMemberOnTrue(node, still_unassigned, InstNode.nameEqual) then
3535 30 (_, index) := Vector.find(unassigned, function InstNode.nameEqual(node1 = node));
3536
3537
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 if index > 0 then
3538 30 Vector.remove(unassigned, index);
3539 end if;
3540 end if;
3541 end for;
3542 end if;
3543 end markProvenAssignedIf;
3544
3545 function instPartialDerivedVars
3546 input SCode.ClassDef classDef;
3547 input list<InstNode> inputs;
3548 input Function fn;
3549 input InstContext.Type context;
3550 input SourceInfo info;
3551 output list<Integer> derivedVars = {};
3552 protected
3553 Integer index;
3554 algorithm
3555 () := match classDef
3556 case SCode.ClassDef.PDER()
3557 algorithm
3558
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 for var in classDef.derivedVariables loop
3559 2 index := List.positionOnTrue(inputs, function InstNode.isNamed(name = var));
3560
3561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if index < 1 then
3562 ✗ Error.addSourceMessage(Error.PARTIAL_DERIVATIVE_INPUT_NOT_FOUND,
3563 {var, AbsynUtil.pathString(getDerivedFunctionName(fn))}, info);
3564 ✗ fail();
3565 end if;
3566
3567 derivedVars := index :: derivedVars;
3568 end for;
3569
3570 2 derivedVars := listReverseInPlace(derivedVars);
3571 then
3572 ();
3573
3574 else ();
3575 end match;
3576 end instPartialDerivedVars;
3577 end Function;
3578
3579 annotation(__OpenModelica_Interface="nf_frontend");
3580 end NFFunction;
3581