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 |