OMCompiler/Compiler/NFFrontEnd/NFTyping.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 NFTyping | ||
| 37 | " file: NFTyping.mo | ||
| 38 | package: NFTyping | ||
| 39 | description: NFInst typing. | ||
| 40 | |||
| 41 | |||
| 42 | Functions used by NFInst for typing. | ||
| 43 | " | ||
| 44 | |||
| 45 | import Binding = NFBinding; | ||
| 46 | import Component = NFComponent; | ||
| 47 | import NFComponent.ComponentState; | ||
| 48 | import Dimension = NFDimension; | ||
| 49 | import Equation = NFEquation; | ||
| 50 | import Class = NFClass; | ||
| 51 | import Expression = NFExpression; | ||
| 52 | import NFInstNode.InstNode; | ||
| 53 | import NFInstNode; | ||
| 54 | import NFModifier.Modifier; | ||
| 55 | import SimplifyExp = NFSimplifyExp; | ||
| 56 | import Statement = NFStatement; | ||
| 57 | import NFType.Type; | ||
| 58 | import Operator = NFOperator; | ||
| 59 | import NFPrefixes.{Variability, Purity, ConnectorType}; | ||
| 60 | import Prefixes = NFPrefixes; | ||
| 61 | import Connector = NFConnector; | ||
| 62 | import Connection = NFConnection; | ||
| 63 | import Algorithm = NFAlgorithm; | ||
| 64 | import Record = NFRecord; | ||
| 65 | import InstContext = NFInstContext; | ||
| 66 | |||
| 67 | protected | ||
| 68 | import Absyn; | ||
| 69 | import Attributes = NFAttributes; | ||
| 70 | import Builtin = NFBuiltin; | ||
| 71 | import BuiltinCall = NFBuiltinCall; | ||
| 72 | import Ceval = NFCeval; | ||
| 73 | import Config; | ||
| 74 | import ComponentRef = NFComponentRef; | ||
| 75 | import Origin = NFComponentRef.Origin; | ||
| 76 | import ExecStat.execStat; | ||
| 77 | import Lookup = NFLookup; | ||
| 78 | import MatchKind = NFTypeCheck.MatchKind; | ||
| 79 | import Call = NFCall; | ||
| 80 | import DAE; | ||
| 81 | import NFClassTree.ClassTree; | ||
| 82 | import Subscript = NFSubscript; | ||
| 83 | import TypeCheck = NFTypeCheck; | ||
| 84 | import NFSections.Sections; | ||
| 85 | import List; | ||
| 86 | import MetaModelica.Dangerous.listReverseInPlace; | ||
| 87 | import ComplexType = NFComplexType; | ||
| 88 | import Restriction = NFRestriction; | ||
| 89 | import NFModifier.ModTable; | ||
| 90 | import Package = NFPackage; | ||
| 91 | import NFFunction.Function; | ||
| 92 | import NFInstNode.CachedData; | ||
| 93 | import Direction = NFPrefixes.Direction; | ||
| 94 | import ElementSource; | ||
| 95 | import System; | ||
| 96 | import ErrorExt; | ||
| 97 | import ErrorTypes; | ||
| 98 | import OperatorOverloading = NFOperatorOverloading; | ||
| 99 | import Structural = NFStructural; | ||
| 100 | import Array; | ||
| 101 | |||
| 102 | public | ||
| 103 | uniontype TypingError | ||
| 104 | record NO_ERROR end NO_ERROR; | ||
| 105 | |||
| 106 | record OUT_OF_BOUNDS | ||
| 107 | Integer upperBound; | ||
| 108 | end OUT_OF_BOUNDS; | ||
| 109 | |||
| 110 | record UNKNOWN_TYPE end UNKNOWN_TYPE; | ||
| 111 | |||
| 112 | function isError | ||
| 113 | input TypingError error; | ||
| 114 | output Boolean isError; | ||
| 115 | algorithm | ||
| 116 | isError := match error | ||
| 117 | case NO_ERROR() then false; | ||
| 118 | else true; | ||
| 119 | end match; | ||
| 120 | end isError; | ||
| 121 | end TypingError; | ||
| 122 | |||
| 123 | // Used by typeDimension for catching cyclic dimension involving : | ||
| 124 | constant Expression WHOLEDIM_CREF = Expression.CREF(Type.UNKNOWN(), | ||
| 125 | ComponentRef.CREF(InstNode.NAME_NODE(":"), {}, Type.UNKNOWN(), NFComponentRef.Origin.CREF, ComponentRef.EMPTY())); | ||
| 126 | |||
| 127 | public | ||
| 128 | function typeClass | ||
| 129 | input InstNode cls; | ||
| 130 | input InstContext.Type context; | ||
| 131 | protected | ||
| 132 | InstContext.Type next_context; | ||
| 133 | algorithm | ||
| 134 | 1712 | next_context := InstContext.set(context, NFInstContext.CLASS); | |
| 135 | 1712 | typeClassType(cls, NFBinding.EMPTY_BINDING, next_context, cls); | |
| 136 | 1712 | typeComponents(cls, next_context); | |
| 137 | 1702 | execStat("NFTyping.typeComponents"); | |
| 138 | 1702 | typeBindings(cls, next_context); | |
| 139 | 1654 | execStat("NFTyping.typeBindings"); | |
| 140 | 1654 | typeClassSections(cls, next_context); | |
| 141 | 1621 | execStat("NFTyping.typeClassSections"); | |
| 142 | end typeClass; | ||
| 143 | |||
| 144 | function typeComponents | ||
| 145 | input InstNode cls; | ||
| 146 | input InstContext.Type context; | ||
| 147 | input Boolean preserveDerived = false; | ||
| 148 | protected | ||
| 149 | Class c = InstNode.getClass(cls), c2; | ||
| 150 | ClassTree cls_tree; | ||
| 151 | NFInstNode.ScopeRef con, de; | ||
| 152 | NFInstNode.ScopeRef rec_con; | ||
| 153 | algorithm | ||
| 154 | () := match c | ||
| 155 | case Class.INSTANCED_CLASS(restriction = Restriction.TYPE()) then (); | ||
| 156 | |||
| 157 | case Class.INSTANCED_CLASS(elements = cls_tree as ClassTree.FLAT_TREE()) | ||
| 158 | algorithm | ||
| 159 |
2/2✓ Branch 1 taken 82 times.
✓ Branch 2 taken 80951 times.
|
81033 | if InstContext.inInstanceAPI(context) then |
| 160 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
✓ Branch 3 taken 105 times.
✓ Branch 4 taken 82 times.
|
269 | for c in cls_tree.components loop |
| 161 | 105 | typeComponentTry(c, context); | |
| 162 | end for; | ||
| 163 | else | ||
| 164 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 80951 times.
✓ Branch 3 taken 513907 times.
✓ Branch 4 taken 80939 times.
|
675797 | for c in cls_tree.components loop |
| 165 | 513907 | typeComponent(c, context); | |
| 166 | end for; | ||
| 167 | end if; | ||
| 168 | |||
| 169 | () := match c.ty | ||
| 170 | case Type.COMPLEX(complexTy = ComplexType.RECORD(constructor = rec_con)) | ||
| 171 | algorithm | ||
| 172 | 28914 | typeStructor(InstNode.borrow(rec_con)); | |
| 173 | then | ||
| 174 | (); | ||
| 175 | |||
| 176 | else (); | ||
| 177 | end match; | ||
| 178 | then | ||
| 179 | (); | ||
| 180 | |||
| 181 | // For derived types with dimensions we keep them as they are, because we | ||
| 182 | // need to preserve the dimensions. | ||
| 183 | case Class.TYPED_DERIVED() | ||
| 184 | guard preserveDerived or Type.isArray(c.ty) | ||
| 185 | algorithm | ||
| 186 | 5077 | typeComponents(c.baseClass, context); | |
| 187 | then | ||
| 188 | (); | ||
| 189 | |||
| 190 | // Derived types without dimensions can be collapsed. | ||
| 191 | case Class.TYPED_DERIVED() | ||
| 192 | algorithm | ||
| 193 | 328137 | typeComponents(c.baseClass, context); | |
| 194 | |||
| 195 | // Only collapse for the normal instantiation, for the instance API we | ||
| 196 | // need the derived class chains. | ||
| 197 |
2/2✓ Branch 1 taken 328127 times.
✓ Branch 2 taken 10 times.
|
328137 | if not InstContext.inInstanceAPI(context) then |
| 198 | 328127 | c2 := InstNode.getClass(c.baseClass); | |
| 199 | 328127 | c2 := Class.setRestriction(c.restriction, c2); | |
| 200 | 328127 | InstNode.updateClass(c2, cls); | |
| 201 | end if; | ||
| 202 | then | ||
| 203 | (); | ||
| 204 | |||
| 205 | case Class.INSTANCED_BUILTIN(ty = Type.COMPLEX(complexTy = | ||
| 206 | ComplexType.EXTERNAL_OBJECT(constructor = con, destructor = de))) | ||
| 207 | algorithm | ||
| 208 | 337 | typeStructor(InstNode.borrow(con)); | |
| 209 | 337 | typeStructor(InstNode.borrow(de)); | |
| 210 | then | ||
| 211 | (); | ||
| 212 | |||
| 213 | case Class.INSTANCED_BUILTIN() then (); | ||
| 214 | |||
| 215 | else | ||
| 216 | algorithm | ||
| 217 | ✗ | Error.terminate(getInstanceName() + " got uninstantiated class " + InstNode.name(cls), sourceInfo()); | |
| 218 | ✗ | then | |
| 219 | fail(); | ||
| 220 | |||
| 221 | end match; | ||
| 222 | end typeComponents; | ||
| 223 | |||
| 224 | function typeStructor | ||
| 225 | input InstNode node; | ||
| 226 | protected | ||
| 227 | CachedData cache; | ||
| 228 | list<Function> fnl; | ||
| 229 | InstContext.Type context; | ||
| 230 | algorithm | ||
| 231 | 29588 | cache := InstNode.getFuncCache(node); | |
| 232 | |||
| 233 | () := match cache | ||
| 234 | case CachedData.FUNCTION(funcs = fnl, typed = false) | ||
| 235 | algorithm | ||
| 236 | 4406 | context := InstContext.set(NFInstContext.FUNCTION, NFInstContext.RELAXED); | |
| 237 |
4/4✓ Branch 0 taken 4578 times.
✓ Branch 1 taken 4406 times.
✓ Branch 2 taken 4578 times.
✓ Branch 3 taken 4406 times.
|
8984 | fnl := list(Function.typeFunction(fn, context) for fn in fnl); |
| 238 |
4/4✓ Branch 0 taken 4578 times.
✓ Branch 1 taken 4406 times.
✓ Branch 2 taken 4578 times.
✓ Branch 3 taken 4406 times.
|
8984 | fnl := list(OperatorOverloading.patchOperatorRecordConstructorBinding(fn) for fn in fnl); |
| 239 | 4406 | InstNode.setFuncCache(node, CachedData.FUNCTION(fnl, true, cache.specialBuiltin)); | |
| 240 | then | ||
| 241 | (); | ||
| 242 | |||
| 243 | else (); | ||
| 244 | end match; | ||
| 245 | end typeStructor; | ||
| 246 | |||
| 247 | function typeClassType | ||
| 248 | input InstNode clsNode; | ||
| 249 | input Binding componentBinding; | ||
| 250 | input InstContext.Type context; | ||
| 251 | input InstNode instanceNode; | ||
| 252 | output Type ty; | ||
| 253 | protected | ||
| 254 | Class cls, ty_cls; | ||
| 255 | InstNode ty_node; | ||
| 256 | NFInstNode.ScopeRef node; | ||
| 257 | Type cls_ty; | ||
| 258 | Function fn; | ||
| 259 | Boolean is_expandable; | ||
| 260 | algorithm | ||
| 261 | 855999 | cls := InstNode.getClass(clsNode); | |
| 262 | |||
| 263 | ty := match cls | ||
| 264 | case Class.INSTANCED_CLASS(restriction = Restriction.CONNECTOR(isExpandable = is_expandable)) | ||
| 265 | algorithm | ||
| 266 | 15210 | ty := Type.COMPLEX(InstNode.identityCell(clsNode), makeConnectorType(cls.elements, is_expandable)); | |
| 267 | 15210 | cls.ty := ty; | |
| 268 | 15210 | InstNode.updateClass(cls, clsNode); | |
| 269 | then | ||
| 270 | ty; | ||
| 271 | |||
| 272 | case Class.INSTANCED_CLASS(ty = cls_ty as Type.COMPLEX(complexTy = ComplexType.RECORD(constructor = node))) | ||
| 273 | algorithm | ||
| 274 | 28918 | ty_node := Type.complexNode(cls_ty); | |
| 275 | 28918 | ty := Type.COMPLEX(InstNode.identityCell(ty_node), makeRecordType(node)); | |
| 276 | 28918 | cls.ty := ty; | |
| 277 | 28918 | InstNode.updateClass(cls, clsNode); | |
| 278 | then | ||
| 279 | ty; | ||
| 280 | |||
| 281 | // A long class declaration of a type extending from a type has the type of the base class. | ||
| 282 | case Class.INSTANCED_CLASS(ty = Type.COMPLEX(complexTy = ComplexType.EXTENDS_TYPE(node))) | ||
| 283 | algorithm | ||
| 284 | 14104 | ty := typeClassType(InstNode.borrow(node), componentBinding, context, instanceNode); | |
| 285 | 14104 | cls.ty := ty; | |
| 286 | 14104 | InstNode.updateClass(cls, clsNode); | |
| 287 | then | ||
| 288 | ty; | ||
| 289 | |||
| 290 | // A component of function type, i.e. a functional input parameter. | ||
| 291 | case Class.INSTANCED_CLASS(restriction = Restriction.FUNCTION()) | ||
| 292 | guard InstNode.isComponent(instanceNode) | ||
| 293 | algorithm | ||
| 294 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 30 times.
|
30 | fn :: _ := Function.typeNodeCache(clsNode); |
| 295 | 30 | ty := Type.FUNCTION(fn, NFType.FunctionType.FUNCTIONAL_PARAMETER); | |
| 296 | 30 | cls.ty := ty; | |
| 297 | 30 | InstNode.updateClass(cls, clsNode); | |
| 298 | then | ||
| 299 | ty; | ||
| 300 | |||
| 301 | 39574 | case Class.INSTANCED_CLASS() then cls.ty; | |
| 302 | |||
| 303 | case Class.EXPANDED_DERIVED() | ||
| 304 | algorithm | ||
| 305 | 335880 | typeDimensions(cls.dims, clsNode, componentBinding, context, InstNode.info(clsNode)); | |
| 306 | 335880 | ty := typeClassType(cls.baseClass, componentBinding, context, instanceNode); | |
| 307 | 335880 | ty := Type.liftArrayLeftList(ty, arrayList(cls.dims)); | |
| 308 | 335880 | ty_cls := Class.TYPED_DERIVED(ty, cls.baseClass, cls.restriction); | |
| 309 | 335880 | InstNode.updateClass(ty_cls, clsNode); | |
| 310 | then | ||
| 311 | ty; | ||
| 312 | |||
| 313 | 422283 | case Class.INSTANCED_BUILTIN() then cls.ty; | |
| 314 | ✗ | case Class.TYPED_DERIVED() then cls.ty; | |
| 315 | |||
| 316 | else | ||
| 317 | algorithm | ||
| 318 | ✗ | Error.terminate(getInstanceName() + " got noninstantiated class " + | |
| 319 | InstNode.name(clsNode), sourceInfo()); | ||
| 320 | ✗ | then | |
| 321 | fail(); | ||
| 322 | |||
| 323 | end match; | ||
| 324 | end typeClassType; | ||
| 325 | |||
| 326 | function makeConnectorType | ||
| 327 | input ClassTree ctree; | ||
| 328 | input Boolean isExpandable; | ||
| 329 | output ComplexType connectorTy; | ||
| 330 | protected | ||
| 331 | list<NFInstNode.ScopeRef> pots = {}, flows = {}, streams = {}, exps = {}; | ||
| 332 | ConnectorType.Type cty; | ||
| 333 | algorithm | ||
| 334 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 15209 times.
|
15250 | if isExpandable then |
| 335 |
2/2✓ Branch 1 taken 202 times.
✓ Branch 2 taken 41 times.
|
243 | for c in ClassTree.enumerateComponents(ctree) loop |
| 336 | 202 | cty := Component.connectorType(InstNode.component(InstNode.resolveInner(c))); | |
| 337 | |||
| 338 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 184 times.
|
202 | if intBitAnd(cty, ConnectorType.EXPANDABLE) > 0 then |
| 339 | 18 | exps := InstNode.scopeRef(c) :: exps; | |
| 340 | else | ||
| 341 | 184 | pots := InstNode.scopeRef(c) :: pots; | |
| 342 | end if; | ||
| 343 | end for; | ||
| 344 | |||
| 345 | 41 | connectorTy := ComplexType.EXPANDABLE_CONNECTOR(pots, exps); | |
| 346 | else | ||
| 347 |
2/2✓ Branch 1 taken 41476 times.
✓ Branch 2 taken 15209 times.
|
56685 | for c in ClassTree.enumerateComponents(ctree) loop |
| 348 | 41476 | cty := Component.connectorType(InstNode.component(InstNode.resolveInner(c))); | |
| 349 | |||
| 350 |
2/2✓ Branch 0 taken 16577 times.
✓ Branch 1 taken 24899 times.
|
41476 | if intBitAnd(cty, ConnectorType.FLOW) > 0 then |
| 351 | 16577 | flows := InstNode.scopeRef(c) :: flows; | |
| 352 | elseif intBitAnd(cty, ConnectorType.STREAM) > 0 then | ||
| 353 | 969 | streams := InstNode.scopeRef(c) :: streams; | |
| 354 | elseif intBitAnd(cty, ConnectorType.POTENTIAL) > 0 then | ||
| 355 | 23930 | pots := InstNode.scopeRef(c) :: pots; | |
| 356 | else | ||
| 357 | ✗ | Error.addInternalError("Invalid connector type on component " + InstNode.name(c), InstNode.info(c)); | |
| 358 | ✗ | fail(); | |
| 359 | end if; | ||
| 360 | end for; | ||
| 361 | |||
| 362 | 15209 | connectorTy := ComplexType.CONNECTOR(pots, flows, streams); | |
| 363 | |||
| 364 |
2/2✓ Branch 0 taken 14818 times.
✓ Branch 1 taken 391 times.
|
15209 | if not listEmpty(streams) then |
| 365 | 391 | System.setHasStreamConnectors(true); | |
| 366 | end if; | ||
| 367 | end if; | ||
| 368 | end makeConnectorType; | ||
| 369 | |||
| 370 | function checkConnectorTypeBalance | ||
| 371 | input InstNode component; | ||
| 372 | protected | ||
| 373 | Integer pots, flows, streams; | ||
| 374 | Boolean known_size; | ||
| 375 | Component comp; | ||
| 376 | InstNode parent; | ||
| 377 | algorithm | ||
| 378 | 475129 | comp := InstNode.component(component); | |
| 379 | |||
| 380 |
2/2✓ Branch 2 taken 454912 times.
✓ Branch 3 taken 20217 times.
|
475129 | if not ConnectorType.isConnector(Component.connectorType(comp)) then |
| 381 | 454912 | return; | |
| 382 | end if; | ||
| 383 | |||
| 384 | 20217 | parent := InstNode.instanceParent(component); | |
| 385 |
4/4✓ Branch 1 taken 20004 times.
✓ Branch 2 taken 213 times.
✓ Branch 5 taken 1221 times.
✓ Branch 6 taken 18783 times.
|
20217 | if InstNode.isComponent(parent) and Component.isConnector(InstNode.component(parent)) then |
| 386 | 1221 | return; | |
| 387 | end if; | ||
| 388 | |||
| 389 | 18996 | (pots, flows, streams, known_size) := Component.countConnectorVars(comp); | |
| 390 | |||
| 391 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 18995 times.
|
18996 | if not known_size then |
| 392 | 1 | return; | |
| 393 | end if; | ||
| 394 | |||
| 395 | // Modelica 3.2 section 9.3.1: | ||
| 396 | // For each non-partial connector class the number of flow variables shall | ||
| 397 | // be equal to the number of variables that are neither parameter, constant, | ||
| 398 | // input, output, stream nor flow. | ||
| 399 |
3/4✓ Branch 0 taken 496 times.
✓ Branch 1 taken 18499 times.
✓ Branch 3 taken 496 times.
✗ Branch 4 not taken.
|
18995 | if pots <> flows and not Flags.isConfigFlagSet(Flags.ALLOW_NON_STANDARD_MODELICA, "unbalancedModel") then |
| 400 | 1488 | Error.addStrictMessage(Error.UNBALANCED_CONNECTOR, | |
| 401 | {InstNode.name(component), String(pots), String(flows)}, InstNode.info(component)); | ||
| 402 | end if; | ||
| 403 | |||
| 404 | // Modelica 3.2 section 15.1: | ||
| 405 | // A stream connector must have exactly one scalar variable with the flow prefix. | ||
| 406 |
3/4✓ Branch 0 taken 390 times.
✓ Branch 1 taken 18605 times.
✓ Branch 2 taken 390 times.
✗ Branch 3 not taken.
|
18995 | if streams > 0 and flows <> 1 then |
| 407 | ✗ | Error.addSourceMessage(Error.MISMATCHED_FLOW_IN_STREAM_CONNECTOR, | |
| 408 | {InstNode.name(component), String(flows)}, InstNode.info(component)); | ||
| 409 | ✗ | fail(); | |
| 410 | end if; | ||
| 411 | end checkConnectorTypeBalance; | ||
| 412 | |||
| 413 | function makeRecordType | ||
| 414 | input NFInstNode.ScopeRef constructor; | ||
| 415 | output ComplexType recordTy; | ||
| 416 | protected | ||
| 417 | CachedData cache; | ||
| 418 | Function fn; | ||
| 419 | array<Record.Field> fields; | ||
| 420 | UnorderedMap<String, Integer> indexMap; | ||
| 421 | algorithm | ||
| 422 | 28918 | cache := InstNode.getFuncCache(InstNode.borrow(constructor)); | |
| 423 | |||
| 424 | recordTy := matchcontinue cache | ||
| 425 | case CachedData.FUNCTION() | ||
| 426 | algorithm | ||
| 427 | 28918 | fn := List.find(cache.funcs, Function.isDefaultRecordConstructor); | |
| 428 | 28918 | (fields, indexMap) := Record.collectRecordFields(InstNode.fromHandle(fn.node)); | |
| 429 | 28918 | then | |
| 430 | ComplexType.RECORD(constructor, fields, indexMap); | ||
| 431 | |||
| 432 | else | ||
| 433 | algorithm | ||
| 434 | ✗ | Error.terminate(getInstanceName() + | |
| 435 | " got record type without constructor", sourceInfo()); | ||
| 436 | ✗ | then | |
| 437 | fail(); | ||
| 438 | end matchcontinue; | ||
| 439 | end makeRecordType; | ||
| 440 | |||
| 441 | function typeComponent | ||
| 442 | input InstNode component; | ||
| 443 | input InstContext.Type context; | ||
| 444 | input Boolean typeChildren = true; | ||
| 445 | output Type ty; | ||
| 446 | protected | ||
| 447 | InstNode node; | ||
| 448 | Component c; | ||
| 449 | Boolean is_deleted; | ||
| 450 | array<Dimension> dims; | ||
| 451 | Binding binding; | ||
| 452 | algorithm | ||
| 453 |
4/4✓ Branch 1 taken 1813159 times.
✓ Branch 2 taken 5 times.
✓ Branch 4 taken 1532 times.
✓ Branch 5 taken 1811627 times.
|
1813164 | if InstNode.isEmpty(component) or InstNode.isOnlyOuter(component) then |
| 454 | 1537 | return; | |
| 455 | end if; | ||
| 456 | |||
| 457 | 1811627 | node := InstNode.resolveOuter(component); | |
| 458 | 1811627 | c := InstNode.component(node); | |
| 459 | |||
| 460 | ty := match c | ||
| 461 | // An untyped component, type it. | ||
| 462 | case Component.COMPONENT(ty = Type.UNTYPED(dimensions = dims)) | ||
| 463 | algorithm | ||
| 464 | // Type the component's dimensions. | ||
| 465 | 478112 | typeDimensions(dims, node, c.binding, context, c.info); | |
| 466 | |||
| 467 | // Construct the type of the component and update the node with it. | ||
| 468 |
2/2✓ Branch 1 taken 478100 times.
✓ Branch 2 taken 3 times.
|
478103 | if InstNode.isEmpty(c.classInst) then |
| 469 | ty := Type.UNKNOWN(); | ||
| 470 | else | ||
| 471 | 478100 | ty := typeClassType(c.classInst, c.binding, context, component); | |
| 472 | end if; | ||
| 473 | 478103 | ty := Type.liftArrayLeftList(ty, arrayList(dims)); | |
| 474 | |||
| 475 |
2/2✓ Branch 1 taken 5560 times.
✓ Branch 2 taken 472543 times.
|
478103 | if Binding.isBound(c.condition) then |
| 476 | 5560 | c.condition := typeComponentCondition(c.condition, context, evaluate = true); | |
| 477 | 5559 | is_deleted := Expression.isFalse(Binding.getExp(c.condition)); | |
| 478 | else | ||
| 479 | is_deleted := false; | ||
| 480 | end if; | ||
| 481 | |||
| 482 |
2/2✓ Branch 0 taken 478101 times.
✓ Branch 1 taken 1 time.
|
478102 | if typeChildren then |
| 483 | 478101 | c.ty := ty; | |
| 484 | 478101 | c.state := ComponentState.Typed; | |
| 485 | 478101 | InstNode.updateComponent(c, node); | |
| 486 | |||
| 487 |
4/4✓ Branch 0 taken 475134 times.
✓ Branch 1 taken 2967 times.
✓ Branch 3 taken 475131 times.
✓ Branch 4 taken 3 times.
|
478101 | if not is_deleted and not InstNode.isEmpty(c.classInst) then |
| 488 | // Check that flow/stream variables are Real. | ||
| 489 | 475131 | checkComponentStreamAttribute(c.attributes.connectorType, ty, component); | |
| 490 | |||
| 491 | // Type the component's children. | ||
| 492 | 475131 | typeComponents(c.classInst, context); | |
| 493 | |||
| 494 | 475129 | checkConnectorTypeBalance(node); | |
| 495 | end if; | ||
| 496 | end if; | ||
| 497 | then | ||
| 498 | ty; | ||
| 499 | |||
| 500 | // A component that has already been typed, skip it. | ||
| 501 | 1317623 | case Component.COMPONENT() then c.ty; | |
| 502 | 15892 | case Component.ITERATOR() then c.ty; | |
| 503 | case Component.ENUM_LITERAL(literal = Expression.ENUM_LITERAL(ty = ty)) then ty; | ||
| 504 | ✗ | case Component.INVALID_COMPONENT() then Component.getType(c); | |
| 505 | |||
| 506 | // Any other type of component shouldn't show up here. | ||
| 507 | else | ||
| 508 | algorithm | ||
| 509 | ✗ | Error.terminate(getInstanceName() + " got noninstantiated component " + InstNode.name(component), sourceInfo()); | |
| 510 | ✗ | then | |
| 511 | fail(); | ||
| 512 | |||
| 513 | end match; | ||
| 514 | end typeComponent; | ||
| 515 | |||
| 516 | function typeComponentTry | ||
| 517 | input InstNode componentNode; | ||
| 518 | input InstContext.Type context; | ||
| 519 | protected | ||
| 520 | Component comp; | ||
| 521 | algorithm | ||
| 522 | 105 | ErrorExt.setCheckpoint(getInstanceName()); | |
| 523 | try | ||
| 524 | 105 | typeComponent(componentNode, context); | |
| 525 | else | ||
| 526 | ✗ | comp := InstNode.component(componentNode); | |
| 527 | ✗ | comp := Component.INVALID_COMPONENT(comp, ErrorExt.printCheckpointMessagesStr()); | |
| 528 | ✗ | InstNode.updateComponent(comp, componentNode); | |
| 529 | end try; | ||
| 530 | 105 | ErrorExt.delCheckpoint(getInstanceName()); | |
| 531 | end typeComponentTry; | ||
| 532 | |||
| 533 | function checkComponentStreamAttribute | ||
| 534 | input ConnectorType.Type cty; | ||
| 535 | input Type ty; | ||
| 536 | input InstNode component; | ||
| 537 | protected | ||
| 538 | Type ety; | ||
| 539 | algorithm | ||
| 540 |
2/2✓ Branch 1 taken 457724 times.
✓ Branch 2 taken 17407 times.
|
475131 | if ConnectorType.isFlowOrStream(cty) then |
| 541 | 17407 | ety := Type.arrayElementType(ty); | |
| 542 | |||
| 543 |
3/4✓ Branch 1 taken 16913 times.
✓ Branch 2 taken 494 times.
✓ Branch 4 taken 494 times.
✗ Branch 5 not taken.
|
17407 | if not (Type.isReal(ety) or Type.isComplex(ety)) then |
| 544 | ✗ | Error.addSourceMessageAndFail(Error.NON_REAL_FLOW_OR_STREAM, | |
| 545 | {ConnectorType.toString(cty), InstNode.name(component)}, InstNode.info(component)); | ||
| 546 | end if; | ||
| 547 | end if; | ||
| 548 | end checkComponentStreamAttribute; | ||
| 549 | |||
| 550 | function typeIterator | ||
| 551 | input InstNode iterator; | ||
| 552 | input Expression range; | ||
| 553 | input InstContext.Type context; | ||
| 554 | input Boolean structural "If the iteration range must be a parameter expression or not."; | ||
| 555 | output Expression outRange; | ||
| 556 | output Type ty; | ||
| 557 | output Variability var; | ||
| 558 | output Purity purity; | ||
| 559 | protected | ||
| 560 | Component c = InstNode.component(iterator); | ||
| 561 | Expression exp; | ||
| 562 | SourceInfo info; | ||
| 563 | algorithm | ||
| 564 | (outRange, ty, var) := match c | ||
| 565 | case Component.ITERATOR(info = info) | ||
| 566 | algorithm | ||
| 567 | 3836 | (exp, ty, var, purity) := typeExp(range, InstContext.set(context, NFInstContext.ITERATION_RANGE), info); | |
| 568 | |||
| 569 | // If the iteration range is structural, it must be a parameter expression (unless we don't scalarize and it is a non structural parameter). | ||
| 570 |
7/8✓ Branch 0 taken 2835 times.
✓ Branch 1 taken 1001 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 2820 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 1 time.
✗ Branch 7 not taken.
✓ Branch 8 taken 14 times.
|
3836 | if structural and var > Variability.PARAMETER and (not var == Variability.NON_STRUCTURAL_PARAMETER or Flags.isSet(Flags.NF_SCALARIZE)) then |
| 571 | 2 | Error.addSourceMessageAndFail(Error.NON_PARAMETER_ITERATOR_RANGE, | |
| 572 | {Expression.toString(exp)}, info); | ||
| 573 | end if; | ||
| 574 | |||
| 575 | // The iteration range must be a vector expression. | ||
| 576 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3835 times.
|
3835 | if not Type.isVector(ty) then |
| 577 | ✗ | Error.addSourceMessageAndFail(Error.FOR_EXPRESSION_TYPE_ERROR, | |
| 578 | {Expression.toString(exp), Type.toString(ty)}, info); | ||
| 579 | end if; | ||
| 580 | |||
| 581 | // The type of the iterator is the element type of the range expression. | ||
| 582 | 3835 | c := Component.ITERATOR(Type.arrayElementType(ty), var, info); | |
| 583 | 3835 | InstNode.updateComponent(c, iterator); | |
| 584 |
2/2✓ Branch 0 taken 1354 times.
✓ Branch 1 taken 2481 times.
|
3835 | then |
| 585 | (exp, ty, var); | ||
| 586 | |||
| 587 | else | ||
| 588 | algorithm | ||
| 589 | ✗ | Error.terminate(getInstanceName() + " got non-iterator " + InstNode.name(iterator), sourceInfo()); | |
| 590 | ✗ | then | |
| 591 | fail(); | ||
| 592 | |||
| 593 | end match; | ||
| 594 | end typeIterator; | ||
| 595 | |||
| 596 | function typeDimensions | ||
| 597 | input output array<Dimension> dimensions; | ||
| 598 | input InstNode component; | ||
| 599 | input Binding binding; | ||
| 600 | input InstContext.Type context; | ||
| 601 | input SourceInfo info; | ||
| 602 | algorithm | ||
| 603 |
2/2✓ Branch 0 taken 735791 times.
✓ Branch 1 taken 78201 times.
|
906867 | for i in 1:arrayLength(dimensions) loop |
| 604 | 92884 | typeDimension(dimensions, i, component, binding, context, info); | |
| 605 | end for; | ||
| 606 | end typeDimensions; | ||
| 607 | |||
| 608 | function typeDimension | ||
| 609 | input array<Dimension> dimensions; | ||
| 610 | input Integer index; | ||
| 611 | input InstNode component; | ||
| 612 | input Binding binding; | ||
| 613 | input InstContext.Type context; | ||
| 614 | input SourceInfo info; | ||
| 615 | output Dimension dimension = dimensions[index]; | ||
| 616 | algorithm | ||
| 617 | dimension := match dimension | ||
| 618 | local | ||
| 619 | Expression exp; | ||
| 620 | Variability var; | ||
| 621 | Dimension dim; | ||
| 622 | Binding b; | ||
| 623 | Type ty; | ||
| 624 | TypingError ty_err; | ||
| 625 | Integer parent_dims; | ||
| 626 | Ceval.EvalTarget target; | ||
| 627 | |||
| 628 | // A dimension that we're already trying to type. | ||
| 629 | case Dimension.UNTYPED(isProcessing = true) | ||
| 630 | algorithm | ||
| 631 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 5 times.
|
13 | if InstContext.inFunction(context) then |
| 632 | // If we are in a functions we allow e.g. size expression of unknown dimensions. | ||
| 633 | dim := Dimension.UNKNOWN(); | ||
| 634 | 8 | arrayUpdate(dimensions, index, dim); | |
| 635 | else | ||
| 636 | // Otherwise leave the dimension as it is, which is sometimes fine if | ||
| 637 | // the dimension isn't used or generates an error in typeCrefDim. | ||
| 638 | dim := dimension; | ||
| 639 | end if; | ||
| 640 | then | ||
| 641 | dim; | ||
| 642 | |||
| 643 | // If the dimension is not typed, type it. | ||
| 644 | case Dimension.UNTYPED() | ||
| 645 | algorithm | ||
| 646 | 86821 | arrayUpdate(dimensions, index, Dimension.UNTYPED(dimension.dimension, true)); | |
| 647 | |||
| 648 | 86821 | (exp, ty, var) := typeExp(dimension.dimension, InstContext.set(context, NFInstContext.DIMENSION), info); | |
| 649 | 86819 | TypeCheck.checkDimensionType(exp, ty, info); | |
| 650 | |||
| 651 |
2/2✓ Branch 1 taken 56525 times.
✓ Branch 2 taken 30293 times.
|
86818 | if not InstContext.inFunction(context) then |
| 652 | // Dimensions must be parameter expressions in a non-function class. | ||
| 653 |
2/2✓ Branch 0 taken 56500 times.
✓ Branch 1 taken 25 times.
|
56525 | if var <= Variability.PARAMETER then |
| 654 |
2/2✓ Branch 1 taken 28961 times.
✓ Branch 2 taken 27539 times.
|
56500 | if InstContext.inRelaxed(context) then |
| 655 | 28961 | exp := Ceval.tryEvalExp(exp); | |
| 656 | else | ||
| 657 | 55078 | target := Ceval.EvalTarget.new(info, context, | |
| 658 | SOME(Ceval.EvalTargetData.DIMENSION_DATA(component, index, exp))); | ||
| 659 | 27539 | exp := Ceval.tryEvalExpResizable(exp, target); | |
| 660 | end if; | ||
| 661 | elseif not var == Variability.NON_STRUCTURAL_PARAMETER then | ||
| 662 | ✗ | Error.addSourceMessage(Error.DIMENSION_NOT_KNOWN, {Expression.toString(exp)}, info); | |
| 663 | ✗ | fail(); | |
| 664 | end if; | ||
| 665 | else | ||
| 666 | // For functions, only evaluate constant and structural parameter expressions. | ||
| 667 |
3/4✓ Branch 0 taken 29170 times.
✓ Branch 1 taken 1123 times.
✓ Branch 3 taken 29170 times.
✗ Branch 4 not taken.
|
30293 | if var <= Variability.STRUCTURAL_PARAMETER and |
| 668 | not Expression.contains(exp, Expression.isFunctionInputCref) then | ||
| 669 | 29170 | exp := Ceval.tryEvalExp(exp); | |
| 670 | end if; | ||
| 671 | end if; | ||
| 672 | |||
| 673 | 86815 | exp := subscriptDimExp(exp, component); | |
| 674 | 86815 | dim := Dimension.fromExp(exp, var); | |
| 675 | 86815 | arrayUpdate(dimensions, index, dim); | |
| 676 | then | ||
| 677 | dim; | ||
| 678 | |||
| 679 | // Dimensions in a function can be flexible (non-input component with no | ||
| 680 | // binding) or determined by the call arguments (input component), keep the | ||
| 681 | // dimension unknown for these cases. | ||
| 682 | case Dimension.UNKNOWN() guard InstContext.inFunction(context) and | ||
| 683 | ((Binding.isUnbound(binding) and InstNode.isOutput(component)) or | ||
| 684 | not InstNode.isOutput(component)) | ||
| 685 | then dimension; | ||
| 686 | |||
| 687 | // A dimension of a function output parameter that depends on e.g. an input | ||
| 688 | // can't be determined here, leave it unknown and let the call typing handle it. | ||
| 689 | // TODO: This assumes any binding that contains crefs can't be evaluated to | ||
| 690 | // avoid e.g. using the default value of an input parameter, but some | ||
| 691 | // cases such as an output dimension depending on another output | ||
| 692 | // dimension could be fine. | ||
| 693 | case Dimension.UNKNOWN() guard InstContext.inFunction(context) and | ||
| 694 | Binding.hasExp(binding) and | ||
| 695 | Expression.contains(Binding.getExp(binding), Expression.isCref) | ||
| 696 | then dimension; | ||
| 697 | |||
| 698 | // If the dimension is unknown in a class, try to infer it from the components binding. | ||
| 699 | case Dimension.UNKNOWN() | ||
| 700 | algorithm | ||
| 701 | b := binding; | ||
| 702 | 1042 | parent_dims := 0; | |
| 703 | // Update the dimension as processing, using a : cref to get the correct | ||
| 704 | // error message if there are cycles. | ||
| 705 | 1042 | arrayUpdate(dimensions, index, Dimension.UNTYPED(WHOLEDIM_CREF, true)); | |
| 706 | |||
| 707 |
2/2✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1022 times.
|
1042 | if Binding.isUnbound(binding) then |
| 708 | // If the component has no binding, try to use its parent's binding | ||
| 709 | // (i.e. for record fields where the record instance has a binding). | ||
| 710 | 20 | (b, parent_dims) := getRecordElementBinding(component, context); | |
| 711 | |||
| 712 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 16 times.
|
20 | if Binding.isUnbound(b) then |
| 713 | // If the component still doesn't have a binding, try to use the start attribute instead. | ||
| 714 | // TODO: Any attribute should actually be fine to use here. | ||
| 715 | 4 | parent_dims := 0; | |
| 716 | 4 | b := Class.lookupAttributeBinding("start", InstNode.getClass(component)); | |
| 717 | 4 | b := Binding.mapExp(b, function Expression.filterSplitIndices(node = component)); | |
| 718 | end if; | ||
| 719 | end if; | ||
| 720 | |||
| 721 | (dim, ty_err) := match b | ||
| 722 | // Print an error if there's no binding. | ||
| 723 | case Binding.UNBOUND() | ||
| 724 | guard not InstContext.inRelaxed(context) | ||
| 725 | algorithm | ||
| 726 | 2 | Error.addSourceMessage(Error.FAILURE_TO_DEDUCE_DIMS_NO_MOD, | |
| 727 | {String(index), InstNode.name(component)}, info); | ||
| 728 | 1 | then | |
| 729 | fail(); | ||
| 730 | |||
| 731 | case Binding.UNTYPED_BINDING() | ||
| 732 | 1023 | then deduceDimensionFromExp(b.bindingExp, NONE(), index, parent_dims, component, context, info); | |
| 733 | |||
| 734 | case Binding.TYPED_BINDING() | ||
| 735 | 16 | then deduceDimensionFromExp(b.bindingExp, SOME(b.bindingType), index, parent_dims, component, context, info); | |
| 736 | 2 | else (dimension, TypingError.NO_ERROR()); | |
| 737 | end match; | ||
| 738 | |||
| 739 | () := match ty_err | ||
| 740 | case TypingError.OUT_OF_BOUNDS() | ||
| 741 | guard not InstContext.inRelaxed(context) | ||
| 742 | algorithm | ||
| 743 | 3 | Error.addSourceMessage(Error.DIMENSION_DEDUCTION_FROM_BINDING_FAILURE, | |
| 744 | {String(index), InstNode.name(component), Binding.toString(b)}, info); | ||
| 745 | 1 | then | |
| 746 | fail(); | ||
| 747 | |||
| 748 | else (); | ||
| 749 | end match; | ||
| 750 | |||
| 751 | // Make sure the dimension is constant evaluated, and also mark it as structural. | ||
| 752 | dim := match dim | ||
| 753 | case Dimension.EXP(exp = exp) | ||
| 754 | algorithm | ||
| 755 | 1 | Structural.markExp(exp); | |
| 756 | |||
| 757 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
|
1 | if InstContext.inRelaxed(context) then |
| 758 | ✗ | exp := Ceval.tryEvalExp(exp); | |
| 759 | else | ||
| 760 | 2 | target := Ceval.EvalTarget.new(info, context, | |
| 761 | SOME(Ceval.EvalTargetData.DIMENSION_DATA(component, index, exp))); | ||
| 762 | 1 | exp := Ceval.evalExp(exp, target); | |
| 763 | end if; | ||
| 764 | |||
| 765 | 1 | exp := subscriptDimExp(exp, component); | |
| 766 | 1 | then | |
| 767 | Dimension.fromExp(exp, dim.var); | ||
| 768 | |||
| 769 | case Dimension.UNKNOWN() | ||
| 770 | guard not InstContext.inRelaxed(context) | ||
| 771 | algorithm | ||
| 772 | ✗ | Error.addInternalError(getInstanceName() + " returned unknown dimension in a non-function context", info); | |
| 773 | ✗ | then | |
| 774 | fail(); | ||
| 775 | |||
| 776 | else dim; | ||
| 777 | end match; | ||
| 778 | |||
| 779 | 1037 | arrayUpdate(dimensions, index, dim); | |
| 780 | then | ||
| 781 | dim; | ||
| 782 | |||
| 783 | // Other kinds of dimensions are already typed. | ||
| 784 | else dimension; | ||
| 785 | end match; | ||
| 786 | end typeDimension; | ||
| 787 | |||
| 788 | function deduceDimensionFromExp | ||
| 789 | input Expression exp; | ||
| 790 | input Option<Type> ty; | ||
| 791 | input Integer index; | ||
| 792 | input Integer parentDims; | ||
| 793 | input InstNode component; | ||
| 794 | input InstContext.Type context; | ||
| 795 | input SourceInfo info; | ||
| 796 | output Dimension dim; | ||
| 797 | output TypingError error; | ||
| 798 | protected | ||
| 799 | Option<Expression> oe; | ||
| 800 | Expression e; | ||
| 801 | Integer dim_index; | ||
| 802 | algorithm | ||
| 803 | // If the binding expression comes from a parent of the component rather than | ||
| 804 | // the component iself the dimension index needs to be offset by the number of | ||
| 805 | // dimensions of the parent(s). | ||
| 806 | 1039 | dim_index := index + parentDims; | |
| 807 | |||
| 808 |
4/6✗ Branch 0 not taken.
✓ Branch 1 taken 1039 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 1023 times.
✓ Branch 6 taken 16 times.
✗ Branch 7 not taken.
|
1039 | if isSome(ty) and not Type.isConditionalArray(Util.getOption(ty)) then |
| 809 | // If the type is known, take the dimension directly from it. | ||
| 810 | 16 | (dim, error) := nthDimensionBoundsChecked(Util.getOption(ty), dim_index); | |
| 811 | |||
| 812 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 9 times.
|
16 | if Dimension.isUnknown(dim) then |
| 813 | // Fall back to typing the expression if the dimension we got from the | ||
| 814 | // type is unknown, which can happen when the type of the binding isn't | ||
| 815 | // fully typed yet due to cyclic dependencies. | ||
| 816 | 7 | (dim, oe, error) := typeExpDim(exp, dim_index, | |
| 817 | InstContext.set(context, NFInstContext.DIMENSION), info); | ||
| 818 | else | ||
| 819 | 9 | oe := NONE(); | |
| 820 | end if; | ||
| 821 | else | ||
| 822 | // If the type is unknown, try to type the expression only as much as is | ||
| 823 | // needed to get the dimension we're looking for. | ||
| 824 | 1023 | (dim, oe, error) := typeExpDim(exp, dim_index, | |
| 825 | InstContext.set(context, NFInstContext.DIMENSION), info); | ||
| 826 | end if; | ||
| 827 | |||
| 828 | // If the deduced dimension is unknown, evaluate the binding and try again. | ||
| 829 |
4/4✓ Branch 1 taken 1026 times.
✓ Branch 2 taken 10 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 9 times.
|
1036 | if Dimension.isUnknown(dim) and not TypingError.isError(error) then |
| 830 | // Use the typed expression from typeExpDim if it was returned. | ||
| 831 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
|
9 | e := if isSome(oe) then Util.getOption(oe) else exp; |
| 832 | |||
| 833 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 3 times.
|
9 | if InstContext.inRelaxed(context) then |
| 834 | 6 | e := Ceval.tryEvalExp(e); | |
| 835 | else | ||
| 836 | 6 | e := Ceval.evalExp(e, Ceval.EvalTarget.new(info, context, | |
| 837 | SOME(Ceval.EvalTargetData.DIMENSION_DATA(component, index, e)))); | ||
| 838 | end if; | ||
| 839 | |||
| 840 | 9 | (dim, error) := nthDimensionBoundsChecked(Expression.typeOf(e), dim_index); | |
| 841 | end if; | ||
| 842 | end deduceDimensionFromExp; | ||
| 843 | |||
| 844 | function subscriptDimExp | ||
| 845 | "Tries to fix dimension expressions that are lacking subscripts after having | ||
| 846 | been evaluated." | ||
| 847 | input output Expression dimExp; | ||
| 848 | input InstNode component; | ||
| 849 | protected | ||
| 850 | Integer exp_dims, parent_dims; | ||
| 851 | InstNode parent; | ||
| 852 | list<Subscript> subs; | ||
| 853 | algorithm | ||
| 854 | 86816 | exp_dims := Expression.dimensionCount(dimExp, true); | |
| 855 | |||
| 856 |
1/2✓ Branch 0 taken 86816 times.
✗ Branch 1 not taken.
|
86816 | if exp_dims == 0 then |
| 857 | // If the expression is a scalar like it should we don't need to do anything. | ||
| 858 | 86816 | return; | |
| 859 | end if; | ||
| 860 | |||
| 861 | // If the expression has too many dimensions, add split subscripts based on | ||
| 862 | // the component's parent's dimensions until we get a scalar expression. | ||
| 863 | subs := {}; | ||
| 864 | ✗ | parent := InstNode.instanceParent(component); | |
| 865 | |||
| 866 | ✗ | while exp_dims > 0 and not InstNode.isEmpty(parent) loop | |
| 867 | ✗ | parent_dims := InstNode.dimensionCount(parent); | |
| 868 | |||
| 869 | ✗ | for i in parent_dims:-1:1 loop | |
| 870 | ✗ | subs := Subscript.makeSplitIndex(parent, i) :: subs; | |
| 871 | ✗ | exp_dims := exp_dims - 1; | |
| 872 | |||
| 873 | ✗ | if exp_dims == 0 then | |
| 874 | break; | ||
| 875 | end if; | ||
| 876 | end for; | ||
| 877 | |||
| 878 | ✗ | parent := InstNode.instanceParent(parent); | |
| 879 | end while; | ||
| 880 | |||
| 881 | ✗ | dimExp := Expression.applySubscripts(subs, dimExp); | |
| 882 | end subscriptDimExp; | ||
| 883 | |||
| 884 | function simplifyDimExp | ||
| 885 | input output Expression dimExp; | ||
| 886 | protected | ||
| 887 | Expression exp; | ||
| 888 | algorithm | ||
| 889 | dimExp := match dimExp | ||
| 890 | case Expression.ARRAY() | ||
| 891 | guard Expression.arrayAllEqual(dimExp) | ||
| 892 | ✗ | then Expression.arrayFirstScalar(dimExp); | |
| 893 | |||
| 894 | case Expression.SUBSCRIPTED_EXP(split = true) | ||
| 895 | guard Expression.isArray(dimExp.exp) and Expression.arrayAllEqual(dimExp.exp) | ||
| 896 | 1 | then Expression.arrayFirstScalar(dimExp.exp); | |
| 897 | |||
| 898 | else dimExp; | ||
| 899 | end match; | ||
| 900 | end simplifyDimExp; | ||
| 901 | |||
| 902 | function makeDimension | ||
| 903 | input Expression dimExp; | ||
| 904 | input Expression unevaledExp; | ||
| 905 | input Variability variability; | ||
| 906 | output Dimension outDimension; | ||
| 907 | protected | ||
| 908 | Expression exp = dimExp; | ||
| 909 | algorithm | ||
| 910 | ✗ | if Expression.isArray(exp) then | |
| 911 | ✗ | if Expression.arrayAllEqual(exp) then | |
| 912 | ✗ | exp := Expression.arrayFirstScalar(exp); | |
| 913 | else | ||
| 914 | |||
| 915 | end if; | ||
| 916 | end if; | ||
| 917 | |||
| 918 | ✗ | outDimension := Dimension.fromExp(exp, variability); | |
| 919 | end makeDimension; | ||
| 920 | |||
| 921 | function getRecordElementBinding | ||
| 922 | "Tries to fetch the binding for a given record field by using the binding of | ||
| 923 | the record instance." | ||
| 924 | input InstNode component; | ||
| 925 | input InstContext.Type context; | ||
| 926 | output Binding binding; | ||
| 927 | output Integer parentDims = 0; | ||
| 928 | protected | ||
| 929 | InstNode parent; | ||
| 930 | Component comp; | ||
| 931 | Binding parent_binding; | ||
| 932 | algorithm | ||
| 933 | 31 | parent := InstNode.instanceParent(component); | |
| 934 | |||
| 935 |
2/2✓ Branch 1 taken 27 times.
✓ Branch 2 taken 4 times.
|
31 | if InstNode.isComponent(parent) then |
| 936 | // Get the binding of the component's parent. | ||
| 937 | 27 | comp := InstNode.component(parent); | |
| 938 | 27 | parent_binding := Component.getBinding(comp); | |
| 939 | |||
| 940 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 16 times.
|
27 | if Binding.isUnbound(parent_binding) then |
| 941 | // If the parent has no binding, try the parent's parent. | ||
| 942 | 11 | (binding, parentDims) := getRecordElementBinding(parent, context); | |
| 943 | else | ||
| 944 | // Otherwise type the binding, so we can safely look up the field name. | ||
| 945 | 16 | binding := typeBinding(parent_binding, InstContext.set(context, NFInstContext.DIMENSION)); | |
| 946 | |||
| 947 | // If the binding wasn't typed before, update the parent component with it | ||
| 948 | // so we don't have to type it again. | ||
| 949 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
|
16 | if not referenceEq(parent_binding, binding) then |
| 950 | 10 | InstNode.componentApply(parent, Component.setBinding, binding); | |
| 951 | end if; | ||
| 952 | end if; | ||
| 953 | |||
| 954 | 27 | parentDims := parentDims + Component.dimensionCount(comp); | |
| 955 | |||
| 956 | // If we found a binding, get the binding for the field from it. | ||
| 957 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1 time.
|
27 | if Binding.isBound(binding) then |
| 958 | 26 | binding := Binding.recordFieldBinding(component, binding); | |
| 959 | end if; | ||
| 960 | else | ||
| 961 | binding := NFBinding.EMPTY_BINDING; | ||
| 962 | end if; | ||
| 963 | end getRecordElementBinding; | ||
| 964 | |||
| 965 | function typeBindings | ||
| 966 | input InstNode cls; | ||
| 967 | input InstContext.Type context; | ||
| 968 | protected | ||
| 969 | Class c; | ||
| 970 | ClassTree cls_tree; | ||
| 971 | algorithm | ||
| 972 | 534237 | c := InstNode.getClass(cls); | |
| 973 | |||
| 974 | () := match c | ||
| 975 | case Class.INSTANCED_CLASS(elements = cls_tree as ClassTree.FLAT_TREE()) | ||
| 976 | algorithm | ||
| 977 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 71259 times.
✓ Branch 3 taken 513447 times.
✓ Branch 4 taken 71210 times.
|
655916 | for c in cls_tree.components loop |
| 978 | 513447 | typeComponentBinding(c, context); | |
| 979 | end for; | ||
| 980 | then | ||
| 981 | (); | ||
| 982 | |||
| 983 | case Class.INSTANCED_BUILTIN(elements = cls_tree as ClassTree.FLAT_TREE()) | ||
| 984 | algorithm | ||
| 985 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 455565 times.
✓ Branch 3 taken 4743108 times.
✓ Branch 4 taken 455559 times.
|
5654232 | for c in cls_tree.components loop |
| 986 | 4743108 | typeComponentBinding(c, context); | |
| 987 | end for; | ||
| 988 | then | ||
| 989 | (); | ||
| 990 | |||
| 991 | case Class.INSTANCED_BUILTIN() then (); | ||
| 992 | |||
| 993 | case Class.TYPED_DERIVED() | ||
| 994 | algorithm | ||
| 995 | 5396 | typeBindings(c.baseClass, context); | |
| 996 | then | ||
| 997 | (); | ||
| 998 | |||
| 999 | else | ||
| 1000 | algorithm | ||
| 1001 | ✗ | Error.terminate(getInstanceName() + " got uninstantiated class " + InstNode.name(cls), sourceInfo()); | |
| 1002 | ✗ | then | |
| 1003 | fail(); | ||
| 1004 | |||
| 1005 | end match; | ||
| 1006 | end typeBindings; | ||
| 1007 | |||
| 1008 | function typeComponentBinding | ||
| 1009 | input InstNode component; | ||
| 1010 | input InstContext.Type context; | ||
| 1011 | input Boolean typeChildren = true; | ||
| 1012 | protected | ||
| 1013 | InstNode node; | ||
| 1014 | Component c; | ||
| 1015 | Binding binding; | ||
| 1016 | String name; | ||
| 1017 | Variability comp_var; | ||
| 1018 | Attributes attrs; | ||
| 1019 | Type ty; | ||
| 1020 | algorithm | ||
| 1021 |
4/4✓ Branch 1 taken 5571297 times.
✓ Branch 2 taken 5 times.
✓ Branch 4 taken 1532 times.
✓ Branch 5 taken 5569765 times.
|
5571302 | if InstNode.isEmpty(component) or InstNode.isOnlyOuter(component) then |
| 1022 | 1537 | return; | |
| 1023 | end if; | ||
| 1024 | |||
| 1025 | 5569765 | node := InstNode.resolveOuter(component); | |
| 1026 | 5569765 | c := InstNode.component(node); | |
| 1027 | |||
| 1028 | () := match c | ||
| 1029 | case Component.COMPONENT() | ||
| 1030 | guard Component.isDeleted(c) or Component.isInvalid(c) | ||
| 1031 | then (); | ||
| 1032 | |||
| 1033 | case Component.COMPONENT(binding = Binding.UNTYPED_BINDING(), attributes = attrs) | ||
| 1034 | guard c.state == ComponentState.Typed | ||
| 1035 | algorithm | ||
| 1036 | 142061 | name := InstNode.name(component); | |
| 1037 | 142061 | binding := c.binding; | |
| 1038 | |||
| 1039 | 142061 | ErrorExt.setCheckpoint(getInstanceName()); | |
| 1040 | try | ||
| 1041 | 142061 | binding := typeBinding(binding, InstContext.set(context, NFInstContext.BINDING)); | |
| 1042 | |||
| 1043 |
3/8✗ Branch 1 not taken.
✓ Branch 2 taken 142020 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 9 taken 142018 times.
✓ Branch 10 taken 2 times.
|
142020 | if not (InstContext.inAnnotation(context) and stringEq(name, "graphics") or |
| 1044 | InstNode.isEmpty(c.classInst)) then | ||
| 1045 | 142018 | binding := TypeCheck.matchBinding(binding, c.ty, name, node, context); | |
| 1046 | end if; | ||
| 1047 | |||
| 1048 | 142017 | comp_var := checkComponentBindingVariability(node, c, binding, context); | |
| 1049 | |||
| 1050 |
2/2✓ Branch 0 taken 4839 times.
✓ Branch 1 taken 137175 times.
|
142014 | if comp_var <> attrs.variability then |
| 1051 | 4839 | attrs.variability := comp_var; | |
| 1052 | 4839 | c.attributes := attrs; | |
| 1053 | end if; | ||
| 1054 | else | ||
| 1055 |
3/4✓ Branch 1 taken 46 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 46 times.
|
47 | if Binding.isBound(c.condition) or InstContext.inInstanceAPI(context) then |
| 1056 | 1 | binding := Binding.INVALID_BINDING(binding, ErrorExt.getCheckpointMessages()); | |
| 1057 | else | ||
| 1058 | 46 | ErrorExt.delCheckpoint(getInstanceName()); | |
| 1059 | 46 | fail(); | |
| 1060 | end if; | ||
| 1061 | end try; | ||
| 1062 | 142015 | ErrorExt.delCheckpoint(getInstanceName()); | |
| 1063 | |||
| 1064 | 142015 | c.binding := binding; | |
| 1065 | 142015 | c.state := ComponentState.TypeChecked; | |
| 1066 | |||
| 1067 | 142015 | InstNode.updateComponent(c, node); | |
| 1068 | |||
| 1069 |
4/4✓ Branch 0 taken 128164 times.
✓ Branch 1 taken 13851 times.
✓ Branch 3 taken 128162 times.
✓ Branch 4 taken 2 times.
|
142015 | if typeChildren and not InstNode.isEmpty(c.classInst) then |
| 1070 | 128162 | typeBindings(c.classInst, context); | |
| 1071 | end if; | ||
| 1072 | then | ||
| 1073 | (); | ||
| 1074 | |||
| 1075 | // A component without a binding, or with a binding that's already been typed. | ||
| 1076 | case Component.COMPONENT() | ||
| 1077 | guard c.state >= ComponentState.Typed | ||
| 1078 | algorithm | ||
| 1079 | // Type check the binding if it hasn't already been checked. | ||
| 1080 |
2/2✓ Branch 0 taken 329375 times.
✓ Branch 1 taken 234988 times.
|
564363 | if c.state == ComponentState.Typed then |
| 1081 |
2/2✓ Branch 1 taken 42 times.
✓ Branch 2 taken 329333 times.
|
329375 | if Binding.isTyped(c.binding) then |
| 1082 | 42 | c.binding := TypeCheck.matchBinding(c.binding, c.ty, InstNode.name(component), node, context); | |
| 1083 | 42 | checkComponentBindingVariability(component, c, c.binding, context); | |
| 1084 | end if; | ||
| 1085 | |||
| 1086 | 329375 | c.state := ComponentState.TypeChecked; | |
| 1087 | 329375 | InstNode.updateComponent(c, node); | |
| 1088 | end if; | ||
| 1089 | |||
| 1090 |
4/4✓ Branch 0 taken 398904 times.
✓ Branch 1 taken 165459 times.
✓ Branch 3 taken 398903 times.
✓ Branch 4 taken 1 time.
|
564363 | if typeChildren and not InstNode.isEmpty(c.classInst) then |
| 1091 | 398903 | typeBindings(c.classInst, context); | |
| 1092 | end if; | ||
| 1093 | then | ||
| 1094 | (); | ||
| 1095 | |||
| 1096 | // An untyped component with a binding. This might happen when typing a | ||
| 1097 | // dimension and having to evaluate the binding of a not yet typed | ||
| 1098 | // component. Type only the binding and let the case above handle the rest. | ||
| 1099 | case Component.COMPONENT(binding = Binding.UNTYPED_BINDING(), attributes = attrs) | ||
| 1100 | guard c.state < ComponentState.Typed | ||
| 1101 | algorithm | ||
| 1102 | ✗ | binding := typeBinding(c.binding, InstContext.set(context, NFInstContext.BINDING)); | |
| 1103 | ✗ | comp_var := checkComponentBindingVariability(component, c, binding, context); | |
| 1104 | |||
| 1105 | ✗ | if comp_var <> attrs.variability then | |
| 1106 | ✗ | attrs.variability := comp_var; | |
| 1107 | ✗ | c.attributes := attrs; | |
| 1108 | end if; | ||
| 1109 | |||
| 1110 | ✗ | c.binding := binding; | |
| 1111 | ✗ | InstNode.updateComponent(c, node); | |
| 1112 | then | ||
| 1113 | (); | ||
| 1114 | |||
| 1115 | // An untyped component without a binding or an already type checked component, do nothing. | ||
| 1116 | case Component.COMPONENT() then (); | ||
| 1117 | |||
| 1118 | case Component.ENUM_LITERAL() then (); | ||
| 1119 | case Component.TYPE_ATTRIBUTE(modifier = Modifier.NOMOD()) then (); | ||
| 1120 | |||
| 1121 | case Component.TYPE_ATTRIBUTE() | ||
| 1122 | algorithm | ||
| 1123 | 763293 | c.modifier := typeTypeAttribute(c.modifier, c.ty, component, context); | |
| 1124 | 763287 | InstNode.updateComponent(c, node); | |
| 1125 | then | ||
| 1126 | (); | ||
| 1127 | |||
| 1128 | case Component.INVALID_COMPONENT() then (); | ||
| 1129 | |||
| 1130 | else | ||
| 1131 | algorithm | ||
| 1132 | ✗ | Error.terminate(getInstanceName() + " got invalid node " + InstNode.name(node), sourceInfo()); | |
| 1133 | ✗ | then | |
| 1134 | fail(); | ||
| 1135 | |||
| 1136 | end match; | ||
| 1137 | end typeComponentBinding; | ||
| 1138 | |||
| 1139 | function checkComponentBindingVariability | ||
| 1140 | input InstNode node; | ||
| 1141 | input Component component; | ||
| 1142 | input Binding binding; | ||
| 1143 | input InstContext.Type context; | ||
| 1144 | output Variability var; | ||
| 1145 | protected | ||
| 1146 | Variability comp_eff_var, bind_var, bind_eff_var; | ||
| 1147 | algorithm | ||
| 1148 | 142059 | var := Component.variability(component); | |
| 1149 | 142059 | comp_eff_var := Prefixes.effectiveVariability(var); | |
| 1150 | 142059 | bind_var := Binding.variability(binding); | |
| 1151 | 142059 | bind_eff_var := Prefixes.effectiveVariability(bind_var); | |
| 1152 | |||
| 1153 |
4/4✓ Branch 0 taken 262 times.
✓ Branch 1 taken 141797 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 258 times.
|
142059 | if bind_eff_var > comp_eff_var and not InstContext.inFunction(context) then |
| 1154 | 16 | Error.addSourceMessage(Error.HIGHER_VARIABILITY_BINDING, { | |
| 1155 | InstNode.name(node), | ||
| 1156 | Prefixes.variabilityString(comp_eff_var), | ||
| 1157 | "'" + Binding.toString(Component.getBinding(component)) + "'", | ||
| 1158 | Prefixes.variabilityString(bind_eff_var) | ||
| 1159 | }, | ||
| 1160 | Binding.getInfo(binding)); | ||
| 1161 | |||
| 1162 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
|
4 | if not InstContext.inRelaxed(context) then |
| 1163 | 3 | fail(); | |
| 1164 | end if; | ||
| 1165 | end if; | ||
| 1166 | |||
| 1167 |
2/2✓ Branch 0 taken 103067 times.
✓ Branch 1 taken 38989 times.
|
142056 | if var == Variability.PARAMETER then |
| 1168 |
6/6✓ Branch 0 taken 24547 times.
✓ Branch 1 taken 14442 times.
✓ Branch 3 taken 22204 times.
✓ Branch 4 taken 2343 times.
✓ Branch 6 taken 19833 times.
✓ Branch 7 taken 2371 times.
|
38989 | if bind_var <= Variability.STRUCTURAL_PARAMETER and |
| 1169 | (InstNode.isInheritedProtected(node) or Component.isFinal(component)) then | ||
| 1170 | // A protected or final parameter with a structural parameter binding should also be evaluated. | ||
| 1171 | var := Variability.STRUCTURAL_PARAMETER; | ||
| 1172 | elseif bind_var == Variability.NON_STRUCTURAL_PARAMETER then | ||
| 1173 | // A parameter with a non-structural parameter binding should not be evaluated. | ||
| 1174 | var := Variability.NON_STRUCTURAL_PARAMETER; | ||
| 1175 | end if; | ||
| 1176 | end if; | ||
| 1177 | end checkComponentBindingVariability; | ||
| 1178 | |||
| 1179 | function typeBinding | ||
| 1180 | input output Binding binding; | ||
| 1181 | input InstContext.Type context; | ||
| 1182 | algorithm | ||
| 1183 | binding := match binding | ||
| 1184 | local | ||
| 1185 | Expression exp; | ||
| 1186 | Type ty; | ||
| 1187 | Variability var; | ||
| 1188 | Purity purity; | ||
| 1189 | SourceInfo info; | ||
| 1190 | |||
| 1191 | case Binding.UNTYPED_BINDING(bindingExp = exp) | ||
| 1192 | algorithm | ||
| 1193 | 822403 | info := Binding.getInfo(binding); | |
| 1194 | 822403 | (exp, ty, var, purity) := typeExp(exp, context, info); | |
| 1195 | 822362 | then | |
| 1196 | Binding.TYPED_BINDING(exp, ty, var, purity, binding.eachType, | ||
| 1197 | Mutable.create(NFBinding.EvalState.NOT_EVALUATED), false, | ||
| 1198 | binding.source, binding.confidence, binding.info); | ||
| 1199 | |||
| 1200 | case Binding.TYPED_BINDING() then binding; | ||
| 1201 | case Binding.UNBOUND() then binding; | ||
| 1202 | |||
| 1203 | else | ||
| 1204 | algorithm | ||
| 1205 | ✗ | Error.terminate(getInstanceName() + " got uninstantiated binding", sourceInfo()); | |
| 1206 | ✗ | then | |
| 1207 | fail(); | ||
| 1208 | |||
| 1209 | end match; | ||
| 1210 | end typeBinding; | ||
| 1211 | |||
| 1212 | function typeComponentCondition | ||
| 1213 | input output Binding condition; | ||
| 1214 | input InstContext.Type context; | ||
| 1215 | input Boolean evaluate = false; | ||
| 1216 | algorithm | ||
| 1217 | condition := match condition | ||
| 1218 | local | ||
| 1219 | Expression exp; | ||
| 1220 | Type ty; | ||
| 1221 | Variability var; | ||
| 1222 | Purity purity; | ||
| 1223 | SourceInfo info; | ||
| 1224 | MatchKind mk; | ||
| 1225 | NFBinding.EvalState eval_state; | ||
| 1226 | InstContext.Type next_context; | ||
| 1227 | |||
| 1228 | case Binding.UNTYPED_BINDING(bindingExp = exp) | ||
| 1229 | algorithm | ||
| 1230 | 5560 | next_context := InstContext.set(context, NFInstContext.CONDITION); | |
| 1231 | 5560 | info := Binding.getInfo(condition); | |
| 1232 | 5560 | (exp, ty, var, purity) := typeExp(exp, next_context, info); | |
| 1233 | 5560 | (exp, _, mk) := TypeCheck.matchTypes(ty, Type.BOOLEAN(), exp); | |
| 1234 | |||
| 1235 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 5559 times.
|
5560 | if TypeCheck.isIncompatibleMatch(mk) then |
| 1236 | 3 | Error.addSourceMessage(Error.IF_CONDITION_TYPE_ERROR, | |
| 1237 | {Expression.toString(exp), Type.toString(ty)}, info); | ||
| 1238 | 1 | fail(); | |
| 1239 | end if; | ||
| 1240 | |||
| 1241 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5559 times.
|
5559 | if var > Variability.PARAMETER then |
| 1242 | ✗ | Error.addSourceMessage(Error.COMPONENT_CONDITION_VARIABILITY, | |
| 1243 | {Expression.toString(exp)}, info); | ||
| 1244 | ✗ | fail(); | |
| 1245 | end if; | ||
| 1246 | |||
| 1247 | 5559 | eval_state := NFBinding.EvalState.NOT_EVALUATED; | |
| 1248 | |||
| 1249 |
1/2✓ Branch 0 taken 5559 times.
✗ Branch 1 not taken.
|
5559 | if evaluate then |
| 1250 | 5559 | ErrorExt.setCheckpoint(getInstanceName()); | |
| 1251 | try | ||
| 1252 | 5559 | exp := Ceval.evalExp(exp, Ceval.EvalTarget.new(info, next_context)); | |
| 1253 | 5559 | exp := simplifyDimExp(exp); | |
| 1254 | eval_state := NFBinding.EvalState.EVALUATED; | ||
| 1255 | else | ||
| 1256 | end try; | ||
| 1257 | 5559 | ErrorExt.rollBack(getInstanceName()); | |
| 1258 | end if; | ||
| 1259 | 5559 | then | |
| 1260 | Binding.TYPED_BINDING(exp, ty, var, purity, NFBinding.EachType.NOT_EACH, | ||
| 1261 | Mutable.create(eval_state), false, condition.source, condition.confidence, info); | ||
| 1262 | |||
| 1263 | end match; | ||
| 1264 | end typeComponentCondition; | ||
| 1265 | |||
| 1266 | function typeTypeAttribute | ||
| 1267 | input output Modifier attribute; | ||
| 1268 | input Type attrType; | ||
| 1269 | input InstNode component; | ||
| 1270 | input InstContext.Type context; | ||
| 1271 | protected | ||
| 1272 | String name; | ||
| 1273 | Binding binding; | ||
| 1274 | InstNode parent; | ||
| 1275 | algorithm | ||
| 1276 | attribute := match attribute | ||
| 1277 | // Modifier with submodifier, e.g. Real x(start(y = 1)), is an error. | ||
| 1278 | case Modifier.MODIFIER() | ||
| 1279 | guard not ModTable.isEmpty(attribute.subModifiers) | ||
| 1280 | algorithm | ||
| 1281 | // Print an error for the first submodifier. The builtin attributes | ||
| 1282 | // don't have types as such, so for the error message to make sense we | ||
| 1283 | // join the attribute name and submodifier name together (e.g. start.y). | ||
| 1284 | 1 | name := attribute.name + "." + Util.tuple21(listHead(ModTable.toList(attribute.subModifiers))); | |
| 1285 | 2 | Error.addSourceMessage(Error.MISSING_MODIFIED_ELEMENT, | |
| 1286 | {name, Type.toString(attrType)}, attribute.info); | ||
| 1287 | 1 | then | |
| 1288 | fail(); | ||
| 1289 | |||
| 1290 | // Modifier with no binding, e.g. Real x(final start). | ||
| 1291 | // Remove it so we don't have to deal with it in later. | ||
| 1292 | case Modifier.MODIFIER() | ||
| 1293 | guard Binding.isUnbound(attribute.binding) | ||
| 1294 | then | ||
| 1295 | NFModifier.NOMOD(); | ||
| 1296 | |||
| 1297 | // Modifier that has already been typed. | ||
| 1298 | case Modifier.MODIFIER(binding = Binding.TYPED_BINDING()) | ||
| 1299 | then attribute; | ||
| 1300 | |||
| 1301 | // Normal modifier with no submodifiers. | ||
| 1302 | case Modifier.MODIFIER(name = name, binding = binding) | ||
| 1303 | algorithm | ||
| 1304 | // Type and type check the attribute. | ||
| 1305 |
1/2✓ Branch 1 taken 680266 times.
✗ Branch 2 not taken.
|
680266 | if Binding.isBound(binding) then |
| 1306 | 680266 | binding := typeBinding(binding, context); | |
| 1307 | 680266 | parent := InstNode.parent(component); | |
| 1308 | 680266 | binding := TypeCheck.matchBinding(binding, attrType, name, parent, context); | |
| 1309 | |||
| 1310 | // Check the variability. All builtin attributes have parameter variability, | ||
| 1311 | // unless we're in a function in which case we don't care. | ||
| 1312 |
3/4✓ Branch 1 taken 1 time.
✓ Branch 2 taken 680261 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
|
680262 | if Binding.variability(binding) >= Variability.DISCRETE and not InstContext.inFunction(context) then |
| 1313 | 3 | Error.addSourceMessage(Error.HIGHER_VARIABILITY_BINDING, | |
| 1314 | {name, Prefixes.variabilityString(Variability.PARAMETER), | ||
| 1315 | "'" + Binding.toString(binding) + "'", Prefixes.variabilityString(Binding.variability(binding))}, | ||
| 1316 | Binding.getInfo(binding)); | ||
| 1317 | 1 | fail(); | |
| 1318 | end if; | ||
| 1319 | |||
| 1320 | 680261 | attribute.binding := binding; | |
| 1321 | end if; | ||
| 1322 | then | ||
| 1323 | attribute; | ||
| 1324 | |||
| 1325 | end match; | ||
| 1326 | end typeTypeAttribute; | ||
| 1327 | |||
| 1328 | function typeExp | ||
| 1329 | "Types an untyped expression, returning the typed expression itself along with | ||
| 1330 | its type and variability." | ||
| 1331 | input output Expression exp; | ||
| 1332 | input InstContext.Type context; | ||
| 1333 | input SourceInfo info; | ||
| 1334 | input Boolean retype = false; | ||
| 1335 | output Type ty; | ||
| 1336 | output Variability variability; | ||
| 1337 | output Purity purity; | ||
| 1338 | algorithm | ||
| 1339 | (exp, ty, variability, purity) := match exp | ||
| 1340 | local | ||
| 1341 | Expression e1, e2; | ||
| 1342 | Variability var1, var2; | ||
| 1343 | Purity pur1, pur2; | ||
| 1344 | Type ty1, ty2; | ||
| 1345 | InstContext.Type next_context; | ||
| 1346 | |||
| 1347 | 341211 | case Expression.INTEGER() then (exp, Type.INTEGER(), Variability.CONSTANT, Purity.PURE); | |
| 1348 | 299418 | case Expression.REAL() then (exp, Type.REAL(), Variability.CONSTANT, Purity.PURE); | |
| 1349 | 498238 | case Expression.STRING() then (exp, Type.STRING(), Variability.CONSTANT, Purity.PURE); | |
| 1350 | 44969 | case Expression.BOOLEAN() then (exp, Type.BOOLEAN(), Variability.CONSTANT, Purity.PURE); | |
| 1351 | 19862 | case Expression.ENUM_LITERAL() then (exp, exp.ty, Variability.CONSTANT, Purity.PURE); | |
| 1352 | 1052462 | case Expression.CREF() then typeCrefExp(exp.cref, context, info); | |
| 1353 | |||
| 1354 | case Expression.TYPENAME() | ||
| 1355 | algorithm | ||
| 1356 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 55 times.
|
56 | if not InstContext.inValidTypenameScope(context) then |
| 1357 | // Typenames may only be used as iteration ranges or dimensions. | ||
| 1358 | 2 | Error.addSourceMessage(Error.INVALID_TYPENAME_USE, | |
| 1359 | {Type.typenameString(Type.arrayElementType(exp.ty))}, info); | ||
| 1360 | 1 | fail(); | |
| 1361 | end if; | ||
| 1362 | 55 | then | |
| 1363 | (exp, exp.ty, Variability.CONSTANT, Purity.PURE); | ||
| 1364 | |||
| 1365 | 23840 | case Expression.ARRAY() then typeArray(exp.elements, exp.literal, exp.ty, context, info); | |
| 1366 | 2174 | case Expression.MATRIX() then typeMatrix(exp.elements, context, info); | |
| 1367 | 9854 | case Expression.RANGE() then typeRange(exp, context, info); | |
| 1368 | 955 | case Expression.TUPLE() then typeTuple(exp.elements, context, info); | |
| 1369 | 2034 | case Expression.SIZE() then typeSize(exp, context, info); | |
| 1370 | |||
| 1371 | case Expression.END() | ||
| 1372 | algorithm | ||
| 1373 | // end is replaced in subscripts before we get here, so any end still | ||
| 1374 | // left should be outside a subscript and thus illegal. | ||
| 1375 | ✗ | Error.addSourceMessage(Error.END_ILLEGAL_USE_ERROR, {}, info); | |
| 1376 | ✗ | then | |
| 1377 | fail(); | ||
| 1378 | |||
| 1379 | case Expression.BINARY() | ||
| 1380 | algorithm | ||
| 1381 | 613566 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 1382 | 613566 | (e1, ty1, var1, pur1) := typeExp(exp.exp1, next_context, info); | |
| 1383 | 613566 | (e2, ty2, var2, pur2) := typeExp(exp.exp2, next_context, info); | |
| 1384 | 613565 | (exp, ty) := TypeCheck.checkBinaryOperation(e1, ty1, var1, exp.operator, e2, ty2, var2, context, info, retype); | |
| 1385 | 613562 | then | |
| 1386 | (exp, ty, Prefixes.variabilityMax(var1, var2), Prefixes.purityMin(pur1, pur2)); | ||
| 1387 | |||
| 1388 | case Expression.UNARY() | ||
| 1389 | algorithm | ||
| 1390 | 35433 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 1391 | 35433 | (e1, ty1, var1, pur1) := typeExp(exp.exp, next_context, info); | |
| 1392 | 35433 | (exp, ty) := TypeCheck.checkUnaryOperation(e1, ty1, var1, exp.operator, context, info); | |
| 1393 | 35433 | then | |
| 1394 | (exp, ty, var1, pur1); | ||
| 1395 | |||
| 1396 | case Expression.LBINARY() | ||
| 1397 | algorithm | ||
| 1398 | 9890 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 1399 | 9890 | (e1, ty1, var1, pur1) := typeExp(exp.exp1, next_context, info); | |
| 1400 | 9890 | (e2, ty2, var2, pur2) := typeExp(exp.exp2, next_context, info); | |
| 1401 | 9890 | (exp, ty) := TypeCheck.checkLogicalBinaryOperation(e1, ty1, var1, exp.operator, e2, ty2, var2, context, info); | |
| 1402 | 9890 | then | |
| 1403 | (exp, ty, Prefixes.variabilityMax(var1, var2), Prefixes.purityMin(pur1, pur2)); | ||
| 1404 | |||
| 1405 | case Expression.LUNARY() | ||
| 1406 | algorithm | ||
| 1407 | 3668 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 1408 | 3668 | (e1, ty1, var1, pur1) := typeExp(exp.exp, next_context, info); | |
| 1409 | 3668 | (exp, ty) := TypeCheck.checkLogicalUnaryOperation(e1, ty1, var1, exp.operator, context, info); | |
| 1410 | 3668 | then | |
| 1411 | (exp, ty, var1, pur1); | ||
| 1412 | |||
| 1413 | case Expression.RELATION() | ||
| 1414 | algorithm | ||
| 1415 | 30433 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 1416 | 30433 | (e1, ty1, var1, pur1) := typeExp(exp.exp1, next_context, info); | |
| 1417 | 30432 | (e2, ty2, var2, pur2) := typeExp(exp.exp2, next_context, info); | |
| 1418 | 30432 | (exp, ty) := TypeCheck.checkRelationOperation(e1, ty1, var1, exp.operator, e2, ty2, var2, exp.index, context, info); | |
| 1419 | 30432 | variability := Prefixes.variabilityMax(var1, var2); | |
| 1420 | 30432 | purity := Prefixes.purityMin(pur1, pur2); | |
| 1421 | |||
| 1422 | // A relation involving continuous expressions which is not inside | ||
| 1423 | // noEvent is a discrete expression. | ||
| 1424 |
4/4✓ Branch 1 taken 29524 times.
✓ Branch 2 taken 908 times.
✓ Branch 3 taken 3354 times.
✓ Branch 4 taken 26170 times.
|
30432 | if not InstContext.inNoEvent(context) and variability == Variability.CONTINUOUS then |
| 1425 | 3354 | variability := Variability.DISCRETE; | |
| 1426 | end if; | ||
| 1427 | 30432 | then | |
| 1428 | (exp, ty, variability, purity); | ||
| 1429 | |||
| 1430 | 14225 | case Expression.IF() then typeIfExpression(exp, context, info); | |
| 1431 | 54 | case Expression.RECORD() then typeRecordExp(exp, context, info); | |
| 1432 | |||
| 1433 | case Expression.CALL() | ||
| 1434 | algorithm | ||
| 1435 | 103303 | (e1, ty, var1, pur1) := Call.typeCall(exp, context, info, retype); | |
| 1436 | // If the call has multiple outputs and isn't alone on either side of an | ||
| 1437 | // equation/algorithm, select the first output. | ||
| 1438 |
4/4✓ Branch 1 taken 1132 times.
✓ Branch 2 taken 102133 times.
✓ Branch 4 taken 65 times.
✓ Branch 5 taken 1067 times.
|
103265 | if Type.isTuple(ty) and not InstContext.isSingleExpression(context) then |
| 1439 | 65 | ty := Type.firstTupleType(ty); | |
| 1440 | 65 | e1 := Expression.tupleElement(e1, 1); | |
| 1441 | end if; | ||
| 1442 | 103265 | then | |
| 1443 | (e1, ty, var1, pur1); | ||
| 1444 | |||
| 1445 | case Expression.CAST() | ||
| 1446 | algorithm | ||
| 1447 | 36 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 1448 | 36 | (e1, ty, variability, purity) := typeExp(exp.exp, next_context, info, retype); | |
| 1449 | 36 | exp.exp := e1; | |
| 1450 | 36 | exp.ty := Type.copyDims(ty, exp.ty); | |
| 1451 | 36 | then | |
| 1452 | (exp, exp.ty, variability, purity); | ||
| 1453 | |||
| 1454 | case Expression.SUBSCRIPTED_EXP() | ||
| 1455 | 711102 | then typeSubscriptedExp(exp, context, info); | |
| 1456 | |||
| 1457 | case Expression.RECORD_ELEMENT(ty = Type.UNKNOWN()) | ||
| 1458 | ✗ | then typeRecordElement(exp, context, info); | |
| 1459 | |||
| 1460 | case Expression.MUTABLE() | ||
| 1461 | algorithm | ||
| 1462 | 1 | e1 := Mutable.access(exp.exp); | |
| 1463 | 1 | (e1, ty, variability, purity) := typeExp(e1, context, info, retype); | |
| 1464 | 1 | exp.exp := Mutable.create(e1); | |
| 1465 | 1 | then | |
| 1466 | (exp, ty, variability, purity); | ||
| 1467 | |||
| 1468 | case Expression.PARTIAL_FUNCTION_APPLICATION() | ||
| 1469 | 27 | then Function.typePartialApplication(exp, context, info); | |
| 1470 | |||
| 1471 | ✗ | case Expression.FILENAME() then (exp, Type.STRING(), Variability.CONSTANT, Purity.PURE); | |
| 1472 | |||
| 1473 | 412 | case Expression.MULTARY() then typeExp(SimplifyExp.splitMultary(exp), context, info, retype); | |
| 1474 | |||
| 1475 | 14 | else (exp, Expression.typeOf(exp), Expression.variability(exp), Expression.purity(exp)); | |
| 1476 | |||
| 1477 | end match; | ||
| 1478 | |||
| 1479 | // Expressions inside when-clauses and initial sections are discrete. | ||
| 1480 |
4/4✓ Branch 1 taken 1681394 times.
✓ Branch 2 taken 2135779 times.
✓ Branch 3 taken 1517721 times.
✓ Branch 4 taken 618058 times.
|
3817173 | if InstContext.inDiscreteScope(context) and variability == Variability.CONTINUOUS then |
| 1481 | 618058 | variability := Variability.DISCRETE; | |
| 1482 | end if; | ||
| 1483 | end typeExp; | ||
| 1484 | |||
| 1485 | function typeExpl | ||
| 1486 | input list<Expression> expl; | ||
| 1487 | input InstContext.Type context; | ||
| 1488 | input SourceInfo info; | ||
| 1489 | output list<Expression> explTyped = {}; | ||
| 1490 | output list<Type> tyl = {}; | ||
| 1491 | output list<Variability> varl = {}; | ||
| 1492 | protected | ||
| 1493 | Expression exp; | ||
| 1494 | Variability var; | ||
| 1495 | Type ty; | ||
| 1496 | algorithm | ||
| 1497 |
2/2✓ Branch 1 taken 2200 times.
✓ Branch 2 taken 954 times.
|
3154 | for e in listReverse(expl) loop |
| 1498 | 2200 | (exp, ty, var) := typeExp(e, context, info); | |
| 1499 | explTyped := exp :: explTyped; | ||
| 1500 | 2200 | tyl := ty :: tyl; | |
| 1501 | 2200 | varl := var :: varl; | |
| 1502 | end for; | ||
| 1503 | end typeExpl; | ||
| 1504 | |||
| 1505 | function typeRecordExp | ||
| 1506 | input output Expression exp; | ||
| 1507 | input InstContext.Type context; | ||
| 1508 | input SourceInfo info; | ||
| 1509 | output Type ty; | ||
| 1510 | output Variability variability = Variability.CONSTANT; | ||
| 1511 | output Purity purity = Purity.PURE; | ||
| 1512 | protected | ||
| 1513 | Absyn.Path path; | ||
| 1514 | list<Expression> elems, ty_elems = {}; | ||
| 1515 | Variability var; | ||
| 1516 | Purity pur; | ||
| 1517 | InstContext.Type next_context; | ||
| 1518 | algorithm | ||
| 1519 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
|
54 | Expression.RECORD(path, ty, elems) := exp; |
| 1520 | 54 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 1521 | |||
| 1522 |
2/2✓ Branch 0 taken 146 times.
✓ Branch 1 taken 54 times.
|
200 | for e in elems loop |
| 1523 | 146 | (e, _, var, pur) := typeExp(e, context, info); | |
| 1524 | 146 | variability := Prefixes.variabilityMax(var, variability); | |
| 1525 | 146 | purity := Prefixes.purityMin(pur, purity); | |
| 1526 | ty_elems := e :: ty_elems; | ||
| 1527 | end for; | ||
| 1528 | |||
| 1529 | 54 | exp := Expression.makeRecord(path, ty, listReverseInPlace(ty_elems)); | |
| 1530 | end typeRecordExp; | ||
| 1531 | |||
| 1532 | function typeRecordElement | ||
| 1533 | input Expression exp; | ||
| 1534 | input InstContext.Type context; | ||
| 1535 | input SourceInfo info; | ||
| 1536 | output Expression outExp; | ||
| 1537 | output Type ty; | ||
| 1538 | output Variability variability; | ||
| 1539 | output Purity purity; | ||
| 1540 | protected | ||
| 1541 | Expression rec_exp; | ||
| 1542 | Type rec_ty; | ||
| 1543 | String name; | ||
| 1544 | Boolean found; | ||
| 1545 | algorithm | ||
| 1546 | ✗ | Expression.RECORD_ELEMENT(recordExp = rec_exp, fieldName = name) := exp; | |
| 1547 | ✗ | (rec_exp, rec_ty, variability, purity) := | |
| 1548 | typeExp(rec_exp, InstContext.set(context, NFInstContext.SUBEXPRESSION), info); | ||
| 1549 | |||
| 1550 | ✗ | found := Type.isRecord(Type.arrayElementType(rec_ty)); | |
| 1551 | ✗ | if found then | |
| 1552 | try | ||
| 1553 | ✗ | _ := Class.lookupComponentIndex(name, | |
| 1554 | InstNode.getClass(Type.complexNode(Type.arrayElementType(rec_ty)))); | ||
| 1555 | else | ||
| 1556 | found := false; | ||
| 1557 | end try; | ||
| 1558 | end if; | ||
| 1559 | |||
| 1560 | ✗ | if not found then | |
| 1561 | ✗ | Error.addSourceMessage(Error.RECORD_ELEMENT_NOT_FOUND, | |
| 1562 | {Expression.toString(rec_exp), Type.toString(rec_ty), name}, info); | ||
| 1563 | ✗ | fail(); | |
| 1564 | end if; | ||
| 1565 | |||
| 1566 | ✗ | outExp := Expression.recordElement(name, rec_exp); | |
| 1567 | ✗ | ty := Expression.typeOf(outExp); | |
| 1568 | end typeRecordElement; | ||
| 1569 | |||
| 1570 | function typeSubscriptedExp | ||
| 1571 | input output Expression exp; | ||
| 1572 | input InstContext.Type context; | ||
| 1573 | input SourceInfo info; | ||
| 1574 | output Type ty; | ||
| 1575 | output Variability variability; | ||
| 1576 | output Purity purity; | ||
| 1577 | protected | ||
| 1578 | Expression e; | ||
| 1579 | list<Subscript> subs, expanded_subs; | ||
| 1580 | list<Expression> fill_dims; | ||
| 1581 | Boolean split; | ||
| 1582 | Variability subs_var; | ||
| 1583 | algorithm | ||
| 1584 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 711102 times.
|
711102 | Expression.SUBSCRIPTED_EXP(e, subs, ty, split) := exp; |
| 1585 | |||
| 1586 | // split = true means the expression is subscripted because it came from a | ||
| 1587 | // modifier that was propagated down. In this case it should have proxy | ||
| 1588 | // subscripts that we need to deal with. | ||
| 1589 |
2/2✓ Branch 0 taken 711088 times.
✓ Branch 1 taken 14 times.
|
711102 | if split then |
| 1590 | // Expand proxy subscripts into split index subscripts. | ||
| 1591 | 711088 | (expanded_subs, fill_dims) := expandProxySubscripts(subs, context); | |
| 1592 | |||
| 1593 | // Type the expression that's being subscripted. | ||
| 1594 | //(exp, ty, variability, purity) := typeExp(e, context, info); | ||
| 1595 | 711088 | (exp, ty, variability, purity) := typeSubscriptedExp2(e, expanded_subs, context, info); | |
| 1596 | |||
| 1597 | // If we have fill dimensions, use them to create a fill call. | ||
| 1598 |
2/2✓ Branch 0 taken 80862 times.
✓ Branch 1 taken 630226 times.
|
711088 | if not listEmpty(fill_dims) then |
| 1599 | 80862 | fill_dims := listReverseInPlace(fill_dims); | |
| 1600 |
4/4✓ Branch 0 taken 83226 times.
✓ Branch 1 taken 80862 times.
✓ Branch 2 taken 83226 times.
✓ Branch 3 taken 80862 times.
|
164088 | ty := Type.liftArrayLeftList(ty, list(Dimension.fromExp(d, Variability.CONSTANT) for d in fill_dims)); |
| 1601 | 80862 | exp := Expression.CALL( | |
| 1602 | Call.makeTypedCall(NFBuiltinFuncs.FILL_FUNC, exp :: fill_dims, variability, purity, ty)); | ||
| 1603 | end if; | ||
| 1604 | |||
| 1605 | // If we have any subscripts after expanding the proxies we create a new | ||
| 1606 | // subscripted expression. Otherwise we can just return the typed expression | ||
| 1607 | // as it is. | ||
| 1608 |
2/2✓ Branch 0 taken 88407 times.
✓ Branch 1 taken 622681 times.
|
711088 | if not listEmpty(expanded_subs) then |
| 1609 | // Subscripting the expression might not be possible if the type is wrong, | ||
| 1610 | // but we ignore it here so we can handle it during type checking instead | ||
| 1611 | // when we can give better error messages. | ||
| 1612 | 88407 | ty := Type.subscript(ty, expanded_subs, failOnError = false); | |
| 1613 | |||
| 1614 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 88405 times.
|
88407 | if Type.isUnknown(ty) then |
| 1615 | 2 | exp := Expression.SUBSCRIPTED_EXP(exp, expanded_subs, ty, true); | |
| 1616 | else | ||
| 1617 | 88405 | exp := Expression.applySubscripts(expanded_subs, exp); | |
| 1618 | end if; | ||
| 1619 | |||
| 1620 | // Take the purity and variability of the subscripts into consideration. | ||
| 1621 |
2/2✓ Branch 0 taken 88207 times.
✓ Branch 1 taken 200 times.
|
88407 | if purity == Purity.PURE then |
| 1622 | 88207 | purity := Subscript.purityList(expanded_subs); | |
| 1623 | end if; | ||
| 1624 | |||
| 1625 |
2/2✓ Branch 0 taken 87481 times.
✓ Branch 1 taken 926 times.
|
88407 | if variability <> Variability.CONTINUOUS then |
| 1626 | 87481 | variability := Prefixes.variabilityMax(variability, | |
| 1627 | Subscript.variabilityList(expanded_subs)); | ||
| 1628 | end if; | ||
| 1629 | end if; | ||
| 1630 | else | ||
| 1631 | 14 | (e, ty, variability, purity) := typeExp(e, context, info); | |
| 1632 | 14 | (subs, subs_var) := typeSubscripts(subs, ty, exp, context, info); | |
| 1633 | 14 | ty := Type.subscript(ty, subs); | |
| 1634 | 14 | exp := Expression.SUBSCRIPTED_EXP(e, subs, ty, false); | |
| 1635 | end if; | ||
| 1636 | end typeSubscriptedExp; | ||
| 1637 | |||
| 1638 | function expandProxySubscripts | ||
| 1639 | "Expand proxy subscripts into split index subscripts. A proxy subscript | ||
| 1640 | generates as many index subscripts as the number of dimensions on the | ||
| 1641 | element it refers to (which might be none if the element is a scalar)." | ||
| 1642 | input list<Subscript> subscripts; | ||
| 1643 | input InstContext.Type context; | ||
| 1644 | output list<Subscript> outSubscripts = {}; | ||
| 1645 | output list<Expression> fillDimensions = {}; | ||
| 1646 | protected | ||
| 1647 | Integer dim_count; | ||
| 1648 | Expression cr_exp; | ||
| 1649 | Type ty; | ||
| 1650 | list<Dimension> dims; | ||
| 1651 | Dimension dim; | ||
| 1652 | algorithm | ||
| 1653 |
2/2✓ Branch 0 taken 724283 times.
✓ Branch 1 taken 711088 times.
|
1435371 | for s in subscripts loop |
| 1654 | outSubscripts := match s | ||
| 1655 | case Subscript.SPLIT_PROXY() | ||
| 1656 | algorithm | ||
| 1657 | // Count the number of dimensions on the parent the subscript came | ||
| 1658 | // from, and add that many split index subscripts to the list. | ||
| 1659 | 724282 | dim_count := InstNode.dimensionCount(InstNode.borrow(s.parent)); | |
| 1660 | |||
| 1661 |
2/2✓ Branch 0 taken 88408 times.
✓ Branch 1 taken 635874 times.
|
815061 | for i in 1:dim_count loop |
| 1662 | 90779 | outSubscripts := Subscript.makeSplitIndex(InstNode.borrow(s.parent), i) :: outSubscripts; | |
| 1663 | end for; | ||
| 1664 | |||
| 1665 | // If the origin and parent of the subscript is not the same it | ||
| 1666 | // means the expression comes from a class modifier, like | ||
| 1667 | // type T = Real[3](start = {1, 2, 3}). | ||
| 1668 | // In this case we might need to add dimensions when applying the | ||
| 1669 | // binding expression to a component. For a component like | ||
| 1670 | // T x[1, 2] | ||
| 1671 | // we then have origin = T and parent = x and generate | ||
| 1672 | // T x[1, 2](start = fill({1, 2, 3}, size(x, 1), size(x, 2))). | ||
| 1673 |
2/2✓ Branch 3 taken 556982 times.
✓ Branch 4 taken 167300 times.
|
724282 | if not InstNode.refEqual(InstNode.borrow(s.origin), InstNode.borrow(s.parent)) then |
| 1674 | // The number of fill dimensions is size(parent) - size(origin). | ||
| 1675 | 556982 | dim_count := dim_count - InstNode.dimensionCount(InstNode.borrow(s.origin)); | |
| 1676 | |||
| 1677 | // Add size expressions to the list of fill dimensions. | ||
| 1678 |
2/2✓ Branch 0 taken 80862 times.
✓ Branch 1 taken 476120 times.
|
556982 | if dim_count > 0 then |
| 1679 | 80862 | ty := InstNode.getType(InstNode.borrow(s.parent)); | |
| 1680 | 80862 | cr_exp := Expression.fromCref(ComponentRef.fromNode(InstNode.borrow(s.parent), ty)); | |
| 1681 | 80862 | dims := Type.arrayDims(ty); | |
| 1682 | |||
| 1683 | 164088 | for i in 1:dim_count loop | |
| 1684 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 83226 times.
|
83226 | dim :: dims := dims; |
| 1685 | |||
| 1686 |
2/2✓ Branch 1 taken 81837 times.
✓ Branch 2 taken 1389 times.
|
83226 | if Dimension.isKnown(dim, allowExp = true) then |
| 1687 | 81837 | fillDimensions := Dimension.sizeExp(dim) :: fillDimensions; | |
| 1688 | else | ||
| 1689 | 2778 | fillDimensions := Expression.SIZE(cr_exp, SOME(Expression.INTEGER(i))) :: fillDimensions; | |
| 1690 | end if; | ||
| 1691 | end for; | ||
| 1692 | end if; | ||
| 1693 | end if; | ||
| 1694 | then | ||
| 1695 | outSubscripts; | ||
| 1696 | |||
| 1697 | else s :: outSubscripts; | ||
| 1698 | end match; | ||
| 1699 | end for; | ||
| 1700 | |||
| 1701 | 711088 | outSubscripts := List.trim(outSubscripts, Subscript.isWhole); | |
| 1702 | 711088 | outSubscripts := listReverseInPlace(outSubscripts); | |
| 1703 | end expandProxySubscripts; | ||
| 1704 | |||
| 1705 | function typeSubscriptedExp2 | ||
| 1706 | input Expression exp; | ||
| 1707 | input list<Subscript> splitSubs; | ||
| 1708 | input InstContext.Type context; | ||
| 1709 | input SourceInfo info; | ||
| 1710 | output Expression outExp; | ||
| 1711 | output Type ty = Type.UNKNOWN(); | ||
| 1712 | output Variability variability; | ||
| 1713 | output Purity purity; | ||
| 1714 | protected | ||
| 1715 | list<Expression> expl; | ||
| 1716 | algorithm | ||
| 1717 | (outExp, ty, variability, purity) := match exp | ||
| 1718 | case Expression.ARRAY() | ||
| 1719 | guard not listEmpty(splitSubs) and not arrayEmpty(exp.elements) | ||
| 1720 | algorithm | ||
| 1721 | expl := {}; | ||
| 1722 | 3728 | variability := Variability.CONSTANT; | |
| 1723 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3728 times.
|
3728 | purity := Purity.PURE; |
| 1724 | |||
| 1725 |
2/2✓ Branch 1 taken 11173 times.
✓ Branch 2 taken 3728 times.
|
14901 | for e in exp.elements loop |
| 1726 | 11173 | (e, ty, variability, purity) := typeSubscriptedExp2(e, listRest(splitSubs), context, info); | |
| 1727 | expl := e :: expl; | ||
| 1728 | end for; | ||
| 1729 | |||
| 1730 | 3728 | expl := listReverseInPlace(expl); | |
| 1731 | 3728 | ty := Type.liftArrayLeft(ty, Dimension.fromInteger(listLength(expl))); | |
| 1732 | 3728 | outExp := Expression.makeArray(ty, listArray(expl), exp.literal); | |
| 1733 | 3728 | then | |
| 1734 | (outExp, ty, variability, purity); | ||
| 1735 | |||
| 1736 | 718533 | else typeExp(exp, context, info); | |
| 1737 | end match; | ||
| 1738 | end typeSubscriptedExp2; | ||
| 1739 | |||
| 1740 | function typeExpDim | ||
| 1741 | "Returns the requested dimension of the given expression, while doing as | ||
| 1742 | little typing as possible. This function returns TypingError.OUT_OF_BOUNDS if | ||
| 1743 | the given index doesn't refer to a valid dimension, in which case the | ||
| 1744 | returned dimension is undefined." | ||
| 1745 | input Expression exp; | ||
| 1746 | input Integer dimIndex; | ||
| 1747 | input InstContext.Type context; | ||
| 1748 | input SourceInfo info; | ||
| 1749 | output Dimension dim; | ||
| 1750 | output Option<Expression> typedExp = NONE(); | ||
| 1751 | output TypingError error; | ||
| 1752 | protected | ||
| 1753 | Type ty; | ||
| 1754 | Expression e; | ||
| 1755 | InstContext.Type next_context; | ||
| 1756 | algorithm | ||
| 1757 | 7359 | ty := Expression.typeOf(exp); | |
| 1758 | |||
| 1759 | // If the expression has already been typed, just get the dimension from the type. | ||
| 1760 |
2/2✓ Branch 1 taken 4241 times.
✓ Branch 2 taken 3118 times.
|
7359 | if Type.isKnown(ty) then |
| 1761 | 4241 | (dim, error) := nthDimensionBoundsChecked(ty, dimIndex); | |
| 1762 | typedExp := SOME(exp); | ||
| 1763 | |||
| 1764 |
2/2✓ Branch 1 taken 4109 times.
✓ Branch 2 taken 132 times.
|
4241 | if not Dimension.isUnknown(dim) then |
| 1765 | 4109 | return; | |
| 1766 | end if; | ||
| 1767 | end if; | ||
| 1768 | |||
| 1769 | // Clear any scope flags. For dimensions we only really care about if we're | ||
| 1770 | // in a class or function and other flags shouldn't influence the typing. | ||
| 1771 | 3250 | next_context := InstContext.clearExpFlags(context); | |
| 1772 | |||
| 1773 | // Otherwise we try to type as little as possible of the expression to get | ||
| 1774 | // the dimension we need, to avoid introducing unnecessary cycles. | ||
| 1775 | (dim, error) := match exp | ||
| 1776 | // An untyped array, use typeArrayDim to get the dimension. | ||
| 1777 | case Expression.ARRAY(ty = Type.UNKNOWN()) | ||
| 1778 | 150 | then typeArrayDim(exp, dimIndex); | |
| 1779 | |||
| 1780 | // A cref, use typeCrefDim to get the dimension. | ||
| 1781 | case Expression.CREF() | ||
| 1782 | 2223 | then typeCrefDim(exp.cref, dimIndex, next_context, info); | |
| 1783 | |||
| 1784 | // Any other expression, type the whole expression and get the dimension | ||
| 1785 | // from the type. | ||
| 1786 | else | ||
| 1787 | algorithm | ||
| 1788 | 877 | (e, ty, _) := typeExp(exp, next_context, info); | |
| 1789 | |||
| 1790 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 874 times.
|
874 | if Type.isTuple(ty) then |
| 1791 | ✗ | ty := Type.firstTupleType(ty); | |
| 1792 | ✗ | e := Expression.tupleElement(e, 1); | |
| 1793 | end if; | ||
| 1794 | |||
| 1795 |
2/2✓ Branch 1 taken 22 times.
✓ Branch 2 taken 852 times.
|
874 | if Type.isConditionalArray(ty) then |
| 1796 | 22 | e := Expression.map(e, | |
| 1797 | function evaluateArrayIf(target = Ceval.EvalTarget.new(info, next_context))); | ||
| 1798 | 22 | (e, ty, _) := typeExp(e, next_context, info); | |
| 1799 | end if; | ||
| 1800 | |||
| 1801 | typedExp := SOME(e); | ||
| 1802 | 874 | then | |
| 1803 | nthDimensionBoundsChecked(ty, dimIndex); | ||
| 1804 | |||
| 1805 | end match; | ||
| 1806 | end typeExpDim; | ||
| 1807 | |||
| 1808 | function evaluateArrayIf | ||
| 1809 | input Expression exp; | ||
| 1810 | input Ceval.EvalTarget target; | ||
| 1811 | output Expression outExp; | ||
| 1812 | algorithm | ||
| 1813 | outExp := match exp | ||
| 1814 | local | ||
| 1815 | Expression cond; | ||
| 1816 | |||
| 1817 | case Expression.IF() guard Type.isConditionalArray(exp.ty) | ||
| 1818 | algorithm | ||
| 1819 | 22 | cond := Ceval.evalExp(exp.condition, target); | |
| 1820 | |||
| 1821 |
2/2✓ Branch 1 taken 21 times.
✓ Branch 2 taken 1 time.
|
22 | if Expression.isTrue(cond) then |
| 1822 | 21 | outExp := exp.trueBranch; | |
| 1823 | elseif Expression.isFalse(cond) then | ||
| 1824 | 1 | outExp := exp.falseBranch; | |
| 1825 | else | ||
| 1826 | ✗ | Error.addInternalError(getInstanceName() + " failed on " + | |
| 1827 | Expression.toString(exp), Ceval.EvalTarget.getInfo(target)); | ||
| 1828 | ✗ | fail(); | |
| 1829 | end if; | ||
| 1830 | then | ||
| 1831 | outExp; | ||
| 1832 | |||
| 1833 | else exp; | ||
| 1834 | end match; | ||
| 1835 | end evaluateArrayIf; | ||
| 1836 | |||
| 1837 | function typeArrayDim | ||
| 1838 | "Returns the requested dimension of an array dimension. This function is meant | ||
| 1839 | to be used on an untyped array, for a typed array it's better to just use | ||
| 1840 | e.g. nthDimensionBoundsChecked on its type." | ||
| 1841 | input Expression arrayExp; | ||
| 1842 | input Integer dimIndex; | ||
| 1843 | output Dimension dim; | ||
| 1844 | output TypingError error; | ||
| 1845 | algorithm | ||
| 1846 | // We don't yet know the number of dimensions, but the index must at least be 1. | ||
| 1847 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
|
150 | if dimIndex < 1 then |
| 1848 | dim := Dimension.UNKNOWN(); | ||
| 1849 | ✗ | error := TypingError.OUT_OF_BOUNDS(Expression.dimensionCount(arrayExp, true)); | |
| 1850 | else | ||
| 1851 | 150 | (dim, error) := typeArrayDim2(arrayExp, dimIndex); | |
| 1852 | end if; | ||
| 1853 | end typeArrayDim; | ||
| 1854 | |||
| 1855 | function typeArrayDim2 | ||
| 1856 | input Expression arrayExp; | ||
| 1857 | input Integer dimIndex; | ||
| 1858 | input Integer dimCount = 0; | ||
| 1859 | output Dimension dim; | ||
| 1860 | output TypingError error; | ||
| 1861 | algorithm | ||
| 1862 | (dim, error) := match (arrayExp, dimIndex) | ||
| 1863 | case (Expression.ARRAY(), 1) | ||
| 1864 | 149 | then (Dimension.fromExpArray(arrayExp.elements), TypingError.NO_ERROR()); | |
| 1865 | |||
| 1866 | // Modelica arrays are non-ragged and only the last dimension of an array | ||
| 1867 | // expression can be empty, so just traverse into the first element. | ||
| 1868 | case (Expression.ARRAY(), _) | ||
| 1869 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
6 | then typeArrayDim2(arrayGet(arrayExp.elements, 1), dimIndex - 1, dimCount + 1); |
| 1870 | |||
| 1871 | else | ||
| 1872 | algorithm | ||
| 1873 | dim := Dimension.UNKNOWN(); | ||
| 1874 | 1 | error := TypingError.OUT_OF_BOUNDS(dimCount); | |
| 1875 | then | ||
| 1876 | (dim, error); | ||
| 1877 | |||
| 1878 | end match; | ||
| 1879 | end typeArrayDim2; | ||
| 1880 | |||
| 1881 | function typeCrefDim | ||
| 1882 | input ComponentRef cref; | ||
| 1883 | input Integer dimIndex; | ||
| 1884 | input InstContext.Type context; | ||
| 1885 | input SourceInfo info; | ||
| 1886 | output Dimension dim; | ||
| 1887 | output TypingError error = TypingError.NO_ERROR(); | ||
| 1888 | protected | ||
| 1889 | list<ComponentRef> crl; | ||
| 1890 | Integer index, dim_count, dim_total = 0; | ||
| 1891 | InstNode node; | ||
| 1892 | Component c; | ||
| 1893 | Type ty; | ||
| 1894 | array<Dimension> dims; | ||
| 1895 | algorithm | ||
| 1896 | // TODO: If the cref has subscripts it becomes trickier to correctly calculate | ||
| 1897 | // the dimension. For now we take the easy way out and just type the | ||
| 1898 | // whole cref, but doing so might introduce unnecessary cycles. | ||
| 1899 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 2218 times.
|
2223 | if ComponentRef.hasSubscripts(cref) then |
| 1900 | 5 | (_, ty) := typeCref(cref, context, info); | |
| 1901 | 5 | (dim, error) := nthDimensionBoundsChecked(ty, dimIndex); | |
| 1902 | 5 | return; | |
| 1903 | end if; | ||
| 1904 | |||
| 1905 | // Loop through the cref in reverse, reducing the index by the number of | ||
| 1906 | // dimensions each component has until we find a component that the index is | ||
| 1907 | // valid for. This is done even if the index is 0 or negative, since the loop | ||
| 1908 | // also sums up the total number of dimensions which is needed to give a good | ||
| 1909 | // error message. | ||
| 1910 | 2218 | crl := ComponentRef.toListReverse(cref, includeScope = false); | |
| 1911 | index := dimIndex; | ||
| 1912 | |||
| 1913 |
2/2✓ Branch 0 taken 2240 times.
✓ Branch 1 taken 1 time.
|
2241 | for cr in crl loop |
| 1914 | () := match cr | ||
| 1915 | case ComponentRef.CREF(subscripts = _) | ||
| 1916 | guard InstNode.isComponent(ComponentRef.node(cr)) | ||
| 1917 | algorithm | ||
| 1918 | 2236 | node := InstNode.resolveOuter(ComponentRef.node(cr)); | |
| 1919 | 2236 | c := InstNode.component(node); | |
| 1920 | |||
| 1921 | // If the component is untyped it might have an array type whose dimensions | ||
| 1922 | // we need to take into consideration. To avoid making this more complicated | ||
| 1923 | // than it already is we make sure that the component is typed in that case. | ||
| 1924 |
2/2✓ Branch 3 taken 1 time.
✓ Branch 4 taken 2235 times.
|
2236 | if Class.hasDimensions(InstNode.getClass(Component.classInstance(c))) then |
| 1925 | 1 | typeComponent(node, context); | |
| 1926 | 1 | c := InstNode.component(node); | |
| 1927 | end if; | ||
| 1928 | |||
| 1929 | dim_count := match c | ||
| 1930 | case Component.COMPONENT(ty = Type.UNTYPED(dimensions = dims)) | ||
| 1931 | algorithm | ||
| 1932 | dim_count := arrayLength(dims); | ||
| 1933 | |||
| 1934 |
2/2✓ Branch 0 taken 181 times.
✓ Branch 1 taken 1 time.
|
182 | if index <= dim_count and index > 0 then |
| 1935 | 181 | dim := typeDimension(dims, index, node, c.binding, context, c.info); | |
| 1936 | 179 | checkCyclicDimension(dim, node, index, c.info); | |
| 1937 | 175 | return; | |
| 1938 | end if; | ||
| 1939 | then | ||
| 1940 | dim_count; | ||
| 1941 | |||
| 1942 | case Component.COMPONENT() | ||
| 1943 | algorithm | ||
| 1944 | 2054 | dim_count := Type.dimensionCount(c.ty); | |
| 1945 | |||
| 1946 |
2/2✓ Branch 0 taken 2036 times.
✓ Branch 1 taken 18 times.
|
2054 | if index <= dim_count and index > 0 then |
| 1947 | 2036 | dim := Type.nthDimension(c.ty, index); | |
| 1948 | 2036 | return; | |
| 1949 | end if; | ||
| 1950 | then | ||
| 1951 | dim_count; | ||
| 1952 | |||
| 1953 | else 0; | ||
| 1954 | end match; | ||
| 1955 | |||
| 1956 | 19 | index := index - dim_count; | |
| 1957 | 19 | dim_total := dim_total + dim_count; | |
| 1958 | then | ||
| 1959 | (); | ||
| 1960 | |||
| 1961 | else (); | ||
| 1962 | end match; | ||
| 1963 | end for; | ||
| 1964 | |||
| 1965 | dim := Dimension.UNKNOWN(); | ||
| 1966 | 1 | error := TypingError.OUT_OF_BOUNDS(dim_total); | |
| 1967 | end typeCrefDim; | ||
| 1968 | |||
| 1969 | function checkCyclicDimension | ||
| 1970 | input Dimension dim; | ||
| 1971 | input InstNode component; | ||
| 1972 | input Integer index; | ||
| 1973 | input SourceInfo info; | ||
| 1974 | algorithm | ||
| 1975 | () := match dim | ||
| 1976 | case Dimension.UNTYPED(isProcessing = true) | ||
| 1977 | algorithm | ||
| 1978 | // TODO: Tell the user which variables are involved in the loop (can be | ||
| 1979 | // found with DFS on the dimension expression. Maybe have a limit | ||
| 1980 | // on the output in case there's a lot of dimensions involved. | ||
| 1981 | 12 | Error.addSourceMessage(Error.CYCLIC_DIMENSIONS, | |
| 1982 | {String(index), InstNode.name(component), Expression.toString(dim.dimension)}, info); | ||
| 1983 | 4 | then | |
| 1984 | fail(); | ||
| 1985 | |||
| 1986 | else (); | ||
| 1987 | end match; | ||
| 1988 | end checkCyclicDimension; | ||
| 1989 | |||
| 1990 | function nthDimensionBoundsChecked | ||
| 1991 | "Returns the requested dimension from the given type, along with a TypingError | ||
| 1992 | indicating whether the index was valid or not." | ||
| 1993 | input Type ty; | ||
| 1994 | input Integer dimIndex; | ||
| 1995 | input Integer offset = 0 "The number of dimensions to skip due to subscripts."; | ||
| 1996 | output Dimension dim; | ||
| 1997 | output TypingError error; | ||
| 1998 | protected | ||
| 1999 | Integer dim_size = Type.dimensionCount(ty); | ||
| 2000 | Integer index = dimIndex + offset; | ||
| 2001 | algorithm | ||
| 2002 | // Check that the dimension index is within bounds. | ||
| 2003 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5142 times.
|
5145 | if index < 1 or index > dim_size then |
| 2004 | dim := Dimension.UNKNOWN(); | ||
| 2005 | 3 | error := TypingError.OUT_OF_BOUNDS(dim_size - offset); | |
| 2006 | else | ||
| 2007 | try | ||
| 2008 | 5142 | dim := Type.nthDimension(ty, index); | |
| 2009 | error := TypingError.NO_ERROR(); | ||
| 2010 | else | ||
| 2011 | // Type.nthDimension doesn't work for e.g. conditional arrays that don't have a | ||
| 2012 | // selected branch yet. Return an error in that case instead of just failing. | ||
| 2013 | dim := Dimension.UNKNOWN(); | ||
| 2014 | error := TypingError.UNKNOWN_TYPE(); | ||
| 2015 | end try; | ||
| 2016 | end if; | ||
| 2017 | end nthDimensionBoundsChecked; | ||
| 2018 | |||
| 2019 | function typeCrefExp | ||
| 2020 | input ComponentRef cref; | ||
| 2021 | input InstContext.Type context; | ||
| 2022 | input SourceInfo info; | ||
| 2023 | output Expression exp; | ||
| 2024 | output Type ty; | ||
| 2025 | output Variability variability; | ||
| 2026 | output Purity purity; | ||
| 2027 | protected | ||
| 2028 | ComponentRef cr; | ||
| 2029 | Variability node_var, subs_var; | ||
| 2030 | algorithm | ||
| 2031 | 1052462 | (cr, ty, node_var, subs_var) := typeCref(cref, context, info); | |
| 2032 | 1052458 | exp := Expression.CREF(ty, cr); | |
| 2033 | 1052458 | variability := Prefixes.variabilityMax(node_var, subs_var); | |
| 2034 | 1052458 | purity := ComponentRef.purity(cref); | |
| 2035 | end typeCrefExp; | ||
| 2036 | |||
| 2037 | function typeCref | ||
| 2038 | input output ComponentRef cref; | ||
| 2039 | input InstContext.Type context; | ||
| 2040 | input SourceInfo info; | ||
| 2041 | output Type ty; | ||
| 2042 | output Variability nodeVariability; | ||
| 2043 | output Variability subsVariability; | ||
| 2044 | algorithm | ||
| 2045 | // Check that time isn't used in a function context. | ||
| 2046 | // TODO: Fix NFBuiltin.TIME_CREF so that the compiler treats it like an actual | ||
| 2047 | // constant, then maybe we can use referenceEq here instead. | ||
| 2048 |
4/4✓ Branch 1 taken 668307 times.
✓ Branch 2 taken 385439 times.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 668306 times.
|
1053746 | if InstContext.inFunction(context) and |
| 2049 | ComponentRef.isTime(cref) then | ||
| 2050 | 1 | Error.addSourceMessage(Error.EXP_INVALID_IN_FUNCTION, {"time"}, info); | |
| 2051 | 1 | fail(); | |
| 2052 | end if; | ||
| 2053 | |||
| 2054 | 1053745 | (cref, subsVariability) := typeCref2(cref, context, info); | |
| 2055 | |||
| 2056 | // Fill all implicit array subscripts with explicit `:` | ||
| 2057 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1053732 times.
|
1053742 | if ComponentRef.hasImplicitTrailingIndex(cref) then |
| 2058 | 10 | cref := ComponentRef.fillSubscripts(cref); | |
| 2059 | end if; | ||
| 2060 | |||
| 2061 | 1053742 | ty := ComponentRef.getSubscriptedType(cref); | |
| 2062 | 1053742 | nodeVariability := ComponentRef.nodeVariability(cref); | |
| 2063 | end typeCref; | ||
| 2064 | |||
| 2065 | function typeCref2 | ||
| 2066 | input output ComponentRef cref; | ||
| 2067 | input InstContext.Type context; | ||
| 2068 | input SourceInfo info; | ||
| 2069 | input Boolean firstPart = true; | ||
| 2070 | output Variability subsVariability; | ||
| 2071 | |||
| 2072 | import NFComponentRef.Origin; | ||
| 2073 | protected | ||
| 2074 | InstNode cr_node; | ||
| 2075 | algorithm | ||
| 2076 | (cref, subsVariability) := match cref | ||
| 2077 | local | ||
| 2078 | ComponentRef rest_cr; | ||
| 2079 | Type node_ty; | ||
| 2080 | list<Subscript> subs; | ||
| 2081 | Variability subs_var, rest_var; | ||
| 2082 | Function fn; | ||
| 2083 | |||
| 2084 | case ComponentRef.CREF(origin = Origin.SCOPE) | ||
| 2085 | algorithm | ||
| 2086 | 646583 | cref.ty := InstNode.getType(ComponentRef.node(cref)); | |
| 2087 | 646583 | cref.restCref := typeCref2(cref.restCref, context, info, false); | |
| 2088 | then | ||
| 2089 | (cref, Variability.CONSTANT); | ||
| 2090 | |||
| 2091 | case ComponentRef.CREF() guard InstNode.isComponent(ComponentRef.node(cref)) | ||
| 2092 | algorithm | ||
| 2093 | 1299151 | cr_node := ComponentRef.node(cref); | |
| 2094 | // The context used when typing a component node depends on where the | ||
| 2095 | // component was declared, not where it's used. This can be different to | ||
| 2096 | // the given context, e.g. for package constants used in a function. | ||
| 2097 |
4/4✓ Branch 0 taken 248598 times.
✓ Branch 1 taken 1050553 times.
✓ Branch 3 taken 248586 times.
✓ Branch 4 taken 12 times.
|
1547737 | node_ty := typeComponent(cr_node, InstContext.nodeContext(cr_node, context), typeChildren = firstPart or not InstContext.inDimension(context)); |
| 2098 | |||
| 2099 | 1299151 | (subs, subs_var) := typeSubscripts(cref.subscripts, node_ty, Expression.CREF(node_ty, cref), context, info); | |
| 2100 | 1299148 | (rest_cr, rest_var) := typeCref2(cref.restCref, context, info, false); | |
| 2101 | 1299148 | subsVariability := Prefixes.variabilityMax(subs_var, rest_var); | |
| 2102 | 1299148 | then | |
| 2103 | (ComponentRef.CREF(cref.node, subs, node_ty, cref.origin, rest_cr), subsVariability); | ||
| 2104 | |||
| 2105 | case ComponentRef.CREF() | ||
| 2106 | guard InstNode.isClass(ComponentRef.node(cref)) and firstPart and | ||
| 2107 | InstNode.isFunction(ComponentRef.node(cref)) | ||
| 2108 | algorithm | ||
| 2109 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
|
14 | fn :: _ := Function.typeNodeCache(ComponentRef.node(cref)); |
| 2110 | 28 | cref.ty := Type.FUNCTION(fn, NFType.FunctionType.FUNCTION_REFERENCE); | |
| 2111 | 14 | cref.restCref := typeCref2(cref.restCref, context, info, false); | |
| 2112 | then | ||
| 2113 | (cref, Variability.CONSTANT); | ||
| 2114 | |||
| 2115 | case ComponentRef.CREF() guard InstNode.isClass(ComponentRef.node(cref)) | ||
| 2116 | algorithm | ||
| 2117 | 42306 | cref.ty := InstNode.getType(ComponentRef.node(cref)); | |
| 2118 | then | ||
| 2119 | (cref, Variability.CONSTANT); | ||
| 2120 | |||
| 2121 | case ComponentRef.CREF() guard InstNode.isName(ComponentRef.node(cref)) | ||
| 2122 | algorithm | ||
| 2123 | 58 | (_, subs_var) := typeSubscripts(cref.subscripts, cref.ty, | |
| 2124 | Expression.CREF(cref.ty, cref), context, info, checkSubscripts = false); | ||
| 2125 | 58 | (rest_cr, rest_var) := typeCref2(cref.restCref, context, info, false); | |
| 2126 | 58 | cref.restCref := rest_cr; | |
| 2127 | 58 | subsVariability := Prefixes.variabilityMax(subs_var, rest_var); | |
| 2128 | 58 | then | |
| 2129 | (cref, rest_var); | ||
| 2130 | |||
| 2131 | else (cref, Variability.CONSTANT); | ||
| 2132 | end match; | ||
| 2133 | end typeCref2; | ||
| 2134 | |||
| 2135 | function typeSubscripts | ||
| 2136 | input list<Subscript> subscripts; | ||
| 2137 | input Type crefType; | ||
| 2138 | input Expression subscriptedExp; | ||
| 2139 | input InstContext.Type context; | ||
| 2140 | input SourceInfo info; | ||
| 2141 | input Boolean checkSubscripts = true; | ||
| 2142 | output list<Subscript> typedSubs; | ||
| 2143 | output Variability variability = Variability.CONSTANT; | ||
| 2144 | protected | ||
| 2145 | list<Dimension> dims; | ||
| 2146 | Dimension dim; | ||
| 2147 | Integer next_context, i; | ||
| 2148 | Subscript sub; | ||
| 2149 | Variability var; | ||
| 2150 | algorithm | ||
| 2151 |
2/2✓ Branch 0 taken 1050749 times.
✓ Branch 1 taken 248474 times.
|
1299223 | if listEmpty(subscripts) then |
| 2152 | typedSubs := subscripts; | ||
| 2153 | 1050749 | return; | |
| 2154 | end if; | ||
| 2155 | |||
| 2156 | 248474 | dims := Type.arrayDims(crefType); | |
| 2157 | typedSubs := {}; | ||
| 2158 | 248474 | next_context := InstContext.set(context, NFInstContext.SUBSCRIPT); | |
| 2159 | i := 1; | ||
| 2160 | |||
| 2161 |
3/4✓ Branch 2 taken 248472 times.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2 times.
|
248474 | if listLength(subscripts) > listLength(dims) and checkSubscripts then |
| 2162 | 6 | Error.addSourceMessage(Error.WRONG_NUMBER_OF_SUBSCRIPTS, | |
| 2163 | {Expression.toString(subscriptedExp), String(listLength(subscripts)), String(listLength(dims))}, info); | ||
| 2164 | 2 | fail(); | |
| 2165 | end if; | ||
| 2166 | |||
| 2167 |
2/2✓ Branch 0 taken 295085 times.
✓ Branch 1 taken 248471 times.
|
543556 | for s in subscripts loop |
| 2168 |
1/2✓ Branch 0 taken 295085 times.
✗ Branch 1 not taken.
|
295085 | if checkSubscripts then |
| 2169 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 295085 times.
|
295085 | dim :: dims := dims; |
| 2170 | else | ||
| 2171 | dim := Dimension.UNKNOWN(); | ||
| 2172 | end if; | ||
| 2173 | |||
| 2174 | 295085 | (sub, var) := typeSubscript(s, dim, subscriptedExp, i, next_context, info, checkSubscripts); | |
| 2175 | typedSubs := sub :: typedSubs; | ||
| 2176 | 295084 | variability := Prefixes.variabilityMax(variability, var); | |
| 2177 | 295084 | i := i + 1; | |
| 2178 | |||
| 2179 | // Mark parameter subscripts as structural so that they're evaluated. | ||
| 2180 | // TODO: Ideally this shouldn't be needed, but the old frontend does it and | ||
| 2181 | // the backend relies on it. | ||
| 2182 |
2/2✓ Branch 0 taken 19 times.
✓ Branch 1 taken 295065 times.
|
295084 | if var == Variability.PARAMETER then |
| 2183 | 19 | Structural.markSubscript(sub); | |
| 2184 | end if; | ||
| 2185 | end for; | ||
| 2186 | |||
| 2187 | 248471 | typedSubs := listReverseInPlace(typedSubs); | |
| 2188 | end typeSubscripts; | ||
| 2189 | |||
| 2190 | function typeSubscript | ||
| 2191 | input Subscript subscript; | ||
| 2192 | input Dimension dimension; | ||
| 2193 | input Expression subscriptedExp; | ||
| 2194 | input Integer index; | ||
| 2195 | input InstContext.Type context; | ||
| 2196 | input SourceInfo info; | ||
| 2197 | input Boolean checkSubscript; | ||
| 2198 | output Subscript outSubscript = subscript; | ||
| 2199 | output Variability variability = Variability.CONSTANT; | ||
| 2200 | protected | ||
| 2201 | Expression e = Expression.EMPTY(Type.UNKNOWN()); | ||
| 2202 | Type ty, matched_ty; | ||
| 2203 | algorithm | ||
| 2204 | (ty, variability) := match subscript | ||
| 2205 | // An untyped subscript, type the expression and create a typed subscript. | ||
| 2206 | case Subscript.UNTYPED() | ||
| 2207 | algorithm | ||
| 2208 | 24599 | e := evaluateEnd(subscript.exp, dimension, subscriptedExp, index, context, info); | |
| 2209 | 24599 | (e, ty, variability) := typeExp(e, context, info); | |
| 2210 | |||
| 2211 | // A slice over a resizable parameter keeps its size symbolic. | ||
| 2212 |
5/6✓ Branch 1 taken 5877 times.
✓ Branch 2 taken 18722 times.
✓ Branch 4 taken 5325 times.
✓ Branch 5 taken 552 times.
✓ Branch 7 taken 5325 times.
✗ Branch 8 not taken.
|
24599 | if Type.isArray(ty) and InstContext.inEquation(context) and |
| 2213 | not Expression.contains(e, Expression.isResizableCref) then | ||
| 2214 | 5325 | Structural.markExp(e); | |
| 2215 | 5325 | e := Ceval.tryEvalExp(e); | |
| 2216 | 5325 | ty := Expression.typeOf(e); | |
| 2217 | end if; | ||
| 2218 | |||
| 2219 |
1/2✓ Branch 0 taken 24599 times.
✗ Branch 1 not taken.
|
24599 | if checkSubscript then |
| 2220 | 24599 | (e, matched_ty) := checkSubscriptType(e, Type.arrayElementType(ty), dimension, info); | |
| 2221 | else | ||
| 2222 | ✗ | matched_ty := ty; | |
| 2223 | end if; | ||
| 2224 | |||
| 2225 |
2/2✓ Branch 1 taken 5877 times.
✓ Branch 2 taken 18721 times.
|
24598 | outSubscript := if Type.isArray(ty) then Subscript.SLICE(e) else Subscript.INDEX(e); |
| 2226 | 24598 | then | |
| 2227 | (matched_ty, variability); | ||
| 2228 | |||
| 2229 | // Other subscripts have already been typed, but still need to be type checked. | ||
| 2230 | case Subscript.INDEX(index = e) | ||
| 2231 | algorithm | ||
| 2232 |
1/2✓ Branch 0 taken 230764 times.
✗ Branch 1 not taken.
|
230764 | if checkSubscript then |
| 2233 | 230764 | (e, ty) := checkSubscriptType(e, Expression.typeOf(e), dimension, info); | |
| 2234 | else | ||
| 2235 | ✗ | ty := Expression.typeOf(e); | |
| 2236 | end if; | ||
| 2237 | |||
| 2238 | 230764 | outSubscript := Subscript.INDEX(e); | |
| 2239 | 230764 | then | |
| 2240 | (ty, Expression.variability(e)); | ||
| 2241 | |||
| 2242 | case Subscript.SLICE(slice = e) | ||
| 2243 | algorithm | ||
| 2244 | ✗ | if checkSubscript then | |
| 2245 | ✗ | (e, ty) := checkSubscriptType(e, Type.unliftArray(Expression.typeOf(e)), dimension, info); | |
| 2246 | else | ||
| 2247 | ✗ | ty := Type.unliftArray(Expression.typeOf(e)); | |
| 2248 | end if; | ||
| 2249 | |||
| 2250 | ✗ | outSubscript := Subscript.SLICE(e); | |
| 2251 | ✗ | then | |
| 2252 | (ty, Expression.variability(e)); | ||
| 2253 | |||
| 2254 | 39722 | case Subscript.WHOLE() then (Type.UNKNOWN(), Dimension.variability(dimension)); | |
| 2255 | |||
| 2256 | else | ||
| 2257 | algorithm | ||
| 2258 | ✗ | Error.terminate(getInstanceName() + " got unknown subscript", sourceInfo()); | |
| 2259 | ✗ | then | |
| 2260 | fail(); | ||
| 2261 | end match; | ||
| 2262 | end typeSubscript; | ||
| 2263 | |||
| 2264 | function checkSubscriptType | ||
| 2265 | input output Expression subscriptExp; | ||
| 2266 | input Type subscriptType; | ||
| 2267 | input Dimension dimension; | ||
| 2268 | input SourceInfo info; | ||
| 2269 | output Type outType; | ||
| 2270 | protected | ||
| 2271 | Type expected_ty; | ||
| 2272 | MatchKind mk; | ||
| 2273 | algorithm | ||
| 2274 | 255363 | expected_ty := Dimension.subscriptType(dimension); | |
| 2275 | 255363 | (subscriptExp, outType, mk) := TypeCheck.matchTypes(subscriptType, | |
| 2276 | expected_ty, subscriptExp, NFTypeCheck.ALLOW_UNKNOWN); | ||
| 2277 | |||
| 2278 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 255362 times.
|
255363 | if TypeCheck.isIncompatibleMatch(mk) then |
| 2279 | 4 | Error.addSourceMessage(Error.SUBSCRIPT_TYPE_MISMATCH, | |
| 2280 | {Expression.toString(subscriptExp), Type.toString(subscriptType), Type.toString(expected_ty)}, info); | ||
| 2281 | 1 | fail(); | |
| 2282 | end if; | ||
| 2283 | end checkSubscriptType; | ||
| 2284 | |||
| 2285 | function typeArray | ||
| 2286 | input array<Expression> elements; | ||
| 2287 | input Boolean isLiteral; | ||
| 2288 | input Type ty; | ||
| 2289 | input InstContext.Type context; | ||
| 2290 | input SourceInfo info; | ||
| 2291 | output Expression arrayExp; | ||
| 2292 | output Type arrayType = Type.UNKNOWN(); | ||
| 2293 | output Variability variability = Variability.CONSTANT; | ||
| 2294 | output Purity purity = Purity.PURE; | ||
| 2295 | protected | ||
| 2296 | Expression exp; | ||
| 2297 | list<Expression> expl = {}, expl2 = {}; | ||
| 2298 | Variability var; | ||
| 2299 | Purity pur; | ||
| 2300 | Type ty1 = Type.UNKNOWN(), ty2, ty3; | ||
| 2301 | list<Type> tys = {}; | ||
| 2302 | MatchKind mk; | ||
| 2303 | Integer array_len, idx; | ||
| 2304 | InstContext.Type next_context; | ||
| 2305 | algorithm | ||
| 2306 | 23840 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 2307 | array_len := arrayLength(elements); | ||
| 2308 | |||
| 2309 |
2/2✓ Branch 0 taken 23836 times.
✓ Branch 1 taken 4 times.
|
23840 | if array_len > 0 then |
| 2310 | 23836 | (exp, ty1, variability, purity) := typeExp(arrayGet(elements, 1), next_context, info); | |
| 2311 | expl := exp :: expl; | ||
| 2312 | 23836 | tys := ty1 :: tys; | |
| 2313 | |||
| 2314 |
2/2✓ Branch 0 taken 22458 times.
✓ Branch 1 taken 1378 times.
|
100492 | for i in 2:array_len loop |
| 2315 | 76656 | (exp, ty2, var, pur) := typeExp(arrayGet(elements, i), next_context, info); | |
| 2316 | 76656 | variability := Prefixes.variabilityMax(var, variability); | |
| 2317 | 76656 | purity := Prefixes.purityMin(pur, purity); | |
| 2318 | |||
| 2319 | 76656 | (, ty3, mk) := TypeCheck.matchTypes(ty2, ty1, exp, NFTypeCheck.IGNORE_DIMENSIONS_IN_RECORDS); | |
| 2320 |
2/2✓ Branch 1 taken 896 times.
✓ Branch 2 taken 75760 times.
|
76656 | if TypeCheck.isIncompatibleMatch(mk) then |
| 2321 | // Try the other way around to get the super-type of the array | ||
| 2322 | 896 | (, ty3, mk) := TypeCheck.matchTypes(ty1, ty2, exp, NFTypeCheck.IGNORE_DIMENSIONS_IN_RECORDS); | |
| 2323 |
2/2✓ Branch 1 taken 849 times.
✓ Branch 2 taken 47 times.
|
896 | if TypeCheck.isCompatibleMatch(mk) then |
| 2324 | 849 | ty1 := ty3; | |
| 2325 | end if; | ||
| 2326 | else | ||
| 2327 | 75760 | ty1 := ty3; | |
| 2328 | end if; | ||
| 2329 | expl := exp :: expl; | ||
| 2330 | 76656 | tys := ty2 :: tys; | |
| 2331 | end for; | ||
| 2332 | else | ||
| 2333 | // If the array is empty it probably already has a type, since empty arrays | ||
| 2334 | // can't be created in Modelica without evaluating some expression. So use | ||
| 2335 | // that type instead of an unknown type so we don't lose it. | ||
| 2336 | 4 | ty1 := Type.arrayElementType(ty); | |
| 2337 | end if; | ||
| 2338 | |||
| 2339 | // Give the actual error-messages here after we got the super-type of the array | ||
| 2340 | idx := array_len; | ||
| 2341 |
2/2✓ Branch 0 taken 100492 times.
✓ Branch 1 taken 23840 times.
|
124332 | for e in expl loop |
| 2342 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 100492 times.
|
100492 | ty2::tys := tys; |
| 2343 | 100492 | (exp, , mk) := TypeCheck.matchTypes(ty2, ty1, e, NFTypeCheck.IGNORE_DIMENSIONS_IN_RECORDS); | |
| 2344 | expl2 := exp::expl2; | ||
| 2345 |
2/2✓ Branch 1 taken 98602 times.
✓ Branch 2 taken 1890 times.
|
100492 | if not InstContext.inAnnotation(context) then // forget errors when handling annotations |
| 2346 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 98602 times.
|
98602 | if TypeCheck.isIncompatibleMatch(mk) then |
| 2347 | ✗ | Error.addSourceMessage(Error.NF_ARRAY_TYPE_MISMATCH, {String(idx), Expression.toString(exp), Type.toString(ty2), Type.toString(ty1)}, info); | |
| 2348 | ✗ | fail(); | |
| 2349 | end if; | ||
| 2350 | end if; | ||
| 2351 | 100492 | idx := idx-1; | |
| 2352 | end for; | ||
| 2353 | |||
| 2354 | 23840 | arrayType := Type.liftArrayLeft(ty1, Dimension.fromExpList(expl2)); | |
| 2355 | 23840 | arrayExp := Expression.makeArray(arrayType, listArray(expl2), isLiteral); | |
| 2356 | end typeArray; | ||
| 2357 | |||
| 2358 | function typeMatrix "The array concatenation operator" | ||
| 2359 | input list<list<Expression>> elements; | ||
| 2360 | input InstContext.Type context; | ||
| 2361 | input SourceInfo info; | ||
| 2362 | output Expression arrayExp; | ||
| 2363 | output Type arrayType = Type.UNKNOWN(); | ||
| 2364 | output Variability variability = Variability.CONSTANT; | ||
| 2365 | output Purity purity = Purity.PURE; | ||
| 2366 | protected | ||
| 2367 | Expression exp; | ||
| 2368 | list<Expression> expl = {}, res = {}; | ||
| 2369 | Variability var; | ||
| 2370 | Purity pur; | ||
| 2371 | Type ty = Type.UNKNOWN(); | ||
| 2372 | list<Type> tys = {}, resTys = {}; | ||
| 2373 | Integer n = 2; | ||
| 2374 | InstContext.Type next_context = InstContext.set(context, NFInstContext.SUBEXPRESSION); | ||
| 2375 | algorithm | ||
| 2376 |
2/2✓ Branch 1 taken 2011 times.
✓ Branch 2 taken 163 times.
|
2174 | if listLength(elements) > 1 then |
| 2377 |
2/2✓ Branch 0 taken 6504 times.
✓ Branch 1 taken 2011 times.
|
8515 | for el in elements loop |
| 2378 | 6504 | (exp, ty, var, pur) := typeMatrixComma(el, next_context, info); | |
| 2379 | 6504 | variability := Prefixes.variabilityMax(var, variability); | |
| 2380 | 6504 | purity := Prefixes.purityMin(pur, purity); | |
| 2381 | expl := exp :: expl; | ||
| 2382 | 6504 | tys := ty :: tys; | |
| 2383 | 6504 | n := max(n, Type.dimensionCount(ty)); | |
| 2384 | end for; | ||
| 2385 |
2/2✓ Branch 0 taken 6504 times.
✓ Branch 1 taken 2011 times.
|
8515 | for e in expl loop |
| 2386 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6504 times.
|
6504 | ty::tys := tys; |
| 2387 | 6504 | (e,ty) := Expression.promote(e, ty, n); | |
| 2388 | 6504 | resTys := ty::resTys; | |
| 2389 | res := e::res; | ||
| 2390 | end for; | ||
| 2391 | 2011 | (arrayExp, arrayType) := BuiltinCall.makeCatExp(1, res, resTys, variability, purity, info); | |
| 2392 | else | ||
| 2393 | 163 | (arrayExp, arrayType, variability, purity) := typeMatrixComma(listHead(elements), next_context, info); | |
| 2394 |
2/2✓ Branch 1 taken 154 times.
✓ Branch 2 taken 9 times.
|
163 | if Type.dimensionCount(arrayType) < 2 then |
| 2395 | 9 | (arrayExp, arrayType) := Expression.promote(arrayExp, arrayType, n); | |
| 2396 | end if; | ||
| 2397 | end if; | ||
| 2398 | end typeMatrix; | ||
| 2399 | |||
| 2400 | function typeMatrixComma | ||
| 2401 | input list<Expression> elements; | ||
| 2402 | input InstContext.Type context; | ||
| 2403 | input SourceInfo info; | ||
| 2404 | output Expression arrayExp; | ||
| 2405 | output Type arrayType; | ||
| 2406 | output Variability variability = Variability.CONSTANT; | ||
| 2407 | output Purity purity = Purity.PURE; | ||
| 2408 | protected | ||
| 2409 | Expression exp; | ||
| 2410 | list<Expression> expl = {}, res = {}; | ||
| 2411 | Variability var; | ||
| 2412 | Purity pur; | ||
| 2413 | Type ty = Type.UNKNOWN(), ty1, ty2, ty3; | ||
| 2414 | list<Type> tys = {}, tys2; | ||
| 2415 | Integer n = 2, pos; | ||
| 2416 | TypeCheck.MatchKind mk; | ||
| 2417 | algorithm | ||
| 2418 | 6667 | Error.assertion(not listEmpty(elements), getInstanceName() + " expected non-empty arguments", sourceInfo()); | |
| 2419 |
2/2✓ Branch 1 taken 5806 times.
✓ Branch 2 taken 861 times.
|
6667 | if listLength(elements) > 1 then |
| 2420 |
2/2✓ Branch 0 taken 17511 times.
✓ Branch 1 taken 5806 times.
|
23317 | for e in elements loop |
| 2421 | 17511 | (exp, ty1, var, pur) := typeExp(e, context, info); | |
| 2422 | expl := exp :: expl; | ||
| 2423 |
2/2✓ Branch 1 taken 5806 times.
✓ Branch 2 taken 11705 times.
|
17511 | if Type.isEqual(ty, Type.UNKNOWN()) then |
| 2424 | 5806 | ty := ty1; | |
| 2425 | else | ||
| 2426 | 11705 | (,,ty2,mk) := TypeCheck.matchExpressions(Expression.INTEGER(0), Type.arrayElementType(ty1), Expression.INTEGER(0), Type.arrayElementType(ty)); | |
| 2427 |
1/2✓ Branch 1 taken 11705 times.
✗ Branch 2 not taken.
|
11705 | if TypeCheck.isCompatibleMatch(mk) then |
| 2428 | 11705 | ty := ty2; | |
| 2429 | end if; | ||
| 2430 | end if; | ||
| 2431 | 17511 | tys := ty1 :: tys; | |
| 2432 | 17511 | variability := Prefixes.variabilityMax(variability, var); | |
| 2433 | 17511 | purity := Prefixes.purityMin(purity, pur); | |
| 2434 | 17511 | n := max(n, Type.dimensionCount(ty)); | |
| 2435 | end for; | ||
| 2436 | tys2 := {}; | ||
| 2437 | res := {}; | ||
| 2438 | 5806 | pos := n+1; | |
| 2439 |
2/2✓ Branch 0 taken 17511 times.
✓ Branch 1 taken 5806 times.
|
23317 | for e in expl loop |
| 2440 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17511 times.
|
17511 | ty1::tys := tys; |
| 2441 | 17511 | pos := pos-1; | |
| 2442 |
1/2✓ Branch 1 taken 17511 times.
✗ Branch 2 not taken.
|
17511 | if Type.dimensionCount(ty1) <> n then |
| 2443 | 17511 | (e,ty1) := Expression.promote(e, ty1, n); | |
| 2444 | end if; | ||
| 2445 | 17511 | ty2 := Type.setArrayElementType(ty1, ty); | |
| 2446 | 17511 | (e, ty3, mk) := TypeCheck.matchTypes(ty1, ty2, e); | |
| 2447 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 17511 times.
|
17511 | if TypeCheck.isIncompatibleMatch(mk) then |
| 2448 | ✗ | Error.addSourceMessageAndFail(Error.ARG_TYPE_MISMATCH, {String(pos), "matrix constructor ", "arg", Expression.toString(e), Type.toString(ty1), Type.toString(ty2)}, info); | |
| 2449 | end if; | ||
| 2450 | res := e :: res; | ||
| 2451 | 17511 | tys2 := ty3 :: tys2; | |
| 2452 | end for; | ||
| 2453 | 5806 | (arrayExp, arrayType) := BuiltinCall.makeCatExp(2, res, tys2, variability, purity, info); | |
| 2454 | else | ||
| 2455 | 861 | (arrayExp, arrayType, variability) := typeExp(listHead(elements), context, info); | |
| 2456 | end if; | ||
| 2457 | end typeMatrixComma; | ||
| 2458 | |||
| 2459 | function typeRange | ||
| 2460 | input output Expression rangeExp; | ||
| 2461 | input InstContext.Type context; | ||
| 2462 | input SourceInfo info; | ||
| 2463 | output Type rangeType; | ||
| 2464 | output Variability variability; | ||
| 2465 | output Purity purity; | ||
| 2466 | protected | ||
| 2467 | Expression start_exp, step_exp, stop_exp; | ||
| 2468 | Type start_ty, step_ty, stop_ty; | ||
| 2469 | Option<Expression> ostep_exp; | ||
| 2470 | Option<Type> ostep_ty; | ||
| 2471 | Variability start_var, step_var, stop_var; | ||
| 2472 | Purity start_pur, step_pur, stop_pur; | ||
| 2473 | TypeCheck.MatchKind ty_match; | ||
| 2474 | InstContext.Type next_context = InstContext.set(context, NFInstContext.SUBEXPRESSION); | ||
| 2475 | algorithm | ||
| 2476 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9854 times.
|
9854 | Expression.RANGE(start = start_exp, step = ostep_exp, stop = stop_exp) := rangeExp; |
| 2477 | |||
| 2478 | // Type start and stop. | ||
| 2479 | 9854 | (start_exp, start_ty, start_var, start_pur) := typeExp(start_exp, next_context, info); | |
| 2480 | 9854 | (stop_exp, stop_ty, stop_var, stop_pur) := typeExp(stop_exp, next_context, info); | |
| 2481 | 9854 | variability := Prefixes.variabilityMax(start_var, stop_var); | |
| 2482 | 9854 | purity := Prefixes.purityMin(start_pur, stop_pur); | |
| 2483 | |||
| 2484 | // Type check start and stop. | ||
| 2485 | 9854 | (start_exp, stop_exp, rangeType, ty_match) := | |
| 2486 | TypeCheck.matchExpressions(start_exp, start_ty, stop_exp, stop_ty); | ||
| 2487 | |||
| 2488 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9854 times.
|
9854 | if TypeCheck.isIncompatibleMatch(ty_match) then |
| 2489 | ✗ | printRangeTypeError(start_exp, start_ty, stop_exp, stop_ty, info); | |
| 2490 | end if; | ||
| 2491 | |||
| 2492 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 9854 times.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 9756 times.
|
9854 | if isSome(ostep_exp) then |
| 2493 | // Type step. | ||
| 2494 | 98 | SOME(step_exp) := ostep_exp; | |
| 2495 | 98 | (step_exp, step_ty, step_var, step_pur) := typeExp(step_exp, next_context, info); | |
| 2496 | 98 | variability := Prefixes.variabilityMax(step_var, variability); | |
| 2497 | 98 | purity := Prefixes.purityMin(step_pur, purity); | |
| 2498 | |||
| 2499 | // Type check start and step. | ||
| 2500 | 98 | (start_exp, step_exp, rangeType, ty_match) := | |
| 2501 | TypeCheck.matchExpressions(start_exp, start_ty, step_exp, step_ty); | ||
| 2502 | |||
| 2503 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 97 times.
|
98 | if TypeCheck.isIncompatibleMatch(ty_match) then |
| 2504 | 1 | printRangeTypeError(start_exp, start_ty, step_exp, step_ty, info); | |
| 2505 | end if; | ||
| 2506 | |||
| 2507 | // We've checked start-stop and start-step now, so step-stop must also be | ||
| 2508 | // type compatible. Stop might need to be type cast here though. | ||
| 2509 | 97 | stop_exp := TypeCheck.matchTypes_cast(stop_ty, rangeType, stop_exp); | |
| 2510 | |||
| 2511 | 97 | ostep_exp := SOME(step_exp); | |
| 2512 | 97 | ostep_ty := SOME(step_ty); | |
| 2513 | else | ||
| 2514 | ostep_exp := NONE(); | ||
| 2515 | ostep_ty := NONE(); | ||
| 2516 | end if; | ||
| 2517 | |||
| 2518 | 9853 | rangeType := TypeCheck.getRangeType(start_exp, ostep_exp, stop_exp, rangeType, info); | |
| 2519 | 9851 | rangeExp := Expression.RANGE(rangeType, start_exp, ostep_exp, stop_exp); | |
| 2520 | |||
| 2521 |
4/4✓ Branch 0 taken 540 times.
✓ Branch 1 taken 9311 times.
✓ Branch 3 taken 497 times.
✓ Branch 4 taken 8814 times.
|
9851 | if variability <= Variability.PARAMETER and |
| 2522 | purity == Purity.PURE and | ||
| 2523 | not InstContext.inFunction(context) then | ||
| 2524 | 8814 | Structural.markExp(rangeExp); | |
| 2525 | end if; | ||
| 2526 | end typeRange; | ||
| 2527 | |||
| 2528 | function typeTuple | ||
| 2529 | input list<Expression> elements; | ||
| 2530 | input InstContext.Type context; | ||
| 2531 | input SourceInfo info; | ||
| 2532 | output Expression tupleExp; | ||
| 2533 | output Type tupleType; | ||
| 2534 | output Variability variability; | ||
| 2535 | output Purity purity = Purity.PURE; | ||
| 2536 | protected | ||
| 2537 | list<Expression> expl; | ||
| 2538 | list<Type> tyl; | ||
| 2539 | list<Variability> valr; | ||
| 2540 | InstContext.Type next_context; | ||
| 2541 | algorithm | ||
| 2542 | // Tuples are only allowed on the lhs side of an equality/assignment, | ||
| 2543 | // and only if they are alone and not part of a larger expression. | ||
| 2544 |
3/4✓ Branch 1 taken 954 times.
✓ Branch 2 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 954 times.
|
955 | if not InstContext.onLHS(context) or InstContext.inSubexpression(context) then |
| 2545 | 2 | Error.addSourceMessage(Error.RHS_TUPLE_EXPRESSION, | |
| 2546 | {Expression.toString(Expression.TUPLE(Type.UNKNOWN(), elements))}, info); | ||
| 2547 | 1 | fail(); | |
| 2548 | end if; | ||
| 2549 | |||
| 2550 | 954 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 2551 | 954 | (expl, tyl, valr) := typeExpl(elements, next_context, info); | |
| 2552 | 954 | tupleType := Type.TUPLE(tyl, NONE()); | |
| 2553 | 954 | tupleExp := Expression.TUPLE(tupleType, expl); | |
| 2554 | |||
| 2555 | // Tuples may only contain component references. | ||
| 2556 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 953 times.
|
954 | if not List.all(expl, Expression.isCref) then |
| 2557 | 2 | Error.addSourceMessage(Error.TUPLE_ASSIGN_CREFS_ONLY, | |
| 2558 | {Expression.toString(tupleExp)}, info); | ||
| 2559 | 1 | fail(); | |
| 2560 | end if; | ||
| 2561 | |||
| 2562 |
1/2✓ Branch 0 taken 953 times.
✗ Branch 1 not taken.
|
953 | variability := if listEmpty(valr) then Variability.CONSTANT else listHead(valr); |
| 2563 | end typeTuple; | ||
| 2564 | |||
| 2565 | function printRangeTypeError | ||
| 2566 | input Expression exp1; | ||
| 2567 | input Type ty1; | ||
| 2568 | input Expression exp2; | ||
| 2569 | input Type ty2; | ||
| 2570 | input SourceInfo info; | ||
| 2571 | algorithm | ||
| 2572 | 5 | Error.addSourceMessage(Error.RANGE_TYPE_MISMATCH, | |
| 2573 | {Expression.toString(exp1), Type.toString(ty1), | ||
| 2574 | Expression.toString(exp2), Type.toString(ty2)}, info); | ||
| 2575 | 1 | fail(); | |
| 2576 | end printRangeTypeError; | ||
| 2577 | |||
| 2578 | function typeSize | ||
| 2579 | "Types a size expression. If evaluate is true the size expression is also | ||
| 2580 | evaluated if the dimension is known and the index is a parameter expression, | ||
| 2581 | otherwise a typed size expression is returned." | ||
| 2582 | input output Expression sizeExp; | ||
| 2583 | input InstContext.Type context; | ||
| 2584 | input SourceInfo info; | ||
| 2585 | input Boolean evaluate = true; | ||
| 2586 | output Type sizeType; | ||
| 2587 | output Variability variability; | ||
| 2588 | output Purity purity; | ||
| 2589 | protected | ||
| 2590 | Expression exp, index; | ||
| 2591 | Type exp_ty, index_ty; | ||
| 2592 | TypeCheck.MatchKind ty_match; | ||
| 2593 | Integer iindex; | ||
| 2594 | Dimension dim; | ||
| 2595 | TypingError ty_err; | ||
| 2596 | Option<Expression> oexp; | ||
| 2597 | InstContext.Type next_context = InstContext.set(context, NFInstContext.SUBEXPRESSION); | ||
| 2598 | array<Expression> expl; | ||
| 2599 | algorithm | ||
| 2600 | (sizeExp, sizeType, variability, purity) := match sizeExp | ||
| 2601 | case Expression.SIZE(exp = exp, dimIndex = SOME(index)) | ||
| 2602 | algorithm | ||
| 2603 | 2178 | (index, index_ty, variability, purity) := typeExp(index, next_context, info); | |
| 2604 | |||
| 2605 | // The second argument must be an Integer. | ||
| 2606 | 2178 | (index, _, ty_match) := | |
| 2607 | TypeCheck.matchTypes(index_ty, Type.INTEGER(), index); | ||
| 2608 | |||
| 2609 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2177 times.
|
2178 | if TypeCheck.isIncompatibleMatch(ty_match) then |
| 2610 | 3 | Error.addSourceMessage(Error.ARG_TYPE_MISMATCH, | |
| 2611 | {"2", "size ", "dim", Expression.toString(index), Type.toString(index_ty), "Integer"}, info); | ||
| 2612 | 1 | fail(); | |
| 2613 | end if; | ||
| 2614 | |||
| 2615 |
4/4✓ Branch 0 taken 2175 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2174 times.
✓ Branch 3 taken 1 time.
|
2177 | if variability <= Variability.STRUCTURAL_PARAMETER and purity == Purity.PURE then |
| 2616 | // Evaluate the index if it's a constant. | ||
| 2617 | 2174 | index := Ceval.evalExp(index, NFCeval.noTarget); | |
| 2618 | |||
| 2619 | // TODO: Print an error if the index couldn't be evaluated to an int. | ||
| 2620 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2174 times.
|
2174 | Expression.INTEGER(iindex) := index; |
| 2621 | |||
| 2622 | // Get the iindex'd dimension of the expression. | ||
| 2623 | 2174 | (dim, oexp, ty_err) := typeExpDim(exp, iindex, next_context, info); | |
| 2624 | 2168 | checkSizeTypingError(ty_err, exp, iindex, info); | |
| 2625 | |||
| 2626 |
4/4✓ Branch 1 taken 675 times.
✓ Branch 2 taken 1490 times.
✓ Branch 3 taken 672 times.
✓ Branch 4 taken 3 times.
|
2165 | if Dimension.isKnown(dim) and evaluate then |
| 2627 | // If the dimension size is known, return its size. | ||
| 2628 | 672 | exp := Dimension.sizeExp(dim); | |
| 2629 | else | ||
| 2630 | // If the dimension size is unknown (e.g. in a function) or | ||
| 2631 | // evaluation is disabled, return a size expression instead. | ||
| 2632 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 1493 times.
✓ Branch 2 taken 1431 times.
✓ Branch 3 taken 62 times.
|
1493 | if isSome(oexp) then |
| 2633 | 62 | SOME(exp) := oexp; | |
| 2634 | else | ||
| 2635 | 1431 | exp := typeExp(exp, next_context, info); | |
| 2636 | end if; | ||
| 2637 | |||
| 2638 | 1493 | exp := Expression.SIZE(exp, SOME(index)); | |
| 2639 | end if; | ||
| 2640 | |||
| 2641 |
4/4✓ Branch 1 taken 1506 times.
✓ Branch 2 taken 659 times.
✓ Branch 4 taken 16 times.
✓ Branch 5 taken 1490 times.
|
2165 | if not InstContext.inFunction(context) or Dimension.isKnown(dim) then |
| 2642 | // size is constant outside functions, or for known dimensions inside functions. | ||
| 2643 | 675 | variability := Variability.CONSTANT; | |
| 2644 | else | ||
| 2645 | // size is discrete for : in functions. | ||
| 2646 | 1490 | variability := Variability.DISCRETE; | |
| 2647 | 1490 | purity := Purity.IMPURE; | |
| 2648 | end if; | ||
| 2649 | else | ||
| 2650 | // If the index is not a constant, type the whole expression. | ||
| 2651 | 3 | (exp, exp_ty, _, purity) := typeExp(sizeExp.exp, next_context, info); | |
| 2652 | |||
| 2653 | // Check that it's an array. | ||
| 2654 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if not Type.isArray(exp_ty) then |
| 2655 | ✗ | Error.addSourceMessage(Error.INVALID_ARGUMENT_TYPE_FIRST_ARRAY, {"size"}, info); | |
| 2656 | ✗ | fail(); | |
| 2657 | end if; | ||
| 2658 | |||
| 2659 |
3/4✓ Branch 1 taken 1 time.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
|
3 | if Type.isEmptyArray(exp_ty) and not InstContext.inFunction(context) then |
| 2660 | // If the expression has any dimensions that are 0 it might not be safe to generate | ||
| 2661 | // a size expression with it, since it might be either a variable that's no longer | ||
| 2662 | // present in the flat model or an array expression that doesn't have enough | ||
| 2663 | // dimensions (e.g. Real[0, 2] => {}). In that case make an array with the dimension | ||
| 2664 | // sizes of the expression and index that instead. | ||
| 2665 | 1 | expl := Array.mapList(Type.arrayDims(exp_ty), Dimension.sizeExp); | |
| 2666 | 1 | exp := Expression.makeExpArray(expl, Type.INTEGER()); | |
| 2667 | 2 | exp := Expression.makeSubscriptedExp({Subscript.makeIndex(index)}, exp); | |
| 2668 | else | ||
| 2669 | // Since we don't know which dimension to take the size of, return a size expression. | ||
| 2670 | 2 | exp := Expression.SIZE(exp, SOME(index)); | |
| 2671 | end if; | ||
| 2672 | end if; | ||
| 2673 | 2168 | then | |
| 2674 | (exp, Type.INTEGER(), variability, purity); | ||
| 2675 | |||
| 2676 | case Expression.SIZE() | ||
| 2677 | algorithm | ||
| 2678 | 6 | (exp, exp_ty, _) := typeExp(sizeExp.exp, next_context, info); | |
| 2679 | 6 | sizeType := Type.sizeType(exp_ty); | |
| 2680 | 6 | then | |
| 2681 | (Expression.SIZE(exp, NONE()), sizeType, Variability.PARAMETER, Purity.PURE); | ||
| 2682 | |||
| 2683 | end match; | ||
| 2684 | end typeSize; | ||
| 2685 | |||
| 2686 | function checkSizeTypingError | ||
| 2687 | input TypingError typingError; | ||
| 2688 | input Expression exp; | ||
| 2689 | input Integer index; | ||
| 2690 | input SourceInfo info; | ||
| 2691 | algorithm | ||
| 2692 | () := match typingError | ||
| 2693 | case NO_ERROR() then (); | ||
| 2694 | |||
| 2695 | // The first argument wasn't an array. | ||
| 2696 | case OUT_OF_BOUNDS(0) | ||
| 2697 | algorithm | ||
| 2698 | 1 | Error.addSourceMessage(Error.INVALID_ARGUMENT_TYPE_FIRST_ARRAY, {"size"}, info); | |
| 2699 | 1 | then | |
| 2700 | fail(); | ||
| 2701 | |||
| 2702 | // The index referred to an invalid dimension. | ||
| 2703 | case OUT_OF_BOUNDS() | ||
| 2704 | algorithm | ||
| 2705 | 6 | Error.addSourceMessage(Error.INVALID_SIZE_INDEX, | |
| 2706 | {String(index), Expression.toString(exp), String(typingError.upperBound)}, info); | ||
| 2707 | 2 | then | |
| 2708 | fail(); | ||
| 2709 | end match; | ||
| 2710 | end checkSizeTypingError; | ||
| 2711 | |||
| 2712 | function evaluateEnd | ||
| 2713 | input Expression exp; | ||
| 2714 | input Dimension dim; | ||
| 2715 | input Expression subscriptedExp; | ||
| 2716 | input Integer index; | ||
| 2717 | input InstContext.Type context; | ||
| 2718 | input SourceInfo info; | ||
| 2719 | output Expression outExp; | ||
| 2720 | algorithm | ||
| 2721 | outExp := match exp | ||
| 2722 | local | ||
| 2723 | |||
| 2724 | 26 | case Expression.END() then Dimension.endExp(dim, subscriptedExp, index); | |
| 2725 | |||
| 2726 | // Stop when encountering a cref, any 'end' in a cref expression refers to | ||
| 2727 | // the cref's dimensions and will be evaluated when the cref is typed. | ||
| 2728 | case Expression.CREF() then exp; | ||
| 2729 | |||
| 2730 | 23377 | else Expression.mapShallow(exp, | |
| 2731 | function evaluateEnd(dim = dim, subscriptedExp = subscriptedExp, index = index, info = info, context = context)); | ||
| 2732 | |||
| 2733 | end match; | ||
| 2734 | end evaluateEnd; | ||
| 2735 | |||
| 2736 | function typeIfExpression | ||
| 2737 | input output Expression ifExp; | ||
| 2738 | input InstContext.Type context; | ||
| 2739 | input SourceInfo info; | ||
| 2740 | output Type ty; | ||
| 2741 | output Variability var; | ||
| 2742 | output Purity purity; | ||
| 2743 | protected | ||
| 2744 | Expression cond, tb, fb, tb2, fb2; | ||
| 2745 | InstContext.Type next_context; | ||
| 2746 | Type cond_ty, tb_ty, fb_ty; | ||
| 2747 | Variability cond_var, tb_var, fb_var; | ||
| 2748 | Purity cond_pur, tb_pur, fb_pur; | ||
| 2749 | MatchKind ty_match; | ||
| 2750 | algorithm | ||
| 2751 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14225 times.
|
14225 | Expression.IF(condition = cond, trueBranch = tb, falseBranch = fb) := ifExp; |
| 2752 | 14225 | next_context := InstContext.set(context, NFInstContext.SUBEXPRESSION); | |
| 2753 | |||
| 2754 | 14225 | (cond, cond_ty, cond_var, cond_pur) := typeExp(cond, next_context, info); | |
| 2755 | |||
| 2756 | // The condition must be a scalar boolean. | ||
| 2757 | 14225 | (cond, _, ty_match) := TypeCheck.matchTypes(cond_ty, Type.BOOLEAN(), cond); | |
| 2758 | |||
| 2759 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14225 times.
|
14225 | if TypeCheck.isIncompatibleMatch(ty_match) then |
| 2760 | ✗ | Error.addSourceMessage(Error.IF_CONDITION_TYPE_ERROR, | |
| 2761 | {Expression.toString(cond), Type.toString(cond_ty)}, info); | ||
| 2762 | ✗ | fail(); | |
| 2763 | end if; | ||
| 2764 | |||
| 2765 | 14225 | (tb, tb_ty, tb_var, tb_pur) := typeExp(tb, next_context, info); | |
| 2766 | 14225 | (fb, fb_ty, fb_var, fb_pur) := typeExp(fb, next_context, info); | |
| 2767 | // Type match the branches, using the context for the whole if-expression rather than the branches. | ||
| 2768 | 14225 | (tb2, fb2, ty, ty_match) := TypeCheck.matchIfBranches(tb, tb_ty, fb, fb_ty, context); | |
| 2769 | |||
| 2770 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14225 times.
|
14225 | if TypeCheck.isIncompatibleMatch(ty_match) then |
| 2771 | ✗ | Error.addSourceMessage(Error.TYPE_MISMATCH_IF_EXP, | |
| 2772 | {"", Expression.toString(tb), Type.toString(tb_ty), | ||
| 2773 | Expression.toString(fb), Type.toString(fb_ty)}, info); | ||
| 2774 | ✗ | fail(); | |
| 2775 | end if; | ||
| 2776 | |||
| 2777 | // Evaluate the if-expression if exactly one branch contains a der call, | ||
| 2778 | // since this can affect the number of state variables. | ||
| 2779 |
4/6✓ Branch 6 taken 1041 times.
✓ Branch 7 taken 13184 times.
✓ Branch 9 taken 1041 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 1041 times.
✗ Branch 14 not taken.
|
14225 | if Expression.contains(tb2, function Expression.isCallNamed(name = "der")) <> |
| 2780 | Expression.contains(fb2, function Expression.isCallNamed(name = "der")) and | ||
| 2781 | Flags.getConfigString(Flags.EVALUATE_STRUCTURAL_PARAMETERS) == "all" then | ||
| 2782 | 1041 | Structural.markExp(cond); | |
| 2783 | end if; | ||
| 2784 | |||
| 2785 | 14225 | ifExp := Expression.IF(ty, cond, tb2, fb2); | |
| 2786 | 14225 | var := Prefixes.variabilityMax(cond_var, Prefixes.variabilityMax(tb_var, fb_var)); | |
| 2787 | 14225 | purity := Prefixes.purityMin(cond_pur, Prefixes.purityMin(tb_pur, fb_pur)); | |
| 2788 | end typeIfExpression; | ||
| 2789 | |||
| 2790 | function typeClassSections | ||
| 2791 | input InstNode classNode; | ||
| 2792 | input InstContext.Type context; | ||
| 2793 | protected | ||
| 2794 | Class cls, typed_cls; | ||
| 2795 | array<InstNode> components; | ||
| 2796 | Sections sections; | ||
| 2797 | SourceInfo info; | ||
| 2798 | InstContext.Type initial_context; | ||
| 2799 | algorithm | ||
| 2800 | 263285 | cls := InstNode.getClass(classNode); | |
| 2801 | |||
| 2802 | () := match cls | ||
| 2803 | case Class.INSTANCED_CLASS() guard Type.isBasic(Type.arrayElementType(cls.ty)) then (); | ||
| 2804 | |||
| 2805 | case Class.INSTANCED_CLASS(elements = ClassTree.FLAT_TREE(components = components), | ||
| 2806 | sections = sections) | ||
| 2807 | algorithm | ||
| 2808 | sections := match sections | ||
| 2809 | case Sections.SECTIONS() | ||
| 2810 | algorithm | ||
| 2811 | 9040 | initial_context := InstContext.set(context, NFInstContext.INITIAL); | |
| 2812 | 9040 | then | |
| 2813 | Sections.map(sections, | ||
| 2814 | function typeEquation(context = InstContext.set(context, NFInstContext.EQUATION)), | ||
| 2815 | function typeAlgorithm(context = InstContext.set(context, NFInstContext.ALGORITHM)), | ||
| 2816 | function typeEquation(context = InstContext.set(initial_context, NFInstContext.EQUATION)), | ||
| 2817 | function typeAlgorithm(context = InstContext.set(initial_context, NFInstContext.ALGORITHM))); | ||
| 2818 | |||
| 2819 | case Sections.EXTERNAL() | ||
| 2820 | algorithm | ||
| 2821 | ✗ | Error.addSourceMessage(Error.TRANS_VIOLATION, | |
| 2822 | {InstNode.name(classNode), Restriction.toString(cls.restriction), "external declaration"}, | ||
| 2823 | InstNode.info(classNode)); | ||
| 2824 | ✗ | then | |
| 2825 | fail(); | ||
| 2826 | |||
| 2827 | else sections; | ||
| 2828 | end match; | ||
| 2829 | |||
| 2830 | 37208 | typed_cls := Class.setSections(sections, cls); | |
| 2831 | |||
| 2832 |
2/2✓ Branch 1 taken 261380 times.
✓ Branch 2 taken 37208 times.
|
298588 | for c in components loop |
| 2833 | 261380 | typeComponentSections(InstNode.resolveOuter(c), context); | |
| 2834 | end for; | ||
| 2835 | |||
| 2836 | 37208 | InstNode.updateClass(typed_cls, classNode); | |
| 2837 | then | ||
| 2838 | (); | ||
| 2839 | |||
| 2840 | case Class.INSTANCED_BUILTIN() then (); | ||
| 2841 | |||
| 2842 | case Class.TYPED_DERIVED() | ||
| 2843 | algorithm | ||
| 2844 | 4724 | typeClassSections(cls.baseClass, context); | |
| 2845 | then | ||
| 2846 | (); | ||
| 2847 | |||
| 2848 | else | ||
| 2849 | algorithm | ||
| 2850 | ✗ | Error.terminate(getInstanceName() + " got uninstantiated class " + InstNode.name(classNode), sourceInfo()); | |
| 2851 | ✗ | then | |
| 2852 | fail(); | ||
| 2853 | end match; | ||
| 2854 | end typeClassSections; | ||
| 2855 | |||
| 2856 | function typeFunctionSections | ||
| 2857 | input InstNode classNode; | ||
| 2858 | input InstContext.Type context; | ||
| 2859 | protected | ||
| 2860 | Class cls, typed_cls; | ||
| 2861 | Sections sections; | ||
| 2862 | SourceInfo info; | ||
| 2863 | Algorithm alg; | ||
| 2864 | algorithm | ||
| 2865 | 26155 | cls := InstNode.getClass(classNode); | |
| 2866 | |||
| 2867 | () := match cls | ||
| 2868 | case Class.INSTANCED_CLASS(sections = sections) | ||
| 2869 | algorithm | ||
| 2870 | sections := match sections | ||
| 2871 | case Sections.SECTIONS({}, {}, {alg}, {}) | ||
| 2872 | algorithm | ||
| 2873 | 31509 | sections.algorithms := {typeAlgorithm(alg, InstContext.set(context, NFInstContext.ALGORITHM))}; | |
| 2874 | then | ||
| 2875 | sections; | ||
| 2876 | |||
| 2877 | case Sections.SECTIONS() | ||
| 2878 | algorithm | ||
| 2879 | 2 | Error.addSourceMessage(Error.MULTIPLE_SECTIONS_IN_FUNCTION, | |
| 2880 | {InstNode.name(classNode)}, InstNode.info(classNode)); | ||
| 2881 | 1 | then | |
| 2882 | fail(); | ||
| 2883 | |||
| 2884 | case Sections.EXTERNAL(explicit = true) | ||
| 2885 | algorithm | ||
| 2886 | 1232 | info := InstNode.info(classNode); | |
| 2887 |
4/4✓ Branch 0 taken 2824 times.
✓ Branch 1 taken 1231 times.
✓ Branch 2 taken 2824 times.
✓ Branch 3 taken 1231 times.
|
5286 | sections.args := list(typeExternalArg(arg, info, classNode) for arg in sections.args); |
| 2888 | 1231 | sections.outputRef := typeCref(sections.outputRef, context, info); | |
| 2889 | 1231 | checkExternalCallResult(sections.outputRef, info); | |
| 2890 | then | ||
| 2891 | sections; | ||
| 2892 | |||
| 2893 | case Sections.EXTERNAL() | ||
| 2894 | 4735 | then makeDefaultExternalCall(sections, classNode); | |
| 2895 | |||
| 2896 | else sections; | ||
| 2897 | end match; | ||
| 2898 | |||
| 2899 | 26148 | typed_cls := Class.setSections(sections, cls); | |
| 2900 | 26148 | InstNode.updateClass(typed_cls, classNode); | |
| 2901 | then | ||
| 2902 | (); | ||
| 2903 | |||
| 2904 | case Class.TYPED_DERIVED() | ||
| 2905 | algorithm | ||
| 2906 | 1 | typeFunctionSections(cls.baseClass, context); | |
| 2907 | then | ||
| 2908 | (); | ||
| 2909 | |||
| 2910 | else | ||
| 2911 | algorithm | ||
| 2912 | ✗ | Error.terminate(getInstanceName() + " got uninstantiated class " + InstNode.name(classNode), sourceInfo()); | |
| 2913 | ✗ | then | |
| 2914 | fail(); | ||
| 2915 | end match; | ||
| 2916 | end typeFunctionSections; | ||
| 2917 | |||
| 2918 | function typeExternalArg | ||
| 2919 | input Expression arg; | ||
| 2920 | input SourceInfo info; | ||
| 2921 | input InstNode node; | ||
| 2922 | output Expression outArg; | ||
| 2923 | protected | ||
| 2924 | Type ty; | ||
| 2925 | Variability var; | ||
| 2926 | Expression index; | ||
| 2927 | algorithm | ||
| 2928 | outArg := match arg | ||
| 2929 | case Expression.SIZE(dimIndex = SOME(_)) | ||
| 2930 | algorithm | ||
| 2931 | 150 | outArg := typeSize(arg, NFInstContext.FUNCTION, info, evaluate = false); | |
| 2932 |
3/6✗ Branch 0 not taken.
✓ Branch 1 taken 150 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 150 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 150 times.
|
150 | Expression.SIZE(dimIndex = SOME(index)) := outArg; |
| 2933 | |||
| 2934 | // Size expression must have a constant dimension index. | ||
| 2935 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 149 times.
|
150 | if not Expression.isInteger(index) then |
| 2936 | 2 | Error.addSourceMessage(Error.EXTERNAL_ARG_NONCONSTANT_SIZE_INDEX, | |
| 2937 | {Expression.toString(arg)}, info); | ||
| 2938 | 1 | fail(); | |
| 2939 | end if; | ||
| 2940 | then | ||
| 2941 | outArg; | ||
| 2942 | |||
| 2943 | else | ||
| 2944 | algorithm | ||
| 2945 | 2674 | (outArg, ty, var) := typeExp(arg, NFInstContext.FUNCTION, info); | |
| 2946 | 2674 | Call.updateExternalRecordArgsInType(ty); | |
| 2947 | then | ||
| 2948 | match arg | ||
| 2949 | // All kinds of crefs are allowed. | ||
| 2950 | case Expression.CREF() then outArg; | ||
| 2951 | else | ||
| 2952 | algorithm | ||
| 2953 | // The only other kind of expression that's allowed is scalar constants. | ||
| 2954 |
2/4✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
|
7 | if Type.isScalarBuiltin(ty) and var == Variability.CONSTANT then |
| 2955 | 7 | outArg := Ceval.evalExp(outArg, Ceval.EvalTarget.new(info, NFInstContext.FUNCTION)); | |
| 2956 | else | ||
| 2957 | ✗ | Error.addSourceMessage(Error.EXTERNAL_ARG_WRONG_EXP, | |
| 2958 | {Expression.toString(outArg)}, info); | ||
| 2959 | ✗ | fail(); | |
| 2960 | end if; | ||
| 2961 | then | ||
| 2962 | outArg; | ||
| 2963 | end match; | ||
| 2964 | end match; | ||
| 2965 | end typeExternalArg; | ||
| 2966 | |||
| 2967 | function makeDefaultExternalCall | ||
| 2968 | "Constructs a default external call for an external function. If only one | ||
| 2969 | output exists a call 'output = func(input1, input2, ...)' is generated, | ||
| 2970 | otherwise a call 'func(param1, param2, ...)' is generated from the function's | ||
| 2971 | formal parameters and local variables." | ||
| 2972 | input output Sections extDecl; | ||
| 2973 | input InstNode fnNode; | ||
| 2974 | algorithm | ||
| 2975 | extDecl := match extDecl | ||
| 2976 | local | ||
| 2977 | list<Expression> args; | ||
| 2978 | Function fn; | ||
| 2979 | Boolean single_output; | ||
| 2980 | array<InstNode> comps; | ||
| 2981 | Component comp; | ||
| 2982 | Type ty; | ||
| 2983 | InstNode node; | ||
| 2984 | NFInstNode.NodeHandle out_cell; | ||
| 2985 | Expression exp; | ||
| 2986 | |||
| 2987 | case Sections.EXTERNAL() | ||
| 2988 | algorithm | ||
| 2989 | // An explicit function call isn't needed for builtin calls. | ||
| 2990 |
3/4✓ Branch 0 taken 4730 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 4730 times.
✗ Branch 4 not taken.
|
4735 | if extDecl.language == "builtin" then |
| 2991 | 4730 | return; | |
| 2992 | end if; | ||
| 2993 | |||
| 2994 | // Fetch the cached function. | ||
| 2995 |
3/6✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5 times.
|
5 | CachedData.FUNCTION(funcs = {fn}) := InstNode.getFuncCache(fnNode); |
| 2996 | // Check whether we have a single output or not. | ||
| 2997 | 5 | single_output := listLength(fn.outputs) == 1; | |
| 2998 | |||
| 2999 | // When there's a single array output we can't generate a call on the | ||
| 3000 | // 'output = func(inputs)' form, so print a warning and treat is as | ||
| 3001 | // though it's not a single output. | ||
| 3002 |
4/4✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 3 times.
|
5 | if single_output and Type.isArray(Function.returnType(fn)) then |
| 3003 | single_output := false; | ||
| 3004 | 1 | Error.addSourceMessage(Error.EXT_FN_SINGLE_RETURN_ARRAY, | |
| 3005 | {extDecl.language}, InstNode.info(fnNode)); | ||
| 3006 | end if; | ||
| 3007 | |||
| 3008 | // If we have a single output, set the external declaration's output to | ||
| 3009 | // be a reference to the function's output. Otherwise leave it as empty. | ||
| 3010 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 time.
|
4 | if single_output then |
| 3011 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
3 | {out_cell} := fn.outputs; |
| 3012 | 3 | node := InstNode.fromHandle(out_cell); | |
| 3013 | 3 | ty := InstNode.getType(node); | |
| 3014 | 3 | extDecl.outputRef := ComponentRef.fromNode(node, ty); | |
| 3015 | end if; | ||
| 3016 | |||
| 3017 | // Generate function arguments from the function's components. | ||
| 3018 | 5 | comps := ClassTree.getComponents(Class.classTree(InstNode.getClass(InstNode.fromHandle(fn.node)))); | |
| 3019 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if arrayLength(comps) > 0 then |
| 3020 | args := {}; | ||
| 3021 |
2/2✓ Branch 1 taken 13 times.
✓ Branch 2 taken 5 times.
|
18 | for c in comps loop |
| 3022 | 13 | comp := InstNode.component(c); | |
| 3023 | |||
| 3024 | // Skip outputs if there's only a single output. | ||
| 3025 |
4/4✓ Branch 0 taken 8 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 3 times.
|
13 | if not single_output or Component.direction(comp) <> Direction.OUTPUT then |
| 3026 | // Generate a cref for the component and add it to the list of arguments. | ||
| 3027 | 10 | ty := Component.getType(comp); | |
| 3028 | 10 | exp := Expression.CREF(ty, ComponentRef.fromNode(c, ty)); | |
| 3029 | args := exp :: args; | ||
| 3030 | |||
| 3031 | // If the component is an array, generate a size expression for | ||
| 3032 | // each dimension too. | ||
| 3033 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 9 times.
|
11 | for i in 1:Type.dimensionCount(ty) loop |
| 3034 | 2 | args := Expression.SIZE(exp, SOME(Expression.INTEGER(i))) :: args; | |
| 3035 | end for; | ||
| 3036 | end if; | ||
| 3037 | end for; | ||
| 3038 | |||
| 3039 | 5 | extDecl.args := listReverse(args); | |
| 3040 | end if; | ||
| 3041 | then | ||
| 3042 | extDecl; | ||
| 3043 | |||
| 3044 | end match; | ||
| 3045 | end makeDefaultExternalCall; | ||
| 3046 | |||
| 3047 | function checkExternalCallResult | ||
| 3048 | input ComponentRef result; | ||
| 3049 | input SourceInfo info; | ||
| 3050 | protected | ||
| 3051 | Type ty; | ||
| 3052 | algorithm | ||
| 3053 |
2/2✓ Branch 1 taken 142 times.
✓ Branch 2 taken 1089 times.
|
1231 | if not ComponentRef.isCref(result) then |
| 3054 | 142 | return; | |
| 3055 | end if; | ||
| 3056 | |||
| 3057 | 1089 | ty := ComponentRef.nodeType(result); | |
| 3058 | |||
| 3059 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1088 times.
|
1089 | if Type.isArray(ty) then |
| 3060 | 2 | Error.addSourceMessage(Error.EXTERNAL_FUNCTION_RESULT_ARRAY_TYPE, | |
| 3061 | {Type.toString(ty)}, info); | ||
| 3062 | 1 | fail(); | |
| 3063 | end if; | ||
| 3064 | |||
| 3065 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1088 times.
|
1088 | if ComponentRef.variability(result) < Variability.DISCRETE then |
| 3066 | ✗ | Error.addSourceMessage(Error.EXTERNAL_FUNCTION_RESULT_NOT_VAR, {}, info); | |
| 3067 | ✗ | fail(); | |
| 3068 | end if; | ||
| 3069 | end checkExternalCallResult; | ||
| 3070 | |||
| 3071 | function typeComponentSections | ||
| 3072 | input InstNode component; | ||
| 3073 | input InstContext.Type context; | ||
| 3074 | protected | ||
| 3075 | Component comp; | ||
| 3076 | algorithm | ||
| 3077 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 261375 times.
|
261380 | if InstNode.isEmpty(component) then |
| 3078 | 5 | return; | |
| 3079 | end if; | ||
| 3080 | |||
| 3081 | 261375 | comp := InstNode.component(component); | |
| 3082 | |||
| 3083 |
4/4✓ Branch 1 taken 258435 times.
✓ Branch 2 taken 2940 times.
✓ Branch 4 taken 1528 times.
✓ Branch 5 taken 256907 times.
|
261375 | if Component.isDeleted(comp) or InstNode.isOnlyOuter(component) then |
| 3084 | 4468 | return; | |
| 3085 | end if; | ||
| 3086 | |||
| 3087 | () := match comp | ||
| 3088 | case Component.COMPONENT() | ||
| 3089 | guard comp.state >= ComponentState.TypeChecked | ||
| 3090 | algorithm | ||
| 3091 | 256907 | typeClassSections(comp.classInst, context); | |
| 3092 | then | ||
| 3093 | (); | ||
| 3094 | |||
| 3095 | else | ||
| 3096 | algorithm | ||
| 3097 | ✗ | Error.terminate(getInstanceName() + " got uninstantiated component " + InstNode.name(component), sourceInfo()); | |
| 3098 | ✗ | then | |
| 3099 | fail(); | ||
| 3100 | |||
| 3101 | end match; | ||
| 3102 | end typeComponentSections; | ||
| 3103 | |||
| 3104 | function typeEquation | ||
| 3105 | input output Equation eq; | ||
| 3106 | input InstContext.Type context; | ||
| 3107 | algorithm | ||
| 3108 | eq := match eq | ||
| 3109 | local | ||
| 3110 | Expression e1, e2, e3; | ||
| 3111 | list<Equation> body; | ||
| 3112 | InstNode iterator; | ||
| 3113 | Integer next_context; | ||
| 3114 | SourceInfo info; | ||
| 3115 | Variability var; | ||
| 3116 | |||
| 3117 | 57444 | case Equation.EQUALITY() then typeEqualityEquation(eq.lhs, eq.rhs, context, eq.scope, eq.source); | |
| 3118 | 13136 | case Equation.CONNECT() then typeConnect(eq.lhs, eq.rhs, context, eq.scope, eq.source); | |
| 3119 | 1558 | case Equation.FOR() then typeForEquation(eq, context); | |
| 3120 | 6903 | case Equation.IF() then typeIfEquation(eq.branches, context, eq.scope, eq.source); | |
| 3121 | 359 | case Equation.WHEN() then typeWhenEquation(eq.branches, context, eq.scope, eq.source); | |
| 3122 | |||
| 3123 | case Equation.ASSERT() | ||
| 3124 | algorithm | ||
| 3125 | 3523 | info := ElementSource.getInfo(eq.source); | |
| 3126 | 3523 | (e1, e2, e3) := typeAssert(eq.condition, eq.message, eq.level, context, info); | |
| 3127 | 3521 | then | |
| 3128 | Equation.ASSERT(e1, e2, e3, eq.scope, eq.source); | ||
| 3129 | |||
| 3130 | case Equation.TERMINATE() | ||
| 3131 | algorithm | ||
| 3132 | 9 | info := ElementSource.getInfo(eq.source); | |
| 3133 | 9 | e1 := typeOperatorArg(eq.message, Type.STRING(), context, "terminate", "message", 1, info); | |
| 3134 | 9 | then | |
| 3135 | Equation.TERMINATE(e1, eq.scope, eq.source); | ||
| 3136 | |||
| 3137 | case Equation.REINIT() | ||
| 3138 | algorithm | ||
| 3139 | 43 | (e1, e2) := typeReinit(eq.cref, eq.reinitExp, context, eq.source); | |
| 3140 | 40 | then | |
| 3141 | Equation.REINIT(e1, e2, eq.scope, eq.source); | ||
| 3142 | |||
| 3143 | case Equation.NORETCALL() | ||
| 3144 | algorithm | ||
| 3145 | 1316 | e1 := typeExp(eq.exp, context, ElementSource.getInfo(eq.source)); | |
| 3146 | 1315 | then | |
| 3147 | Equation.NORETCALL(e1, eq.scope, eq.source); | ||
| 3148 | |||
| 3149 | else eq; | ||
| 3150 | end match; | ||
| 3151 | end typeEquation; | ||
| 3152 | |||
| 3153 | function typeConnect | ||
| 3154 | input Expression lhsConn; | ||
| 3155 | input Expression rhsConn; | ||
| 3156 | input InstContext.Type context; | ||
| 3157 | input NFInstNode.ScopeRef scope; | ||
| 3158 | input DAE.ElementSource source; | ||
| 3159 | output Equation connEq; | ||
| 3160 | protected | ||
| 3161 | Expression lhs, rhs; | ||
| 3162 | Type lhs_ty, rhs_ty; | ||
| 3163 | MatchKind mk; | ||
| 3164 | InstContext.Type next_context; | ||
| 3165 | SourceInfo info; | ||
| 3166 | Boolean lhs_deleted, rhs_deleted; | ||
| 3167 | algorithm | ||
| 3168 | 13136 | info := ElementSource.getInfo(source); | |
| 3169 | |||
| 3170 | // Connections may not be used in if-equations or for-equations unless the | ||
| 3171 | // conditions are evaluable expressions. | ||
| 3172 | // TODO: Also check for cardinality etc. as per 8.3.3. | ||
| 3173 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 13136 times.
|
13136 | if InstContext.inNonexpandable(context) then |
| 3174 | ✗ | Error.addSourceMessage(Error.IN_NON_EVALUABLE_IF_OR_FOR, {"connect"}, info); | |
| 3175 | ✗ | fail(); | |
| 3176 | end if; | ||
| 3177 | |||
| 3178 | 13136 | next_context := InstContext.set(context, NFInstContext.CONNECT); | |
| 3179 | 13136 | (lhs, lhs_ty, lhs_deleted) := typeConnector(lhsConn, next_context, info); | |
| 3180 | 13131 | (rhs, rhs_ty, rhs_deleted) := typeConnector(rhsConn, next_context, info); | |
| 3181 | |||
| 3182 | // Check that the connectors have matching types, but only if they're not expandable. | ||
| 3183 | // Expandable connectors can only be type checked after they've been augmented during | ||
| 3184 | // the connection handling. | ||
| 3185 |
7/8✓ Branch 0 taken 12300 times.
✓ Branch 1 taken 831 times.
✓ Branch 2 taken 11944 times.
✓ Branch 3 taken 356 times.
✓ Branch 6 taken 11934 times.
✓ Branch 7 taken 10 times.
✓ Branch 10 taken 11934 times.
✗ Branch 11 not taken.
|
13131 | if not (lhs_deleted or rhs_deleted) and |
| 3186 | not (Type.isExpandableConnector(Type.arrayElementType(lhs_ty)) or | ||
| 3187 | Type.isExpandableConnector(Type.arrayElementType(rhs_ty))) then | ||
| 3188 | 11934 | (lhs, rhs, _, mk) := TypeCheck.matchExpressions(lhs, lhs_ty, rhs, rhs_ty, NFTypeCheck.ALLOW_UNKNOWN); | |
| 3189 | |||
| 3190 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 11933 times.
|
11934 | if TypeCheck.isIncompatibleMatch(mk) then |
| 3191 | // TODO: Better error message. | ||
| 3192 | 3 | Error.addSourceMessage(Error.CONNECT_TYPE_MISMATCH, | |
| 3193 | {Expression.toString(lhsConn), Expression.toString(rhsConn)}, info); | ||
| 3194 | 1 | fail(); | |
| 3195 | end if; | ||
| 3196 | end if; | ||
| 3197 | |||
| 3198 | 13130 | connEq := Equation.CONNECT(lhs, rhs, scope, source); | |
| 3199 | end typeConnect; | ||
| 3200 | |||
| 3201 | function typeConnector | ||
| 3202 | input output Expression connExp; | ||
| 3203 | input InstContext.Type context; | ||
| 3204 | input SourceInfo info; | ||
| 3205 | output Type ty; | ||
| 3206 | output Boolean deleted; | ||
| 3207 | algorithm | ||
| 3208 | 26267 | (connExp, ty, _) := typeExp(connExp, context, info); | |
| 3209 | 26267 | deleted := checkConnector(connExp, info); | |
| 3210 | end typeConnector; | ||
| 3211 | |||
| 3212 | function checkConnector | ||
| 3213 | input Expression connExp; | ||
| 3214 | input SourceInfo info; | ||
| 3215 | output Boolean deleted; | ||
| 3216 | protected | ||
| 3217 | ComponentRef cr; | ||
| 3218 | list<Subscript> subs; | ||
| 3219 | algorithm | ||
| 3220 | () := match connExp | ||
| 3221 | case Expression.CREF(cref = cr as ComponentRef.CREF(origin = Origin.CREF)) | ||
| 3222 | algorithm | ||
| 3223 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 3 taken 26264 times.
|
26267 | if not InstNode.isConnector(ComponentRef.node(cr)) then |
| 3224 | 6 | Error.addSourceMessageAndFail(Error.INVALID_CONNECTOR_TYPE, | |
| 3225 | {ComponentRef.toString(cr)}, info); | ||
| 3226 | end if; | ||
| 3227 | |||
| 3228 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 26263 times.
|
26264 | if not checkConnectorForm(cr) then |
| 3229 | 2 | Error.addSourceMessageAndFail(Error.INVALID_CONNECTOR_FORM, | |
| 3230 | {ComponentRef.toString(cr)}, info); | ||
| 3231 | end if; | ||
| 3232 | |||
| 3233 | // subscripts that depend on resizable parameters are allowed | ||
| 3234 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 26262 times.
|
26263 | if ComponentRef.subscriptsVariability(cr) > Variability.NON_STRUCTURAL_PARAMETER then |
| 3235 | 1 | subs := ComponentRef.subscriptsAllFlat(cr); | |
| 3236 |
1/2✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
|
1 | for sub in subs loop |
| 3237 |
1/2✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
|
1 | if Subscript.variability(sub) > Variability.NON_STRUCTURAL_PARAMETER then |
| 3238 | 3 | Error.addSourceMessage(Error.CONNECTOR_NON_PARAMETER_SUBSCRIPT, | |
| 3239 | {Expression.toString(connExp), Subscript.toString(sub)}, info); | ||
| 3240 | 1 | fail(); | |
| 3241 | end if; | ||
| 3242 | end for; | ||
| 3243 | end if; | ||
| 3244 | |||
| 3245 | 26262 | deleted := ComponentRef.isDeleted(cr); | |
| 3246 | then | ||
| 3247 | (); | ||
| 3248 | |||
| 3249 | else | ||
| 3250 | algorithm | ||
| 3251 | ✗ | Error.addSourceMessage(Error.INVALID_CONNECTOR_TYPE, | |
| 3252 | {Expression.toString(connExp)}, info); | ||
| 3253 | ✗ | then | |
| 3254 | fail(); | ||
| 3255 | end match; | ||
| 3256 | end checkConnector; | ||
| 3257 | |||
| 3258 | function checkConnectorForm | ||
| 3259 | "Helper function for checkConnector. Checks that a connector cref uses the | ||
| 3260 | correct form, i.e. either c1.c2...cn or m.c." | ||
| 3261 | input ComponentRef cref; | ||
| 3262 | input Boolean isConnector = true; | ||
| 3263 | output Boolean valid; | ||
| 3264 | algorithm | ||
| 3265 | valid := match cref | ||
| 3266 | // The only part of the connector reference allowed to not be a | ||
| 3267 | // non-connector is the very last part. | ||
| 3268 | case ComponentRef.CREF(origin = Origin.CREF) | ||
| 3269 |
2/2✓ Branch 0 taken 45672 times.
✓ Branch 1 taken 1 time.
|
45673 | then if isConnector then |
| 3270 | checkConnectorForm(cref.restCref, InstNode.isConnector(ComponentRef.node(cref))) else false; | ||
| 3271 | |||
| 3272 | else true; | ||
| 3273 | end match; | ||
| 3274 | end checkConnectorForm; | ||
| 3275 | |||
| 3276 | function checkLhsInWhen | ||
| 3277 | input Expression exp; | ||
| 3278 | output Boolean isValid; | ||
| 3279 | algorithm | ||
| 3280 | isValid := match exp | ||
| 3281 | case Expression.CREF() then true; | ||
| 3282 | case Expression.TUPLE() | ||
| 3283 | algorithm | ||
| 3284 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18 | for e in exp.elements loop |
| 3285 | 12 | checkLhsInWhen(e); | |
| 3286 | end for; | ||
| 3287 | then | ||
| 3288 | true; | ||
| 3289 | else false; | ||
| 3290 | end match; | ||
| 3291 | end checkLhsInWhen; | ||
| 3292 | |||
| 3293 | function typeAssert | ||
| 3294 | input output Expression condition; | ||
| 3295 | input output Expression message; | ||
| 3296 | input output Expression level; | ||
| 3297 | input InstContext.Type context; | ||
| 3298 | input SourceInfo info; | ||
| 3299 | protected | ||
| 3300 | InstContext.Type next_context; | ||
| 3301 | Variability level_var; | ||
| 3302 | algorithm | ||
| 3303 | 6178 | next_context := InstContext.set(context, NFInstContext.ASSERT); | |
| 3304 | |||
| 3305 | 6178 | condition := typeOperatorArg(condition, Type.BOOLEAN(), | |
| 3306 | InstContext.set(next_context, NFInstContext.CONDITION), "assert", "condition", 1, info); | ||
| 3307 | 6178 | message := typeOperatorArg(message, Type.STRING(), | |
| 3308 | next_context, "assert", "message", 2, info); | ||
| 3309 | 6177 | (level, level_var) := typeOperatorArg(level, NFBuiltin.ASSERTIONLEVEL_TYPE, | |
| 3310 | next_context, "assert", "level", 3, info); | ||
| 3311 | |||
| 3312 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6176 times.
|
6177 | if level_var > Variability.PARAMETER then |
| 3313 | 3 | Error.addSourceMessage(Error.FUNCTION_SLOT_VARIABILITY, | |
| 3314 | {"level", Expression.toString(level), "assert", | ||
| 3315 | Prefixes.variabilityString(level_var), "parameter"}, info); | ||
| 3316 | 1 | fail(); | |
| 3317 | end if; | ||
| 3318 | |||
| 3319 | 6176 | Structural.markExp(level); | |
| 3320 | end typeAssert; | ||
| 3321 | |||
| 3322 | function typeAlgorithm | ||
| 3323 | input output Algorithm alg; | ||
| 3324 | input InstContext.Type context; | ||
| 3325 | algorithm | ||
| 3326 |
4/4✓ Branch 0 taken 70634 times.
✓ Branch 1 taken 15947 times.
✓ Branch 2 taken 70634 times.
✓ Branch 3 taken 15947 times.
|
86581 | alg.statements := list(typeStatement(s, context) for s in alg.statements); |
| 3327 | end typeAlgorithm; | ||
| 3328 | |||
| 3329 | function typeStatements | ||
| 3330 | input output list<Statement> alg; | ||
| 3331 | input InstContext.Type context; | ||
| 3332 | algorithm | ||
| 3333 |
4/4✓ Branch 0 taken 888 times.
✓ Branch 1 taken 624 times.
✓ Branch 2 taken 888 times.
✓ Branch 3 taken 624 times.
|
1512 | alg := list(typeStatement(stmt, context) for stmt in alg); |
| 3334 | end typeStatements; | ||
| 3335 | |||
| 3336 | function typeStatement | ||
| 3337 | input output Statement st; | ||
| 3338 | input InstContext.Type context; | ||
| 3339 | algorithm | ||
| 3340 | st := match st | ||
| 3341 | local | ||
| 3342 | Expression cond, e1, e2, e3; | ||
| 3343 | Type ty1, ty2; | ||
| 3344 | list<Statement> sts1, body; | ||
| 3345 | list<tuple<Expression, list<Statement>>> tybrs; | ||
| 3346 | InstNode iterator; | ||
| 3347 | MatchKind mk; | ||
| 3348 | InstContext.Type next_context, cond_context; | ||
| 3349 | SourceInfo info; | ||
| 3350 | Variability var; | ||
| 3351 | |||
| 3352 | case Statement.ASSIGNMENT() | ||
| 3353 | algorithm | ||
| 3354 | 105178 | info := ElementSource.getInfo(st.source); | |
| 3355 | 105178 | (e1, ty1, var) := typeExp(st.lhs, InstContext.set(context, NFInstContext.LHS), info); | |
| 3356 | 105178 | (e2, ty2) := typeExp(st.rhs, InstContext.set(context, NFInstContext.RHS), info); | |
| 3357 | |||
| 3358 | // TODO: Should probably only be allowUnknown = true if in a function. | ||
| 3359 | 105176 | (e2, _, mk) := TypeCheck.matchTypes(ty2, ty1, e2, NFTypeCheck.ALLOW_UNKNOWN); | |
| 3360 | |||
| 3361 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 105175 times.
|
105176 | if TypeCheck.isIncompatibleMatch(mk) then |
| 3362 | 5 | Error.addSourceMessage(Error.ASSIGN_TYPE_MISMATCH_ERROR, | |
| 3363 | {Expression.toString(e1), Expression.toString(e2), | ||
| 3364 | Type.toString(ty1), Type.toString(ty2)}, info); | ||
| 3365 | 1 | fail(); | |
| 3366 | end if; | ||
| 3367 | |||
| 3368 | 105175 | checkAssignment(e1, e2, var, context, info); | |
| 3369 | |||
| 3370 |
2/2✓ Branch 1 taken 97 times.
✓ Branch 2 taken 105077 times.
|
105174 | if Expression.isExternalCall(e2) then |
| 3371 | 97 | Call.updateExternalRecordArgs(Expression.tupleElements(e1)); | |
| 3372 | end if; | ||
| 3373 | 105174 | then | |
| 3374 | Statement.ASSIGNMENT(e1, e2, ty1, st.source); | ||
| 3375 | |||
| 3376 | case Statement.FOR() | ||
| 3377 | algorithm | ||
| 3378 | 624 | info := ElementSource.getInfo(st.source); | |
| 3379 | |||
| 3380 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 624 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 621 times.
|
624 | if isSome(st.range) then |
| 3381 | 621 | SOME(e1) := st.range; | |
| 3382 | else | ||
| 3383 | 3 | e1 := deduceIterationRangeStmt(st, st.iterator, info); | |
| 3384 | end if; | ||
| 3385 | |||
| 3386 | 624 | e1 := typeIterator(st.iterator, e1, context, structural = false); | |
| 3387 | 624 | next_context := InstContext.set(context, NFInstContext.FOR); | |
| 3388 | 624 | body := typeStatements(st.body, next_context); | |
| 3389 | 624 | then | |
| 3390 | Statement.FOR(st.iterator, SOME(e1), body, st.forType, st.source, st.sub_iters); | ||
| 3391 | |||
| 3392 | case Statement.IF() | ||
| 3393 | algorithm | ||
| 3394 | 5777 | next_context := InstContext.set(context, NFInstContext.IF); | |
| 3395 | 5777 | cond_context := InstContext.set(next_context, NFInstContext.CONDITION); | |
| 3396 | |||
| 3397 |
4/4✓ Branch 0 taken 12239 times.
✓ Branch 1 taken 5777 times.
✓ Branch 2 taken 12239 times.
✓ Branch 3 taken 5777 times.
|
18016 | tybrs := list( |
| 3398 | match br case(cond, body) | ||
| 3399 | algorithm | ||
| 3400 | 12239 | e1 := typeCondition(cond, cond_context, st.source, Error.IF_CONDITION_TYPE_ERROR); | |
| 3401 |
4/4✓ Branch 0 taken 40877 times.
✓ Branch 1 taken 12239 times.
✓ Branch 2 taken 40877 times.
✓ Branch 3 taken 12239 times.
|
53116 | sts1 := list(typeStatement(bst, next_context) for bst in body); |
| 3402 | 12239 | then (e1, sts1); | |
| 3403 | end match | ||
| 3404 | for br in st.branches); | ||
| 3405 | 5777 | then | |
| 3406 | Statement.IF(tybrs, st.source); | ||
| 3407 | |||
| 3408 | case Statement.WHEN() | ||
| 3409 | algorithm | ||
| 3410 | 79 | next_context := InstContext.set(context, NFInstContext.WHEN); | |
| 3411 | |||
| 3412 |
4/4✓ Branch 0 taken 113 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 113 times.
✓ Branch 3 taken 78 times.
|
191 | tybrs := list( |
| 3413 | match br case(cond, body) | ||
| 3414 | algorithm | ||
| 3415 | 113 | e1 := typeWhenCondition(cond, context, st.source, allowClock = false); | |
| 3416 |
4/4✓ Branch 0 taken 177 times.
✓ Branch 1 taken 112 times.
✓ Branch 2 taken 177 times.
✓ Branch 3 taken 112 times.
|
289 | sts1 := list(typeStatement(bst, next_context) for bst in body); |
| 3417 | 112 | then (e1, sts1); | |
| 3418 | end match | ||
| 3419 | for br in st.branches); | ||
| 3420 | 78 | then | |
| 3421 | Statement.WHEN(tybrs, st.source); | ||
| 3422 | |||
| 3423 | case Statement.ASSERT() | ||
| 3424 | algorithm | ||
| 3425 | 2655 | info := ElementSource.getInfo(st.source); | |
| 3426 | 2655 | (e1, e2, e3) := typeAssert(st.condition, st.message, st.level, context, info); | |
| 3427 | 2655 | then | |
| 3428 | Statement.ASSERT(e1, e2, e3, st.source); | ||
| 3429 | |||
| 3430 | case Statement.TERMINATE() | ||
| 3431 | algorithm | ||
| 3432 | 2 | info := ElementSource.getInfo(st.source); | |
| 3433 | |||
| 3434 | // terminate is not allowed in a function context. | ||
| 3435 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
|
2 | if InstContext.inFunction(context) then |
| 3436 | 1 | Error.addSourceMessage(Error.EXP_INVALID_IN_FUNCTION, {"terminate"}, info); | |
| 3437 | 1 | fail(); | |
| 3438 | end if; | ||
| 3439 | |||
| 3440 | 1 | e1 := typeOperatorArg(st.message, Type.STRING(), context, "terminate", "message", 1, info); | |
| 3441 | 1 | then | |
| 3442 | Statement.TERMINATE(e1, st.source); | ||
| 3443 | |||
| 3444 | case Statement.REINIT() | ||
| 3445 | algorithm | ||
| 3446 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if InstContext.inFunction(context) then |
| 3447 | ✗ | Error.addSourceMessage(Error.EXP_INVALID_IN_FUNCTION, {"reinit"}, | |
| 3448 | ElementSource.getInfo(st.source)); | ||
| 3449 | ✗ | fail(); | |
| 3450 | end if; | ||
| 3451 | |||
| 3452 | 4 | (e1, e2) := typeReinit(st.cref, st.reinitExp, context, st.source); | |
| 3453 | 4 | then | |
| 3454 | Statement.REINIT(e1, e2, st.source); | ||
| 3455 | |||
| 3456 | case Statement.NORETCALL() | ||
| 3457 | algorithm | ||
| 3458 | 111 | e1 := typeExp(st.exp, context, ElementSource.getInfo(st.source)); | |
| 3459 | 111 | then | |
| 3460 | Statement.NORETCALL(e1, st.source); | ||
| 3461 | |||
| 3462 | case Statement.WHILE() | ||
| 3463 | algorithm | ||
| 3464 | 288 | e1 := typeCondition(st.condition, context, st.source, Error.WHILE_CONDITION_TYPE_ERROR); | |
| 3465 |
4/4✓ Branch 0 taken 2155 times.
✓ Branch 1 taken 288 times.
✓ Branch 2 taken 2155 times.
✓ Branch 3 taken 288 times.
|
2443 | sts1 := list(typeStatement(bst, context) for bst in st.body); |
| 3466 | 288 | then | |
| 3467 | Statement.WHILE(e1, sts1, st.source); | ||
| 3468 | |||
| 3469 | case Statement.FAILURE() | ||
| 3470 | algorithm | ||
| 3471 | ✗ | sts1 := list(typeStatement(bst, context) for bst in st.body); | |
| 3472 | ✗ | then | |
| 3473 | Statement.FAILURE(sts1, st.source); | ||
| 3474 | |||
| 3475 | else st; | ||
| 3476 | end match; | ||
| 3477 | end typeStatement; | ||
| 3478 | |||
| 3479 | function checkAssignment | ||
| 3480 | input Expression lhsExp; | ||
| 3481 | input Expression rhsExp; | ||
| 3482 | input Variability lhsVar; | ||
| 3483 | input InstContext.Type context; | ||
| 3484 | input SourceInfo info; | ||
| 3485 | algorithm | ||
| 3486 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 106840 times.
|
106840 | if InstContext.inInstanceAPI(context) then |
| 3487 | ✗ | return; | |
| 3488 | end if; | ||
| 3489 | |||
| 3490 | () := match lhsExp | ||
| 3491 | local | ||
| 3492 | Integer i; | ||
| 3493 | |||
| 3494 | case Expression.TUPLE() | ||
| 3495 | algorithm | ||
| 3496 | i := 1; | ||
| 3497 |
2/2✓ Branch 0 taken 1665 times.
✓ Branch 1 taken 748 times.
|
2413 | for e in lhsExp.elements loop |
| 3498 | 1665 | checkAssignment(e, Expression.tupleElement(rhsExp, i), Expression.variability(e), context, info); | |
| 3499 | 1665 | i := i + 1; | |
| 3500 | end for; | ||
| 3501 | then | ||
| 3502 | (); | ||
| 3503 | |||
| 3504 | case Expression.CREF() guard InstContext.inFunction(context) | ||
| 3505 | algorithm | ||
| 3506 | // Give an error if trying to assign to an input inside a function. | ||
| 3507 |
4/4✓ Branch 1 taken 105540 times.
✓ Branch 2 taken 1 time.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 105539 times.
|
105541 | if ComponentRef.isCref(lhsExp.cref) and InstNode.isInput(ComponentRef.node(lhsExp.cref)) then |
| 3508 | 2 | Error.addSourceMessage(Error.ASSIGN_READONLY_ERROR, | |
| 3509 | {"input", ComponentRef.toString(lhsExp.cref)}, info); | ||
| 3510 | 1 | fail(); | |
| 3511 | end if; | ||
| 3512 | then | ||
| 3513 | (); | ||
| 3514 | |||
| 3515 | else | ||
| 3516 | algorithm | ||
| 3517 | // Give an error if assigning to a constant, or a parameter that's not | ||
| 3518 | // inside an initial algorithm. | ||
| 3519 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 500 times.
|
551 | if lhsVar < Variability.DISCRETE then |
| 3520 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 51 times.
|
51 | if lhsVar == Variability.CONSTANT then |
| 3521 | ✗ | Error.addSourceMessage(Error.ASSIGN_CONSTANT_ERROR, | |
| 3522 | {Expression.toString(lhsExp), Expression.toString(rhsExp)}, info); | ||
| 3523 | ✗ | fail(); | |
| 3524 | elseif not InstContext.inInitial(context) then | ||
| 3525 | ✗ | Error.addSourceMessage(Error.ASSIGN_PARAM_ERROR, | |
| 3526 | {Expression.toString(lhsExp), Expression.toString(rhsExp)}, info); | ||
| 3527 | ✗ | fail(); | |
| 3528 | end if; | ||
| 3529 | end if; | ||
| 3530 | then | ||
| 3531 | (); | ||
| 3532 | end match; | ||
| 3533 | end checkAssignment; | ||
| 3534 | |||
| 3535 | function typeEqualityEquation | ||
| 3536 | input Expression lhsExp; | ||
| 3537 | input Expression rhsExp; | ||
| 3538 | input InstContext.Type context; | ||
| 3539 | input NFInstNode.ScopeRef scope; | ||
| 3540 | input DAE.ElementSource source; | ||
| 3541 | output Equation eq; | ||
| 3542 | protected | ||
| 3543 | SourceInfo info = ElementSource.getInfo(source); | ||
| 3544 | Expression e1, e2; | ||
| 3545 | Type ty1, ty2, ty; | ||
| 3546 | MatchKind mk; | ||
| 3547 | algorithm | ||
| 3548 |
3/4✓ Branch 1 taken 462 times.
✓ Branch 2 taken 56982 times.
✓ Branch 4 taken 462 times.
✗ Branch 5 not taken.
|
57444 | if InstContext.inWhen(context) and not InstContext.inClocked(context) then |
| 3549 |
2/2✓ Branch 1 taken 461 times.
✓ Branch 2 taken 1 time.
|
462 | if checkLhsInWhen(lhsExp) then |
| 3550 | 461 | Structural.markSubscriptsInExp(lhsExp); | |
| 3551 | else | ||
| 3552 | 2 | Error.addSourceMessage(Error.WHEN_EQ_LHS, {Expression.toString(lhsExp)}, info); | |
| 3553 | 1 | fail(); | |
| 3554 | end if; | ||
| 3555 | end if; | ||
| 3556 | |||
| 3557 | 57443 | (e1, ty1) := typeExp(lhsExp, InstContext.set(context, NFInstContext.LHS), info); | |
| 3558 | 57441 | (e2, ty2) := typeExp(rhsExp, InstContext.set(context, NFInstContext.RHS), info); | |
| 3559 | 57433 | (e2, e1, ty, mk) := TypeCheck.matchExpressions(e2, ty2, e1, ty1); | |
| 3560 | |||
| 3561 |
2/2✓ Branch 1 taken 57 times.
✓ Branch 2 taken 57376 times.
|
57433 | if TypeCheck.isIncompatibleMatch(mk) then |
| 3562 | 114 | Error.addSourceMessage(Error.EQUATION_TYPE_MISMATCH_ERROR, | |
| 3563 | {Expression.toString(lhsExp) + " = " + Expression.toString(rhsExp), | ||
| 3564 | Type.toString(ty1) + " = " + Type.toString(ty2)}, info); | ||
| 3565 | 57 | fail(); | |
| 3566 | end if; | ||
| 3567 | |||
| 3568 | 57376 | eq := Equation.EQUALITY(e1, e2, ty, scope, source, NFEquation.ScalarizeMode.NO_PREFERENCE); | |
| 3569 | |||
| 3570 |
2/2✓ Branch 1 taken 57320 times.
✓ Branch 2 taken 56 times.
|
57376 | if Expression.isExternalCall(e2) then |
| 3571 | 56 | Call.updateExternalRecordArgs(Expression.tupleElements(e1)); | |
| 3572 | end if; | ||
| 3573 | end typeEqualityEquation; | ||
| 3574 | |||
| 3575 | function typeCondition | ||
| 3576 | input output Expression condition; | ||
| 3577 | input InstContext.Type context; | ||
| 3578 | input DAE.ElementSource source; | ||
| 3579 | input ErrorTypes.Message errorMsg; | ||
| 3580 | input Boolean allowVector = false; | ||
| 3581 | input Boolean allowClock = false; | ||
| 3582 | output Type ty; | ||
| 3583 | output Variability variability; | ||
| 3584 | protected | ||
| 3585 | SourceInfo info; | ||
| 3586 | Type ety; | ||
| 3587 | algorithm | ||
| 3588 | 26027 | info := ElementSource.getInfo(source); | |
| 3589 | 26027 | (condition, ty, variability) := typeExp(condition, context, info); | |
| 3590 | |||
| 3591 |
4/4✓ Branch 0 taken 510 times.
✓ Branch 1 taken 25516 times.
✓ Branch 3 taken 76 times.
✓ Branch 4 taken 434 times.
|
26026 | if allowVector and Type.isArray(ty) then |
| 3592 | 76 | ety := Type.unliftArray(ty); | |
| 3593 | else | ||
| 3594 | 25950 | ety := ty; | |
| 3595 | end if; | ||
| 3596 | |||
| 3597 |
4/6✓ Branch 1 taken 37 times.
✓ Branch 2 taken 25989 times.
✓ Branch 3 taken 37 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 37 times.
✗ Branch 7 not taken.
|
26026 | if not (Type.isBoolean(ety) or (allowClock and Type.isClock(ety))) then |
| 3598 | ✗ | Error.addSourceMessage(errorMsg, | |
| 3599 | {Expression.toString(condition), Type.toString(ty)}, info); | ||
| 3600 | ✗ | fail(); | |
| 3601 | end if; | ||
| 3602 | end typeCondition; | ||
| 3603 | |||
| 3604 | function typeForEquation | ||
| 3605 | input Equation eq; | ||
| 3606 | input InstContext.Type context; | ||
| 3607 | output Equation forEq; | ||
| 3608 | protected | ||
| 3609 | InstNode iterator; | ||
| 3610 | Option<Expression> range; | ||
| 3611 | list<Equation> body; | ||
| 3612 | NFInstNode.ScopeRef scope; | ||
| 3613 | DAE.ElementSource src; | ||
| 3614 | SourceInfo info; | ||
| 3615 | Expression range_exp; | ||
| 3616 | Variability range_var; | ||
| 3617 | InstContext.Type next_context; | ||
| 3618 | algorithm | ||
| 3619 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1558 times.
|
1558 | Equation.FOR(iterator, range, body, scope, src) := eq; |
| 3620 | |||
| 3621 |
3/4✗ Branch 0 not taken.
✓ Branch 1 taken 1558 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1553 times.
|
1558 | if isSome(range) then |
| 3622 | 1553 | SOME(range_exp) := range; | |
| 3623 | else | ||
| 3624 | 5 | range_exp := deduceIterationRangeEq(eq, iterator, ElementSource.getInfo(src)); | |
| 3625 | end if; | ||
| 3626 | |||
| 3627 | 1557 | (range_exp, _, range_var) := typeIterator(iterator, range_exp, context, structural = true); | |
| 3628 | 1556 | next_context := InstContext.set(context, NFInstContext.FOR); | |
| 3629 | |||
| 3630 | // A range with resizable parameters is still expandable, its bounds are evaluated to resolve connections. | ||
| 3631 |
2/4✓ Branch 0 taken 1556 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1556 times.
|
1556 | if range_var > Variability.NON_STRUCTURAL_PARAMETER or Structural.isExpressionNotFixed(range_exp, maxDepth = 100) then |
| 3632 | ✗ | next_context := InstContext.set(context, NFInstContext.NONEXPANDABLE); | |
| 3633 | end if; | ||
| 3634 | |||
| 3635 |
4/4✓ Branch 0 taken 2484 times.
✓ Branch 1 taken 1555 times.
✓ Branch 2 taken 2484 times.
✓ Branch 3 taken 1555 times.
|
4039 | body := list(typeEquation(e, next_context) for e in body); |
| 3636 | 1555 | forEq := Equation.FOR(iterator, SOME(range_exp), body, scope, src); | |
| 3637 | end typeForEquation; | ||
| 3638 | |||
| 3639 | function typeIfEquation | ||
| 3640 | input list<Equation.Branch> branches; | ||
| 3641 | input InstContext.Type context; | ||
| 3642 | input NFInstNode.ScopeRef scope; | ||
| 3643 | input DAE.ElementSource source; | ||
| 3644 | output Equation ifEq; | ||
| 3645 | protected | ||
| 3646 | Expression cond; | ||
| 3647 | list<Equation> eql; | ||
| 3648 | Variability accum_var = Variability.CONSTANT, var; | ||
| 3649 | list<Equation.Branch> bl = {}, bl2 = {}; | ||
| 3650 | InstContext.Type next_context = InstContext.set(context, NFInstContext.IF); | ||
| 3651 | InstContext.Type cond_context = InstContext.set(next_context, NFInstContext.CONDITION); | ||
| 3652 | algorithm | ||
| 3653 | // Type the conditions of all the branches. | ||
| 3654 |
2/2✓ Branch 0 taken 12989 times.
✓ Branch 1 taken 6903 times.
|
19892 | for b in branches loop |
| 3655 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12989 times.
|
12989 | Equation.Branch.BRANCH(cond, _, eql) := b; |
| 3656 | 12989 | (cond, _, var) := typeCondition(cond, cond_context, source, Error.IF_CONDITION_TYPE_ERROR); | |
| 3657 | |||
| 3658 | // Conditions on resizable parameters (and iterators over them) are resolved with the connections. | ||
| 3659 |
7/8✓ Branch 0 taken 279 times.
✓ Branch 1 taken 12710 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 278 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 8 taken 1061 times.
✓ Branch 9 taken 11649 times.
|
12989 | if (var > Variability.PARAMETER and not (var == Variability.NON_STRUCTURAL_PARAMETER and |
| 3660 | Flags.getConfigBool(Flags.RESIZABLE_ARRAYS))) or Structural.isExpressionNotFixed(cond, maxDepth = 100) then | ||
| 3661 | // If the condition doesn't fulfill the requirements for allowing | ||
| 3662 | // connections in the branch, mark the context so we can check that when | ||
| 3663 | // typing the body of the branch. | ||
| 3664 | 1340 | next_context := InstContext.set(next_context, NFInstContext.NONEXPANDABLE); | |
| 3665 | elseif var == Variability.PARAMETER and | ||
| 3666 | (accum_var <= Variability.PARAMETER or Equation.containsList(eql, Equation.isConnection)) then | ||
| 3667 | // If all conditions up to and including this one are parameter | ||
| 3668 | // expressions, consider the condition to be structural. | ||
| 3669 | 1482 | var := Variability.STRUCTURAL_PARAMETER; | |
| 3670 | end if; | ||
| 3671 | |||
| 3672 | 12989 | accum_var := Prefixes.variabilityMax(accum_var, var); | |
| 3673 | 12989 | bl := Equation.Branch.BRANCH(cond, var, eql) :: bl; | |
| 3674 | end for; | ||
| 3675 | |||
| 3676 | // Type the bodies of all the branches. | ||
| 3677 |
2/2✓ Branch 0 taken 12989 times.
✓ Branch 1 taken 6903 times.
|
19892 | for b in bl loop |
| 3678 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12989 times.
|
12989 | Equation.Branch.BRANCH(cond, var, eql) := b; |
| 3679 | |||
| 3680 | 12989 | ErrorExt.setCheckpoint(getInstanceName()); | |
| 3681 | try | ||
| 3682 |
4/4✓ Branch 0 taken 23361 times.
✓ Branch 1 taken 12934 times.
✓ Branch 2 taken 23361 times.
✓ Branch 3 taken 12934 times.
|
36295 | eql := list(typeEquation(e, next_context) for e in eql); |
| 3683 | 12934 | bl2 := Equation.makeBranch(cond, eql, var) :: bl2; | |
| 3684 | else | ||
| 3685 | 55 | bl2 := Equation.INVALID_BRANCH(Equation.makeBranch(cond, eql, var), | |
| 3686 | ErrorExt.getCheckpointMessages()) :: bl2; | ||
| 3687 | end try; | ||
| 3688 | 12989 | ErrorExt.delCheckpoint(getInstanceName()); | |
| 3689 | end for; | ||
| 3690 | |||
| 3691 | 6903 | ifEq := Equation.IF(bl2, scope, source); | |
| 3692 | end typeIfEquation; | ||
| 3693 | |||
| 3694 | function typeWhenEquation | ||
| 3695 | input list<Equation.Branch> branches; | ||
| 3696 | input InstContext.Type context; | ||
| 3697 | input NFInstNode.ScopeRef scope; | ||
| 3698 | input DAE.ElementSource source; | ||
| 3699 | output Equation whenEq; | ||
| 3700 | protected | ||
| 3701 | InstContext.Type next_context = InstContext.set(context, NFInstContext.WHEN); | ||
| 3702 | list<Equation.Branch> accum_branches = {}; | ||
| 3703 | Expression cond; | ||
| 3704 | list<Equation> body; | ||
| 3705 | Type ty; | ||
| 3706 | Variability var; | ||
| 3707 | algorithm | ||
| 3708 |
2/2✓ Branch 0 taken 399 times.
✓ Branch 1 taken 352 times.
|
751 | for branch in branches loop |
| 3709 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 399 times.
|
399 | Equation.Branch.BRANCH(cond, _, body) := branch; |
| 3710 | 399 | (cond, ty, var) := typeWhenCondition(cond, context, source, allowClock = true); | |
| 3711 | |||
| 3712 |
2/2✓ Branch 1 taken 37 times.
✓ Branch 2 taken 360 times.
|
397 | if Type.isClock(ty) then |
| 3713 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 36 times.
|
37 | if listLength(branches) <> 1 then |
| 3714 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
|
1 | if referenceEq(branch, listHead(branches)) then |
| 3715 | ✗ | Error.addSourceMessage(Error.ELSE_WHEN_CLOCK, {}, ElementSource.getInfo(source)); | |
| 3716 | else | ||
| 3717 | 1 | Error.addSourceMessage(Error.CLOCKED_WHEN_BRANCH, {}, ElementSource.getInfo(source)); | |
| 3718 | end if; | ||
| 3719 | |||
| 3720 | 1 | fail(); | |
| 3721 | else | ||
| 3722 | 36 | next_context := InstContext.set(context, NFInstContext.CLOCKED); | |
| 3723 | end if; | ||
| 3724 | end if; | ||
| 3725 | |||
| 3726 |
4/4✓ Branch 0 taken 570 times.
✓ Branch 1 taken 392 times.
✓ Branch 2 taken 570 times.
✓ Branch 3 taken 392 times.
|
962 | body := list(typeEquation(eq, next_context) for eq in body); |
| 3727 | 392 | accum_branches := Equation.makeBranch(cond, body, var) :: accum_branches; | |
| 3728 | end for; | ||
| 3729 | |||
| 3730 | 352 | whenEq := Equation.WHEN(listReverseInPlace(accum_branches), scope, source); | |
| 3731 | end typeWhenEquation; | ||
| 3732 | |||
| 3733 | function typeWhenCondition | ||
| 3734 | input Expression condition; | ||
| 3735 | input InstContext.Type context; | ||
| 3736 | input DAE.ElementSource source; | ||
| 3737 | input Boolean allowClock; | ||
| 3738 | output Expression outCondition; | ||
| 3739 | output Type ty; | ||
| 3740 | output Variability variability; | ||
| 3741 | algorithm | ||
| 3742 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 511 times.
|
512 | if InstContext.inNonexpandable(context) then |
| 3743 | 1 | Error.addSourceMessage(Error.IN_NON_EVALUABLE_IF_OR_FOR, {"when"}, ElementSource.getInfo(source)); | |
| 3744 | 1 | fail(); | |
| 3745 | end if; | ||
| 3746 | |||
| 3747 | 511 | (outCondition, ty, variability) := typeCondition(condition, context, source, | |
| 3748 | Error.WHEN_CONDITION_TYPE_ERROR, allowVector = true, allowClock = allowClock); | ||
| 3749 | |||
| 3750 |
4/4✓ Branch 0 taken 14 times.
✓ Branch 1 taken 496 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 13 times.
|
510 | if variability > Variability.IMPLICITLY_DISCRETE and not Type.isClock(ty) then |
| 3751 | 2 | Error.addSourceMessage(Error.NON_DISCRETE_WHEN_CONDITION, | |
| 3752 | {Expression.toString(condition)}, ElementSource.getInfo(source)); | ||
| 3753 | 1 | fail(); | |
| 3754 | end if; | ||
| 3755 | |||
| 3756 |
1/2✓ Branch 1 taken 509 times.
✗ Branch 2 not taken.
|
509 | if not checkWhenInitial(outCondition) then |
| 3757 | ✗ | Error.addSourceMessage(Error.INITIAL_CALL_WARNING, | |
| 3758 | {Expression.toString(condition)}, ElementSource.getInfo(source)); | ||
| 3759 | end if; | ||
| 3760 | end typeWhenCondition; | ||
| 3761 | |||
| 3762 | function checkWhenInitial | ||
| 3763 | "Checks that initial() is only used as either initial() or | ||
| 3764 | {..., initial(), ...} in a when-equation condition." | ||
| 3765 | input Expression condition; | ||
| 3766 | output Boolean invalid; | ||
| 3767 | algorithm | ||
| 3768 | invalid := match condition | ||
| 3769 | case Expression.ARRAY() | ||
| 3770 | algorithm | ||
| 3771 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
✓ Branch 3 taken 69 times.
✗ Branch 4 not taken.
|
138 | for e in condition.elements loop |
| 3772 |
1/2✓ Branch 1 taken 69 times.
✗ Branch 2 not taken.
|
69 | if checkWhenInitial(e) then |
| 3773 | invalid := true; | ||
| 3774 | 69 | return; | |
| 3775 | end if; | ||
| 3776 | end for; | ||
| 3777 | then | ||
| 3778 | false; | ||
| 3779 | |||
| 3780 | 509 | else not Expression.containsShallow(condition, | |
| 3781 | function Expression.contains(func = function Expression.isCallNamed(name = "initial"))); | ||
| 3782 | |||
| 3783 | end match; | ||
| 3784 | end checkWhenInitial; | ||
| 3785 | |||
| 3786 | function typeOperatorArg | ||
| 3787 | input output Expression arg; | ||
| 3788 | input Type expectedType; | ||
| 3789 | input InstContext.Type context; | ||
| 3790 | input String operatorName; | ||
| 3791 | input String argName; | ||
| 3792 | input Integer argIndex; | ||
| 3793 | input SourceInfo info; | ||
| 3794 | output Variability var; | ||
| 3795 | protected | ||
| 3796 | Type ty; | ||
| 3797 | MatchKind mk; | ||
| 3798 | algorithm | ||
| 3799 | 18543 | (arg, ty, var) := typeExp(arg, context, info); | |
| 3800 | 18543 | (arg, _, mk) := TypeCheck.matchTypes(ty, expectedType, arg); | |
| 3801 | |||
| 3802 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 18542 times.
|
18543 | if TypeCheck.isIncompatibleMatch(mk) then |
| 3803 | 5 | Error.addSourceMessage(Error.ARG_TYPE_MISMATCH, | |
| 3804 | {intString(argIndex), operatorName, argName, Expression.toString(arg), | ||
| 3805 | Type.toString(ty), Type.toString(expectedType)}, info); | ||
| 3806 | 1 | fail(); | |
| 3807 | end if; | ||
| 3808 | end typeOperatorArg; | ||
| 3809 | |||
| 3810 | function typeReinit | ||
| 3811 | input output Expression crefExp; | ||
| 3812 | input output Expression exp; | ||
| 3813 | input InstContext.Type context; | ||
| 3814 | input DAE.ElementSource source; | ||
| 3815 | protected | ||
| 3816 | MatchKind mk; | ||
| 3817 | Type ty1, ty2; | ||
| 3818 | ComponentRef cref; | ||
| 3819 | SourceInfo info; | ||
| 3820 | algorithm | ||
| 3821 | 47 | info := ElementSource.getInfo(source); | |
| 3822 | 47 | (crefExp, ty1, _) := typeExp(crefExp, context, info); | |
| 3823 | 47 | (exp, ty2, _) := typeExp(exp, context, info); | |
| 3824 | |||
| 3825 | // The first argument must be a cref. | ||
| 3826 | cref := match crefExp | ||
| 3827 | case Expression.CREF() | ||
| 3828 | algorithm | ||
| 3829 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 46 times.
|
47 | if ComponentRef.isIterator(crefExp.cref) then |
| 3830 | 2 | Error.addSourceMessage(Error.ASSIGN_ITERATOR_ERROR, | |
| 3831 | {ComponentRef.toString(crefExp.cref)}, info); | ||
| 3832 | 1 | fail(); | |
| 3833 | end if; | ||
| 3834 | 46 | then | |
| 3835 | crefExp.cref; | ||
| 3836 | |||
| 3837 | else | ||
| 3838 | algorithm | ||
| 3839 | ✗ | Error.addSourceMessage(Error.REINIT_MUST_BE_VAR_OR_ARRAY, {}, info); | |
| 3840 | ✗ | then | |
| 3841 | fail(); | ||
| 3842 | end match; | ||
| 3843 | |||
| 3844 | // The first argument must be a continuous time variable. | ||
| 3845 | // Check the variability of the cref instead of the variability returned by | ||
| 3846 | // typeExp, since expressions in when-equations count as discrete. | ||
| 3847 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 45 times.
|
46 | if ComponentRef.nodeVariability(cref) < Variability.IMPLICITLY_DISCRETE then |
| 3848 | 3 | Error.addSourceMessage(Error.REINIT_MUST_BE_VAR, | |
| 3849 | {Expression.toString(crefExp), | ||
| 3850 | Prefixes.variabilityString(ComponentRef.nodeVariability(cref))}, info); | ||
| 3851 | 1 | fail(); | |
| 3852 | end if; | ||
| 3853 | |||
| 3854 | // The first argument must be a subtype of Real. | ||
| 3855 | 45 | (_, _, mk) := TypeCheck.matchTypes(Type.arrayElementType(ty1), Type.REAL(), crefExp); | |
| 3856 | |||
| 3857 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
|
45 | if TypeCheck.isIncompatibleMatch(mk) then |
| 3858 | ✗ | Error.addSourceMessage(Error.REINIT_MUST_BE_REAL, | |
| 3859 | {Expression.toString(crefExp), Type.toString(Type.arrayElementType(ty1))}, info); | ||
| 3860 | ✗ | fail(); | |
| 3861 | end if; | ||
| 3862 | |||
| 3863 | // The second argument must be type compatible with the first. | ||
| 3864 | 45 | (exp, _, mk) := TypeCheck.matchTypes(ty2, ty1, exp); | |
| 3865 | |||
| 3866 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 44 times.
|
45 | if TypeCheck.isIncompatibleMatch(mk) then |
| 3867 | 4 | Error.addSourceMessage(Error.ARG_TYPE_MISMATCH, | |
| 3868 | {"2", "reinit", "", Expression.toString(exp), Type.toString(ty2), Type.toString(ty1)}, info); | ||
| 3869 | 1 | fail(); | |
| 3870 | end if; | ||
| 3871 | end typeReinit; | ||
| 3872 | |||
| 3873 | function deduceIterationRangeEq | ||
| 3874 | input Equation eq; | ||
| 3875 | input InstNode iterator; | ||
| 3876 | input SourceInfo info; | ||
| 3877 | output Expression iterationRange; | ||
| 3878 | protected | ||
| 3879 | list<tuple<ComponentRef, Integer>> crefs; | ||
| 3880 | algorithm | ||
| 3881 | 5 | crefs := Equation.foldExp(eq, function collectIteratorCrefs(iterator = iterator), {}); | |
| 3882 | 5 | iterationRange := deduceIterationRange(crefs, iterator, info); | |
| 3883 | end deduceIterationRangeEq; | ||
| 3884 | |||
| 3885 | function deduceIterationRangeStmt | ||
| 3886 | input Statement stmt; | ||
| 3887 | input InstNode iterator; | ||
| 3888 | input SourceInfo info; | ||
| 3889 | output Expression iterationRange; | ||
| 3890 | protected | ||
| 3891 | list<tuple<ComponentRef, Integer>> crefs; | ||
| 3892 | algorithm | ||
| 3893 | 3 | crefs := Statement.foldExp(stmt, function collectIteratorCrefs(iterator = iterator), {}); | |
| 3894 | 3 | iterationRange := deduceIterationRange(crefs, iterator, info); | |
| 3895 | end deduceIterationRangeStmt; | ||
| 3896 | |||
| 3897 | function deduceIterationRangeExp | ||
| 3898 | input Expression exp; | ||
| 3899 | input InstNode iterator; | ||
| 3900 | input SourceInfo info; | ||
| 3901 | output Expression iterationRange; | ||
| 3902 | protected | ||
| 3903 | list<tuple<ComponentRef, Integer>> crefs; | ||
| 3904 | algorithm | ||
| 3905 | 2 | crefs := Expression.fold(exp, function collectIteratorCrefs2(iterator = iterator), {}); | |
| 3906 | 2 | iterationRange := deduceIterationRange(crefs, iterator, info); | |
| 3907 | end deduceIterationRangeExp; | ||
| 3908 | |||
| 3909 | function deduceIterationRange | ||
| 3910 | "Deduces the range of an iterator given a list of crefs and dimension indices | ||
| 3911 | that the iterator was used to index." | ||
| 3912 | input list<tuple<ComponentRef, Integer>> crefs; | ||
| 3913 | input InstNode iterator; | ||
| 3914 | input SourceInfo info; | ||
| 3915 | output Expression iterationRange; | ||
| 3916 | protected | ||
| 3917 | tuple<ComponentRef, Integer> range_cr; | ||
| 3918 | ComponentRef cr; | ||
| 3919 | Integer dim_index; | ||
| 3920 | Dimension dim; | ||
| 3921 | Expression start_exp, stop_exp; | ||
| 3922 | algorithm | ||
| 3923 | // The iterator needs to be used in a subscript somewhere to be able to deduce it. | ||
| 3924 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
10 | if listEmpty(crefs) then |
| 3925 | ✗ | Error.addSourceMessage(Error.IMPLICIT_ITERATOR_NOT_FOUND_IN_LOOP_BODY, | |
| 3926 | {InstNode.name(iterator)}, info); | ||
| 3927 | ✗ | fail(); | |
| 3928 | end if; | ||
| 3929 | |||
| 3930 | // Check that the dimensions are all the same. | ||
| 3931 | 10 | range_cr := List.reduce(crefs, function deduceIterationRange2(info = info)); | |
| 3932 | 9 | (cr, dim_index) := range_cr; | |
| 3933 | |||
| 3934 | // Deduced iteration range is 1:size(cr, dim_index) | ||
| 3935 | 9 | dim := Type.nthDimension(InstNode.getType(ComponentRef.node(cr)), dim_index); | |
| 3936 | 9 | start_exp := Dimension.lowerBoundExp(dim); | |
| 3937 | 9 | stop_exp := Dimension.endExp(dim, Expression.CREF(Type.UNKNOWN(), cr), dim_index); | |
| 3938 | 9 | iterationRange := Expression.RANGE(Type.UNKNOWN(), start_exp, NONE(), stop_exp); | |
| 3939 | end deduceIterationRange; | ||
| 3940 | |||
| 3941 | function collectIteratorCrefs | ||
| 3942 | "Traverses an expression and return a list of crefs that the given iterator | ||
| 3943 | was used as a subscript in, as well as the index of the dimension that the | ||
| 3944 | subscripts are indexing." | ||
| 3945 | input Expression exp; | ||
| 3946 | input InstNode iterator; | ||
| 3947 | input output list<tuple<ComponentRef, Integer>> crefs; | ||
| 3948 | algorithm | ||
| 3949 | 16 | crefs := Expression.fold(exp, function collectIteratorCrefs2(iterator = iterator), crefs); | |
| 3950 | end collectIteratorCrefs; | ||
| 3951 | |||
| 3952 | function collectIteratorCrefs2 | ||
| 3953 | "Helper function to collectIteratorCrefs, collects the iterator crefs in an | ||
| 3954 | expression." | ||
| 3955 | input Expression exp; | ||
| 3956 | input InstNode iterator; | ||
| 3957 | input output list<tuple<ComponentRef, Integer>> crefs; | ||
| 3958 | protected | ||
| 3959 | ComponentRef cref; | ||
| 3960 | Integer index; | ||
| 3961 | list<Subscript> subs; | ||
| 3962 | algorithm | ||
| 3963 | () := match exp | ||
| 3964 | case Expression.CREF(cref = cref) | ||
| 3965 | algorithm | ||
| 3966 |
2/2✓ Branch 1 taken 41 times.
✓ Branch 2 taken 37 times.
|
78 | while ComponentRef.isCref(cref) loop |
| 3967 | 41 | (cref, subs) := ComponentRef.stripSubscripts(cref); | |
| 3968 | index := 1; | ||
| 3969 | |||
| 3970 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 41 times.
|
57 | for sub in subs loop |
| 3971 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | if Subscript.equalsIterator(sub, iterator) then |
| 3972 | 16 | crefs := (cref, index) :: crefs; | |
| 3973 | end if; | ||
| 3974 | |||
| 3975 | 16 | index := index + 1; | |
| 3976 | end for; | ||
| 3977 | |||
| 3978 | 41 | cref := ComponentRef.rest(cref); | |
| 3979 | end while; | ||
| 3980 | then | ||
| 3981 | (); | ||
| 3982 | |||
| 3983 | else (); | ||
| 3984 | end match; | ||
| 3985 | end collectIteratorCrefs2; | ||
| 3986 | |||
| 3987 | function deduceIterationRange2 | ||
| 3988 | "Helper function to deduceIterationRange, check that two dimensions are the same." | ||
| 3989 | input tuple<ComponentRef, Integer> range1; | ||
| 3990 | input tuple<ComponentRef, Integer> range2; | ||
| 3991 | input SourceInfo info; | ||
| 3992 | output tuple<ComponentRef, Integer> range = range2; | ||
| 3993 | protected | ||
| 3994 | ComponentRef cref1, cref2; | ||
| 3995 | Integer index1, index2; | ||
| 3996 | InstNode node1, node2; | ||
| 3997 | Dimension dim1, dim2; | ||
| 3998 | algorithm | ||
| 3999 | 6 | (cref1, index1) := range1; | |
| 4000 | 6 | (cref2, index2) := range2; | |
| 4001 | 6 | node1 := ComponentRef.node(cref1); | |
| 4002 | 6 | node2 := ComponentRef.node(cref2); | |
| 4003 | |||
| 4004 | // Skip the check if they refer to the same dimension. | ||
| 4005 |
3/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 5 times.
|
6 | if index1 == index2 and InstNode.refEqual(node1, node2) then |
| 4006 | 1 | return; | |
| 4007 | end if; | ||
| 4008 | |||
| 4009 | // The crefs are probably untyped here, so use the type of the instance nodes instead. | ||
| 4010 | 5 | dim1 := Type.nthDimension(InstNode.getType(node1), index1); | |
| 4011 | 5 | dim2 := Type.nthDimension(InstNode.getType(node2), index2); | |
| 4012 | |||
| 4013 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 4 times.
|
5 | if not Dimension.isEqualKnownSize(dim1, node1, index1, dim2, node2, index2) then |
| 4014 | 3 | Error.addSourceMessage(Error.INCOMPATIBLE_IMPLICIT_RANGES, | |
| 4015 | {String(index1), ComponentRef.toString(cref1), | ||
| 4016 | String(index2), ComponentRef.toString(cref2)}, info); | ||
| 4017 | 1 | fail(); | |
| 4018 | end if; | ||
| 4019 | end deduceIterationRange2; | ||
| 4020 | |||
| 4021 | annotation(__OpenModelica_Interface="nf_frontend"); | ||
| 4022 | end NFTyping; | ||
| 4023 |