OMCompiler/Compiler/NFFrontEnd/NFOperatorOverloading.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 NFOperatorOverloading | ||
| 37 | import Absyn; | ||
| 38 | import AbsynUtil; | ||
| 39 | import NFInstNode.InstNode; | ||
| 40 | import NFFunction.Function; | ||
| 41 | import Type = NFType; | ||
| 42 | |||
| 43 | protected | ||
| 44 | import Record = NFRecord; | ||
| 45 | import ComponentRef = NFComponentRef; | ||
| 46 | import NFClassTree.ClassTree; | ||
| 47 | import Class = NFClass; | ||
| 48 | import Component = NFComponent; | ||
| 49 | import Binding = NFBinding; | ||
| 50 | import Expression = NFExpression; | ||
| 51 | import Call = NFCall; | ||
| 52 | import SCodeUtil; | ||
| 53 | import InstContext = NFInstContext; | ||
| 54 | |||
| 55 | public | ||
| 56 | function instConstructor | ||
| 57 | input Absyn.Path path; | ||
| 58 | input output InstNode recordNode; | ||
| 59 | input InstContext.Type context; | ||
| 60 | input SourceInfo info; | ||
| 61 | protected | ||
| 62 | ComponentRef ctor_ref = ComponentRef.EMPTY(); | ||
| 63 | Absyn.Path ctor_path; | ||
| 64 | Boolean ctor_overloaded; | ||
| 65 | InstNode ctor_node; | ||
| 66 | algorithm | ||
| 67 | // Check if the operator record has an overloaded constructor declared. | ||
| 68 | try | ||
| 69 | 250 | ctor_ref := Function.lookupFunctionSimple("'constructor'", recordNode, context); | |
| 70 | ctor_overloaded := true; | ||
| 71 | else | ||
| 72 | ctor_overloaded := false; | ||
| 73 | end try; | ||
| 74 | |||
| 75 |
2/2✓ Branch 0 taken 237 times.
✓ Branch 1 taken 13 times.
|
250 | if ctor_overloaded then |
| 76 | // If it has an overloaded constructor, instantiate it and add the | ||
| 77 | // function(s) to the record node. | ||
| 78 | 237 | (_, ctor_node) := Function.instFunctionRef(ctor_ref, context, info); | |
| 79 | 237 | ctor_path := InstNode.fullPath(ctor_node); | |
| 80 | |||
| 81 |
2/2✓ Branch 1 taken 237 times.
✓ Branch 2 taken 236 times.
|
710 | for f in Function.getCachedFuncs(ctor_node) loop |
| 82 | 237 | checkOperatorConstructorOutput(f, Class.lastBaseClass(recordNode), ctor_path, info); | |
| 83 | 236 | recordNode := InstNode.cacheAddFunc(recordNode, f, false); | |
| 84 | end for; | ||
| 85 | end if; | ||
| 86 | |||
| 87 | 249 | recordNode := Record.instDefaultConstructor(path, recordNode, context, info); | |
| 88 | end instConstructor; | ||
| 89 | |||
| 90 | function instOperatorFunctions | ||
| 91 | input output InstNode node; | ||
| 92 | input InstContext.Type context; | ||
| 93 | input SourceInfo info; | ||
| 94 | protected | ||
| 95 | ClassTree tree; | ||
| 96 | array<InstNode> mclss; | ||
| 97 | list<Function> allfuncs = {}, funcs; | ||
| 98 | algorithm | ||
| 99 | 135 | checkOperatorRestrictions(node); | |
| 100 | 135 | tree := Class.classTree(InstNode.getClass(node)); | |
| 101 | |||
| 102 | () := match tree | ||
| 103 | case ClassTree.FLAT_TREE(classes = mclss) | ||
| 104 | algorithm | ||
| 105 |
2/2✓ Branch 1 taken 202 times.
✓ Branch 2 taken 135 times.
|
337 | for op in mclss loop |
| 106 | 202 | Function.instFunctionNode(op, context, info); | |
| 107 | 202 | funcs := Function.getCachedFuncs(op); | |
| 108 | 202 | allfuncs := listAppend(funcs, allfuncs); | |
| 109 | end for; | ||
| 110 | |||
| 111 |
2/2✓ Branch 0 taken 202 times.
✓ Branch 1 taken 135 times.
|
337 | for f in allfuncs loop |
| 112 | 202 | node := InstNode.cacheAddFunc(node, f, false); | |
| 113 | end for; | ||
| 114 | then | ||
| 115 | (); | ||
| 116 | |||
| 117 | else | ||
| 118 | algorithm | ||
| 119 | ✗ | Error.terminate(getInstanceName() + " got non-instantiated function", sourceInfo()); | |
| 120 | ✗ | then | |
| 121 | fail(); | ||
| 122 | |||
| 123 | end match; | ||
| 124 | end instOperatorFunctions; | ||
| 125 | |||
| 126 | function checkOperatorRestrictions | ||
| 127 | input InstNode operatorNode; | ||
| 128 | algorithm | ||
| 129 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 173 times.
|
173 | if not SCodeUtil.isElementEncapsulated(InstNode.definition(operatorNode)) then |
| 130 | ✗ | Error.addSourceMessage(Error.OPERATOR_NOT_ENCAPSULATED, | |
| 131 | {AbsynUtil.pathString(InstNode.fullPath(operatorNode))}, | ||
| 132 | InstNode.info(operatorNode)); | ||
| 133 | ✗ | fail(); | |
| 134 | end if; | ||
| 135 | end checkOperatorRestrictions; | ||
| 136 | |||
| 137 | function lookupOperatorFunctionsInType | ||
| 138 | input String operatorName; | ||
| 139 | input Type ty; | ||
| 140 | output list<Function> functions; | ||
| 141 | protected | ||
| 142 | InstNode node; | ||
| 143 | Type elem_ty; | ||
| 144 | ComponentRef fn_ref = ComponentRef.EMPTY(); | ||
| 145 | Boolean is_defined; | ||
| 146 | algorithm | ||
| 147 | functions := match Type.arrayElementType(ty) | ||
| 148 | case elem_ty as Type.COMPLEX() | ||
| 149 | algorithm | ||
| 150 | 1683 | node := Type.complexNode(elem_ty); | |
| 151 | |||
| 152 | try | ||
| 153 | 1683 | fn_ref := Function.lookupFunctionSimple(operatorName, node, NFInstContext.NO_CONTEXT); | |
| 154 | is_defined := true; | ||
| 155 | else | ||
| 156 | is_defined := false; | ||
| 157 | end try; | ||
| 158 | |||
| 159 |
2/2✓ Branch 0 taken 1682 times.
✓ Branch 1 taken 1 time.
|
1683 | if is_defined then |
| 160 | 1682 | fn_ref := Function.instFunctionRef(fn_ref, NFInstContext.NO_CONTEXT, InstNode.info(node)); | |
| 161 | 1682 | functions := Function.typeRefCache(fn_ref); | |
| 162 | else | ||
| 163 | functions := {}; | ||
| 164 | end if; | ||
| 165 | then | ||
| 166 | functions; | ||
| 167 | |||
| 168 | else {}; | ||
| 169 | end match; | ||
| 170 | end lookupOperatorFunctionsInType; | ||
| 171 | |||
| 172 | function patchOperatorRecordConstructorBinding | ||
| 173 | "Patches operator record constructors to avoid recursive binding. | ||
| 174 | |||
| 175 | They often have outputs declared as: | ||
| 176 | output RecordType result = RecordType(args) | ||
| 177 | |||
| 178 | The binding in such cases causes a recursive definition of the constructor, | ||
| 179 | so to avoid that we rewrite any calls to the constructor in the binding as | ||
| 180 | record expressions." | ||
| 181 | input output Function fn; | ||
| 182 | protected | ||
| 183 | InstNode output_node; | ||
| 184 | Component output_comp; | ||
| 185 | Binding output_binding; | ||
| 186 | algorithm | ||
| 187 | // Due to how this function is used it might also be called on destructors, | ||
| 188 | // which we just ignore. | ||
| 189 |
2/2✓ Branch 1 taken 64 times.
✓ Branch 2 taken 4514 times.
|
4578 | if listLength(fn.outputs) <> 1 then |
| 190 | 64 | return; | |
| 191 | end if; | ||
| 192 | |||
| 193 | 4514 | output_node := InstNode.fromHandle(listHead(fn.outputs)); | |
| 194 | 4514 | output_comp := InstNode.component(output_node); | |
| 195 | 4514 | output_binding := Component.getBinding(output_comp); | |
| 196 | |||
| 197 |
2/2✓ Branch 1 taken 4508 times.
✓ Branch 2 taken 6 times.
|
4514 | if not Binding.isBound(output_binding) then |
| 198 | 4508 | return; | |
| 199 | end if; | ||
| 200 | |||
| 201 | 6 | output_binding := Binding.mapExp(output_binding, | |
| 202 | function patchOperatorRecordConstructorBinding_traverser(constructorFn = fn)); | ||
| 203 | 6 | output_comp := Component.setBinding(output_binding, output_comp); | |
| 204 | 6 | output_node := InstNode.updateComponent(output_comp, output_node); | |
| 205 | end patchOperatorRecordConstructorBinding; | ||
| 206 | |||
| 207 | protected | ||
| 208 | function checkOperatorConstructorOutput | ||
| 209 | input Function fn; | ||
| 210 | input InstNode recordNode; | ||
| 211 | input Absyn.Path path; | ||
| 212 | input SourceInfo info; | ||
| 213 | protected | ||
| 214 | InstNode output_node, output_ty; | ||
| 215 | algorithm | ||
| 216 |
2/2✓ Branch 1 taken 1 time.
✓ Branch 2 taken 236 times.
|
237 | if listLength(fn.outputs) <> 1 then |
| 217 | 2 | Error.addSourceMessage(Error.OPERATOR_OVERLOADING_ONE_OUTPUT_ERROR, | |
| 218 | {AbsynUtil.pathString(path)}, info); | ||
| 219 | 1 | fail(); | |
| 220 | end if; | ||
| 221 | |||
| 222 | 236 | output_node := InstNode.fromHandle(listHead(fn.outputs)); | |
| 223 | 236 | output_ty := InstNode.classScope(output_node); | |
| 224 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 236 times.
|
236 | if not InstNode.isSame(output_ty, recordNode) then |
| 225 | ✗ | Error.addSourceMessage(Error.OPERATOR_OVERLOADING_INVALID_OUTPUT_TYPE, | |
| 226 | {InstNode.name(output_node), AbsynUtil.pathString(path), | ||
| 227 | InstNode.name(recordNode), InstNode.name(output_ty)}, info); | ||
| 228 | ✗ | fail(); | |
| 229 | end if; | ||
| 230 | end checkOperatorConstructorOutput; | ||
| 231 | |||
| 232 | function patchOperatorRecordConstructorBinding_traverser | ||
| 233 | input Expression exp; | ||
| 234 | input Function constructorFn; | ||
| 235 | output Expression outExp; | ||
| 236 | protected | ||
| 237 | Function fn; | ||
| 238 | list<Expression> args; | ||
| 239 | Type ty; | ||
| 240 | algorithm | ||
| 241 | outExp := match exp | ||
| 242 | case Expression.CALL(call = Call.TYPED_CALL(fn = fn, ty = ty, arguments = args)) | ||
| 243 | guard referenceEq(InstNode.fromHandle(constructorFn.node), InstNode.fromHandle(fn.node)) | ||
| 244 | 4 | then Expression.makeRecord(Function.name(constructorFn), ty, args); | |
| 245 | |||
| 246 | else exp; | ||
| 247 | end match; | ||
| 248 | end patchOperatorRecordConstructorBinding_traverser; | ||
| 249 | |||
| 250 | annotation(__OpenModelica_Interface="nf_frontend"); | ||
| 251 | end NFOperatorOverloading; | ||
| 252 |