GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 98.4% 254 / 0 / 258
Functions: 100.0% 18 / 0 / 18
Branches: 65.0% 290 / 0 / 446

src/typechecker/FunctionManager.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "FunctionManager.h"
4
5 #include <ast/ASTNodes.h>
6 #include <exception/SemanticError.h>
7 #include <model/GenericType.h>
8 #include <symboltablebuilder/Scope.h>
9 #include <symboltablebuilder/SymbolTableBuilder.h>
10 #include <typechecker/TypeChecker.h>
11 #include <typechecker/TypeMatcher.h>
12 #include <util/CodeLoc.h>
13 #include <util/CustomHashFunctions.h>
14
15 namespace spice::compiler {
16
17 // Static member initialization
18 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
19 size_t FunctionManager::lookupCacheHits = 0;
20 size_t FunctionManager::lookupCacheMisses = 0;
21
22 14434 Function *FunctionManager::insert(Scope *insertScope, const Function &baseFunction, std::vector<Function *> *nodeFunctionList) {
23 // Open a new manifestation list for the function definition
24
3/6
✓ Branch 2 → 3 taken 14434 times.
✗ Branch 2 → 41 not taken.
✓ Branch 3 → 4 taken 14434 times.
✗ Branch 3 → 38 not taken.
✓ Branch 4 → 5 taken 14434 times.
✗ Branch 4 → 36 not taken.
14434 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
25
1/2
✓ Branch 8 → 9 taken 14434 times.
✗ Branch 8 → 42 not taken.
14434 insertScope->functions.emplace(fctId, FunctionManifestationList());
26
27 // Collect substantiations
28 14434 std::vector<Function> manifestations;
29
1/2
✓ Branch 10 → 11 taken 14434 times.
✗ Branch 10 → 46 not taken.
14434 substantiateOptionalParams(baseFunction, manifestations);
30
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 14434 times.
14434 assert(!manifestations.empty());
31
32 // Save substantiations in declaration node
33 14434 Function *manifestationPtr = nullptr;
34
2/2
✓ Branch 24 → 16 taken 15411 times.
✓ Branch 24 → 25 taken 14432 times.
29843 for (const Function &manifestation : manifestations) {
35
2/2
✓ Branch 17 → 18 taken 15409 times.
✓ Branch 17 → 45 taken 2 times.
15411 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
36
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 15409 times.
15409 assert(manifestationPtr != nullptr);
37
1/2
✓ Branch 20 → 21 taken 15409 times.
✗ Branch 20 → 22 not taken.
15409 if (nodeFunctionList)
38
1/2
✓ Branch 21 → 22 taken 15409 times.
✗ Branch 21 → 45 not taken.
15409 nodeFunctionList->push_back(manifestationPtr);
39 }
40
41
1/2
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 14432 times.
14432 if (!nodeFunctionList)
42 return manifestationPtr;
43
44
1/2
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 14432 times.
14432 assert(!nodeFunctionList->empty());
45 14432 return nodeFunctionList->front();
46 14436 }
47
48 /**
49 * Create definite functions from ambiguous ones, in regard to optional arguments.
50 *
51 * Example:
52 * int testFunc(string, int?, double?)
53 * gets
54 * int testFunc(string)
55 * int testFunc(string, int)
56 * int testFunc(string, int, double)
57 *
58 * This method also accepts functions, that are already definite, but does nothing to them.
59 *
60 * @param baseFunction Ambiguous base function
61 * @param manifestations Vector to store the definite manifestations
62 * @return True, if there were optional arguments found
63 */
64 14434 void FunctionManager::substantiateOptionalParams(const Function &baseFunction, std::vector<Function> &manifestations) {
65 // Handle the case of no parameters -> simply return the base function
66
2/2
✓ Branch 3 → 4 taken 3466 times.
✓ Branch 3 → 6 taken 10968 times.
14434 if (baseFunction.paramList.empty()) {
67
1/2
✓ Branch 4 → 5 taken 3466 times.
✗ Branch 4 → 39 not taken.
3466 manifestations.push_back(baseFunction);
68 3466 return;
69 }
70
71 10968 ParamList currentFunctionParamTypes;
72
1/2
✓ Branch 7 → 8 taken 10968 times.
✗ Branch 7 → 37 not taken.
10968 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
73 10968 bool metFirstOptionalParam = false;
74
1/2
✓ Branch 8 → 9 taken 10968 times.
✗ Branch 8 → 37 not taken.
10968 Function manifestation = baseFunction;
75
76 // Loop over all parameters
77
2/2
✓ Branch 24 → 11 taken 17096 times.
✓ Branch 24 → 25 taken 10968 times.
28064 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
78 // Check if we have a mandatory parameter
79
2/2
✓ Branch 12 → 13 taken 16119 times.
✓ Branch 12 → 15 taken 977 times.
17096 if (!isOptional) {
80
1/2
✓ Branch 13 → 14 taken 16119 times.
✗ Branch 13 → 32 not taken.
16119 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
81 16119 continue;
82 }
83
84 // Add substantiation without the optional parameter
85
2/2
✓ Branch 15 → 16 taken 971 times.
✓ Branch 15 → 19 taken 6 times.
977 if (!metFirstOptionalParam) {
86
1/2
✓ Branch 16 → 17 taken 971 times.
✗ Branch 16 → 34 not taken.
971 manifestation.paramList = currentFunctionParamTypes;
87
1/2
✓ Branch 17 → 18 taken 971 times.
✗ Branch 17 → 34 not taken.
971 manifestations.push_back(manifestation);
88 // Now we cannot accept mandatory parameters anymore
89 971 metFirstOptionalParam = true;
90 }
91
92 // Add substantiation with the optional parameter
93
1/2
✓ Branch 19 → 20 taken 977 times.
✗ Branch 19 → 33 not taken.
977 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
94
1/2
✓ Branch 20 → 21 taken 977 times.
✗ Branch 20 → 34 not taken.
977 manifestation.paramList = currentFunctionParamTypes;
95
1/2
✓ Branch 21 → 22 taken 977 times.
✗ Branch 21 → 34 not taken.
977 manifestations.push_back(manifestation);
96 }
97
98 // Ensure at least once manifestation
99
2/2
✓ Branch 26 → 27 taken 9997 times.
✓ Branch 26 → 28 taken 971 times.
10968 if (manifestations.empty())
100
1/2
✓ Branch 27 → 28 taken 9997 times.
✗ Branch 27 → 35 not taken.
9997 manifestations.push_back(baseFunction);
101 10968 }
102
103 6 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
104 6 ParamList paramList;
105
2/2
✓ Branch 8 → 4 taken 2 times.
✓ Branch 8 → 9 taken 6 times.
8 for (const QualType &paramType : paramTypes)
106
1/2
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 24 not taken.
2 paramList.push_back({paramType, false});
107
5/10
✓ Branch 11 → 12 taken 6 times.
✗ Branch 11 → 36 not taken.
✓ Branch 12 → 13 taken 6 times.
✗ Branch 12 → 33 not taken.
✓ Branch 13 → 14 taken 6 times.
✗ Branch 13 → 32 not taken.
✓ Branch 14 → 15 taken 6 times.
✗ Branch 14 → 31 not taken.
✓ Branch 16 → 17 taken 6 times.
✗ Branch 16 → 26 not taken.
12 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
108 6 }
109
110 18460 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
111
2/4
✓ Branch 2 → 3 taken 18460 times.
✗ Branch 2 → 70 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 18460 times.
18460 assert(newManifestation.hasSubstantiatedParams());
112
113
1/2
✓ Branch 5 → 6 taken 18460 times.
✗ Branch 5 → 70 not taken.
18460 const std::string signature = newManifestation.getSignature(true, true, false);
114
115 // Check if the function exists already
116
5/8
✓ Branch 6 → 7 taken 18460 times.
✗ Branch 6 → 59 not taken.
✓ Branch 7 → 8 taken 18460 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 18460 times.
✗ Branch 8 → 59 not taken.
✓ Branch 30 → 10 taken 296032 times.
✓ Branch 30 → 31 taken 18458 times.
314490 for (const auto &manifestations : insertScope->functions | std::views::values) {
117
3/4
✓ Branch 11 → 12 taken 296032 times.
✗ Branch 11 → 59 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 28 taken 296030 times.
296032 if (manifestations.contains(signature)) {
118
2/2
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 1 time.
2 const SemanticErrorType errorType = newManifestation.isFunction() ? FUNCTION_DECLARED_TWICE : PROCEDURE_DECLARED_TWICE;
119
4/8
✓ Branch 20 → 21 taken 2 times.
✗ Branch 20 → 53 not taken.
✓ Branch 21 → 22 taken 2 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 2 times.
✗ Branch 22 → 49 not taken.
✓ Branch 23 → 24 taken 2 times.
✗ Branch 23 → 47 not taken.
2 throw SemanticError(declNode, errorType, "'" + newManifestation.getSignature(true, false) + "' is declared twice");
120 }
121 }
122
123 // Retrieve the matching manifestation list of the scope
124
3/6
✓ Branch 31 → 32 taken 18458 times.
✗ Branch 31 → 65 not taken.
✓ Branch 32 → 33 taken 18458 times.
✗ Branch 32 → 62 not taken.
✓ Branch 33 → 34 taken 18458 times.
✗ Branch 33 → 60 not taken.
18458 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
125
2/4
✓ Branch 36 → 37 taken 18458 times.
✗ Branch 36 → 66 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 18458 times.
18458 assert(insertScope->functions.contains(fctId));
126
1/2
✓ Branch 39 → 40 taken 18458 times.
✗ Branch 39 → 66 not taken.
18458 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
127
128 // Add substantiated function
129
1/2
✓ Branch 40 → 41 taken 18458 times.
✗ Branch 40 → 66 not taken.
18458 manifestationList.emplace(signature, newManifestation);
130
1/2
✓ Branch 41 → 42 taken 18458 times.
✗ Branch 41 → 66 not taken.
36916 return &manifestationList.at(signature);
131 18460 }
132
133 /**
134 * Checks if a function exists by matching it, but not setting it to used
135 *
136 * @param matchScope Scope to match against
137 * @param reqName Function name requirement
138 * @param reqThisType This type requirement
139 * @param reqArgs Argument requirement
140 * @param strictQualifierMatching Match argument and this type qualifiers strictly
141 * @return Found function or nullptr
142 */
143 13651 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
144 const ArgList &reqArgs, bool strictQualifierMatching) {
145
2/4
✓ Branch 2 → 3 taken 13651 times.
✗ Branch 2 → 65 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 13651 times.
13651 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
146
147 // Do cache lookup
148 13651 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
149
3/4
✓ Branch 8 → 9 taken 13651 times.
✗ Branch 8 → 66 not taken.
✓ Branch 11 → 12 taken 3827 times.
✓ Branch 11 → 14 taken 9824 times.
13651 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
150 3827 lookupCacheHits++;
151 3827 return it->second;
152 }
153 9824 lookupCacheMisses++;
154
155 2899 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
156
5/8
✓ Branch 14 → 16 taken 9824 times.
✗ Branch 14 → 75 not taken.
✓ Branch 16 → 17 taken 9443 times.
✓ Branch 16 → 20 taken 381 times.
✓ Branch 17 → 18 taken 9443 times.
✗ Branch 17 → 75 not taken.
✓ Branch 18 → 19 taken 9443 times.
✗ Branch 18 → 20 not taken.
9824 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
157
158 // Loop over function registry to find functions, that match the requirements of the call
159 9824 std::vector<const Function *> matches;
160
2/2
✓ Branch 55 → 23 taken 80618 times.
✓ Branch 55 → 56 taken 9824 times.
90442 for (const auto &[defCodeLocStr, manifestations] : matchScope->functions) {
161
2/2
✓ Branch 52 → 28 taken 90904 times.
✓ Branch 52 → 53 taken 28774 times.
119678 for (const auto &[signature, presetFunction] : manifestations) {
162
2/4
✓ Branch 31 → 32 taken 90904 times.
✗ Branch 31 → 70 not taken.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 90904 times.
90904 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
163
164 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
165 // - search for generic fct: Only match against generic preset functions
166
3/4
✓ Branch 34 → 35 taken 90904 times.
✗ Branch 34 → 70 not taken.
✓ Branch 35 → 36 taken 32078 times.
✓ Branch 35 → 37 taken 58826 times.
90904 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
167 39060 continue;
168
169 // Copy the function to be able to substantiate types
170
1/2
✓ Branch 37 → 38 taken 58826 times.
✗ Branch 37 → 70 not taken.
58826 Function candidate = presetFunction;
171
172 // Create empty type mapping
173 58826 TypeMapping &typeMapping = candidate.typeMapping;
174
175 58826 bool forceSubstantiation = false;
176
1/2
✓ Branch 38 → 39 taken 58826 times.
✗ Branch 38 → 68 not taken.
58826 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
177 strictQualifierMatching, forceSubstantiation, nullptr);
178
2/2
✓ Branch 39 → 40 taken 50345 times.
✓ Branch 39 → 41 taken 8481 times.
58826 if (matchResult == MatchResult::SKIP_FUNCTION)
179 50345 break; // Leave the whole function
180
2/2
✓ Branch 41 → 42 taken 6982 times.
✓ Branch 41 → 43 taken 1499 times.
8481 if (matchResult == MatchResult::SKIP_MANIFESTATION)
181 6982 continue; // Leave this manifestation and try the next one
182
183 // Add to matches
184
3/6
✓ Branch 43 → 44 taken 1499 times.
✗ Branch 43 → 67 not taken.
✓ Branch 44 → 45 taken 1499 times.
✗ Branch 44 → 67 not taken.
✓ Branch 45 → 46 taken 1499 times.
✗ Branch 45 → 67 not taken.
1499 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
185
186 1499 break; // Leave the whole manifestation list to not double-match the manifestation
187
2/2
✓ Branch 48 → 49 taken 6982 times.
✓ Branch 48 → 50 taken 51844 times.
58826 }
188 }
189
190 // Return the very match or a nullptr
191
2/2
✓ Branch 57 → 58 taken 1499 times.
✓ Branch 57 → 60 taken 8325 times.
9824 return !matches.empty() ? matches.front() : nullptr;
192 9824 }
193
194 /**
195 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
196 * If more than one function matches the requirement, an error gets thrown.
197 *
198 * @param matchScope Scope to match against
199 * @param reqName Function name requirement
200 * @param reqThisType This type requirement
201 * @param reqArgs Argument requirement
202 * @param templateTypeHints Template type requirement
203 * @param strictQualifierMatching Match argument and this type qualifiers strictly
204 * @param callNode Call AST node for printing error messages
205 * @return Matched function or nullptr
206 */
207 81553 Function *FunctionManager::match(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
208 const ArgList &reqArgs, const QualTypeList &templateTypeHints, bool strictQualifierMatching,
209 const ASTNode *callNode) {
210
2/4
✓ Branch 2 → 3 taken 81553 times.
✗ Branch 2 → 172 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 81553 times.
81553 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
211
212 // Do cache lookup
213 81553 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
214
3/4
✓ Branch 6 → 7 taken 81553 times.
✗ Branch 6 → 173 not taken.
✓ Branch 9 → 10 taken 13094 times.
✓ Branch 9 → 12 taken 68459 times.
81553 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
215 13094 lookupCacheHits++;
216 13094 return it->second;
217 }
218 68459 lookupCacheMisses++;
219
220 // Loop over function registry to find functions, that match the requirements of the call
221 68459 std::vector<Function *> matches;
222
2/2
✓ Branch 140 → 15 taken 928760 times.
✓ Branch 140 → 141 taken 68458 times.
997218 for (auto &[fctId, manifestations] : matchScope->functions) {
223
2/2
✓ Branch 137 → 20 taken 939326 times.
✓ Branch 137 → 138 taken 50681 times.
990007 for (const auto &[signature, presetFunction] : manifestations) {
224
2/4
✓ Branch 23 → 24 taken 939326 times.
✗ Branch 23 → 196 not taken.
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 26 taken 939326 times.
939326 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
225
226 // Skip generic and newly inserted substantiations to prevent double matching of a function
227
6/8
✓ Branch 26 → 27 taken 939326 times.
✗ Branch 26 → 196 not taken.
✓ Branch 27 → 28 taken 930991 times.
✓ Branch 27 → 29 taken 8335 times.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 30 taken 930991 times.
✓ Branch 31 → 32 taken 8335 times.
✓ Branch 31 → 33 taken 930991 times.
939326 if (presetFunction.isGenericSubstantiation() || presetFunction.isNewlyInserted)
228 61247 continue;
229
230 // Copy the function to be able to substantiate types
231
1/2
✓ Branch 33 → 34 taken 930991 times.
✗ Branch 33 → 196 not taken.
930991 Function candidate = presetFunction;
232
233 // Prepare type mapping, based on the given initial type mapping
234 930991 TypeMapping &typeMapping = candidate.typeMapping;
235 930991 typeMapping.clear();
236
2/2
✓ Branch 44 → 36 taken 41868 times.
✓ Branch 44 → 45 taken 930991 times.
972859 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
237
2/4
✓ Branch 36 → 37 taken 41868 times.
✗ Branch 36 → 194 not taken.
✓ Branch 37 → 38 taken 41868 times.
✗ Branch 37 → 194 not taken.
41868 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
238
1/2
✓ Branch 38 → 39 taken 41868 times.
✗ Branch 38 → 194 not taken.
41868 const QualType &templateType = templateTypeHints.at(i);
239
1/2
✓ Branch 39 → 40 taken 41868 times.
✗ Branch 39 → 194 not taken.
41868 typeMapping.emplace(typeName, templateType);
240 }
241
242 930991 bool forceSubstantiation = false;
243
2/2
✓ Branch 45 → 46 taken 930990 times.
✓ Branch 45 → 194 taken 1 time.
930991 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
244 strictQualifierMatching, forceSubstantiation, callNode);
245
2/2
✓ Branch 46 → 47 taken 874651 times.
✓ Branch 46 → 48 taken 56339 times.
930990 if (matchResult == MatchResult::SKIP_FUNCTION)
246 874651 break; // Leave the whole function
247
2/2
✓ Branch 48 → 49 taken 46836 times.
✓ Branch 48 → 50 taken 9503 times.
56339 if (matchResult == MatchResult::SKIP_MANIFESTATION)
248 46836 continue; // Leave this manifestation and try the next one
249
250 // We found a match! -> Set the actual candidate and its entry to used
251 9503 candidate.used = true;
252 9503 candidate.entry->used = true;
253
254 // Check if the function is generic needs to be substantiated
255
6/6
✓ Branch 51 → 52 taken 6163 times.
✓ Branch 51 → 54 taken 3340 times.
✓ Branch 52 → 53 taken 6076 times.
✓ Branch 52 → 54 taken 87 times.
✓ Branch 55 → 56 taken 6076 times.
✓ Branch 55 → 68 taken 3427 times.
9503 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
256
5/10
✓ Branch 56 → 57 taken 6076 times.
✗ Branch 56 → 174 not taken.
✓ Branch 57 → 58 taken 6076 times.
✗ Branch 57 → 62 not taken.
✓ Branch 58 → 59 taken 6076 times.
✗ Branch 58 → 174 not taken.
✓ Branch 59 → 60 taken 6076 times.
✗ Branch 59 → 174 not taken.
✓ Branch 60 → 61 taken 6076 times.
✗ Branch 60 → 62 not taken.
6076 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
257
2/4
✓ Branch 63 → 64 taken 6076 times.
✗ Branch 63 → 174 not taken.
✓ Branch 64 → 65 taken 6076 times.
✗ Branch 64 → 174 not taken.
6076 Function *match = &matchScope->functions.at(fctId).at(signature);
258 6076 match->used = true;
259
1/2
✓ Branch 65 → 66 taken 6076 times.
✗ Branch 65 → 174 not taken.
6076 matches.push_back(match);
260 6076 continue; // Match was successful -> match the next function
261 6076 }
262
263 // Check if we already have this manifestation and can simply re-use it
264
1/2
✓ Branch 68 → 69 taken 3427 times.
✗ Branch 68 → 194 not taken.
3427 const std::string newSignature = candidate.getSignature(true, true, false);
265
3/4
✓ Branch 69 → 70 taken 3427 times.
✗ Branch 69 → 176 not taken.
✓ Branch 72 → 73 taken 378 times.
✓ Branch 72 → 77 taken 3049 times.
3427 if (const auto it = manifestations.find(newSignature); it != manifestations.end()) {
266 378 it->second.used = true;
267
1/2
✓ Branch 75 → 76 taken 378 times.
✗ Branch 75 → 175 not taken.
378 matches.push_back(&it->second);
268 378 break; // Leave the whole manifestation list to not double-match the manifestation
269 }
270
271 // Insert the substantiated version if required
272
1/2
✓ Branch 77 → 78 taken 3049 times.
✗ Branch 77 → 192 not taken.
3049 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
273
2/4
✓ Branch 78 → 79 taken 3049 times.
✗ Branch 78 → 192 not taken.
✓ Branch 79 → 80 taken 3049 times.
✗ Branch 79 → 192 not taken.
3049 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
274 3049 substantiatedFunction->alreadyTypeChecked = false;
275
2/4
✓ Branch 80 → 81 taken 3049 times.
✗ Branch 80 → 192 not taken.
✓ Branch 81 → 82 taken 3049 times.
✗ Branch 81 → 192 not taken.
3049 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
276 3049 substantiatedFunction->isNewlyInserted = true; // To not iterate over it in the same matching
277
278 // Copy function entry
279
1/2
✓ Branch 82 → 83 taken 3049 times.
✗ Branch 82 → 192 not taken.
3049 const std::string newScopeName = substantiatedFunction->getScopeName();
280
1/2
✓ Branch 83 → 84 taken 3049 times.
✗ Branch 83 → 190 not taken.
3049 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
281
1/2
✓ Branch 86 → 87 taken 3049 times.
✗ Branch 86 → 190 not taken.
3049 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newScopeName);
282
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 3049 times.
3049 assert(substantiatedFunction->entry != nullptr);
283
284 // Copy function scope
285
1/2
✓ Branch 89 → 90 taken 3049 times.
✗ Branch 89 → 190 not taken.
3049 const std::string oldScopeName = presetFunction.getScopeName();
286
1/2
✓ Branch 90 → 91 taken 3049 times.
✗ Branch 90 → 188 not taken.
3049 Scope *childScope = matchScope->copyChildScope(oldScopeName, newScopeName);
287
1/2
✗ Branch 91 → 92 not taken.
✓ Branch 91 → 93 taken 3049 times.
3049 assert(childScope != nullptr);
288 3049 childScope->isGenericScope = false;
289 3049 substantiatedFunction->bodyScope = childScope;
290
291 // Insert symbols for generic type names with concrete types into the child block
292
2/2
✓ Branch 103 → 95 taken 4256 times.
✓ Branch 103 → 104 taken 3049 times.
7305 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
293
2/4
✓ Branch 98 → 99 taken 4256 times.
✗ Branch 98 → 179 not taken.
✓ Branch 99 → 100 taken 4256 times.
✗ Branch 99 → 177 not taken.
4256 childScope->insertGenericType(typeName, GenericType(concreteType));
294
295 // Substantiate the 'this' entry in the new function scope
296
5/6
✓ Branch 107 → 108 taken 1892 times.
✓ Branch 107 → 111 taken 1157 times.
✓ Branch 109 → 110 taken 1892 times.
✗ Branch 109 → 111 not taken.
✓ Branch 112 → 113 taken 1892 times.
✓ Branch 112 → 126 taken 1157 times.
3049 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
297
1/2
✓ Branch 115 → 116 taken 1892 times.
✗ Branch 115 → 183 not taken.
5676 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
298
1/2
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 123 taken 1892 times.
1892 assert(thisEntry != nullptr);
299
2/4
✓ Branch 123 → 124 taken 1892 times.
✗ Branch 123 → 187 not taken.
✓ Branch 124 → 125 taken 1892 times.
✗ Branch 124 → 187 not taken.
1892 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
300 }
301
302 // Add to matched functions
303
1/2
✓ Branch 126 → 127 taken 3049 times.
✗ Branch 126 → 188 not taken.
3049 matches.push_back(substantiatedFunction);
304
305 3049 break; // Leave the whole manifestation list to not double-match the manifestation
306
2/2
✓ Branch 133 → 134 taken 52912 times.
✓ Branch 133 → 135 taken 878078 times.
934418 }
307 }
308
309 // If no matches were found, return a nullptr
310
2/2
✓ Branch 142 → 143 taken 58956 times.
✓ Branch 142 → 144 taken 9502 times.
68458 if (matches.empty())
311 58956 return nullptr;
312
313 // Check if more than one function matches the requirements
314
2/2
✓ Branch 145 → 146 taken 1 time.
✓ Branch 145 → 165 taken 9501 times.
9502 if (matches.size() > 1) {
315
1/2
✓ Branch 146 → 147 taken 1 time.
✗ Branch 146 → 211 not taken.
1 std::stringstream errorMessage;
316
3/6
✓ Branch 147 → 148 taken 1 time.
✗ Branch 147 → 209 not taken.
✓ Branch 148 → 149 taken 1 time.
✗ Branch 148 → 209 not taken.
✓ Branch 149 → 150 taken 1 time.
✗ Branch 149 → 209 not taken.
1 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
317
2/2
✓ Branch 159 → 152 taken 2 times.
✓ Branch 159 → 160 taken 1 time.
3 for (const Function *match : matches)
318
3/6
✓ Branch 153 → 154 taken 2 times.
✗ Branch 153 → 202 not taken.
✓ Branch 154 → 155 taken 2 times.
✗ Branch 154 → 201 not taken.
✓ Branch 155 → 156 taken 2 times.
✗ Branch 155 → 199 not taken.
2 errorMessage << "\n " << match->getSignature();
319
2/4
✓ Branch 161 → 162 taken 1 time.
✗ Branch 161 → 206 not taken.
✓ Branch 162 → 163 taken 1 time.
✗ Branch 162 → 203 not taken.
1 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
320 1 }
321 9501 Function *matchedFunction = matches.front();
322 9501 matchedFunction->isNewlyInserted = false;
323
324 // Insert into cache
325
1/2
✓ Branch 166 → 167 taken 9501 times.
✗ Branch 166 → 212 not taken.
9501 lookupCache[cacheKey] = matchedFunction;
326
327 // Trigger revisit in type checker if required
328
1/2
✓ Branch 167 → 168 taken 9501 times.
✗ Branch 167 → 212 not taken.
9501 TypeChecker::requestRevisitIfRequired(matchedFunction);
329
330 // Return the very match
331 9501 return matchedFunction;
332 68459 }
333
334 989817 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
335 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
336 bool strictQualifierMatching, bool &forceSubstantiation,
337 const ASTNode *callNode) {
338 // Check name requirement
339
2/2
✓ Branch 3 → 4 taken 924996 times.
✓ Branch 3 → 5 taken 64821 times.
989817 if (!matchName(candidate, reqName))
340 924996 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
341
342 // Check 'this' type requirement
343
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 8 taken 64819 times.
64821 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
344 2 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
345
346 // Check arg types requirement
347
2/2
✓ Branch 9 → 10 taken 53815 times.
✓ Branch 9 → 11 taken 11003 times.
64819 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
348 53815 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
349
350 // Check if there are unresolved generic types
351
2/2
✓ Branch 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 11002 times.
11003 if (typeMapping.size() < candidate.templateTypes.size())
352 1 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
353
354 // Substantiate return type
355 11002 substantiateReturnType(candidate, typeMapping, callNode);
356
357 // Set the match scope to the scope of the concrete substantiation
358 11002 const QualType &thisType = candidate.thisType;
359
2/2
✓ Branch 17 → 18 taken 6180 times.
✓ Branch 17 → 25 taken 4822 times.
11002 if (!thisType.is(TY_DYN)) {
360 // If we only have the generic struct scope, lookup the concrete manifestation scope
361
2/2
✓ Branch 18 → 19 taken 63 times.
✓ Branch 18 → 23 taken 6117 times.
6180 if (matchScope->isGenericScope) {
362 63 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
363
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 63 times.
63 assert(spiceStruct != nullptr);
364 63 matchScope = spiceStruct->scope;
365 }
366
1/2
✓ Branch 23 → 24 taken 6180 times.
✗ Branch 23 → 27 not taken.
6180 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
367 }
368
369 11002 return MatchResult::MATCHED;
370 }
371
372 /**
373 * Checks if the matching candidate fulfills the name requirement
374 *
375 * @param candidate Matching candidate function
376 * @param reqName Requested function name
377 * @return Fulfilled or not
378 */
379 989817 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
380
381 /**
382 * Checks if the matching candidate fulfills the 'this' type requirement
383 *
384 * @param candidate Matching candidate function
385 * @param reqThisType Requested 'this' type
386 * @param typeMapping Concrete template type mapping
387 * @param strictQualifierMatching Match qualifiers strictly
388 * @param callNode Call AST node for printing error messages
389 * @return Fulfilled or not
390 */
391 64821 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
392 bool strictQualifierMatching, const ASTNode *callNode) {
393 64821 QualType &candidateThisType = candidate.thisType;
394
395 // Shortcut for procedures
396
7/10
✓ Branch 2 → 3 taken 64821 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 45065 times.
✓ Branch 3 → 7 taken 19756 times.
✓ Branch 4 → 5 taken 45065 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 45065 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 45065 times.
✓ Branch 8 → 10 taken 19756 times.
64821 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
397 45065 return true;
398
399 // Give the type matcher a way to retrieve instances of GenericType by their name
400 42603 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
401 3091 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
402 19756 };
403
404 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
405
3/4
✓ Branch 11 → 12 taken 19756 times.
✗ Branch 11 → 21 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 19754 times.
19756 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
406 strictQualifierMatching))
407 2 return false;
408
409 // Substantiate the candidate param type, based on the type mapping
410
3/4
✓ Branch 14 → 15 taken 19754 times.
✗ Branch 14 → 21 not taken.
✓ Branch 15 → 16 taken 3224 times.
✓ Branch 15 → 17 taken 16530 times.
19754 if (candidateThisType.hasAnyGenericParts())
411
1/2
✓ Branch 16 → 17 taken 3224 times.
✗ Branch 16 → 21 not taken.
3224 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
412
413 19754 return true;
414 19756 }
415
416 /**
417 * Checks if the matching candidate fulfills the argument types requirement
418 *
419 * @param candidate Matching candidate function
420 * @param reqArgs Requested argument types
421 * @param typeMapping Concrete template type mapping
422 * @param strictQualifierMatching Match qualifiers strictly
423 * @param needsSubstantiation We want to create a substantiation after successfully matching
424 * @param callNode Call AST node for printing error messages
425 * @return Fulfilled or not
426 */
427 64819 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
428 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
429 64819 std::vector<Param> &candidateParamList = candidate.paramList;
430
431 // If the number of arguments does not match with the number of params, the matching fails
432
6/6
✓ Branch 2 → 3 taken 64734 times.
✓ Branch 2 → 7 taken 85 times.
✓ Branch 5 → 6 taken 8234 times.
✓ Branch 5 → 7 taken 56500 times.
✓ Branch 8 → 9 taken 8234 times.
✓ Branch 8 → 10 taken 56585 times.
64819 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
433 8234 return false;
434 // In the case of a vararg function, we only disallow fewer arguments than parameters
435
4/6
✓ Branch 10 → 11 taken 85 times.
✓ Branch 10 → 15 taken 56500 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 85 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 56585 times.
56585 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
436 return false;
437
438 // Give the type matcher a way to retrieve instances of GenericType by their name
439 119015 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
440 5845 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
441 56585 };
442
443 // Loop over all parameters
444
2/2
✓ Branch 66 → 20 taken 62203 times.
✓ Branch 66 → 67 taken 11003 times.
73206 for (size_t i = 0; i < reqArgs.size(); i++) {
445 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
446 // but we need to modify the candidate param list to accept them
447
6/6
✓ Branch 20 → 21 taken 341 times.
✓ Branch 20 → 24 taken 61862 times.
✓ Branch 22 → 23 taken 89 times.
✓ Branch 22 → 24 taken 252 times.
✓ Branch 25 → 26 taken 89 times.
✓ Branch 25 → 29 taken 62114 times.
62203 if (candidate.isVararg && i >= candidateParamList.size()) {
448
2/4
✓ Branch 26 → 27 taken 89 times.
✗ Branch 26 → 71 not taken.
✓ Branch 27 → 28 taken 89 times.
✗ Branch 27 → 71 not taken.
89 candidateParamList.push_back(Param(reqArgs.at(i).first, false));
449 89 needsSubstantiation = true; // We need to modify the candidate param types
450 89 continue;
451 }
452
453 // Retrieve actual and requested types
454
2/4
✓ Branch 29 → 30 taken 62114 times.
✗ Branch 29 → 84 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 62114 times.
62114 assert(!candidateParamList.at(i).isOptional);
455
1/2
✓ Branch 32 → 33 taken 62114 times.
✗ Branch 32 → 84 not taken.
62114 QualType &candidateType = candidateParamList.at(i).qualType;
456
1/2
✓ Branch 33 → 34 taken 62114 times.
✗ Branch 33 → 84 not taken.
62114 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
457
458 // Check if the requested param type matches the candidate param type. The type mapping may be extended
459
3/4
✓ Branch 36 → 37 taken 62114 times.
✗ Branch 36 → 84 not taken.
✓ Branch 37 → 38 taken 45581 times.
✓ Branch 37 → 39 taken 16533 times.
62114 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
460 strictQualifierMatching))
461 45581 return false;
462
463 // Substantiate the candidate param type, based on the type mapping
464
3/4
✓ Branch 39 → 40 taken 16533 times.
✗ Branch 39 → 84 not taken.
✓ Branch 40 → 41 taken 4810 times.
✓ Branch 40 → 42 taken 11723 times.
16533 if (candidateType.hasAnyGenericParts())
465
1/2
✓ Branch 41 → 42 taken 4810 times.
✗ Branch 41 → 84 not taken.
4810 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
466
467 // Check if we try to bind a non-ref temporary to a non-const ref parameter
468
3/4
✓ Branch 42 → 43 taken 16533 times.
✗ Branch 42 → 84 not taken.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 54 taken 16532 times.
16533 if (!candidateType.canBind(requestedType, isArgTemporary)) {
469
1/2
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 53 not taken.
1 if (callNode)
470
2/4
✓ Branch 48 → 49 taken 1 time.
✗ Branch 48 → 75 not taken.
✓ Branch 49 → 50 taken 1 time.
✗ Branch 49 → 72 not taken.
3 throw SemanticError(callNode, TEMP_TO_NON_CONST_REF, "Temporary values can only be bound to const reference parameters");
471 return false;
472 }
473
474 // If we have a function/procedure type we need to take care of the information, if it takes captures
475
9/12
✓ Branch 54 → 55 taken 16532 times.
✗ Branch 54 → 81 not taken.
✓ Branch 55 → 56 taken 16532 times.
✗ Branch 55 → 81 not taken.
✓ Branch 56 → 57 taken 33 times.
✓ Branch 56 → 60 taken 16499 times.
✓ Branch 57 → 58 taken 33 times.
✗ Branch 57 → 81 not taken.
✓ Branch 58 → 59 taken 3 times.
✓ Branch 58 → 60 taken 30 times.
✓ Branch 61 → 62 taken 3 times.
✓ Branch 61 → 64 taken 16529 times.
16532 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
476
1/2
✓ Branch 62 → 63 taken 3 times.
✗ Branch 62 → 83 not taken.
3 candidateType = candidateType.getWithLambdaCaptures();
477 3 needsSubstantiation = true;
478 }
479 }
480
481 11003 return true;
482 56585 }
483
484 /**
485 * Substantiates the candidate return type, based on the given type mapping
486 *
487 * @param candidate Matching candidate function
488 * @param typeMapping Concrete template type mapping
489 * @param callNode AST node for error messages
490 */
491 11002 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
492
2/2
✓ Branch 3 → 4 taken 730 times.
✓ Branch 3 → 5 taken 10272 times.
11002 if (candidate.returnType.hasAnyGenericParts())
493 730 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
494 11002 }
495
496 /**
497 * Searches the candidate template types for a generic type object with a certain name and return it
498 *
499 * @param candidate Matching candidate function
500 * @param templateTypeName Template type name
501 * @return Generic type object
502 */
503 8936 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
504 const std::string &templateTypeName) {
505
1/2
✓ Branch 11 → 4 taken 10369 times.
✗ Branch 11 → 12 not taken.
10369 for (const GenericType &templateType : candidate.templateTypes) {
506
3/4
✓ Branch 5 → 6 taken 10369 times.
✗ Branch 5 → 14 not taken.
✓ Branch 7 → 8 taken 8936 times.
✓ Branch 7 → 9 taken 1433 times.
10369 if (templateType.getSubType() == templateTypeName)
507 8936 return &templateType;
508 }
509 return nullptr;
510 }
511
512 /**
513 * Calculate the cache key for the function lookup cache
514 *
515 * @param scope Scope to match against
516 * @param name Function name requirement
517 * @param thisType This type requirement
518 * @param args Argument requirement
519 * @param templateTypes Template type requirement
520 * @return Cache key
521 */
522 95204 uint64_t FunctionManager::getCacheKey(const Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
523 const QualTypeList &templateTypes) {
524 95204 uint64_t hash = 0;
525 95204 hashCombine64(hash, hashPointer(scope));
526 95204 hashCombine64(hash, std::hash<std::string>{}(name));
527 95204 hashCombine64(hash, std::hash<QualType>{}(thisType));
528
2/2
✓ Branch 19 → 10 taken 144675 times.
✓ Branch 19 → 20 taken 95204 times.
239879 for (const auto &[first, second] : args) {
529 144675 hashCombine64(hash, std::hash<QualType>{}(first));
530 144675 hashCombine64(hash, std::hash<bool>{}(second));
531 }
532 95204 hashCombine64(hash, hashVector(templateTypes));
533 95204 return hash;
534 }
535
536 /**
537 * Clear the lookup cache
538 */
539 458 void FunctionManager::cleanup() {
540 458 lookupCache.clear();
541 458 lookupCacheHits = 0;
542 458 lookupCacheMisses = 0;
543 458 }
544
545 /**
546 * Dump usage statistics for the lookup cache
547 */
548 223 std::string FunctionManager::dumpLookupCacheStatistics() {
549
1/2
✓ Branch 2 → 3 taken 223 times.
✗ Branch 2 → 22 not taken.
223 std::stringstream stats;
550
2/4
✓ Branch 3 → 4 taken 223 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 223 times.
✗ Branch 4 → 20 not taken.
223 stats << "FunctionManager lookup cache statistics:" << std::endl;
551
3/6
✓ Branch 5 → 6 taken 223 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 223 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 223 times.
✗ Branch 8 → 20 not taken.
223 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
552
3/6
✓ Branch 9 → 10 taken 223 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 223 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 223 times.
✗ Branch 11 → 20 not taken.
223 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
553
3/6
✓ Branch 12 → 13 taken 223 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 223 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 223 times.
✗ Branch 14 → 20 not taken.
223 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
554
1/2
✓ Branch 15 → 16 taken 223 times.
✗ Branch 15 → 20 not taken.
446 return stats.str();
555 223 }
556
557 } // namespace spice::compiler
558