GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.0% 359 / 0 / 374
Functions: 100.0% 33 / 0 / 33
Branches: 64.7% 494 / 0 / 764

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 <algorithm>
6 #include <limits>
7
8 #include <ast/ASTNodes.h>
9 #include <exception/SemanticError.h>
10 #include <model/GenericType.h>
11 #include <symboltablebuilder/Scope.h>
12 #include <symboltablebuilder/SymbolTableBuilder.h>
13 #include <typechecker/TypeChecker.h>
14 #include <typechecker/TypeMatcher.h>
15 #include <util/CodeLoc.h>
16 #include <util/Concurrency.h>
17 #include <util/CustomHashFunctions.h>
18
19 namespace spice::compiler {
20
21 // Static member initialization
22 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
23 size_t FunctionManager::lookupCacheHits = 0;
24 size_t FunctionManager::lookupCacheMisses = 0;
25
26 132503 Function *FunctionManager::insert(Scope *insertScope, const Function &baseFunction, std::vector<Function *> *nodeFunctionList) {
27 // Open a new manifestation list for the function definition
28
3/6
✓ Branch 2 → 3 taken 132503 times.
✗ Branch 2 → 49 not taken.
✓ Branch 3 → 4 taken 132503 times.
✗ Branch 3 → 46 not taken.
✓ Branch 4 → 5 taken 132503 times.
✗ Branch 4 → 44 not taken.
132503 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
29
1/2
✓ Branch 8 → 9 taken 132503 times.
✗ Branch 8 → 50 not taken.
132503 insertScope->functions.emplace(fctId, FunctionManifestationList());
30
31 // Collect substantiations
32 132503 std::vector<Function> manifestations;
33
1/2
✓ Branch 10 → 11 taken 132503 times.
✗ Branch 10 → 54 not taken.
132503 substantiateOptionalParams(baseFunction, manifestations);
34
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 132503 times.
132503 assert(!manifestations.empty());
35
36 // Save substantiations in declaration node
37 132503 Function *manifestationPtr = nullptr;
38
2/2
✓ Branch 32 → 16 taken 139465 times.
✓ Branch 32 → 33 taken 132499 times.
404467 for (const Function &manifestation : manifestations) {
39
2/2
✓ Branch 18 → 19 taken 139461 times.
✓ Branch 18 → 53 taken 4 times.
139465 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
40
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 139461 times.
139461 assert(manifestationPtr != nullptr);
41
1/2
✓ Branch 21 → 22 taken 139461 times.
✗ Branch 21 → 23 not taken.
139461 if (nodeFunctionList)
42
1/2
✓ Branch 22 → 23 taken 139461 times.
✗ Branch 22 → 53 not taken.
139461 nodeFunctionList->push_back(manifestationPtr);
43 }
44
45
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 132499 times.
132499 if (!nodeFunctionList)
46 ✗ return manifestationPtr;
47
48
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 132499 times.
132499 assert(!nodeFunctionList->empty());
49 132499 return nodeFunctionList->front();
50 132507 }
51
52 /**
53 * Create definite functions from ambiguous ones, in regard to optional arguments.
54 *
55 * Example:
56 * int testFunc(string, int?, double?)
57 * gets
58 * int testFunc(string)
59 * int testFunc(string, int)
60 * int testFunc(string, int, double)
61 *
62 * This method also accepts functions, that are already definite, but does nothing to them.
63 *
64 * @param baseFunction Ambiguous base function
65 * @param manifestations Vector to store the definite manifestations
66 * @return True, if there were optional arguments found
67 */
68 132503 void FunctionManager::substantiateOptionalParams(const Function &baseFunction, std::vector<Function> &manifestations) {
69 // Handle the case of no parameters -> simply return the base function
70
2/2
✓ Branch 3 → 4 taken 40436 times.
✓ Branch 3 → 6 taken 92067 times.
132503 if (baseFunction.paramList.empty()) {
71
1/2
✓ Branch 4 → 5 taken 40436 times.
✗ Branch 4 → 47 not taken.
40436 manifestations.push_back(baseFunction);
72 40436 return;
73 }
74
75 92067 ParamList currentFunctionParamTypes;
76
1/2
✓ Branch 7 → 8 taken 92067 times.
✗ Branch 7 → 45 not taken.
92067 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
77 92067 bool metFirstOptionalParam = false;
78
1/2
✓ Branch 8 → 9 taken 92067 times.
✗ Branch 8 → 45 not taken.
92067 Function manifestation = baseFunction;
79
80 // Loop over all parameters
81
2/2
✓ Branch 32 → 11 taken 145141 times.
✓ Branch 32 → 33 taken 92067 times.
329275 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
82 // Check if we have a mandatory parameter
83
2/2
✓ Branch 13 → 14 taken 138179 times.
✓ Branch 13 → 16 taken 6962 times.
145141 if (!isOptional) {
84
1/2
✓ Branch 14 → 15 taken 138179 times.
✗ Branch 14 → 40 not taken.
138179 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
85 138179 continue;
86 }
87
88 // Add substantiation without the optional parameter
89
2/2
✓ Branch 16 → 17 taken 6503 times.
✓ Branch 16 → 20 taken 459 times.
6962 if (!metFirstOptionalParam) {
90
1/2
✓ Branch 17 → 18 taken 6503 times.
✗ Branch 17 → 42 not taken.
6503 manifestation.paramList = currentFunctionParamTypes;
91
1/2
✓ Branch 18 → 19 taken 6503 times.
✗ Branch 18 → 42 not taken.
6503 manifestations.push_back(manifestation);
92 // Now we cannot accept mandatory parameters anymore
93 6503 metFirstOptionalParam = true;
94 }
95
96 // Add substantiation with the optional parameter
97
1/2
✓ Branch 20 → 21 taken 6962 times.
✗ Branch 20 → 41 not taken.
6962 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
98
1/2
✓ Branch 21 → 22 taken 6962 times.
✗ Branch 21 → 42 not taken.
6962 manifestation.paramList = currentFunctionParamTypes;
99
1/2
✓ Branch 22 → 23 taken 6962 times.
✗ Branch 22 → 42 not taken.
6962 manifestations.push_back(manifestation);
100 }
101
102 // Ensure at least once manifestation
103
2/2
✓ Branch 34 → 35 taken 85564 times.
✓ Branch 34 → 36 taken 6503 times.
92067 if (manifestations.empty())
104
1/2
✓ Branch 35 → 36 taken 85564 times.
✗ Branch 35 → 43 not taken.
85564 manifestations.push_back(baseFunction);
105 92067 }
106
107 476 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
108 476 ParamList paramList;
109
2/2
✓ Branch 16 → 4 taken 16 times.
✓ Branch 16 → 17 taken 476 times.
968 for (const QualType &paramType : paramTypes)
110
1/2
✓ Branch 6 → 7 taken 16 times.
✗ Branch 6 → 33 not taken.
16 paramList.push_back({paramType, false});
111
5/10
✓ Branch 19 → 20 taken 476 times.
✗ Branch 19 → 45 not taken.
✓ Branch 20 → 21 taken 476 times.
✗ Branch 20 → 42 not taken.
✓ Branch 21 → 22 taken 476 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 476 times.
✗ Branch 22 → 40 not taken.
✓ Branch 24 → 25 taken 476 times.
✗ Branch 24 → 35 not taken.
1428 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
112 476 }
113
114 /**
115 * Search all manifestation lists of the given scope for a function with the given signature
116 *
117 * @param scope Scope to search in
118 * @param signature Signature to search for
119 * @return Found function or nullptr
120 */
121 47138 Function *FunctionManager::findManifestationBySignature(Scope *scope, const std::string &signature) {
122
5/8
✓ Branch 2 → 3 taken 47138 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 47138 times.
✗ Branch 3 → 19 not taken.
✓ Branch 4 → 5 taken 47138 times.
✗ Branch 4 → 19 not taken.
✓ Branch 15 → 6 taken 897942 times.
✓ Branch 15 → 16 taken 42900 times.
940842 for (auto &manifestations : scope->functions | std::views::values)
123
3/4
✓ Branch 7 → 8 taken 897942 times.
✗ Branch 7 → 18 not taken.
✓ Branch 10 → 11 taken 4238 times.
✓ Branch 10 → 13 taken 893704 times.
897942 if (const auto it = manifestations.find(signature); it != manifestations.end())
124 4238 return &it->second;
125 42900 return nullptr;
126 }
127
128 182365 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
129
2/4
✓ Branch 2 → 3 taken 182365 times.
✗ Branch 2 → 70 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 182365 times.
182365 assert(newManifestation.hasSubstantiatedParams());
130
131
1/2
✓ Branch 5 → 6 taken 182365 times.
✗ Branch 5 → 70 not taken.
182365 const std::string signature = newManifestation.getSignature(true, true, false, true);
132
133 // Check if the function exists already
134
5/8
✓ Branch 6 → 7 taken 182365 times.
✗ Branch 6 → 59 not taken.
✓ Branch 7 → 8 taken 182365 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 182365 times.
✗ Branch 8 → 59 not taken.
✓ Branch 30 → 10 taken 3044885 times.
✓ Branch 30 → 31 taken 182361 times.
3227246 for (const auto &manifestations : insertScope->functions | std::views::values) {
135
3/4
✓ Branch 11 → 12 taken 3044885 times.
✗ Branch 11 → 59 not taken.
✓ Branch 12 → 13 taken 4 times.
✓ Branch 12 → 28 taken 3044881 times.
3044885 if (manifestations.contains(signature)) {
136
2/2
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 18 taken 2 times.
4 const SemanticErrorType errorType = newManifestation.isFunction() ? FUNCTION_DECLARED_TWICE : PROCEDURE_DECLARED_TWICE;
137
4/8
✓ Branch 20 → 21 taken 4 times.
✗ Branch 20 → 53 not taken.
✓ Branch 21 → 22 taken 4 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 4 times.
✗ Branch 22 → 49 not taken.
✓ Branch 23 → 24 taken 4 times.
✗ Branch 23 → 47 not taken.
4 throw SemanticError(declNode, errorType, "'" + newManifestation.getSignature(true, false) + "' is declared twice");
138 }
139 }
140
141 // Retrieve the matching manifestation list of the scope
142
3/6
✓ Branch 31 → 32 taken 182361 times.
✗ Branch 31 → 65 not taken.
✓ Branch 32 → 33 taken 182361 times.
✗ Branch 32 → 62 not taken.
✓ Branch 33 → 34 taken 182361 times.
✗ Branch 33 → 60 not taken.
182361 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
143
2/4
✓ Branch 36 → 37 taken 182361 times.
✗ Branch 36 → 66 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 182361 times.
182361 assert(insertScope->functions.contains(fctId));
144
1/2
✓ Branch 39 → 40 taken 182361 times.
✗ Branch 39 → 66 not taken.
182361 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
145
146 // Add substantiated function
147
1/2
✓ Branch 40 → 41 taken 182361 times.
✗ Branch 40 → 66 not taken.
182361 manifestationList.emplace(signature, newManifestation);
148
1/2
✓ Branch 41 → 42 taken 182361 times.
✗ Branch 41 → 66 not taken.
364722 return &manifestationList.at(signature);
149 182365 }
150
151 /**
152 * Checks if a function exists by matching it, but not setting it to used
153 *
154 * @param matchScope Scope to match against
155 * @param reqName Function name requirement
156 * @param reqThisType This type requirement
157 * @param reqArgs Argument requirement
158 * @param strictQualifierMatching Match argument and this type qualifiers strictly
159 * @return Found function or nullptr
160 */
161 151995 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
162 const ArgList &reqArgs, bool strictQualifierMatching) {
163
2/4
✓ Branch 2 → 3 taken 151995 times.
✗ Branch 2 → 66 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 151995 times.
151995 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
164
165 // Unlike match(), lookup() is also called from the IR generator (e.g. to find a copy ctor), so it can run on multiple
166 // threads at once and has to guard the process-wide lookup machinery.
167
1/2
✓ Branch 5 → 6 taken 151995 times.
✗ Branch 5 → 78 not taken.
151995 const ConditionalLock lock(symbolRegistryMutex);
168
169 // Do cache lookup
170 151995 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
171
3/4
✓ Branch 9 → 10 taken 151995 times.
✗ Branch 9 → 67 not taken.
✓ Branch 12 → 13 taken 36348 times.
✓ Branch 12 → 15 taken 115647 times.
151995 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
172 36348 lookupCacheHits++;
173 36348 return it->second;
174 }
175 115647 lookupCacheMisses++;
176
177 38940 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
178
4/8
✓ Branch 15 → 16 taken 115647 times.
✗ Branch 15 → 76 not taken.
✓ Branch 16 → 17 taken 115647 times.
✗ Branch 16 → 20 not taken.
✓ Branch 17 → 18 taken 115647 times.
✗ Branch 17 → 76 not taken.
✓ Branch 18 → 19 taken 115647 times.
✗ Branch 18 → 20 not taken.
115647 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
179
180 // Loop over function registry to find functions, that match the requirements of the call
181 115647 std::vector<const Function *> matches;
182
2/2
✓ Branch 55 → 23 taken 860229 times.
✓ Branch 55 → 56 taken 115647 times.
975876 for (const auto &[defCodeLocStr, manifestations] : matchScope->functions) {
183
2/2
✓ Branch 52 → 28 taken 980499 times.
✓ Branch 52 → 53 taken 362000 times.
1342499 for (const auto &[signature, presetFunction] : manifestations) {
184
2/4
✓ Branch 31 → 32 taken 980499 times.
✗ Branch 31 → 71 not taken.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 980499 times.
980499 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
185
186 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
187 // - search for generic fct: Only match against generic preset functions
188
3/4
✓ Branch 34 → 35 taken 980499 times.
✗ Branch 34 → 71 not taken.
✓ Branch 35 → 36 taken 390210 times.
✓ Branch 35 → 37 taken 590289 times.
980499 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
189 482270 continue;
190
191 // Copy the function to be able to substantiate types
192
1/2
✓ Branch 37 → 38 taken 590289 times.
✗ Branch 37 → 71 not taken.
590289 Function candidate = presetFunction;
193
194 // Create empty type mapping
195 590289 TypeMapping &typeMapping = candidate.typeMapping;
196
197 590289 bool forceSubstantiation = false;
198
1/2
✓ Branch 38 → 39 taken 590289 times.
✗ Branch 38 → 69 not taken.
590289 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
199 strictQualifierMatching, forceSubstantiation, nullptr);
200
2/2
✓ Branch 39 → 40 taken 467054 times.
✓ Branch 39 → 41 taken 123235 times.
590289 if (matchResult == MatchResult::SKIP_FUNCTION)
201 467054 break; // Leave the whole function
202
2/2
✓ Branch 41 → 42 taken 92060 times.
✓ Branch 41 → 43 taken 31175 times.
123235 if (matchResult == MatchResult::SKIP_MANIFESTATION)
203 92060 continue; // Leave this manifestation and try the next one
204
205 // Add to matches
206
3/6
✓ Branch 43 → 44 taken 31175 times.
✗ Branch 43 → 68 not taken.
✓ Branch 44 → 45 taken 31175 times.
✗ Branch 44 → 68 not taken.
✓ Branch 45 → 46 taken 31175 times.
✗ Branch 45 → 68 not taken.
31175 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
207
208 31175 break; // Leave the whole manifestation list to not double-match the manifestation
209
2/2
✓ Branch 48 → 49 taken 92060 times.
✓ Branch 48 → 50 taken 498229 times.
590289 }
210 }
211
212 // Return the very match or a nullptr
213
2/2
✓ Branch 57 → 58 taken 31175 times.
✓ Branch 57 → 60 taken 84472 times.
115647 return !matches.empty() ? matches.front() : nullptr;
214 151995 }
215
216 /**
217 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
218 * If more than one function matches the requirement, an error gets thrown.
219 *
220 * @param matchScope Scope to match against
221 * @param reqName Function name requirement
222 * @param reqThisType This type requirement
223 * @param reqArgs Argument requirement
224 * @param templateTypeHints Template type requirement
225 * @param strictQualifierMatching Match argument and this type qualifiers strictly
226 * @param callNode Call AST node for printing error messages
227 * @return Matched function or nullptr
228 */
229 2596088 Function *FunctionManager::match(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
230 const ArgList &reqArgs, const QualTypeList &templateTypeHints, bool strictQualifierMatching,
231 const ASTNode *callNode) {
232
2/4
✓ Branch 2 → 3 taken 2596088 times.
✗ Branch 2 → 182 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2596088 times.
2596088 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
233
234 // Do cache lookup
235 2596088 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
236
3/4
✓ Branch 6 → 7 taken 2596088 times.
✗ Branch 6 → 183 not taken.
✓ Branch 9 → 10 taken 228306 times.
✓ Branch 9 → 12 taken 2367782 times.
2596088 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
237 228306 lookupCacheHits++;
238 228306 return it->second;
239 }
240 2367782 lookupCacheMisses++;
241
242 // Loop over function registry to find functions, that match the requirements of the call
243 2367782 std::vector<Function *> matches;
244
2/2
✓ Branch 141 → 14 taken 22462436 times.
✓ Branch 141 → 142 taken 2367780 times.
24830216 for (auto &[fctId, manifestations] : matchScope->functions) {
245
2/2
✓ Branch 138 → 19 taken 23233175 times.
✓ Branch 138 → 139 taken 678847 times.
23912022 for (const auto &[signature, presetFunction] : manifestations) {
246
2/4
✓ Branch 22 → 23 taken 23233175 times.
✗ Branch 22 → 205 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 23233175 times.
23233175 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
247
248 // Skip generic and newly inserted substantiations to prevent double matching of a function
249
6/8
✓ Branch 25 → 26 taken 23233175 times.
✗ Branch 25 → 205 not taken.
✓ Branch 26 → 27 taken 22474193 times.
✓ Branch 26 → 28 taken 758982 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 22474193 times.
✓ Branch 30 → 31 taken 758982 times.
✓ Branch 30 → 32 taken 22474193 times.
23233175 if (presetFunction.isGenericSubstantiation() || presetFunction.isNewlyInserted)
250 1449586 continue;
251
252 // Copy the function to be able to substantiate types
253
1/2
✓ Branch 32 → 33 taken 22474193 times.
✗ Branch 32 → 205 not taken.
22474193 Function candidate = presetFunction;
254
255 // Prepare type mapping, based on the given initial type mapping
256 22474193 TypeMapping &typeMapping = candidate.typeMapping;
257 22474193 typeMapping.clear();
258
2/2
✓ Branch 46 → 35 taken 147017 times.
✓ Branch 46 → 47 taken 22474193 times.
22621210 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
259 // Skip template slots that are not generic (anymore). The default members of an already-concrete struct
260 // manifestation carry the concrete types in their template type list, and only generic types have a sub type
261 // usable as a mapping key here - getSubType() would assert on e.g. a primitive or a 'heap byte*'.
262
1/2
✓ Branch 35 → 36 taken 147017 times.
✗ Branch 35 → 203 not taken.
147017 const GenericType &candidateTemplateType = candidate.templateTypes.at(i);
263
3/4
✓ Branch 36 → 37 taken 147017 times.
✗ Branch 36 → 203 not taken.
✓ Branch 37 → 38 taken 1458 times.
✓ Branch 37 → 39 taken 145559 times.
147017 if (!candidateTemplateType.is(TY_GENERIC))
264 1458 continue;
265
1/2
✓ Branch 39 → 40 taken 145559 times.
✗ Branch 39 → 203 not taken.
145559 const std::string &typeName = candidateTemplateType.getSubType();
266
1/2
✓ Branch 40 → 41 taken 145559 times.
✗ Branch 40 → 203 not taken.
145559 const QualType &templateType = templateTypeHints.at(i);
267
1/2
✓ Branch 41 → 42 taken 145559 times.
✗ Branch 41 → 203 not taken.
145559 typeMapping.emplace(typeName, templateType);
268 }
269
270 22474193 bool forceSubstantiation = false;
271
2/2
✓ Branch 47 → 48 taken 22474191 times.
✓ Branch 47 → 203 taken 2 times.
22474193 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
272 strictQualifierMatching, forceSubstantiation, callNode);
273
2/2
✓ Branch 48 → 49 taken 21736449 times.
✓ Branch 48 → 50 taken 737742 times.
22474191 if (matchResult == MatchResult::SKIP_FUNCTION)
274 21736449 break; // Leave the whole function
275
2/2
✓ Branch 50 → 51 taken 624204 times.
✓ Branch 50 → 52 taken 113538 times.
737742 if (matchResult == MatchResult::SKIP_MANIFESTATION)
276 624204 continue; // Leave this manifestation and try the next one
277
278 // We found a match! -> Set the actual candidate and its entry to used
279 113538 candidate.used = true;
280 113538 candidate.entry->used = true;
281
282 // Check if the function is generic needs to be substantiated
283
6/6
✓ Branch 53 → 54 taken 68034 times.
✓ Branch 53 → 56 taken 45504 times.
✓ Branch 54 → 55 taken 66400 times.
✓ Branch 54 → 56 taken 1634 times.
✓ Branch 57 → 58 taken 66400 times.
✓ Branch 57 → 70 taken 47138 times.
113538 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
284
5/10
✓ Branch 58 → 59 taken 66400 times.
✗ Branch 58 → 184 not taken.
✓ Branch 59 → 60 taken 66400 times.
✗ Branch 59 → 64 not taken.
✓ Branch 60 → 61 taken 66400 times.
✗ Branch 60 → 184 not taken.
✓ Branch 61 → 62 taken 66400 times.
✗ Branch 61 → 184 not taken.
✓ Branch 62 → 63 taken 66400 times.
✗ Branch 62 → 64 not taken.
66400 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
285
2/4
✓ Branch 65 → 66 taken 66400 times.
✗ Branch 65 → 184 not taken.
✓ Branch 66 → 67 taken 66400 times.
✗ Branch 66 → 184 not taken.
66400 Function *match = &matchScope->functions.at(fctId).at(signature);
286 66400 match->used = true;
287
1/2
✓ Branch 67 → 68 taken 66400 times.
✗ Branch 67 → 184 not taken.
66400 matches.push_back(match);
288 66400 continue; // Match was successful -> match the next function
289 66400 }
290
291 // Check if we already have this manifestation and can simply re-use it. matchManifestation may have redirected
292 // matchScope from the generic struct scope to the concrete manifestation scope; the substantiation is inserted
293 // there, so that is also where an already existing one has to be looked for. Searching the manifestation list we
294 // are currently iterating instead would miss it and insert a duplicate (which insertSubstantiation then reports
295 // as 'declared twice').
296
1/2
✓ Branch 70 → 71 taken 47138 times.
✗ Branch 70 → 203 not taken.
47138 const std::string newSignature = candidate.getSignature(true, true, false, true);
297
3/4
✓ Branch 71 → 72 taken 47138 times.
✗ Branch 71 → 185 not taken.
✓ Branch 72 → 73 taken 4238 times.
✓ Branch 72 → 75 taken 42900 times.
47138 if (Function *existingManifestation = findManifestationBySignature(matchScope, newSignature)) {
298 4238 existingManifestation->used = true;
299
1/2
✓ Branch 73 → 74 taken 4238 times.
✗ Branch 73 → 185 not taken.
4238 matches.push_back(existingManifestation);
300 4238 break; // Leave the whole manifestation list to not double-match the manifestation
301 }
302
303 // Insert the substantiated version if required
304
1/2
✓ Branch 75 → 77 taken 42900 times.
✗ Branch 75 → 201 not taken.
42900 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
305
2/4
✓ Branch 77 → 78 taken 42900 times.
✗ Branch 77 → 201 not taken.
✓ Branch 78 → 79 taken 42900 times.
✗ Branch 78 → 201 not taken.
42900 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
306 42900 substantiatedFunction->alreadyTypeChecked = false;
307
2/4
✓ Branch 79 → 80 taken 42900 times.
✗ Branch 79 → 201 not taken.
✓ Branch 80 → 81 taken 42900 times.
✗ Branch 80 → 201 not taken.
42900 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
308 42900 substantiatedFunction->isNewlyInserted = true; // To not iterate over it in the same matching
309
310 // Copy function entry
311
1/2
✓ Branch 81 → 82 taken 42900 times.
✗ Branch 81 → 201 not taken.
42900 const std::string newScopeName = substantiatedFunction->getScopeName();
312
1/2
✓ Branch 82 → 83 taken 42900 times.
✗ Branch 82 → 199 not taken.
42900 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
313
1/2
✓ Branch 85 → 86 taken 42900 times.
✗ Branch 85 → 199 not taken.
42900 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newScopeName);
314
1/2
✗ Branch 86 → 87 not taken.
✓ Branch 86 → 88 taken 42900 times.
42900 assert(substantiatedFunction->entry != nullptr);
315
316 // Copy function scope. Interface method declarations have no body and therefore no child scope to copy - only
317 // their signature needs to be substantiated for call-site type checking, so skip this step for them.
318
1/2
✓ Branch 88 → 89 taken 42900 times.
✗ Branch 88 → 199 not taken.
42900 const std::string oldScopeName = presetFunction.getScopeName();
319
3/4
✓ Branch 89 → 90 taken 42900 times.
✗ Branch 89 → 197 not taken.
✓ Branch 90 → 91 taken 42888 times.
✓ Branch 90 → 127 taken 12 times.
42900 if (matchScope->children.contains(oldScopeName)) {
320
1/2
✓ Branch 91 → 92 taken 42888 times.
✗ Branch 91 → 197 not taken.
42888 Scope *childScope = matchScope->copyChildScope(oldScopeName, newScopeName);
321
1/2
✗ Branch 92 → 93 not taken.
✓ Branch 92 → 94 taken 42888 times.
42888 assert(childScope != nullptr);
322 42888 childScope->isGenericScope = false;
323 42888 substantiatedFunction->bodyScope = childScope;
324
325 // Insert symbols for generic type names with concrete types into the child block
326
2/2
✓ Branch 104 → 96 taken 52124 times.
✓ Branch 104 → 105 taken 42888 times.
95012 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
327
2/4
✓ Branch 99 → 100 taken 52124 times.
✗ Branch 99 → 188 not taken.
✓ Branch 100 → 101 taken 52124 times.
✗ Branch 100 → 186 not taken.
52124 childScope->insertGenericType(typeName, GenericType(concreteType));
328
329 // Substantiate the 'this' entry in the new function scope
330
6/6
✓ Branch 108 → 109 taken 34940 times.
✓ Branch 108 → 112 taken 7948 times.
✓ Branch 110 → 111 taken 34926 times.
✓ Branch 110 → 112 taken 14 times.
✓ Branch 113 → 114 taken 34926 times.
✓ Branch 113 → 127 taken 7962 times.
42888 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
331
1/2
✓ Branch 116 → 117 taken 34926 times.
✗ Branch 116 → 192 not taken.
104778 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
332
1/2
✗ Branch 122 → 123 not taken.
✓ Branch 122 → 124 taken 34926 times.
34926 assert(thisEntry != nullptr);
333
2/4
✓ Branch 124 → 125 taken 34926 times.
✗ Branch 124 → 196 not taken.
✓ Branch 125 → 126 taken 34926 times.
✗ Branch 125 → 196 not taken.
34926 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
334 }
335 }
336
337 // Add to matched functions
338
1/2
✓ Branch 127 → 128 taken 42900 times.
✗ Branch 127 → 197 not taken.
42900 matches.push_back(substantiatedFunction);
339
340 42900 break; // Leave the whole manifestation list to not double-match the manifestation
341
2/2
✓ Branch 134 → 135 taken 690604 times.
✓ Branch 134 → 136 taken 21783587 times.
22521331 }
342 }
343
344 // If no matches were found, return a nullptr
345
2/2
✓ Branch 143 → 144 taken 2254294 times.
✓ Branch 143 → 145 taken 113486 times.
2367780 if (matches.empty())
346 2254294 return nullptr;
347
348 // Tie-breaking: if multiple candidates match, narrow them by qualifier specificity and by preferring
349 // explicitly declared overloads over generic substitutions (see breakOverloadTie).
350
1/2
✓ Branch 145 → 146 taken 113486 times.
✗ Branch 145 → 221 not taken.
113486 breakOverloadTie(matches, reqArgs);
351
352 // Check if more than one function matches the requirements
353
1/2
✗ Branch 147 → 148 not taken.
✓ Branch 147 → 175 taken 113486 times.
113486 if (matches.size() > 1) {
354 ✗ std::stringstream errorMessage;
355 ✗ errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
356 ✗ for (const Function *match : matches)
357 ✗ errorMessage << "\n " << match->getSignature();
358 ✗ throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
359 ✗ }
360 113486 Function *matchedFunction = matches.front();
361 113486 matchedFunction->isNewlyInserted = false;
362
363 // Insert into cache
364
1/2
✓ Branch 176 → 177 taken 113486 times.
✗ Branch 176 → 221 not taken.
113486 lookupCache[cacheKey] = matchedFunction;
365
366 // Trigger revisit in type checker if required
367
1/2
✓ Branch 177 → 178 taken 113486 times.
✗ Branch 177 → 221 not taken.
113486 TypeChecker::requestRevisitIfRequired(matchedFunction);
368
369 // Return the very match
370 113486 return matchedFunction;
371 2367782 }
372
373 23064482 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
374 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
375 bool strictQualifierMatching, bool &forceSubstantiation,
376 const ASTNode *callNode) {
377 // Check name requirement
378
2/2
✓ Branch 3 → 4 taken 22203503 times.
✓ Branch 3 → 5 taken 860979 times.
23064482 if (!matchName(candidate, reqName))
379 22203503 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
380
381 // Check 'this' type requirement
382
2/4
✓ Branch 5 → 6 taken 860979 times.
✗ Branch 5 → 29 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 860979 times.
860979 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
383 ✗ return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
384
385 // Check arg types requirement
386
4/4
✓ Branch 8 → 9 taken 860977 times.
✓ Branch 8 → 29 taken 2 times.
✓ Branch 9 → 10 taken 716260 times.
✓ Branch 9 → 11 taken 144717 times.
860979 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
387 716260 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
388
389 // Check if there are unresolved generic types. Only template slots that actually are generic have to be resolved into
390 // the type mapping - a slot that already holds a concrete type (as for the default members of an already-concrete
391 // struct manifestation, e.g. HashEntry<int, String>::dtor()) never ends up there and must not count as unresolved.
392 64584 const auto isGeneric = [](const GenericType &templateType) { return templateType.is(TY_GENERIC); };
393
3/4
✓ Branch 12 → 13 taken 144717 times.
✗ Branch 12 → 29 not taken.
✓ Branch 13 → 14 taken 4 times.
✓ Branch 13 → 15 taken 144713 times.
144717 if (typeMapping.size() < static_cast<size_t>(std::ranges::count_if(candidate.templateTypes, isGeneric)))
394 4 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
395
396 // Substantiate return type
397
1/2
✓ Branch 15 → 16 taken 144713 times.
✗ Branch 15 → 29 not taken.
144713 substantiateReturnType(candidate, typeMapping, callNode);
398
399 // Set the match scope to the scope of the concrete substantiation
400 144713 const QualType &thisType = candidate.thisType;
401
3/4
✓ Branch 16 → 17 taken 144713 times.
✗ Branch 16 → 29 not taken.
✓ Branch 17 → 18 taken 107464 times.
✓ Branch 17 → 25 taken 37249 times.
144713 if (!thisType.is(TY_DYN)) {
402 // If we only have the generic struct scope, lookup the concrete manifestation scope
403
2/2
✓ Branch 18 → 19 taken 30 times.
✓ Branch 18 → 23 taken 107434 times.
107464 if (matchScope->isGenericScope) {
404
1/2
✓ Branch 19 → 20 taken 30 times.
✗ Branch 19 → 29 not taken.
30 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
405
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 30 times.
30 assert(spiceStruct != nullptr);
406 30 matchScope = spiceStruct->scope;
407 }
408
1/2
✓ Branch 23 → 24 taken 107464 times.
✗ Branch 23 → 28 not taken.
107464 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
409 }
410
411 144713 return MatchResult::MATCHED;
412 }
413
414 /**
415 * Checks if the matching candidate fulfills the name requirement
416 *
417 * @param candidate Matching candidate function
418 * @param reqName Requested function name
419 * @return Fulfilled or not
420 */
421 23064482 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
422
423 /**
424 * Checks if the matching candidate fulfills the 'this' type requirement
425 *
426 * @param candidate Matching candidate function
427 * @param reqThisType Requested 'this' type
428 * @param typeMapping Concrete template type mapping
429 * @param strictQualifierMatching Match qualifiers strictly
430 * @param callNode Call AST node for printing error messages
431 * @return Fulfilled or not
432 */
433 860979 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
434 bool strictQualifierMatching, const ASTNode *callNode) {
435 860979 QualType &candidateThisType = candidate.thisType;
436
437 // Shortcut for procedures
438
7/10
✓ Branch 2 → 3 taken 860979 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 594076 times.
✓ Branch 3 → 7 taken 266903 times.
✓ Branch 4 → 5 taken 594076 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 594076 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 594076 times.
✓ Branch 8 → 10 taken 266903 times.
860979 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
439 594076 return true;
440
441 // Give the type matcher a way to retrieve instances of GenericType by their name
442 585947 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
443 52141 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
444 266903 };
445
446 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
447
2/4
✓ Branch 11 → 12 taken 266903 times.
✗ Branch 11 → 21 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 266903 times.
266903 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
448 strictQualifierMatching))
449 ✗ return false;
450
451 // Substantiate the candidate param type, based on the type mapping
452
3/4
✓ Branch 14 → 15 taken 266903 times.
✗ Branch 14 → 21 not taken.
✓ Branch 15 → 16 taken 55052 times.
✓ Branch 15 → 17 taken 211851 times.
266903 if (candidateThisType.hasAnyGenericParts())
453
1/2
✓ Branch 16 → 17 taken 55052 times.
✗ Branch 16 → 21 not taken.
55052 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
454
455 266903 return true;
456 266903 }
457
458 /**
459 * Checks if the matching candidate fulfills the argument types requirement
460 *
461 * @param candidate Matching candidate function
462 * @param reqArgs Requested argument types
463 * @param typeMapping Concrete template type mapping
464 * @param strictQualifierMatching Match qualifiers strictly
465 * @param needsSubstantiation We want to create a substantiation after successfully matching
466 * @param callNode Call AST node for printing error messages
467 * @return Fulfilled or not
468 */
469 860979 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
470 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
471 860979 std::vector<Param> &candidateParamList = candidate.paramList;
472
473 // If the number of arguments does not match with the number of params, the matching fails
474
6/6
✓ Branch 2 → 3 taken 858860 times.
✓ Branch 2 → 7 taken 2119 times.
✓ Branch 5 → 6 taken 111584 times.
✓ Branch 5 → 7 taken 747276 times.
✓ Branch 8 → 9 taken 111584 times.
✓ Branch 8 → 10 taken 749395 times.
860979 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
475 111584 return false;
476 // In the case of a vararg function, we only disallow fewer arguments than parameters
477
4/6
✓ Branch 10 → 11 taken 2119 times.
✓ Branch 10 → 15 taken 747276 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 2119 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 749395 times.
749395 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
478 ✗ return false;
479
480 // Give the type matcher a way to retrieve instances of GenericType by their name
481 1534339 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
482 35549 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
483 749395 };
484
485 // Loop over all parameters
486
2/2
✓ Branch 66 → 20 taken 762999 times.
✓ Branch 66 → 67 taken 144717 times.
907716 for (size_t i = 0; i < reqArgs.size(); i++) {
487 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
488 // but we need to modify the candidate param list to accept them
489
6/6
✓ Branch 20 → 21 taken 8993 times.
✓ Branch 20 → 24 taken 754006 times.
✓ Branch 22 → 23 taken 4151 times.
✓ Branch 22 → 24 taken 4842 times.
✓ Branch 25 → 26 taken 4151 times.
✓ Branch 25 → 29 taken 758848 times.
762999 if (candidate.isVararg && i >= candidateParamList.size()) {
490
2/4
✓ Branch 26 → 27 taken 4151 times.
✗ Branch 26 → 71 not taken.
✓ Branch 27 → 28 taken 4151 times.
✗ Branch 27 → 71 not taken.
4151 candidateParamList.push_back(Param(reqArgs.at(i).first, false));
491 4151 needsSubstantiation = true; // We need to modify the candidate param types
492 4151 continue;
493 }
494
495 // Retrieve actual and requested types
496
2/4
✓ Branch 29 → 30 taken 758848 times.
✗ Branch 29 → 84 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 758848 times.
758848 assert(!candidateParamList.at(i).isOptional);
497
1/2
✓ Branch 32 → 33 taken 758848 times.
✗ Branch 32 → 84 not taken.
758848 QualType &candidateType = candidateParamList.at(i).qualType;
498
1/2
✓ Branch 33 → 34 taken 758848 times.
✗ Branch 33 → 84 not taken.
758848 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
499
500 // Check if the requested param type matches the candidate param type. The type mapping may be extended
501
3/4
✓ Branch 36 → 37 taken 758848 times.
✗ Branch 36 → 84 not taken.
✓ Branch 37 → 38 taken 604676 times.
✓ Branch 37 → 39 taken 154172 times.
758848 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
502 strictQualifierMatching))
503 604676 return false;
504
505 // Substantiate the candidate param type, based on the type mapping
506
3/4
✓ Branch 39 → 40 taken 154172 times.
✗ Branch 39 → 84 not taken.
✓ Branch 40 → 41 taken 32532 times.
✓ Branch 40 → 42 taken 121640 times.
154172 if (candidateType.hasAnyGenericParts())
507
1/2
✓ Branch 41 → 42 taken 32532 times.
✗ Branch 41 → 84 not taken.
32532 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
508
509 // Check if we try to bind a non-ref temporary to a non-const ref parameter
510
3/4
✓ Branch 42 → 43 taken 154172 times.
✗ Branch 42 → 84 not taken.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 54 taken 154170 times.
154172 if (!candidateType.canBind(requestedType, isArgTemporary)) {
511
1/2
✓ Branch 44 → 45 taken 2 times.
✗ Branch 44 → 53 not taken.
2 if (callNode)
512
2/4
✓ Branch 48 → 49 taken 2 times.
✗ Branch 48 → 75 not taken.
✓ Branch 49 → 50 taken 2 times.
✗ Branch 49 → 72 not taken.
6 throw SemanticError(callNode, TEMP_TO_NON_CONST_REF, "Temporary values can only be bound to const reference parameters");
513 ✗ return false;
514 }
515
516 // If we have a function/procedure type we need to take care of the information, if it takes captures
517
9/12
✓ Branch 54 → 55 taken 154170 times.
✗ Branch 54 → 81 not taken.
✓ Branch 55 → 56 taken 154170 times.
✗ Branch 55 → 81 not taken.
✓ Branch 56 → 57 taken 276 times.
✓ Branch 56 → 60 taken 153894 times.
✓ Branch 57 → 58 taken 276 times.
✗ Branch 57 → 81 not taken.
✓ Branch 58 → 59 taken 104 times.
✓ Branch 58 → 60 taken 172 times.
✓ Branch 61 → 62 taken 104 times.
✓ Branch 61 → 64 taken 154066 times.
154170 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
518
1/2
✓ Branch 62 → 63 taken 104 times.
✗ Branch 62 → 83 not taken.
104 candidateType = candidateType.getWithLambdaCaptures();
519 104 needsSubstantiation = true;
520 }
521 }
522
523 144717 return true;
524 749395 }
525
526 /**
527 * Substantiates the candidate return type, based on the given type mapping
528 *
529 * @param candidate Matching candidate function
530 * @param typeMapping Concrete template type mapping
531 * @param callNode AST node for error messages
532 */
533 144713 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
534
2/2
✓ Branch 3 → 4 taken 13260 times.
✓ Branch 3 → 5 taken 131453 times.
144713 if (candidate.returnType.hasAnyGenericParts())
535 13260 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
536 144713 }
537
538 /**
539 * Searches the candidate template types for a generic type object with a certain name and return it
540 *
541 * @param candidate Matching candidate function
542 * @param templateTypeName Template type name
543 * @return Generic type object
544 */
545 87690 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
546 const std::string &templateTypeName) {
547
1/2
✓ Branch 19 → 4 taken 99302 times.
✗ Branch 19 → 20 not taken.
186992 for (const GenericType &templateType : candidate.templateTypes) {
548
3/4
✓ Branch 6 → 7 taken 99302 times.
✗ Branch 6 → 22 not taken.
✓ Branch 8 → 9 taken 87690 times.
✓ Branch 8 → 10 taken 11612 times.
99302 if (templateType.getSubType() == templateTypeName)
549 87690 return &templateType;
550 }
551 ✗ return nullptr;
552 }
553
554 /**
555 * Narrow a multi-match overload set by preferring the candidate whose parameter qualifiers most closely match
556 * the argument qualifiers. This resolves the typical copy-vs-move ctor ambiguity where both a `const T&`
557 * (copy) and a `T&` (move) ctor match a non-const lvalue argument - we prefer the non-const-ref candidate
558 * (move) since it requires no constification. When the argument is const, we prefer the const-ref candidate
559 * (copy) since binding to a non-const ref would require const-loss. As a secondary criterion, an explicitly
560 * declared (non-generic) overload is preferred over a generic substitution that matches equally well.
561 *
562 * Modifies `matches` in place, removing any candidate that scores worse than the best one. A no-op if there
563 * are fewer than two candidates.
564 *
565 * @param matches Candidate list to narrow
566 * @param reqArgs Argument list from the call site
567 */
568 113486 void FunctionManager::breakOverloadTie(std::vector<Function *> &matches, const ArgList &reqArgs) {
569
2/2
✓ Branch 3 → 4 taken 113434 times.
✓ Branch 3 → 5 taken 52 times.
113486 if (matches.size() < 2)
570 113434 return;
571
572 52 constexpr int CONSTIFY_PENALTY = 1;
573 52 constexpr int CONST_LOSS_PENALTY = 100;
574 // An exact type match is always preferred over a match that required an implicit upcast (struct to
575 // implemented interface, or struct to composed base struct). This keeps e.g. 'f(Derived)' preferred
576 // over 'f(Base)' when called with a Derived, instead of reporting an ambiguity.
577 52 constexpr int UPCAST_PENALTY = 1000;
578 260 const auto scoreSpecificity = [&](const Function *f) {
579 208 int penalty = 0;
580
2/2
✓ Branch 23 → 3 taken 208 times.
✓ Branch 23 → 24 taken 208 times.
416 for (size_t i = 0; i < std::min(reqArgs.size(), f->paramList.size()); i++) {
581
1/2
✓ Branch 3 → 4 taken 208 times.
✗ Branch 3 → 28 not taken.
208 const QualType &paramType = f->paramList.at(i).qualType;
582
1/2
✓ Branch 4 → 5 taken 208 times.
✗ Branch 4 → 28 not taken.
208 const QualType &argType = reqArgs.at(i).first;
583
2/4
✓ Branch 5 → 6 taken 208 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 208 times.
✗ Branch 6 → 26 not taken.
208 const bool paramIsConst = paramType.removeReferenceWrapper().isConst();
584
2/4
✓ Branch 7 → 8 taken 208 times.
✗ Branch 7 → 27 not taken.
✓ Branch 8 → 9 taken 208 times.
✗ Branch 8 → 27 not taken.
208 const bool argIsConst = argType.removeReferenceWrapper().isConst();
585
4/4
✓ Branch 9 → 10 taken 108 times.
✓ Branch 9 → 12 taken 100 times.
✓ Branch 10 → 11 taken 16 times.
✓ Branch 10 → 12 taken 92 times.
208 if (paramIsConst && !argIsConst)
586 16 penalty += CONSTIFY_PENALTY;
587
4/4
✓ Branch 12 → 13 taken 100 times.
✓ Branch 12 → 15 taken 92 times.
✓ Branch 13 → 14 taken 76 times.
✓ Branch 13 → 15 taken 24 times.
192 else if (!paramIsConst && argIsConst)
588 76 penalty += CONST_LOSS_PENALTY;
589 // Penalize matches that were only possible through an implicit upcast
590 208 QualType paramBase = paramType;
591 208 QualType argBase = argType;
592
1/2
✓ Branch 15 → 16 taken 208 times.
✗ Branch 15 → 28 not taken.
208 QualType::unwrapBothWithRefWrappers(paramBase, argBase);
593
2/4
✓ Branch 16 → 17 taken 208 times.
✗ Branch 16 → 28 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 208 times.
208 if (!paramBase.matches(argBase, true, true, true))
594 ✗ penalty += UPCAST_PENALTY;
595 }
596 208 return penalty;
597 52 };
598
599 52 int bestScore = std::numeric_limits<int>::max();
600
2/2
✓ Branch 20 → 7 taken 104 times.
✓ Branch 20 → 21 taken 52 times.
208 for (const Function *m : matches)
601
1/2
✓ Branch 9 → 10 taken 104 times.
✗ Branch 9 → 76 not taken.
104 bestScore = std::min(bestScore, scoreSpecificity(m));
602 52 std::vector<Function *> filtered;
603
1/2
✓ Branch 22 → 23 taken 52 times.
✗ Branch 22 → 80 not taken.
52 filtered.reserve(matches.size());
604
2/2
✓ Branch 39 → 25 taken 104 times.
✓ Branch 39 → 40 taken 52 times.
208 for (Function *m : matches)
605
3/4
✓ Branch 27 → 28 taken 104 times.
✗ Branch 27 → 78 not taken.
✓ Branch 28 → 29 taken 58 times.
✓ Branch 28 → 30 taken 46 times.
104 if (scoreSpecificity(m) == bestScore)
606
1/2
✓ Branch 29 → 30 taken 58 times.
✗ Branch 29 → 78 not taken.
58 filtered.push_back(m);
607 52 matches = std::move(filtered);
608
609 // Secondary tie-break: prefer an explicitly declared (non-generic) overload over a generic substitution
610 // when both match equally well, mirroring C++ overload resolution where a non-template wins over a
611 // template specialization. This resolves e.g. the copy ctor 'Any.ctor(const Any&)' vs. the value ctor
612 // 'Any.ctor<Any>(const Any&)' ambiguity when copy-constructing from another value of the same type. It is
613 // applied after the qualifier-specificity narrowing above, so a more specific generic match still wins.
614 // A generic substitution that loses here and was only inserted for this very match is removed from its
615 // declaration's manifestation list again, so the IR generator never emits a manifestation that was never
616 // type-checked (and so we leave no dead code behind).
617
6/8
✓ Branch 44 → 45 taken 6 times.
✓ Branch 44 → 48 taken 46 times.
✓ Branch 45 → 46 taken 6 times.
✗ Branch 45 → 80 not taken.
✓ Branch 46 → 47 taken 6 times.
✗ Branch 46 → 48 not taken.
✓ Branch 49 → 50 taken 6 times.
✓ Branch 49 → 73 taken 46 times.
62 if (matches.size() > 1 && std::ranges::any_of(matches, [](const Function *m) { return !m->isGenericSubstantiation(); })) {
618
2/2
✓ Branch 71 → 52 taken 12 times.
✓ Branch 71 → 72 taken 6 times.
24 for (Function *m : matches)
619
6/8
✓ Branch 54 → 55 taken 12 times.
✗ Branch 54 → 79 not taken.
✓ Branch 55 → 56 taken 6 times.
✓ Branch 55 → 58 taken 6 times.
✓ Branch 56 → 57 taken 6 times.
✗ Branch 56 → 58 not taken.
✓ Branch 59 → 60 taken 6 times.
✓ Branch 59 → 62 taken 6 times.
12 if (m->isGenericSubstantiation() && m->isNewlyInserted)
620
2/4
✓ Branch 60 → 61 taken 6 times.
✗ Branch 60 → 79 not taken.
✓ Branch 61 → 62 taken 6 times.
✗ Branch 61 → 79 not taken.
6 std::erase(*m->declNode->getFctManifestations(m->name), m);
621
1/2
✓ Branch 72 → 73 taken 6 times.
✗ Branch 72 → 80 not taken.
18 std::erase_if(matches, [](const Function *m) { return m->isGenericSubstantiation(); });
622 }
623 52 }
624
625 /**
626 * Calculate the cache key for the function lookup cache
627 *
628 * @param scope Scope to match against
629 * @param name Function name requirement
630 * @param thisType This type requirement
631 * @param args Argument requirement
632 * @param templateTypes Template type requirement
633 * @return Cache key
634 */
635 2748083 uint64_t FunctionManager::getCacheKey(const Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
636 const QualTypeList &templateTypes) {
637 2748083 uint64_t hash = 0;
638 2748083 hashCombine64(hash, hashPointer(scope));
639 2748083 hashCombine64(hash, std::hash<std::string>{}(name));
640 2748083 hashCombine64(hash, std::hash<QualType>{}(thisType));
641
2/2
✓ Branch 27 → 10 taken 4698083 times.
✓ Branch 27 → 28 taken 2748083 times.
14892332 for (const auto &[first, second] : args) {
642 4698083 hashCombine64(hash, std::hash<QualType>{}(first));
643 4698083 hashCombine64(hash, std::hash<bool>{}(second));
644 }
645 2748083 hashCombine64(hash, hashVector(templateTypes));
646 2748083 return hash;
647 }
648
649 62005 bool FunctionManager::hasCtor(const Scope *matchScope, CtorKind kind) {
650
5/8
✓ Branch 2 → 3 taken 62005 times.
✗ Branch 2 → 60 not taken.
✓ Branch 3 → 4 taken 62005 times.
✗ Branch 3 → 60 not taken.
✓ Branch 4 → 5 taken 62005 times.
✗ Branch 4 → 60 not taken.
✓ Branch 55 → 6 taken 305196 times.
✓ Branch 55 → 56 taken 39789 times.
344985 for (const auto &manifestations : matchScope->functions | std::views::values) {
651
5/8
✓ Branch 7 → 8 taken 305196 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 305196 times.
✗ Branch 8 → 59 not taken.
✓ Branch 9 → 10 taken 305196 times.
✗ Branch 9 → 59 not taken.
✓ Branch 51 → 11 taken 332953 times.
✓ Branch 51 → 52 taken 282980 times.
615933 for (const auto &function : manifestations | std::views::values) {
652 // If it is no ctor, skip it
653
3/4
✓ Branch 12 → 13 taken 332953 times.
✗ Branch 12 → 59 not taken.
✓ Branch 13 → 14 taken 215872 times.
✓ Branch 13 → 15 taken 117081 times.
332953 if (function.name != CTOR_FUNCTION_NAME)
654 215872 continue;
655 // Classify the ctor based on its parameter list
656
6/8
✓ Branch 16 → 17 taken 64405 times.
✓ Branch 16 → 25 taken 52676 times.
✓ Branch 17 → 18 taken 64405 times.
✗ Branch 17 → 58 not taken.
✓ Branch 18 → 19 taken 64405 times.
✗ Branch 18 → 58 not taken.
✓ Branch 19 → 20 taken 28935 times.
✓ Branch 19 → 25 taken 35470 times.
146016 const bool singleSelfRefParam = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
657
5/8
✓ Branch 20 → 21 taken 28935 times.
✗ Branch 20 → 58 not taken.
✓ Branch 21 → 22 taken 28935 times.
✗ Branch 21 → 58 not taken.
✓ Branch 22 → 23 taken 28935 times.
✗ Branch 22 → 58 not taken.
✓ Branch 23 → 24 taken 23724 times.
✓ Branch 23 → 25 taken 5211 times.
28935 function.paramList.at(0).qualType.getBase() == function.thisType;
658
6/8
✓ Branch 26 → 27 taken 23724 times.
✓ Branch 26 → 31 taken 93357 times.
✓ Branch 27 → 28 taken 23724 times.
✗ Branch 27 → 59 not taken.
✓ Branch 28 → 29 taken 23724 times.
✗ Branch 28 → 59 not taken.
✓ Branch 29 → 30 taken 23698 times.
✓ Branch 29 → 31 taken 26 times.
117081 const bool isCopyCtor = singleSelfRefParam && function.paramList.at(0).qualType.isConstRef();
659
6/8
✓ Branch 32 → 33 taken 23724 times.
✓ Branch 32 → 37 taken 93357 times.
✓ Branch 33 → 34 taken 23724 times.
✗ Branch 33 → 59 not taken.
✓ Branch 34 → 35 taken 23724 times.
✗ Branch 34 → 59 not taken.
✓ Branch 35 → 36 taken 26 times.
✓ Branch 35 → 37 taken 23698 times.
117081 const bool isMoveCtor = singleSelfRefParam && !function.paramList.at(0).qualType.isConstRef();
660
3/4
✓ Branch 38 → 39 taken 91065 times.
✓ Branch 38 → 42 taken 14726 times.
✓ Branch 38 → 45 taken 11290 times.
✗ Branch 38 → 49 not taken.
117081 switch (kind) {
661 91065 case CtorKind::COPY:
662
2/2
✓ Branch 39 → 40 taken 14780 times.
✓ Branch 39 → 41 taken 76285 times.
91065 if (isCopyCtor)
663 22216 return true;
664 76285 break;
665 14726 case CtorKind::MOVE:
666
2/2
✓ Branch 42 → 43 taken 8 times.
✓ Branch 42 → 44 taken 14718 times.
14726 if (isMoveCtor)
667 8 return true;
668 14718 break;
669 11290 case CtorKind::ANY_NON_COPY_NON_MOVE:
670
4/4
✓ Branch 45 → 46 taken 7432 times.
✓ Branch 45 → 48 taken 3858 times.
✓ Branch 46 → 47 taken 7428 times.
✓ Branch 46 → 48 taken 4 times.
11290 if (!isCopyCtor && !isMoveCtor)
671 7428 return true;
672 3862 break;
673 }
674 }
675 }
676 39789 return false;
677 }
678
679 2550 bool FunctionManager::hasAnyCtor(const Scope *matchScope) {
680
5/8
✓ Branch 2 → 3 taken 2550 times.
✗ Branch 2 → 24 not taken.
✓ Branch 3 → 4 taken 2550 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 2550 times.
✗ Branch 4 → 24 not taken.
✓ Branch 20 → 6 taken 4107 times.
✓ Branch 20 → 21 taken 2550 times.
6657 for (const auto &manifestations : matchScope->functions | std::views::values)
681
5/8
✓ Branch 7 → 8 taken 4107 times.
✗ Branch 7 → 23 not taken.
✓ Branch 8 → 9 taken 4107 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 4107 times.
✗ Branch 9 → 23 not taken.
✓ Branch 17 → 11 taken 4209 times.
✓ Branch 17 → 18 taken 4107 times.
8316 for (const auto &function : manifestations | std::views::values)
682
2/4
✓ Branch 12 → 13 taken 4209 times.
✗ Branch 12 → 23 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 4209 times.
4209 if (function.name == CTOR_FUNCTION_NAME)
683 ✗ return true;
684 2550 return false;
685 }
686
687 7876 bool FunctionManager::hasAnyNonCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::ANY_NON_COPY_NON_MOVE); }
688
689 46216 bool FunctionManager::hasCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::COPY); }
690
691 7905 bool FunctionManager::hasUserCopyCtor(const Scope *matchScope) {
692
5/8
✓ Branch 2 → 3 taken 7905 times.
✗ Branch 2 → 42 not taken.
✓ Branch 3 → 4 taken 7905 times.
✗ Branch 3 → 42 not taken.
✓ Branch 4 → 5 taken 7905 times.
✗ Branch 4 → 42 not taken.
✓ Branch 37 → 6 taken 41941 times.
✓ Branch 37 → 38 taken 7317 times.
49258 for (const auto &manifestations : matchScope->functions | std::views::values) {
693
5/8
✓ Branch 7 → 8 taken 41941 times.
✗ Branch 7 → 41 not taken.
✓ Branch 8 → 9 taken 41941 times.
✗ Branch 8 → 41 not taken.
✓ Branch 9 → 10 taken 41941 times.
✗ Branch 9 → 41 not taken.
✓ Branch 34 → 11 taken 45005 times.
✓ Branch 34 → 35 taken 41353 times.
86358 for (const auto &function : manifestations | std::views::values) {
694
7/8
✓ Branch 12 → 13 taken 45005 times.
✗ Branch 12 → 41 not taken.
✓ Branch 13 → 14 taken 14696 times.
✓ Branch 13 → 15 taken 30309 times.
✓ Branch 14 → 15 taken 4856 times.
✓ Branch 14 → 16 taken 9840 times.
✓ Branch 17 → 18 taken 35165 times.
✓ Branch 17 → 19 taken 9840 times.
45005 if (function.name != CTOR_FUNCTION_NAME || function.implicitDefault)
695 35165 continue;
696
6/8
✓ Branch 20 → 21 taken 6904 times.
✓ Branch 20 → 29 taken 2936 times.
✓ Branch 21 → 22 taken 6904 times.
✗ Branch 21 → 40 not taken.
✓ Branch 22 → 23 taken 6904 times.
✗ Branch 22 → 40 not taken.
✓ Branch 23 → 24 taken 1068 times.
✓ Branch 23 → 29 taken 5836 times.
10908 const bool isCopyCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isConstRef() &&
697
5/8
✓ Branch 24 → 25 taken 1068 times.
✗ Branch 24 → 40 not taken.
✓ Branch 25 → 26 taken 1068 times.
✗ Branch 25 → 40 not taken.
✓ Branch 26 → 27 taken 1068 times.
✗ Branch 26 → 40 not taken.
✓ Branch 27 → 28 taken 588 times.
✓ Branch 27 → 29 taken 480 times.
1068 function.paramList.at(0).qualType.getBase() == function.thisType;
698
2/2
✓ Branch 30 → 31 taken 588 times.
✓ Branch 30 → 32 taken 9252 times.
9840 if (isCopyCtor)
699 588 return true;
700 }
701 }
702 7317 return false;
703 }
704
705 7913 bool FunctionManager::hasMoveCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::MOVE); }
706
707 51878 Function *FunctionManager::findMoveCtor(Scope *matchScope) {
708
5/8
✓ Branch 2 → 3 taken 51878 times.
✗ Branch 2 → 41 not taken.
✓ Branch 3 → 4 taken 51878 times.
✗ Branch 3 → 41 not taken.
✓ Branch 4 → 5 taken 51878 times.
✗ Branch 4 → 41 not taken.
✓ Branch 36 → 6 taken 535584 times.
✓ Branch 36 → 37 taken 51437 times.
587021 for (auto &manifestations : matchScope->functions | std::views::values) {
709
5/8
✓ Branch 7 → 8 taken 535584 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 535584 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 535584 times.
✗ Branch 9 → 40 not taken.
✓ Branch 33 → 11 taken 675169 times.
✓ Branch 33 → 34 taken 535143 times.
1210312 for (auto &function : manifestations | std::views::values) {
710
3/4
✓ Branch 12 → 13 taken 675169 times.
✗ Branch 12 → 40 not taken.
✓ Branch 13 → 14 taken 525675 times.
✓ Branch 13 → 15 taken 149494 times.
675169 if (function.name != CTOR_FUNCTION_NAME)
711 525675 continue;
712
4/6
✓ Branch 17 → 18 taken 106904 times.
✗ Branch 17 → 39 not taken.
✓ Branch 18 → 19 taken 106904 times.
✗ Branch 18 → 39 not taken.
✓ Branch 19 → 20 taken 66364 times.
✓ Branch 19 → 28 taken 40540 times.
256398 const bool isMoveCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
713
6/8
✓ Branch 16 → 17 taken 106904 times.
✓ Branch 16 → 28 taken 42590 times.
✓ Branch 20 → 21 taken 66364 times.
✗ Branch 20 → 39 not taken.
✓ Branch 21 → 22 taken 66364 times.
✗ Branch 21 → 39 not taken.
✓ Branch 22 → 23 taken 10983 times.
✓ Branch 22 → 28 taken 55381 times.
267381 !function.paramList.at(0).qualType.isConstRef() &&
714
5/8
✓ Branch 23 → 24 taken 10983 times.
✗ Branch 23 → 39 not taken.
✓ Branch 24 → 25 taken 10983 times.
✗ Branch 24 → 39 not taken.
✓ Branch 25 → 26 taken 10983 times.
✗ Branch 25 → 39 not taken.
✓ Branch 26 → 27 taken 441 times.
✓ Branch 26 → 28 taken 10542 times.
10983 function.paramList.at(0).qualType.getBase() == function.thisType;
715
2/2
✓ Branch 29 → 30 taken 441 times.
✓ Branch 29 → 31 taken 149053 times.
149494 if (isMoveCtor)
716 441 return &function;
717 }
718 }
719 51437 return nullptr;
720 }
721
722 30 bool FunctionManager::hasDefaultCtor(const Scope *matchScope) {
723
4/8
✓ Branch 2 → 3 taken 30 times.
✗ Branch 2 → 29 not taken.
✓ Branch 3 → 4 taken 30 times.
✗ Branch 3 → 29 not taken.
✓ Branch 4 → 5 taken 30 times.
✗ Branch 4 → 29 not taken.
✓ Branch 25 → 6 taken 116 times.
✗ Branch 25 → 26 not taken.
116 for (const auto &manifestations : matchScope->functions | std::views::values)
724
5/8
✓ Branch 7 → 8 taken 116 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 116 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 116 times.
✗ Branch 9 → 28 not taken.
✓ Branch 22 → 11 taken 116 times.
✓ Branch 22 → 23 taken 86 times.
202 for (const auto &function : manifestations | std::views::values)
725
6/8
✓ Branch 12 → 13 taken 116 times.
✗ Branch 12 → 28 not taken.
✓ Branch 13 → 14 taken 30 times.
✓ Branch 13 → 17 taken 86 times.
✓ Branch 15 → 16 taken 30 times.
✗ Branch 15 → 17 not taken.
✓ Branch 18 → 19 taken 30 times.
✓ Branch 18 → 20 taken 86 times.
116 if (function.name == CTOR_FUNCTION_NAME && function.paramList.empty())
726 30 return true;
727 ✗ return false;
728 }
729
730 83182 bool FunctionManager::hasDtor(const Scope *matchScope) {
731
5/8
✓ Branch 2 → 3 taken 83182 times.
✗ Branch 2 → 24 not taken.
✓ Branch 3 → 4 taken 83182 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 83182 times.
✗ Branch 4 → 24 not taken.
✓ Branch 20 → 6 taken 695873 times.
✓ Branch 20 → 21 taken 46742 times.
742615 for (const auto &manifestations : matchScope->functions | std::views::values)
732
5/8
✓ Branch 7 → 8 taken 695873 times.
✗ Branch 7 → 23 not taken.
✓ Branch 8 → 9 taken 695873 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 695873 times.
✗ Branch 9 → 23 not taken.
✓ Branch 17 → 11 taken 771433 times.
✓ Branch 17 → 18 taken 659433 times.
1430866 for (const auto &function : manifestations | std::views::values)
733
3/4
✓ Branch 12 → 13 taken 771433 times.
✗ Branch 12 → 23 not taken.
✓ Branch 13 → 14 taken 36440 times.
✓ Branch 13 → 15 taken 734993 times.
771433 if (function.name == DTOR_FUNCTION_NAME)
734 36440 return true;
735 46742 return false;
736 }
737
738 /**
739 * Clear the lookup cache
740 */
741 1349 void FunctionManager::cleanup() {
742 1349 lookupCache.clear();
743 1349 lookupCacheHits = 0;
744 1349 lookupCacheMisses = 0;
745 1349 }
746
747 /**
748 * Dump usage statistics for the lookup cache
749 */
750 785 std::string FunctionManager::dumpLookupCacheStatistics() {
751
1/2
✓ Branch 2 → 3 taken 785 times.
✗ Branch 2 → 22 not taken.
785 std::stringstream stats;
752
2/4
✓ Branch 3 → 4 taken 785 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 785 times.
✗ Branch 4 → 20 not taken.
785 stats << "FunctionManager lookup cache statistics:" << std::endl;
753
3/6
✓ Branch 5 → 6 taken 785 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 785 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 785 times.
✗ Branch 8 → 20 not taken.
785 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
754
3/6
✓ Branch 9 → 10 taken 785 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 785 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 785 times.
✗ Branch 11 → 20 not taken.
785 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
755
3/6
✓ Branch 12 → 13 taken 785 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 785 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 785 times.
✗ Branch 14 → 20 not taken.
785 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
756
1/2
✓ Branch 15 → 16 taken 785 times.
✗ Branch 15 → 20 not taken.
1570 return stats.str();
757 785 }
758
759 } // namespace spice::compiler
760