Linux GNU 11.4.0 Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 75.7% 56 / 0 / 74
Functions: -% 0 / 1 / 1
Branches: 47.1% 16 / 0 / 34

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