OMCompiler/Compiler/NFFrontEnd/NFRecord.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 NFRecord | ||
| 37 | " file: NFRecord.mo | ||
| 38 | package: NFRecord | ||
| 39 | description: package for handling records. | ||
| 40 | |||
| 41 | |||
| 42 | Functions used by NFInst for handling records. | ||
| 43 | " | ||
| 44 | |||
| 45 | import Attributes = NFAttributes; | ||
| 46 | import BaseModelica; | ||
| 47 | import Binding = NFBinding; | ||
| 48 | import Class = NFClass; | ||
| 49 | import Component = NFComponent; | ||
| 50 | import NFComponent.ComponentState; | ||
| 51 | import Dimension = NFDimension; | ||
| 52 | import Expression = NFExpression; | ||
| 53 | import NFInstNode.InstNode; | ||
| 54 | import NFInstNode.InstNodeType; | ||
| 55 | import Type = NFType; | ||
| 56 | import Subscript = NFSubscript; | ||
| 57 | import InstContext = NFInstContext; | ||
| 58 | import IOStream; | ||
| 59 | |||
| 60 | protected | ||
| 61 | import Absyn; | ||
| 62 | import DAE; | ||
| 63 | import Inst = NFInst; | ||
| 64 | import NFInst.InstSettings; | ||
| 65 | import Lookup = NFLookup; | ||
| 66 | import TypeCheck = NFTypeCheck; | ||
| 67 | import Typing = NFTyping; | ||
| 68 | import EvalConstants = NFEvalConstants; | ||
| 69 | import NFPrefixes.Direction; | ||
| 70 | import NFPrefixes.Variability; | ||
| 71 | import NFPrefixes.Visibility; | ||
| 72 | import NFFunction.{Function, FunctionStatus}; | ||
| 73 | import NFClassTree.ClassTree; | ||
| 74 | import ComplexType = NFComplexType; | ||
| 75 | import ComponentRef = NFComponentRef; | ||
| 76 | import MetaModelica.Dangerous.listReverseInPlace; | ||
| 77 | import UnorderedMap; | ||
| 78 | import UnorderedSet; | ||
| 79 | |||
| 80 | public | ||
| 81 | |||
| 82 | encapsulated uniontype Field | ||
| 83 | record INPUT | ||
| 84 | String name; | ||
| 85 | end INPUT; | ||
| 86 | |||
| 87 | record LOCAL | ||
| 88 | String name; | ||
| 89 | end LOCAL; | ||
| 90 | |||
| 91 | function isInput | ||
| 92 | input Field field; | ||
| 93 | output Boolean isInput; | ||
| 94 | algorithm | ||
| 95 | isInput := match field | ||
| 96 | case INPUT() then true; | ||
| 97 | else false; | ||
| 98 | end match; | ||
| 99 | end isInput; | ||
| 100 | |||
| 101 | function name | ||
| 102 | input Field field; | ||
| 103 | output String name; | ||
| 104 | algorithm | ||
| 105 | name := match field | ||
| 106 | 283571 | case INPUT() then field.name; | |
| 107 | 977 | case LOCAL() then field.name; | |
| 108 | end match; | ||
| 109 | end name; | ||
| 110 | end Field; | ||
| 111 | |||
| 112 | function instRecord | ||
| 113 | input InstNode node; | ||
| 114 | input InstContext.Type context = NFInstContext.NO_CONTEXT; | ||
| 115 | output InstNode recordNode; | ||
| 116 | protected | ||
| 117 | InstContext.Type next_context; | ||
| 118 | algorithm | ||
| 119 | // The node we get is usually a record instance, with applied modifiers and so on. | ||
| 120 | // So the first thing we do is to create a "pure" instance of the record. | ||
| 121 | |||
| 122 | // TODO: The lookup will fail for records declared in redeclare modifiers, | ||
| 123 | // since the parent will be the class scope of the modifier instead of | ||
| 124 | // the element being modified. In that case we just reinstantiate the | ||
| 125 | // record completely, but this probably isn't entirely correct. We | ||
| 126 | // should make the expanded but not fully instantiated class available | ||
| 127 | // here somehow. | ||
| 128 | try | ||
| 129 | 2815 | recordNode := Lookup.lookupLocalSimpleName(InstNode.name(node), InstNode.classScope(InstNode.parent(node))); | |
| 130 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 2814 times.
|
2814 | true := referenceEq(InstNode.definition(node), InstNode.definition(recordNode)); |
| 131 | else | ||
| 132 | 1 | recordNode := InstNode.replaceClass(Class.NOT_INSTANTIATED(), node); | |
| 133 | end try; | ||
| 134 | |||
| 135 | 2815 | next_context := InstContext.set(context, NFInstContext.RELAXED); | |
| 136 | 2815 | next_context := InstContext.set(next_context, NFInstContext.FUNCTION); | |
| 137 | 2815 | recordNode := InstNode.makeRootClass(recordNode, InstNode.parent(node)); | |
| 138 | 2815 | recordNode := Inst.instantiate(recordNode, context = next_context); | |
| 139 | 2815 | Inst.instExpressions(recordNode, context = next_context, settings = InstSettings.create()); | |
| 140 | end instRecord; | ||
| 141 | |||
| 142 | function instDefaultConstructor | ||
| 143 | input Absyn.Path path; | ||
| 144 | input output InstNode node; | ||
| 145 | input InstContext.Type context; | ||
| 146 | input SourceInfo info; | ||
| 147 | protected | ||
| 148 | list<InstNode> inputs, locals, all_params; | ||
| 149 | DAE.FunctionAttributes attr; | ||
| 150 | Pointer<FunctionStatus> status; | ||
| 151 | InstNode ctor_node, out_rec; | ||
| 152 | Component out_comp; | ||
| 153 | Class ctor_cls; | ||
| 154 | algorithm | ||
| 155 | 2789 | ctor_node := instRecord(node, context); | |
| 156 | |||
| 157 | // Collect the record fields. | ||
| 158 | 2789 | (inputs, locals, all_params) := collectRecordParams(ctor_node); | |
| 159 | |||
| 160 | // TODO: The local fields can contain depenencies on each other which requires | ||
| 161 | // reordering them such that they can be initialized before they're used. | ||
| 162 | // But the code generation uses the type of the record constructor both | ||
| 163 | // for generating the record struct and the record constructor, so we | ||
| 164 | // can't currently reorder variables here without also messing up the | ||
| 165 | // order of the record itself. | ||
| 166 | //sorted_locals := Function.sortLocals(locals, info); | ||
| 167 | //all_params := listAppend(inputs, sorted_locals); | ||
| 168 | |||
| 169 | // Create the output record element, using the instance created above as both parent and type. | ||
| 170 | 2789 | out_comp := Component.COMPONENT(ctor_node, Type.UNTYPED(node, listArray({})), | |
| 171 | NFBinding.EMPTY_BINDING, NFBinding.EMPTY_BINDING, | ||
| 172 | NFAttributes.OUTPUT_ATTR, SCode.noComment, | ||
| 173 | ComponentState.FullyInstantiated, Absyn.dummyInfo); | ||
| 174 | 2789 | out_rec := InstNode.fromComponent("$out" + InstNode.name(ctor_node), out_comp, ctor_node); | |
| 175 | |||
| 176 | // Make a record constructor class and create a node for the constructor. | ||
| 177 | 2789 | ctor_cls := Class.makeRecordConstructor(all_params, out_rec); | |
| 178 | // A new entity, not an update of the record: it must not publish itself | ||
| 179 | // into the record's identity cell, or the record's own type resolves here. | ||
| 180 | 2789 | ctor_node := InstNode.reidentify(InstNode.replaceClass(ctor_cls, ctor_node)); | |
| 181 | 2789 | InstNode.classApply(ctor_node, Class.setType, Type.COMPLEX(InstNode.identityCell(ctor_node), ComplexType.CLASS())); | |
| 182 | |||
| 183 | // Create the constructor function and add it to the function cache. | ||
| 184 | attr := DAE.FUNCTION_ATTRIBUTES_DEFAULT; | ||
| 185 | 2789 | status := Pointer.create(FunctionStatus.INITIAL); | |
| 186 | 5578 | InstNode.cacheAddFunc(node, Function.FUNCTION(path, InstNode.handle(ctor_node), inputs, {InstNode.handle(out_rec)}, locals, | |
| 187 | NONE(), {}, Type.UNKNOWN(), attr, {}, {}, listArray({}), status, Pointer.create(0)), false); | ||
| 188 | end instDefaultConstructor; | ||
| 189 | |||
| 190 | function checkLocalFieldOrder | ||
| 191 | "Checks if the local variables in a record constructor requires reordering, | ||
| 192 | and issues an error in that case since we can't handle it yet." | ||
| 193 | input list<InstNode> locals; | ||
| 194 | input InstNode recNode; | ||
| 195 | input SourceInfo info; | ||
| 196 | protected | ||
| 197 | UnorderedSet<InstNode> locals_set; | ||
| 198 | list<InstNode> locs, deps; | ||
| 199 | InstNode loc; | ||
| 200 | algorithm | ||
| 201 |
2/2✓ Branch 1 taken 1574 times.
✓ Branch 2 taken 1 time.
|
1575 | if listLength(locals) <= 1 then |
| 202 | 1574 | return; | |
| 203 | end if; | ||
| 204 | |||
| 205 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
|
1 | loc :: locs := listReverse(locals); |
| 206 | 1 | locals_set := UnorderedSet.fromList({loc}, InstNode.hash, InstNode.refEqual); | |
| 207 | |||
| 208 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
|
2 | for l in locs loop |
| 209 | 1 | deps := Function.getLocalDependencies(l, locals_set); | |
| 210 | |||
| 211 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
|
1 | if not listEmpty(deps) then |
| 212 | ✗ | Error.addSourceMessage(Error.UNSUPPORTED_RECORD_REORDERING, | |
| 213 | {InstNode.name(recNode)}, info); | ||
| 214 | ✗ | fail(); | |
| 215 | end if; | ||
| 216 | |||
| 217 | 1 | UnorderedSet.add(l, locals_set); | |
| 218 | end for; | ||
| 219 | end checkLocalFieldOrder; | ||
| 220 | |||
| 221 | function collectRecordParams | ||
| 222 | input InstNode recNode; | ||
| 223 | output list<InstNode> inputs = {}; | ||
| 224 | output list<InstNode> locals = {}; | ||
| 225 | output list<InstNode> allParams = {}; | ||
| 226 | protected | ||
| 227 | InstNode comp; | ||
| 228 | array<InstNode> comps; | ||
| 229 | array<Mutable<InstNode>> pcomps; | ||
| 230 | ClassTree tree; | ||
| 231 | algorithm | ||
| 232 | 2789 | tree := Class.classTree(InstNode.getClass(recNode)); | |
| 233 | |||
| 234 | () := match tree | ||
| 235 | case ClassTree.FLAT_TREE(components = comps) | ||
| 236 | algorithm | ||
| 237 |
2/2✓ Branch 0 taken 2779 times.
✓ Branch 1 taken 10 times.
|
27344 | for i in arrayLength(comps):-1:1 loop |
| 238 | 24555 | comp := comps[i]; | |
| 239 | 24555 | (inputs, locals) := collectRecordParam(comp, inputs, locals); | |
| 240 | allParams := comp :: allParams; | ||
| 241 | end for; | ||
| 242 | then | ||
| 243 | (); | ||
| 244 | |||
| 245 | case ClassTree.INSTANTIATED_TREE(components = pcomps) | ||
| 246 | algorithm | ||
| 247 | ✗ | for i in arrayLength(pcomps):-1:1 loop | |
| 248 | ✗ | comp := Mutable.access(pcomps[i]); | |
| 249 | ✗ | (inputs, locals) := collectRecordParam(comp, inputs, locals); | |
| 250 | allParams := comp :: allParams; | ||
| 251 | end for; | ||
| 252 | then | ||
| 253 | (); | ||
| 254 | |||
| 255 | else | ||
| 256 | algorithm | ||
| 257 | ✗ | Error.terminate(getInstanceName() + " got non-instantiated function", sourceInfo()); | |
| 258 | ✗ | then | |
| 259 | fail(); | ||
| 260 | |||
| 261 | end match; | ||
| 262 | end collectRecordParams; | ||
| 263 | |||
| 264 | function collectRecordParam | ||
| 265 | input InstNode component; | ||
| 266 | input output list<InstNode> inputs; | ||
| 267 | input output list<InstNode> locals; | ||
| 268 | protected | ||
| 269 | Component comp; | ||
| 270 | InstNode comp_node = InstNode.resolveInner(component); | ||
| 271 | algorithm | ||
| 272 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24555 times.
|
24555 | if InstNode.isProtected(comp_node) then |
| 273 | locals := comp_node :: locals; | ||
| 274 | ✗ | return; | |
| 275 | end if; | ||
| 276 | |||
| 277 | 24555 | comp := InstNode.component(comp_node); | |
| 278 | |||
| 279 |
2/2✓ Branch 1 taken 231 times.
✓ Branch 2 taken 24324 times.
|
24555 | if Component.isFinal(comp) then |
| 280 | 231 | setFieldDirection(comp_node, Direction.NONE); | |
| 281 | locals := comp_node :: locals; | ||
| 282 | else | ||
| 283 | 24324 | setFieldDirection(comp_node, Direction.INPUT); | |
| 284 | 24324 | InstNode.componentApply(comp_node, Component.setVariability, Variability.CONTINUOUS); | |
| 285 | inputs := comp_node :: inputs; | ||
| 286 | end if; | ||
| 287 | end collectRecordParam; | ||
| 288 | |||
| 289 | function setFieldDirection | ||
| 290 | input InstNode field; | ||
| 291 | input Direction direction; | ||
| 292 | algorithm | ||
| 293 | 24555 | InstNode.componentApply(field, Component.setDirection, direction); | |
| 294 | end setFieldDirection; | ||
| 295 | |||
| 296 | function collectRecordFields | ||
| 297 | input InstNode recNode; | ||
| 298 | output array<Field> fields; | ||
| 299 | output UnorderedMap<String, Integer> indexMap; | ||
| 300 | protected | ||
| 301 | list<Field> field_lst; | ||
| 302 | ClassTree tree; | ||
| 303 | algorithm | ||
| 304 | 28918 | tree := Class.classTree(InstNode.getClass(recNode)); | |
| 305 | 28918 | field_lst := ClassTree.foldComponents(tree, collectRecordField, {}); | |
| 306 | 28918 | fields := listArray(listReverseInPlace(field_lst)); | |
| 307 | 28918 | indexMap := UnorderedMap.new<Integer>(stringHashDjb2, stringEq, arrayLength(fields)); | |
| 308 | 28918 | Type.updateRecordFieldsIndexMap(fields, indexMap); | |
| 309 | end collectRecordFields; | ||
| 310 | |||
| 311 | function collectRecordField | ||
| 312 | input InstNode component; | ||
| 313 | input output list<Field> fields; | ||
| 314 | protected | ||
| 315 | InstNode comp_node = InstNode.resolveInner(component); | ||
| 316 | Component comp; | ||
| 317 | algorithm | ||
| 318 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 241037 times.
|
241037 | if InstNode.isProtected(comp_node) then |
| 319 | ✗ | fields := Field.LOCAL(InstNode.name(comp_node)) :: fields; | |
| 320 | else | ||
| 321 | 241037 | comp := InstNode.component(comp_node); | |
| 322 | |||
| 323 |
2/2✓ Branch 1 taken 1115 times.
✓ Branch 2 taken 239922 times.
|
241037 | if Component.isFinal(comp) then |
| 324 | 1115 | fields := Field.LOCAL(InstNode.name(comp_node)) :: fields; | |
| 325 | elseif not Component.isOutput(comp) then | ||
| 326 | 211004 | fields := Field.INPUT(InstNode.name(comp_node)) :: fields; | |
| 327 | end if; | ||
| 328 | end if; | ||
| 329 | end collectRecordField; | ||
| 330 | |||
| 331 | function fieldsToDAE | ||
| 332 | input list<Field> fields; | ||
| 333 | output list<String> fieldNames = {}; | ||
| 334 | algorithm | ||
| 335 | ✗ | for field in fields loop | |
| 336 | () := match field | ||
| 337 | case Field.INPUT() | ||
| 338 | algorithm | ||
| 339 | ✗ | fieldNames := field.name :: fieldNames; | |
| 340 | then | ||
| 341 | (); | ||
| 342 | |||
| 343 | else (); | ||
| 344 | end match; | ||
| 345 | end for; | ||
| 346 | end fieldsToDAE; | ||
| 347 | |||
| 348 | function foldInputFields<T, ArgT> | ||
| 349 | input list<Field> fields; | ||
| 350 | input list<T> args; | ||
| 351 | input FuncT func; | ||
| 352 | input output ArgT foldArg; | ||
| 353 | |||
| 354 | partial function FuncT | ||
| 355 | input T arg; | ||
| 356 | input output ArgT foldArg; | ||
| 357 | end FuncT; | ||
| 358 | protected | ||
| 359 | T arg; | ||
| 360 | list<T> rest_args = args; | ||
| 361 | algorithm | ||
| 362 | ✗ | for field in fields loop | |
| 363 | ✗ | arg :: rest_args := rest_args; | |
| 364 | |||
| 365 | ✗ | if Field.isInput(field) then | |
| 366 | ✗ | foldArg := func(arg, foldArg); | |
| 367 | end if; | ||
| 368 | end for; | ||
| 369 | end foldInputFields; | ||
| 370 | |||
| 371 | function toDeclarationStream | ||
| 372 | input InstNode recordNode; | ||
| 373 | input String indent; | ||
| 374 | input output IOStream.IOStream s; | ||
| 375 | protected | ||
| 376 | InstNode node; | ||
| 377 | algorithm | ||
| 378 | ✗ | node := getDeclarationNode(recordNode, evaluate = false); | |
| 379 | ✗ | s := IOStream.append(s, indent); | |
| 380 | ✗ | s := IOStream.append(s, InstNode.toString(node)); | |
| 381 | end toDeclarationStream; | ||
| 382 | |||
| 383 | function toFlatDeclarationStream | ||
| 384 | input InstNode recordNode; | ||
| 385 | input BaseModelica.OutputFormat format; | ||
| 386 | input String indent; | ||
| 387 | input output IOStream.IOStream s; | ||
| 388 | protected | ||
| 389 | InstNode node; | ||
| 390 | algorithm | ||
| 391 | 26 | node := getDeclarationNode(recordNode, evaluate = true); | |
| 392 | 26 | s := IOStream.append(s, InstNode.toFlatString(node, format, indent)); | |
| 393 | end toFlatDeclarationStream; | ||
| 394 | |||
| 395 | function getDeclarationNode | ||
| 396 | input InstNode recordNode; | ||
| 397 | input Boolean evaluate; | ||
| 398 | output InstNode declNode; | ||
| 399 | protected | ||
| 400 | InstNodeType node_ty; | ||
| 401 | algorithm | ||
| 402 | 26 | node_ty := InstNode.nodeType(recordNode); | |
| 403 | 26 | declNode := instRecord(recordNode); | |
| 404 | 26 | Typing.typeClass(declNode, NFInstContext.RELAXED); | |
| 405 | // Keep the node type from the original node to get the correct name. | ||
| 406 | 26 | declNode := InstNode.setNodeType(node_ty, declNode); | |
| 407 | |||
| 408 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | if evaluate then |
| 409 | 26 | EvalConstants.evaluateRecordDeclaration(declNode); | |
| 410 | end if; | ||
| 411 | end getDeclarationNode; | ||
| 412 | |||
| 413 | annotation(__OpenModelica_Interface="nf_frontend"); | ||
| 414 | end NFRecord; | ||
| 415 |