GCC Code Coverage Report


Directory: ../
File: src/typechecker/FunctionManager.cpp
Date: 2025-08-26 18:26:32
Exec Total Coverage
Lines: 257 263 97.7%
Functions: 20 20 100.0%
Branches: 296 464 63.8%

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "FunctionManager.h"
4 #include "TypeChecker.h"
5
6 #include <ast/ASTNodes.h>
7 #include <exception/SemanticError.h>
8 #include <model/GenericType.h>
9 #include <symboltablebuilder/Scope.h>
10 #include <symboltablebuilder/SymbolTableBuilder.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 11817 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 0 (2→3) taken 11817 times.
✗ Branch 1 (2→43) not taken.
✓ Branch 2 (3→4) taken 11817 times.
✗ Branch 3 (3→40) not taken.
✓ Branch 4 (4→5) taken 11817 times.
✗ Branch 5 (4→38) not taken.
11817 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
25
2/4
✓ Branch 0 (8→9) taken 11817 times.
✗ Branch 1 (8→46) not taken.
✓ Branch 2 (9→10) taken 11817 times.
✗ Branch 3 (9→44) not taken.
11817 insertScope->functions.insert({fctId, FunctionManifestationList()});
26
27 // Collect substantiations
28 11817 std::vector<Function> manifestations;
29
1/2
✓ Branch 0 (12→13) taken 11817 times.
✗ Branch 1 (12→51) not taken.
11817 substantiateOptionalParams(baseFunction, manifestations);
30
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 11817 times.
11817 assert(!manifestations.empty());
31
32 // Save substantiations in declaration node
33 11817 Function *manifestationPtr = nullptr;
34
2/2
✓ Branch 0 (26→18) taken 12651 times.
✓ Branch 1 (26→27) taken 11815 times.
24466 for (const Function &manifestation : manifestations) {
35
2/2
✓ Branch 0 (19→20) taken 12649 times.
✓ Branch 1 (19→50) taken 2 times.
12651 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
36
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 12649 times.
12649 assert(manifestationPtr != nullptr);
37
1/2
✓ Branch 0 (22→23) taken 12649 times.
✗ Branch 1 (22→24) not taken.
12649 if (nodeFunctionList)
38
1/2
✓ Branch 0 (23→24) taken 12649 times.
✗ Branch 1 (23→50) not taken.
12649 nodeFunctionList->push_back(manifestationPtr);
39 }
40
41
1/2
✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→29) taken 11815 times.
11815 if (!nodeFunctionList)
42 return manifestationPtr;
43
44
1/2
✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 11815 times.
11815 assert(!nodeFunctionList->empty());
45 11815 return nodeFunctionList->front();
46 11819 }
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 11817 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 0 (3→4) taken 2761 times.
✓ Branch 1 (3→6) taken 9056 times.
11817 if (baseFunction.paramList.empty()) {
67
1/2
✓ Branch 0 (4→5) taken 2761 times.
✗ Branch 1 (4→39) not taken.
2761 manifestations.push_back(baseFunction);
68 2761 return;
69 }
70
71 9056 ParamList currentFunctionParamTypes;
72
1/2
✓ Branch 0 (7→8) taken 9056 times.
✗ Branch 1 (7→37) not taken.
9056 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
73 9056 bool metFirstOptionalParam = false;
74
1/2
✓ Branch 0 (8→9) taken 9056 times.
✗ Branch 1 (8→37) not taken.
9056 Function manifestation = baseFunction;
75
76 // Loop over all parameters
77
2/2
✓ Branch 0 (24→11) taken 14087 times.
✓ Branch 1 (24→25) taken 9056 times.
23143 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
78 // Check if we have a mandatory parameter
79
2/2
✓ Branch 0 (12→13) taken 13253 times.
✓ Branch 1 (12→15) taken 834 times.
14087 if (!isOptional) {
80
1/2
✓ Branch 0 (13→14) taken 13253 times.
✗ Branch 1 (13→32) not taken.
13253 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
81 13253 continue;
82 }
83
84 // Add substantiation without the optional parameter
85
2/2
✓ Branch 0 (15→16) taken 828 times.
✓ Branch 1 (15→19) taken 6 times.
834 if (!metFirstOptionalParam) {
86
1/2
✓ Branch 0 (16→17) taken 828 times.
✗ Branch 1 (16→34) not taken.
828 manifestation.paramList = currentFunctionParamTypes;
87
1/2
✓ Branch 0 (17→18) taken 828 times.
✗ Branch 1 (17→34) not taken.
828 manifestations.push_back(manifestation);
88 // Now we cannot accept mandatory parameters anymore
89 828 metFirstOptionalParam = true;
90 }
91
92 // Add substantiation with the optional parameter
93
1/2
✓ Branch 0 (19→20) taken 834 times.
✗ Branch 1 (19→33) not taken.
834 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
94
1/2
✓ Branch 0 (20→21) taken 834 times.
✗ Branch 1 (20→34) not taken.
834 manifestation.paramList = currentFunctionParamTypes;
95
1/2
✓ Branch 0 (21→22) taken 834 times.
✗ Branch 1 (21→34) not taken.
834 manifestations.push_back(manifestation);
96 }
97
98 // Ensure at least once manifestation
99
2/2
✓ Branch 0 (26→27) taken 8228 times.
✓ Branch 1 (26→28) taken 828 times.
9056 if (manifestations.empty())
100
1/2
✓ Branch 0 (27→28) taken 8228 times.
✗ Branch 1 (27→35) not taken.
8228 manifestations.push_back(baseFunction);
101 9056 }
102
103 2 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
104 2 ParamList paramList;
105
2/2
✓ Branch 0 (8→4) taken 2 times.
✓ Branch 1 (8→9) taken 2 times.
4 for (const QualType &paramType : paramTypes)
106
1/2
✓ Branch 0 (5→6) taken 2 times.
✗ Branch 1 (5→24) not taken.
2 paramList.push_back({paramType, false});
107
4/8
✓ Branch 0 (11→12) taken 2 times.
✗ Branch 1 (11→31) not taken.
✓ Branch 2 (12→13) taken 2 times.
✗ Branch 3 (12→28) not taken.
✓ Branch 4 (13→14) taken 2 times.
✗ Branch 5 (13→27) not taken.
✓ Branch 6 (14→15) taken 2 times.
✗ Branch 7 (14→26) not taken.
4 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
108 2 }
109
110 14526 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
111
2/4
✓ Branch 0 (2→3) taken 14526 times.
✗ Branch 1 (2→65) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 14526 times.
14526 assert(newManifestation.hasSubstantiatedParams());
112
113
1/2
✓ Branch 0 (5→6) taken 14526 times.
✗ Branch 1 (5→65) not taken.
14526 const std::string signature = newManifestation.getSignature();
114
115 // Check if the function exists already
116
5/8
✓ Branch 0 (6→7) taken 14526 times.
✗ Branch 1 (6→54) not taken.
✓ Branch 2 (7→8) taken 14526 times.
✗ Branch 3 (7→54) not taken.
✓ Branch 4 (8→9) taken 14526 times.
✗ Branch 5 (8→54) not taken.
✓ Branch 6 (28→10) taken 204209 times.
✓ Branch 7 (28→29) taken 14524 times.
218733 for (const auto &manifestations : insertScope->functions | std::views::values) {
117
3/4
✓ Branch 0 (11→12) taken 204209 times.
✗ Branch 1 (11→54) not taken.
✓ Branch 2 (12→13) taken 2 times.
✓ Branch 3 (12→26) taken 204207 times.
204209 if (manifestations.contains(signature)) {
118
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→18) taken 1 times.
2 const SemanticErrorType errorType = newManifestation.isFunction() ? FUNCTION_DECLARED_TWICE : PROCEDURE_DECLARED_TWICE;
119
3/6
✓ Branch 0 (20→21) taken 2 times.
✗ Branch 1 (20→49) not taken.
✓ Branch 2 (21→22) taken 2 times.
✗ Branch 3 (21→47) not taken.
✓ Branch 4 (22→23) taken 2 times.
✗ Branch 5 (22→45) not taken.
2 throw SemanticError(declNode, errorType, "'" + signature + "' is declared twice");
120 }
121 }
122
123 // Retrieve the matching manifestation list of the scope
124
3/6
✓ Branch 0 (29→30) taken 14524 times.
✗ Branch 1 (29→60) not taken.
✓ Branch 2 (30→31) taken 14524 times.
✗ Branch 3 (30→57) not taken.
✓ Branch 4 (31→32) taken 14524 times.
✗ Branch 5 (31→55) not taken.
14524 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
125
2/4
✓ Branch 0 (34→35) taken 14524 times.
✗ Branch 1 (34→61) not taken.
✗ Branch 2 (35→36) not taken.
✓ Branch 3 (35→37) taken 14524 times.
14524 assert(insertScope->functions.contains(fctId));
126
1/2
✓ Branch 0 (37→38) taken 14524 times.
✗ Branch 1 (37→61) not taken.
14524 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
127
128 // Add substantiated function
129
1/2
✓ Branch 0 (38→39) taken 14524 times.
✗ Branch 1 (38→61) not taken.
14524 manifestationList.emplace(signature, newManifestation);
130
1/2
✓ Branch 0 (39→40) taken 14524 times.
✗ Branch 1 (39→61) not taken.
29048 return &manifestationList.at(signature);
131 14526 }
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 10703 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
144 const ArgList &reqArgs, bool strictQualifierMatching) {
145
2/4
✓ Branch 0 (2→3) taken 10703 times.
✗ Branch 1 (2→66) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 10703 times.
10703 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
146
147 // Do cache lookup
148
1/2
✓ Branch 0 (6→7) taken 10703 times.
✗ Branch 1 (6→67) not taken.
10703 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
149
3/4
✓ Branch 0 (8→9) taken 10703 times.
✗ Branch 1 (8→83) not taken.
✓ Branch 2 (9→10) taken 3076 times.
✓ Branch 3 (9→12) taken 7627 times.
10703 if (lookupCache.contains(cacheKey)) {
150 3076 lookupCacheHits++;
151
1/2
✓ Branch 0 (10→11) taken 3076 times.
✗ Branch 1 (10→83) not taken.
3076 return lookupCache.at(cacheKey);
152 }
153 7627 lookupCacheMisses++;
154
155 2265 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
156
5/8
✓ Branch 0 (12→13) taken 7627 times.
✗ Branch 1 (12→83) not taken.
✓ Branch 2 (13→14) taken 7434 times.
✓ Branch 3 (13→17) taken 193 times.
✓ Branch 4 (14→15) taken 7434 times.
✗ Branch 5 (14→83) not taken.
✓ Branch 6 (15→16) taken 7434 times.
✗ Branch 7 (15→17) not taken.
7627 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
157
158 // Copy the registry to prevent iterating over items, that are created within the loop
159
1/2
✓ Branch 0 (18→19) taken 7627 times.
✗ Branch 1 (18→83) not taken.
7627 FunctionRegistry functionRegistry = matchScope->functions;
160 // Loop over function registry to find functions, that match the requirements of the call
161 7627 std::vector<const Function *> matches;
162
2/2
✓ Branch 0 (55→21) taken 63794 times.
✓ Branch 1 (55→56) taken 7627 times.
71421 for (const auto &[defCodeLocStr, m] : functionRegistry) {
163 // Copy the manifestation list to prevent iterating over items, that are created within the loop
164
1/2
✓ Branch 0 (24→25) taken 63794 times.
✗ Branch 1 (24→77) not taken.
63794 const FunctionManifestationList manifestations = m;
165
2/2
✓ Branch 0 (51→27) taken 66612 times.
✓ Branch 1 (51→52) taken 22495 times.
89107 for (const auto &[signature, presetFunction] : manifestations) {
166
2/4
✓ Branch 0 (30→31) taken 66612 times.
✗ Branch 1 (30→73) not taken.
✗ Branch 2 (31→32) not taken.
✓ Branch 3 (31→33) taken 66612 times.
66612 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
167
168 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
169 // - search for generic fct: Only match against generic preset functions
170
3/4
✓ Branch 0 (33→34) taken 66612 times.
✗ Branch 1 (33→73) not taken.
✓ Branch 2 (34→35) taken 19876 times.
✓ Branch 3 (34→36) taken 46736 times.
66612 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
171 25313 continue;
172
173 // Copy the function to be able to substantiate types
174
1/2
✓ Branch 0 (36→37) taken 46736 times.
✗ Branch 1 (36→73) not taken.
46736 Function candidate = presetFunction;
175
176 // Create empty type mapping
177 46736 TypeMapping &typeMapping = candidate.typeMapping;
178
179 46736 bool forceSubstantiation = false;
180
1/2
✓ Branch 0 (37→38) taken 46736 times.
✗ Branch 1 (37→71) not taken.
46736 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
181 strictQualifierMatching, forceSubstantiation, nullptr);
182
2/2
✓ Branch 0 (38→39) taken 40094 times.
✓ Branch 1 (38→40) taken 6642 times.
46736 if (matchResult == MatchResult::SKIP_FUNCTION)
183 40094 break; // Leave the whole function
184
2/2
✓ Branch 0 (40→41) taken 5437 times.
✓ Branch 1 (40→42) taken 1205 times.
6642 if (matchResult == MatchResult::SKIP_MANIFESTATION)
185 5437 continue; // Leave this manifestation and try the next one
186
187 // Add to matches
188
3/6
✓ Branch 0 (42→43) taken 1205 times.
✗ Branch 1 (42→70) not taken.
✓ Branch 2 (43→44) taken 1205 times.
✗ Branch 3 (43→70) not taken.
✓ Branch 4 (44→45) taken 1205 times.
✗ Branch 5 (44→70) not taken.
1205 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
189
190 1205 break; // Leave the whole manifestation list to not double-match the manifestation
191
2/2
✓ Branch 0 (47→48) taken 5437 times.
✓ Branch 1 (47→49) taken 41299 times.
46736 }
192 63794 }
193
194 // Return the very match or a nullptr
195
2/2
✓ Branch 0 (57→58) taken 1205 times.
✓ Branch 1 (57→60) taken 6422 times.
7627 return !matches.empty() ? matches.front() : nullptr;
196 7627 }
197
198 /**
199 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
200 * If more than one function matches the requirement, an error gets thrown.
201 *
202 * @param typeChecker Type Checker
203 * @param matchScope Scope to match against
204 * @param reqName Function name requirement
205 * @param reqThisType This type requirement
206 * @param reqArgs Argument requirement
207 * @param templateTypeHints Template type requirement
208 * @param strictQualifierMatching Match argument and this type qualifiers strictly
209 * @param callNode Call AST node for printing error messages
210 * @return Matched function or nullptr
211 */
212 66269 Function *FunctionManager::match(TypeChecker *typeChecker, Scope *matchScope, const std::string &reqName,
213 const QualType &reqThisType, const ArgList &reqArgs, const QualTypeList &templateTypeHints,
214 bool strictQualifierMatching, const ASTNode *callNode) {
215
2/4
✓ Branch 0 (2→3) taken 66269 times.
✗ Branch 1 (2→174) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 66269 times.
66269 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
216
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 66269 times.
66269 assert(typeChecker != nullptr && "The match() function must be called from the TypeChecker");
217
218 // Do cache lookup
219
1/2
✓ Branch 0 (7→8) taken 66269 times.
✗ Branch 1 (7→222) not taken.
66269 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
220
3/4
✓ Branch 0 (8→9) taken 66269 times.
✗ Branch 1 (8→222) not taken.
✓ Branch 2 (9→10) taken 10807 times.
✓ Branch 3 (9→12) taken 55462 times.
66269 if (lookupCache.contains(cacheKey)) {
221 10807 lookupCacheHits++;
222
1/2
✓ Branch 0 (10→11) taken 10807 times.
✗ Branch 1 (10→222) not taken.
10807 return lookupCache.at(cacheKey);
223 }
224 55462 lookupCacheMisses++;
225
226 // Copy the registry to prevent iterating over items, that are created within the loop
227
1/2
✓ Branch 0 (12→13) taken 55462 times.
✗ Branch 1 (12→222) not taken.
55462 FunctionRegistry functionRegistry = matchScope->functions;
228 // Loop over function registry to find functions, that match the requirements of the call
229 55462 std::vector<Function *> matches;
230
2/2
✓ Branch 0 (139→15) taken 690074 times.
✓ Branch 1 (139→140) taken 55461 times.
745535 for (const auto &[fctId, m] : functionRegistry) {
231 // Copy the manifestation list to prevent iterating over items, that are created within the loop
232
1/2
✓ Branch 0 (18→19) taken 690074 times.
✗ Branch 1 (18→203) not taken.
690074 const FunctionManifestationList manifestations = m;
233
2/2
✓ Branch 0 (135→21) taken 732886 times.
✓ Branch 1 (135→136) taken 41257 times.
774143 for (const auto &[signature, presetFunction] : manifestations) {
234
2/4
✓ Branch 0 (24→25) taken 732886 times.
✗ Branch 1 (24→199) not taken.
✗ Branch 2 (25→26) not taken.
✓ Branch 3 (25→27) taken 732886 times.
732886 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
235
236 // Skip generic substantiations to prevent double matching of a function
237
3/4
✓ Branch 0 (27→28) taken 732886 times.
✗ Branch 1 (27→199) not taken.
✓ Branch 2 (28→29) taken 40925 times.
✓ Branch 3 (28→30) taken 691961 times.
732886 if (presetFunction.isGenericSubstantiation())
238 84069 continue;
239
240 // Copy the function to be able to substantiate types
241
1/2
✓ Branch 0 (30→31) taken 691961 times.
✗ Branch 1 (30→199) not taken.
691961 Function candidate = presetFunction;
242
243 // Prepare type mapping, based on the given initial type mapping
244 691961 TypeMapping &typeMapping = candidate.typeMapping;
245 691961 typeMapping.clear();
246
2/2
✓ Branch 0 (43→33) taken 2967 times.
✓ Branch 1 (43→44) taken 691961 times.
694928 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
247
2/4
✓ Branch 0 (33→34) taken 2967 times.
✗ Branch 1 (33→197) not taken.
✓ Branch 2 (34→35) taken 2967 times.
✗ Branch 3 (34→197) not taken.
2967 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
248
1/2
✓ Branch 0 (35→36) taken 2967 times.
✗ Branch 1 (35→197) not taken.
2967 const QualType &templateType = templateTypeHints.at(i);
249
2/4
✓ Branch 0 (36→37) taken 2967 times.
✗ Branch 1 (36→177) not taken.
✓ Branch 2 (37→38) taken 2967 times.
✗ Branch 3 (37→175) not taken.
2967 typeMapping.insert({typeName, templateType});
250 }
251
252 691961 bool forceSubstantiation = false;
253
2/2
✓ Branch 0 (44→45) taken 691960 times.
✓ Branch 1 (44→197) taken 1 times.
691961 MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
254 strictQualifierMatching, forceSubstantiation, callNode);
255
2/2
✓ Branch 0 (45→46) taken 646813 times.
✓ Branch 1 (45→47) taken 45147 times.
691960 if (matchResult == MatchResult::SKIP_FUNCTION)
256 646813 break; // Leave the whole function
257
2/2
✓ Branch 0 (47→48) taken 37980 times.
✓ Branch 1 (47→49) taken 7167 times.
45147 if (matchResult == MatchResult::SKIP_MANIFESTATION)
258 37980 continue; // Leave this manifestation and try the next one
259
260 // We found a match! -> Set the actual candidate and its entry to used
261 7167 candidate.used = true;
262 7167 candidate.entry->used = true;
263
264 // Check if the function is generic needs to be substantiated
265
6/6
✓ Branch 0 (50→51) taken 5242 times.
✓ Branch 1 (50→53) taken 1925 times.
✓ Branch 2 (51→52) taken 5164 times.
✓ Branch 3 (51→53) taken 78 times.
✓ Branch 4 (54→55) taken 5164 times.
✓ Branch 5 (54→67) taken 2003 times.
7167 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
266
5/10
✓ Branch 0 (55→56) taken 5164 times.
✗ Branch 1 (55→197) not taken.
✓ Branch 2 (56→57) taken 5164 times.
✗ Branch 3 (56→61) not taken.
✓ Branch 4 (57→58) taken 5164 times.
✗ Branch 5 (57→197) not taken.
✓ Branch 6 (58→59) taken 5164 times.
✗ Branch 7 (58→197) not taken.
✓ Branch 8 (59→60) taken 5164 times.
✗ Branch 9 (59→61) not taken.
5164 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
267
3/6
✓ Branch 0 (62→63) taken 5164 times.
✗ Branch 1 (62→178) not taken.
✓ Branch 2 (63→64) taken 5164 times.
✗ Branch 3 (63→178) not taken.
✓ Branch 4 (64→65) taken 5164 times.
✗ Branch 5 (64→178) not taken.
5164 matches.push_back(&matchScope->functions.at(fctId).at(signature));
268 5164 matches.back()->used = true;
269 5164 continue; // Match was successful -> match the next function
270 }
271
272 // Check if we already have this manifestation and can simply re-use it
273
1/2
✓ Branch 0 (67→68) taken 2003 times.
✗ Branch 1 (67→197) not taken.
2003 const std::string nonGenericSignature = candidate.getSignature();
274
4/6
✓ Branch 0 (68→69) taken 2003 times.
✗ Branch 1 (68→195) not taken.
✓ Branch 2 (69→70) taken 2003 times.
✗ Branch 3 (69→195) not taken.
✓ Branch 4 (70→71) taken 128 times.
✓ Branch 5 (70→75) taken 1875 times.
2003 if (matchScope->functions.at(fctId).contains(nonGenericSignature)) {
275
3/6
✓ Branch 0 (71→72) taken 128 times.
✗ Branch 1 (71→179) not taken.
✓ Branch 2 (72→73) taken 128 times.
✗ Branch 3 (72→179) not taken.
✓ Branch 4 (73→74) taken 128 times.
✗ Branch 5 (73→179) not taken.
128 matches.push_back(&matchScope->functions.at(fctId).at(nonGenericSignature));
276 128 break; // Leave the whole manifestation list to not double-match the manifestation
277 }
278
279 // Insert the substantiated version if required
280
1/2
✓ Branch 0 (75→76) taken 1875 times.
✗ Branch 1 (75→195) not taken.
1875 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
281
2/4
✓ Branch 0 (76→77) taken 1875 times.
✗ Branch 1 (76→195) not taken.
✓ Branch 2 (77→78) taken 1875 times.
✗ Branch 3 (77→195) not taken.
1875 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
282 1875 substantiatedFunction->alreadyTypeChecked = false;
283
2/4
✓ Branch 0 (78→79) taken 1875 times.
✗ Branch 1 (78→195) not taken.
✓ Branch 2 (79→80) taken 1875 times.
✗ Branch 3 (79→195) not taken.
1875 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
284
285 // Copy function entry
286
1/2
✓ Branch 0 (80→81) taken 1875 times.
✗ Branch 1 (80→195) not taken.
1875 const std::string newSignature = substantiatedFunction->getSignature(false);
287
1/2
✓ Branch 0 (81→82) taken 1875 times.
✗ Branch 1 (81→193) not taken.
1875 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
288
1/2
✓ Branch 0 (84→85) taken 1875 times.
✗ Branch 1 (84→193) not taken.
1875 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newSignature);
289
1/2
✗ Branch 0 (85→86) not taken.
✓ Branch 1 (85→87) taken 1875 times.
1875 assert(substantiatedFunction->entry != nullptr);
290
291 // Copy function scope
292
1/2
✓ Branch 0 (87→88) taken 1875 times.
✗ Branch 1 (87→193) not taken.
1875 const std::string oldSignature = presetFunction.getSignature(false);
293
1/2
✓ Branch 0 (88→89) taken 1875 times.
✗ Branch 1 (88→191) not taken.
1875 Scope *childScope = matchScope->copyChildScope(oldSignature, newSignature);
294
1/2
✗ Branch 0 (89→90) not taken.
✓ Branch 1 (89→91) taken 1875 times.
1875 assert(childScope != nullptr);
295 1875 childScope->isGenericScope = false;
296 1875 substantiatedFunction->bodyScope = childScope;
297
298 // Insert symbols for generic type names with concrete types into the child block
299
2/2
✓ Branch 0 (101→93) taken 2145 times.
✓ Branch 1 (101→102) taken 1875 times.
4020 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
300
2/4
✓ Branch 0 (96→97) taken 2145 times.
✗ Branch 1 (96→182) not taken.
✓ Branch 2 (97→98) taken 2145 times.
✗ Branch 3 (97→180) not taken.
2145 childScope->insertGenericType(typeName, GenericType(concreteType));
301
302 // Substantiate the 'this' entry in the new function scope
303
6/6
✓ Branch 0 (105→106) taken 1501 times.
✓ Branch 1 (105→109) taken 374 times.
✓ Branch 2 (107→108) taken 1500 times.
✓ Branch 3 (107→109) taken 1 times.
✓ Branch 4 (110→111) taken 1500 times.
✓ Branch 5 (110→124) taken 375 times.
1875 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
304
1/2
✓ Branch 0 (113→114) taken 1500 times.
✗ Branch 1 (113→186) not taken.
4500 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
305
1/2
✗ Branch 0 (119→120) not taken.
✓ Branch 1 (119→121) taken 1500 times.
1500 assert(thisEntry != nullptr);
306
2/4
✓ Branch 0 (121→122) taken 1500 times.
✗ Branch 1 (121→190) not taken.
✓ Branch 2 (122→123) taken 1500 times.
✗ Branch 3 (122→190) not taken.
1500 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
307 }
308
309 // Add to matched functions
310
1/2
✓ Branch 0 (124→125) taken 1875 times.
✗ Branch 1 (124→191) not taken.
1875 matches.push_back(substantiatedFunction);
311
312 1875 break; // Leave the whole manifestation list to not double-match the manifestation
313
2/2
✓ Branch 0 (131→132) taken 43144 times.
✓ Branch 1 (131→133) taken 648816 times.
693964 }
314 690074 }
315
316 // If no matches were found, return a nullptr
317
2/2
✓ Branch 0 (141→142) taken 48295 times.
✓ Branch 1 (141→143) taken 7166 times.
55461 if (matches.empty())
318 48295 return nullptr;
319
320 // Check if more than one function matches the requirements
321
2/2
✓ Branch 0 (144→145) taken 1 times.
✓ Branch 1 (144→164) taken 7165 times.
7166 if (matches.size() > 1) {
322
1/2
✓ Branch 0 (145→146) taken 1 times.
✗ Branch 1 (145→217) not taken.
1 std::stringstream errorMessage;
323
3/6
✓ Branch 0 (146→147) taken 1 times.
✗ Branch 1 (146→215) not taken.
✓ Branch 2 (147→148) taken 1 times.
✗ Branch 3 (147→215) not taken.
✓ Branch 4 (148→149) taken 1 times.
✗ Branch 5 (148→215) not taken.
1 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
324
2/2
✓ Branch 0 (158→151) taken 2 times.
✓ Branch 1 (158→159) taken 1 times.
3 for (const Function *match : matches)
325
3/6
✓ Branch 0 (152→153) taken 2 times.
✗ Branch 1 (152→208) not taken.
✓ Branch 2 (153→154) taken 2 times.
✗ Branch 3 (153→207) not taken.
✓ Branch 4 (154→155) taken 2 times.
✗ Branch 5 (154→205) not taken.
2 errorMessage << "\n " << match->getSignature();
326
2/4
✓ Branch 0 (160→161) taken 1 times.
✗ Branch 1 (160→212) not taken.
✓ Branch 2 (161→162) taken 1 times.
✗ Branch 3 (161→209) not taken.
1 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
327 1 }
328
329 // Insert into cache
330
1/2
✓ Branch 0 (165→166) taken 7165 times.
✗ Branch 1 (165→218) not taken.
7165 lookupCache[cacheKey] = matches.front();
331
332 // Trigger revisit in type checker if required
333
1/2
✓ Branch 0 (167→168) taken 7165 times.
✗ Branch 1 (167→218) not taken.
7165 typeChecker->requestRevisitIfRequired(matches.front());
334
335 // Return the very match
336 7165 return matches.front();
337 55464 }
338
339 738697 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
340 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
341 bool strictQualifierMatching, bool &forceSubstantiation,
342 const ASTNode *callNode) {
343 // Check name requirement
344
2/2
✓ Branch 0 (3→4) taken 686907 times.
✓ Branch 1 (3→5) taken 51790 times.
738697 if (!matchName(candidate, reqName))
345 686907 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
346
347 // Check 'this' type requirement
348
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 51790 times.
51790 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
349 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
350
351 // Check arg types requirement
352
2/2
✓ Branch 0 (9→10) taken 43416 times.
✓ Branch 1 (9→11) taken 8373 times.
51790 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
353 43416 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
354
355 // Check if there are unresolved generic types
356
2/2
✓ Branch 0 (13→14) taken 1 times.
✓ Branch 1 (13→15) taken 8372 times.
8373 if (typeMapping.size() < candidate.templateTypes.size())
357 1 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
358
359 // Substantiate return type
360 8372 substantiateReturnType(candidate, typeMapping, callNode);
361
362 // Set the match scope to the scope of the concrete substantiation
363 8372 const QualType &thisType = candidate.thisType;
364
2/2
✓ Branch 0 (17→18) taken 5111 times.
✓ Branch 1 (17→25) taken 3261 times.
8372 if (!thisType.is(TY_DYN)) {
365 // If we only have the generic struct scope, lookup the concrete manifestation scope
366
2/2
✓ Branch 0 (18→19) taken 50 times.
✓ Branch 1 (18→23) taken 5061 times.
5111 if (matchScope->isGenericScope) {
367 50 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
368
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 50 times.
50 assert(spiceStruct != nullptr);
369 50 matchScope = spiceStruct->scope;
370 }
371
1/2
✓ Branch 0 (23→24) taken 5111 times.
✗ Branch 1 (23→27) not taken.
5111 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
372 }
373
374 8372 return MatchResult::MATCHED;
375 }
376
377 /**
378 * Checks if the matching candidate fulfills the name requirement
379 *
380 * @param candidate Matching candidate function
381 * @param reqName Requested function name
382 * @return Fulfilled or not
383 */
384 738697 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
385
386 /**
387 * Checks if the matching candidate fulfills the 'this' type requirement
388 *
389 * @param candidate Matching candidate function
390 * @param reqThisType Requested 'this' type
391 * @param typeMapping Concrete template type mapping
392 * @param strictQualifierMatching Match qualifiers strictly
393 * @param callNode Call AST node for printing error messages
394 * @return Fulfilled or not
395 */
396 51790 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
397 bool strictQualifierMatching, const ASTNode *callNode) {
398 51790 QualType &candidateThisType = candidate.thisType;
399
400 // Shortcut for procedures
401
7/10
✓ Branch 0 (2→3) taken 51790 times.
✗ Branch 1 (2→23) not taken.
✓ Branch 2 (3→4) taken 35512 times.
✓ Branch 3 (3→7) taken 16278 times.
✓ Branch 4 (4→5) taken 35512 times.
✗ Branch 5 (4→23) not taken.
✓ Branch 6 (5→6) taken 35512 times.
✗ Branch 7 (5→7) not taken.
✓ Branch 8 (8→9) taken 35512 times.
✓ Branch 9 (8→10) taken 16278 times.
51790 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
402 35512 return true;
403
404 // Give the type matcher a way to retrieve instances of GenericType by their name
405 34784 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
406 2228 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
407 16278 };
408
409 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
410
2/4
✓ Branch 0 (11→12) taken 16278 times.
✗ Branch 1 (11→21) not taken.
✗ Branch 2 (12→13) not taken.
✓ Branch 3 (12→14) taken 16278 times.
16278 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
411 strictQualifierMatching))
412 return false;
413
414 // Substantiate the candidate param type, based on the type mapping
415
3/4
✓ Branch 0 (14→15) taken 16278 times.
✗ Branch 1 (14→21) not taken.
✓ Branch 2 (15→16) taken 2440 times.
✓ Branch 3 (15→17) taken 13838 times.
16278 if (candidateThisType.hasAnyGenericParts())
416
1/2
✓ Branch 0 (16→17) taken 2440 times.
✗ Branch 1 (16→21) not taken.
2440 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
417
418 16278 return true;
419 16278 }
420
421 /**
422 * Checks if the matching candidate fulfills the argument types requirement
423 *
424 * @param candidate Matching candidate function
425 * @param reqArgs Requested argument types
426 * @param typeMapping Concrete template type mapping
427 * @param strictQualifierMatching Match qualifiers strictly
428 * @param needsSubstantiation We want to create a substantiation after successfully matching
429 * @param callNode Call AST node for printing error messages
430 * @return Fulfilled or not
431 */
432 51790 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
433 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
434 51790 std::vector<Param> &candidateParamList = candidate.paramList;
435
436 // If the number of arguments does not match with the number of params, the matching fails
437
6/6
✓ Branch 0 (2→3) taken 51715 times.
✓ Branch 1 (2→7) taken 75 times.
✓ Branch 2 (5→6) taken 6733 times.
✓ Branch 3 (5→7) taken 44982 times.
✓ Branch 4 (8→9) taken 6733 times.
✓ Branch 5 (8→10) taken 45057 times.
51790 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
438 6733 return false;
439 // In the case of a vararg function, we only disallow fewer arguments than parameters
440
4/6
✓ Branch 0 (10→11) taken 75 times.
✓ Branch 1 (10→15) taken 44982 times.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 75 times.
✗ Branch 4 (16→17) not taken.
✓ Branch 5 (16→18) taken 45057 times.
45057 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
441 return false;
442
443 // Give the type matcher a way to retrieve instances of GenericType by their name
444 94140 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
445 4026 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
446 45057 };
447
448 // Loop over all parameters
449
2/2
✓ Branch 0 (66→20) taken 47928 times.
✓ Branch 1 (66→67) taken 8373 times.
56301 for (size_t i = 0; i < reqArgs.size(); i++) {
450 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
451 // but we need to modify the candidate param list to accept them
452
6/6
✓ Branch 0 (20→21) taken 300 times.
✓ Branch 1 (20→24) taken 47628 times.
✓ Branch 2 (22→23) taken 75 times.
✓ Branch 3 (22→24) taken 225 times.
✓ Branch 4 (25→26) taken 75 times.
✓ Branch 5 (25→29) taken 47853 times.
47928 if (candidate.isVararg && i >= candidateParamList.size()) {
453
2/4
✓ Branch 0 (26→27) taken 75 times.
✗ Branch 1 (26→71) not taken.
✓ Branch 2 (27→28) taken 75 times.
✗ Branch 3 (27→71) not taken.
75 candidateParamList.push_back(Param(reqArgs.at(i).first, false));
454 75 needsSubstantiation = true; // We need to modify the candidate param types
455 75 continue;
456 }
457
458 // Retrieve actual and requested types
459
2/4
✓ Branch 0 (29→30) taken 47853 times.
✗ Branch 1 (29→84) not taken.
✗ Branch 2 (30→31) not taken.
✓ Branch 3 (30→32) taken 47853 times.
47853 assert(!candidateParamList.at(i).isOptional);
460
1/2
✓ Branch 0 (32→33) taken 47853 times.
✗ Branch 1 (32→84) not taken.
47853 QualType &candidateType = candidateParamList.at(i).qualType;
461
1/2
✓ Branch 0 (33→34) taken 47853 times.
✗ Branch 1 (33→84) not taken.
47853 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
462
463 // Check if the requested param type matches the candidate param type. The type mapping may be extended
464
3/4
✓ Branch 0 (36→37) taken 47853 times.
✗ Branch 1 (36→84) not taken.
✓ Branch 2 (37→38) taken 36683 times.
✓ Branch 3 (37→39) taken 11170 times.
47853 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
465 strictQualifierMatching))
466 36683 return false;
467
468 // Substantiate the candidate param type, based on the type mapping
469
3/4
✓ Branch 0 (39→40) taken 11170 times.
✗ Branch 1 (39→84) not taken.
✓ Branch 2 (40→41) taken 1723 times.
✓ Branch 3 (40→42) taken 9447 times.
11170 if (candidateType.hasAnyGenericParts())
470
1/2
✓ Branch 0 (41→42) taken 1723 times.
✗ Branch 1 (41→84) not taken.
1723 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
471
472 // Check if we try to bind a non-ref temporary to a non-const ref parameter
473
3/4
✓ Branch 0 (42→43) taken 11170 times.
✗ Branch 1 (42→84) not taken.
✓ Branch 2 (43→44) taken 1 times.
✓ Branch 3 (43→54) taken 11169 times.
11170 if (!candidateType.canBind(requestedType, isArgTemporary)) {
474
1/2
✓ Branch 0 (44→45) taken 1 times.
✗ Branch 1 (44→53) not taken.
1 if (callNode)
475
2/4
✓ Branch 0 (48→49) taken 1 times.
✗ Branch 1 (48→75) not taken.
✓ Branch 2 (49→50) taken 1 times.
✗ Branch 3 (49→72) not taken.
3 throw SemanticError(callNode, TEMP_TO_NON_CONST_REF, "Temporary values can only be bound to const reference parameters");
476 return false;
477 }
478
479 // If we have a function/procedure type we need to take care of the information, if it takes captures
480
9/12
✓ Branch 0 (54→55) taken 11169 times.
✗ Branch 1 (54→81) not taken.
✓ Branch 2 (55→56) taken 11169 times.
✗ Branch 3 (55→81) not taken.
✓ Branch 4 (56→57) taken 19 times.
✓ Branch 5 (56→60) taken 11150 times.
✓ Branch 6 (57→58) taken 19 times.
✗ Branch 7 (57→81) not taken.
✓ Branch 8 (58→59) taken 4 times.
✓ Branch 9 (58→60) taken 15 times.
✓ Branch 10 (61→62) taken 4 times.
✓ Branch 11 (61→64) taken 11165 times.
11169 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
481
1/2
✓ Branch 0 (62→63) taken 4 times.
✗ Branch 1 (62→83) not taken.
4 candidateType = candidateType.getWithLambdaCaptures();
482 4 needsSubstantiation = true;
483 }
484 }
485
486 8373 return true;
487 45057 }
488
489 /**
490 * Substantiates the candidate return type, based on the given type mapping
491 *
492 * @param candidate Matching candidate function
493 * @param typeMapping Concrete template type mapping
494 * @param callNode AST node for error messages
495 */
496 8372 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
497
2/2
✓ Branch 0 (3→4) taken 565 times.
✓ Branch 1 (3→5) taken 7807 times.
8372 if (candidate.returnType.hasAnyGenericParts())
498 565 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
499 8372 }
500
501 /**
502 * Searches the candidate template types for a generic type object with a certain name and return it
503 *
504 * @param candidate Matching candidate function
505 * @param templateTypeName Template type name
506 * @return Generic type object
507 */
508 6254 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
509 const std::string &templateTypeName) {
510
1/2
✓ Branch 0 (11→4) taken 6692 times.
✗ Branch 1 (11→12) not taken.
6692 for (const GenericType &templateType : candidate.templateTypes) {
511
3/4
✓ Branch 0 (5→6) taken 6692 times.
✗ Branch 1 (5→14) not taken.
✓ Branch 2 (7→8) taken 6254 times.
✓ Branch 3 (7→9) taken 438 times.
6692 if (templateType.getSubType() == templateTypeName)
512 6254 return &templateType;
513 }
514 return nullptr;
515 }
516
517 /**
518 * Calculate the cache key for the function lookup cache
519 *
520 * @param scope Scope to match against
521 * @param name Function name requirement
522 * @param thisType This type requirement
523 * @param args Argument requirement
524 * @param templateTypes Template type requirement
525 * @return Cache key
526 */
527 76972 uint64_t FunctionManager::getCacheKey(Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
528 const QualTypeList &templateTypes) {
529 115447 const auto pred1 = [](size_t acc, const Arg &val) {
530 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
531 115447 const uint64_t typeHash = std::hash<QualType>{}(val.first);
532 115447 const uint64_t temporaryHash = std::hash<bool>{}(val.second);
533 115447 const uint64_t newHash = typeHash ^ (temporaryHash << 1);
534 115447 return acc * 31 + newHash;
535 };
536 621 const auto pred2 = [](size_t acc, const QualType &val) {
537 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
538 621 return acc * 31 + std::hash<QualType>{}(val);
539 };
540 // Calculate the cache key
541 76972 const uint64_t scopeHash = std::hash<Scope *>{}(scope);
542 76972 const uint64_t hashName = std::hash<std::string>{}(name);
543 76972 const uint64_t hashThisType = std::hash<QualType>{}(thisType);
544 76972 const uint64_t hashArgs = std::accumulate(args.begin(), args.end(), 0u, pred1);
545 76972 const uint64_t hashTemplateTypes = std::accumulate(templateTypes.begin(), templateTypes.end(), 0u, pred2);
546 76972 return scopeHash ^ (hashName << 1) ^ (hashThisType << 2) ^ (hashArgs << 3) ^ (hashTemplateTypes << 4);
547 }
548
549 /**
550 * Clear the lookup cache
551 */
552 410 void FunctionManager::cleanup() {
553 410 lookupCache.clear();
554 410 lookupCacheHits = 0;
555 410 lookupCacheMisses = 0;
556 410 }
557
558 /**
559 * Dump usage statistics for the lookup cache
560 */
561 193 std::string FunctionManager::dumpLookupCacheStatistics() {
562
1/2
✓ Branch 0 (2→3) taken 193 times.
✗ Branch 1 (2→22) not taken.
193 std::stringstream stats;
563
2/4
✓ Branch 0 (3→4) taken 193 times.
✗ Branch 1 (3→20) not taken.
✓ Branch 2 (4→5) taken 193 times.
✗ Branch 3 (4→20) not taken.
193 stats << "FunctionManager lookup cache statistics:" << std::endl;
564
3/6
✓ Branch 0 (5→6) taken 193 times.
✗ Branch 1 (5→20) not taken.
✓ Branch 2 (7→8) taken 193 times.
✗ Branch 3 (7→20) not taken.
✓ Branch 4 (8→9) taken 193 times.
✗ Branch 5 (8→20) not taken.
193 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
565
3/6
✓ Branch 0 (9→10) taken 193 times.
✗ Branch 1 (9→20) not taken.
✓ Branch 2 (10→11) taken 193 times.
✗ Branch 3 (10→20) not taken.
✓ Branch 4 (11→12) taken 193 times.
✗ Branch 5 (11→20) not taken.
193 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
566
3/6
✓ Branch 0 (12→13) taken 193 times.
✗ Branch 1 (12→20) not taken.
✓ Branch 2 (13→14) taken 193 times.
✗ Branch 3 (13→20) not taken.
✓ Branch 4 (14→15) taken 193 times.
✗ Branch 5 (14→20) not taken.
193 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
567
1/2
✓ Branch 0 (15→16) taken 193 times.
✗ Branch 1 (15→20) not taken.
386 return stats.str();
568 193 }
569
570 } // namespace spice::compiler
571