GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.0% 358 / 0 / 373
Functions: 100.0% 33 / 0 / 33
Branches: 64.6% 491 / 0 / 760

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 41664 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 41664 times.
✗ Branch 2 → 49 not taken.
✓ Branch 3 → 4 taken 41664 times.
✗ Branch 3 → 46 not taken.
✓ Branch 4 → 5 taken 41664 times.
✗ Branch 4 → 44 not taken.
41664 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
29
1/2
✓ Branch 8 → 9 taken 41664 times.
✗ Branch 8 → 50 not taken.
41664 insertScope->functions.emplace(fctId, FunctionManifestationList());
30
31 // Collect substantiations
32 41664 std::vector<Function> manifestations;
33
1/2
✓ Branch 10 → 11 taken 41664 times.
✗ Branch 10 → 54 not taken.
41664 substantiateOptionalParams(baseFunction, manifestations);
34
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 41664 times.
41664 assert(!manifestations.empty());
35
36 // Save substantiations in declaration node
37 41664 Function *manifestationPtr = nullptr;
38
2/2
✓ Branch 32 → 16 taken 43998 times.
✓ Branch 32 → 33 taken 41662 times.
127324 for (const Function &manifestation : manifestations) {
39
2/2
✓ Branch 18 → 19 taken 43996 times.
✓ Branch 18 → 53 taken 2 times.
43998 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
40
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 43996 times.
43996 assert(manifestationPtr != nullptr);
41
1/2
✓ Branch 21 → 22 taken 43996 times.
✗ Branch 21 → 23 not taken.
43996 if (nodeFunctionList)
42
1/2
✓ Branch 22 → 23 taken 43996 times.
✗ Branch 22 → 53 not taken.
43996 nodeFunctionList->push_back(manifestationPtr);
43 }
44
45
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 41662 times.
41662 if (!nodeFunctionList)
46 return manifestationPtr;
47
48
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 41662 times.
41662 assert(!nodeFunctionList->empty());
49 41662 return nodeFunctionList->front();
50 41666 }
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 41664 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 11026 times.
✓ Branch 3 → 6 taken 30638 times.
41664 if (baseFunction.paramList.empty()) {
71
1/2
✓ Branch 4 → 5 taken 11026 times.
✗ Branch 4 → 47 not taken.
11026 manifestations.push_back(baseFunction);
72 11026 return;
73 }
74
75 30638 ParamList currentFunctionParamTypes;
76
1/2
✓ Branch 7 → 8 taken 30638 times.
✗ Branch 7 → 45 not taken.
30638 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
77 30638 bool metFirstOptionalParam = false;
78
1/2
✓ Branch 8 → 9 taken 30638 times.
✗ Branch 8 → 45 not taken.
30638 Function manifestation = baseFunction;
79
80 // Loop over all parameters
81
2/2
✓ Branch 32 → 11 taken 45281 times.
✓ Branch 32 → 33 taken 30638 times.
106557 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
82 // Check if we have a mandatory parameter
83
2/2
✓ Branch 13 → 14 taken 42947 times.
✓ Branch 13 → 16 taken 2334 times.
45281 if (!isOptional) {
84
1/2
✓ Branch 14 → 15 taken 42947 times.
✗ Branch 14 → 40 not taken.
42947 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
85 42947 continue;
86 }
87
88 // Add substantiation without the optional parameter
89
2/2
✓ Branch 16 → 17 taken 2217 times.
✓ Branch 16 → 20 taken 117 times.
2334 if (!metFirstOptionalParam) {
90
1/2
✓ Branch 17 → 18 taken 2217 times.
✗ Branch 17 → 42 not taken.
2217 manifestation.paramList = currentFunctionParamTypes;
91
1/2
✓ Branch 18 → 19 taken 2217 times.
✗ Branch 18 → 42 not taken.
2217 manifestations.push_back(manifestation);
92 // Now we cannot accept mandatory parameters anymore
93 2217 metFirstOptionalParam = true;
94 }
95
96 // Add substantiation with the optional parameter
97
1/2
✓ Branch 20 → 21 taken 2334 times.
✗ Branch 20 → 41 not taken.
2334 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
98
1/2
✓ Branch 21 → 22 taken 2334 times.
✗ Branch 21 → 42 not taken.
2334 manifestation.paramList = currentFunctionParamTypes;
99
1/2
✓ Branch 22 → 23 taken 2334 times.
✗ Branch 22 → 42 not taken.
2334 manifestations.push_back(manifestation);
100 }
101
102 // Ensure at least once manifestation
103
2/2
✓ Branch 34 → 35 taken 28421 times.
✓ Branch 34 → 36 taken 2217 times.
30638 if (manifestations.empty())
104
1/2
✓ Branch 35 → 36 taken 28421 times.
✗ Branch 35 → 43 not taken.
28421 manifestations.push_back(baseFunction);
105 30638 }
106
107 6 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
108 6 ParamList paramList;
109
2/2
✓ Branch 16 → 4 taken 2 times.
✓ Branch 16 → 17 taken 6 times.
14 for (const QualType &paramType : paramTypes)
110
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 33 not taken.
2 paramList.push_back({paramType, false});
111
5/10
✓ Branch 19 → 20 taken 6 times.
✗ Branch 19 → 45 not taken.
✓ Branch 20 → 21 taken 6 times.
✗ Branch 20 → 42 not taken.
✓ Branch 21 → 22 taken 6 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 6 times.
✗ Branch 22 → 40 not taken.
✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 35 not taken.
18 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
112 6 }
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 13788 Function *FunctionManager::findManifestationBySignature(Scope *scope, const std::string &signature) {
122
5/8
✓ Branch 2 → 3 taken 13788 times.
✗ Branch 2 → 19 not taken.
✓ Branch 3 → 4 taken 13788 times.
✗ Branch 3 → 19 not taken.
✓ Branch 4 → 5 taken 13788 times.
✗ Branch 4 → 19 not taken.
✓ Branch 15 → 6 taken 282092 times.
✓ Branch 15 → 16 taken 12600 times.
294692 for (auto &manifestations : scope->functions | std::views::values)
123
3/4
✓ Branch 7 → 8 taken 282092 times.
✗ Branch 7 → 18 not taken.
✓ Branch 10 → 11 taken 1188 times.
✓ Branch 10 → 13 taken 280904 times.
282092 if (const auto it = manifestations.find(signature); it != manifestations.end())
124 1188 return &it->second;
125 12600 return nullptr;
126 }
127
128 56598 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
129
2/4
✓ Branch 2 → 3 taken 56598 times.
✗ Branch 2 → 70 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 56598 times.
56598 assert(newManifestation.hasSubstantiatedParams());
130
131
1/2
✓ Branch 5 → 6 taken 56598 times.
✗ Branch 5 → 70 not taken.
56598 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 56598 times.
✗ Branch 6 → 59 not taken.
✓ Branch 7 → 8 taken 56598 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 56598 times.
✗ Branch 8 → 59 not taken.
✓ Branch 30 → 10 taken 936331 times.
✓ Branch 30 → 31 taken 56596 times.
992927 for (const auto &manifestations : insertScope->functions | std::views::values) {
135
3/4
✓ Branch 11 → 12 taken 936331 times.
✗ Branch 11 → 59 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 28 taken 936329 times.
936331 if (manifestations.contains(signature)) {
136
2/2
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 18 taken 1 time.
2 const SemanticErrorType errorType = newManifestation.isFunction() ? FUNCTION_DECLARED_TWICE : PROCEDURE_DECLARED_TWICE;
137
4/8
✓ Branch 20 → 21 taken 2 times.
✗ Branch 20 → 53 not taken.
✓ Branch 21 → 22 taken 2 times.
✗ Branch 21 → 51 not taken.
✓ Branch 22 → 23 taken 2 times.
✗ Branch 22 → 49 not taken.
✓ Branch 23 → 24 taken 2 times.
✗ Branch 23 → 47 not taken.
2 throw SemanticError(declNode, errorType, "'" + newManifestation.getSignature(true, false) + "' is declared twice");
138 }
139 }
140
141 // Retrieve the matching manifestation list of the scope
142
3/6
✓ Branch 31 → 32 taken 56596 times.
✗ Branch 31 → 65 not taken.
✓ Branch 32 → 33 taken 56596 times.
✗ Branch 32 → 62 not taken.
✓ Branch 33 → 34 taken 56596 times.
✗ Branch 33 → 60 not taken.
56596 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
143
2/4
✓ Branch 36 → 37 taken 56596 times.
✗ Branch 36 → 66 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 56596 times.
56596 assert(insertScope->functions.contains(fctId));
144
1/2
✓ Branch 39 → 40 taken 56596 times.
✗ Branch 39 → 66 not taken.
56596 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
145
146 // Add substantiated function
147
1/2
✓ Branch 40 → 41 taken 56596 times.
✗ Branch 40 → 66 not taken.
56596 manifestationList.emplace(signature, newManifestation);
148
1/2
✓ Branch 41 → 42 taken 56596 times.
✗ Branch 41 → 66 not taken.
113192 return &manifestationList.at(signature);
149 56598 }
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 46152 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 46152 times.
✗ Branch 2 → 66 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 46152 times.
46152 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 46152 times.
✗ Branch 5 → 78 not taken.
46152 const ConditionalLock lock(symbolRegistryMutex);
168
169 // Do cache lookup
170 46152 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
171
3/4
✓ Branch 9 → 10 taken 46152 times.
✗ Branch 9 → 67 not taken.
✓ Branch 12 → 13 taken 10098 times.
✓ Branch 12 → 15 taken 36054 times.
46152 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
172 10098 lookupCacheHits++;
173 10098 return it->second;
174 }
175 36054 lookupCacheMisses++;
176
177 12186 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
178
4/8
✓ Branch 15 → 16 taken 36054 times.
✗ Branch 15 → 76 not taken.
✓ Branch 16 → 17 taken 36054 times.
✗ Branch 16 → 20 not taken.
✓ Branch 17 → 18 taken 36054 times.
✗ Branch 17 → 76 not taken.
✓ Branch 18 → 19 taken 36054 times.
✗ Branch 18 → 20 not taken.
36054 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 36054 std::vector<const Function *> matches;
182
2/2
✓ Branch 55 → 23 taken 258199 times.
✓ Branch 55 → 56 taken 36054 times.
294253 for (const auto &[defCodeLocStr, manifestations] : matchScope->functions) {
183
2/2
✓ Branch 52 → 28 taken 298470 times.
✓ Branch 52 → 53 taken 117557 times.
416027 for (const auto &[signature, presetFunction] : manifestations) {
184
2/4
✓ Branch 31 → 32 taken 298470 times.
✗ Branch 31 → 71 not taken.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 298470 times.
298470 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 298470 times.
✗ Branch 34 → 71 not taken.
✓ Branch 35 → 36 taken 126437 times.
✓ Branch 35 → 37 taken 172033 times.
298470 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
189 157828 continue;
190
191 // Copy the function to be able to substantiate types
192
1/2
✓ Branch 37 → 38 taken 172033 times.
✗ Branch 37 → 71 not taken.
172033 Function candidate = presetFunction;
193
194 // Create empty type mapping
195 172033 TypeMapping &typeMapping = candidate.typeMapping;
196
197 172033 bool forceSubstantiation = false;
198
1/2
✓ Branch 38 → 39 taken 172033 times.
✗ Branch 38 → 69 not taken.
172033 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
199 strictQualifierMatching, forceSubstantiation, nullptr);
200
2/2
✓ Branch 39 → 40 taken 129692 times.
✓ Branch 39 → 41 taken 42341 times.
172033 if (matchResult == MatchResult::SKIP_FUNCTION)
201 129692 break; // Leave the whole function
202
2/2
✓ Branch 41 → 42 taken 31391 times.
✓ Branch 41 → 43 taken 10950 times.
42341 if (matchResult == MatchResult::SKIP_MANIFESTATION)
203 31391 continue; // Leave this manifestation and try the next one
204
205 // Add to matches
206
3/6
✓ Branch 43 → 44 taken 10950 times.
✗ Branch 43 → 68 not taken.
✓ Branch 44 → 45 taken 10950 times.
✗ Branch 44 → 68 not taken.
✓ Branch 45 → 46 taken 10950 times.
✗ Branch 45 → 68 not taken.
10950 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
207
208 10950 break; // Leave the whole manifestation list to not double-match the manifestation
209
2/2
✓ Branch 48 → 49 taken 31391 times.
✓ Branch 48 → 50 taken 140642 times.
172033 }
210 }
211
212 // Return the very match or a nullptr
213
2/2
✓ Branch 57 → 58 taken 10950 times.
✓ Branch 57 → 60 taken 25104 times.
36054 return !matches.empty() ? matches.front() : nullptr;
214 46152 }
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 470649 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 470649 times.
✗ Branch 2 → 180 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 470649 times.
470649 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
233
234 // Do cache lookup
235 470649 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
236
3/4
✓ Branch 6 → 7 taken 470649 times.
✗ Branch 6 → 181 not taken.
✓ Branch 9 → 10 taken 64996 times.
✓ Branch 9 → 12 taken 405653 times.
470649 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
237 64996 lookupCacheHits++;
238 64996 return it->second;
239 }
240 405653 lookupCacheMisses++;
241
242 // Loop over function registry to find functions, that match the requirements of the call
243 405653 std::vector<Function *> matches;
244
2/2
✓ Branch 139 → 14 taken 4480030 times.
✓ Branch 139 → 140 taken 405652 times.
4885682 for (auto &[fctId, manifestations] : matchScope->functions) {
245
2/2
✓ Branch 136 → 19 taken 4626162 times.
✓ Branch 136 → 137 taken 176475 times.
4802637 for (const auto &[signature, presetFunction] : manifestations) {
246
2/4
✓ Branch 22 → 23 taken 4626162 times.
✗ Branch 22 → 203 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 4626162 times.
4626162 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 4626162 times.
✗ Branch 25 → 203 not taken.
✓ Branch 26 → 27 taken 4484799 times.
✓ Branch 26 → 28 taken 141363 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 4484799 times.
✓ Branch 30 → 31 taken 141363 times.
✓ Branch 30 → 32 taken 4484799 times.
4626162 if (presetFunction.isGenericSubstantiation() || presetFunction.isNewlyInserted)
250 322607 continue;
251
252 // Copy the function to be able to substantiate types
253
1/2
✓ Branch 32 → 33 taken 4484799 times.
✗ Branch 32 → 203 not taken.
4484799 Function candidate = presetFunction;
254
255 // Prepare type mapping, based on the given initial type mapping
256 4484799 TypeMapping &typeMapping = candidate.typeMapping;
257 4484799 typeMapping.clear();
258
2/2
✓ Branch 46 → 35 taken 57161 times.
✓ Branch 46 → 47 taken 4484799 times.
4541960 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 57161 times.
✗ Branch 35 → 201 not taken.
57161 const GenericType &candidateTemplateType = candidate.templateTypes.at(i);
263
3/4
✓ Branch 36 → 37 taken 57161 times.
✗ Branch 36 → 201 not taken.
✓ Branch 37 → 38 taken 501 times.
✓ Branch 37 → 39 taken 56660 times.
57161 if (!candidateTemplateType.is(TY_GENERIC))
264 501 continue;
265
1/2
✓ Branch 39 → 40 taken 56660 times.
✗ Branch 39 → 201 not taken.
56660 const std::string &typeName = candidateTemplateType.getSubType();
266
1/2
✓ Branch 40 → 41 taken 56660 times.
✗ Branch 40 → 201 not taken.
56660 const QualType &templateType = templateTypeHints.at(i);
267
1/2
✓ Branch 41 → 42 taken 56660 times.
✗ Branch 41 → 201 not taken.
56660 typeMapping.emplace(typeName, templateType);
268 }
269
270 4484799 bool forceSubstantiation = false;
271
2/2
✓ Branch 47 → 48 taken 4484798 times.
✓ Branch 47 → 201 taken 1 time.
4484799 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
272 strictQualifierMatching, forceSubstantiation, callNode);
273
2/2
✓ Branch 48 → 49 taken 4289766 times.
✓ Branch 48 → 50 taken 195032 times.
4484798 if (matchResult == MatchResult::SKIP_FUNCTION)
274 4289766 break; // Leave the whole function
275
2/2
✓ Branch 50 → 51 taken 164001 times.
✓ Branch 50 → 52 taken 31031 times.
195032 if (matchResult == MatchResult::SKIP_MANIFESTATION)
276 164001 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 31031 candidate.used = true;
280 31031 candidate.entry->used = true;
281
282 // Check if the function is generic needs to be substantiated
283
6/6
✓ Branch 53 → 54 taken 17488 times.
✓ Branch 53 → 56 taken 13543 times.
✓ Branch 54 → 55 taken 17243 times.
✓ Branch 54 → 56 taken 245 times.
✓ Branch 57 → 58 taken 17243 times.
✓ Branch 57 → 70 taken 13788 times.
31031 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
284
5/10
✓ Branch 58 → 59 taken 17243 times.
✗ Branch 58 → 182 not taken.
✓ Branch 59 → 60 taken 17243 times.
✗ Branch 59 → 64 not taken.
✓ Branch 60 → 61 taken 17243 times.
✗ Branch 60 → 182 not taken.
✓ Branch 61 → 62 taken 17243 times.
✗ Branch 61 → 182 not taken.
✓ Branch 62 → 63 taken 17243 times.
✗ Branch 62 → 64 not taken.
17243 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
285
2/4
✓ Branch 65 → 66 taken 17243 times.
✗ Branch 65 → 182 not taken.
✓ Branch 66 → 67 taken 17243 times.
✗ Branch 66 → 182 not taken.
17243 Function *match = &matchScope->functions.at(fctId).at(signature);
286 17243 match->used = true;
287
1/2
✓ Branch 67 → 68 taken 17243 times.
✗ Branch 67 → 182 not taken.
17243 matches.push_back(match);
288 17243 continue; // Match was successful -> match the next function
289 17243 }
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 13788 times.
✗ Branch 70 → 201 not taken.
13788 const std::string newSignature = candidate.getSignature(true, true, false, true);
297
3/4
✓ Branch 71 → 72 taken 13788 times.
✗ Branch 71 → 183 not taken.
✓ Branch 72 → 73 taken 1188 times.
✓ Branch 72 → 75 taken 12600 times.
13788 if (Function *existingManifestation = findManifestationBySignature(matchScope, newSignature)) {
298 1188 existingManifestation->used = true;
299
1/2
✓ Branch 73 → 74 taken 1188 times.
✗ Branch 73 → 183 not taken.
1188 matches.push_back(existingManifestation);
300 1188 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 12600 times.
✗ Branch 75 → 199 not taken.
12600 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
305
2/4
✓ Branch 77 → 78 taken 12600 times.
✗ Branch 77 → 199 not taken.
✓ Branch 78 → 79 taken 12600 times.
✗ Branch 78 → 199 not taken.
12600 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
306 12600 substantiatedFunction->alreadyTypeChecked = false;
307
2/4
✓ Branch 79 → 80 taken 12600 times.
✗ Branch 79 → 199 not taken.
✓ Branch 80 → 81 taken 12600 times.
✗ Branch 80 → 199 not taken.
12600 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
308 12600 substantiatedFunction->isNewlyInserted = true; // To not iterate over it in the same matching
309
310 // Copy function entry
311
1/2
✓ Branch 81 → 82 taken 12600 times.
✗ Branch 81 → 199 not taken.
12600 const std::string newScopeName = substantiatedFunction->getScopeName();
312
1/2
✓ Branch 82 → 83 taken 12600 times.
✗ Branch 82 → 197 not taken.
12600 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
313
1/2
✓ Branch 85 → 86 taken 12600 times.
✗ Branch 85 → 197 not taken.
12600 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newScopeName);
314
1/2
✗ Branch 86 → 87 not taken.
✓ Branch 86 → 88 taken 12600 times.
12600 assert(substantiatedFunction->entry != nullptr);
315
316 // Copy function scope
317
1/2
✓ Branch 88 → 89 taken 12600 times.
✗ Branch 88 → 197 not taken.
12600 const std::string oldScopeName = presetFunction.getScopeName();
318
1/2
✓ Branch 89 → 90 taken 12600 times.
✗ Branch 89 → 195 not taken.
12600 Scope *childScope = matchScope->copyChildScope(oldScopeName, newScopeName);
319
1/2
✗ Branch 90 → 91 not taken.
✓ Branch 90 → 92 taken 12600 times.
12600 assert(childScope != nullptr);
320 12600 childScope->isGenericScope = false;
321 12600 substantiatedFunction->bodyScope = childScope;
322
323 // Insert symbols for generic type names with concrete types into the child block
324
2/2
✓ Branch 102 → 94 taken 15625 times.
✓ Branch 102 → 103 taken 12600 times.
28225 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
325
2/4
✓ Branch 97 → 98 taken 15625 times.
✗ Branch 97 → 186 not taken.
✓ Branch 98 → 99 taken 15625 times.
✗ Branch 98 → 184 not taken.
15625 childScope->insertGenericType(typeName, GenericType(concreteType));
326
327 // Substantiate the 'this' entry in the new function scope
328
6/6
✓ Branch 106 → 107 taken 10066 times.
✓ Branch 106 → 110 taken 2534 times.
✓ Branch 108 → 109 taken 10064 times.
✓ Branch 108 → 110 taken 2 times.
✓ Branch 111 → 112 taken 10064 times.
✓ Branch 111 → 125 taken 2536 times.
12600 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
329
1/2
✓ Branch 114 → 115 taken 10064 times.
✗ Branch 114 → 190 not taken.
30192 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
330
1/2
✗ Branch 120 → 121 not taken.
✓ Branch 120 → 122 taken 10064 times.
10064 assert(thisEntry != nullptr);
331
2/4
✓ Branch 122 → 123 taken 10064 times.
✗ Branch 122 → 194 not taken.
✓ Branch 123 → 124 taken 10064 times.
✗ Branch 123 → 194 not taken.
10064 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
332 }
333
334 // Add to matched functions
335
1/2
✓ Branch 125 → 126 taken 12600 times.
✗ Branch 125 → 195 not taken.
12600 matches.push_back(substantiatedFunction);
336
337 12600 break; // Leave the whole manifestation list to not double-match the manifestation
338
2/2
✓ Branch 132 → 133 taken 181244 times.
✓ Branch 132 → 134 taken 4303554 times.
4498587 }
339 }
340
341 // If no matches were found, return a nullptr
342
2/2
✓ Branch 141 → 142 taken 374691 times.
✓ Branch 141 → 143 taken 30961 times.
405652 if (matches.empty())
343 374691 return nullptr;
344
345 // Tie-breaking: if multiple candidates match, narrow them by qualifier specificity and by preferring
346 // explicitly declared overloads over generic substitutions (see breakOverloadTie).
347
1/2
✓ Branch 143 → 144 taken 30961 times.
✗ Branch 143 → 219 not taken.
30961 breakOverloadTie(matches, reqArgs);
348
349 // Check if more than one function matches the requirements
350
1/2
✗ Branch 145 → 146 not taken.
✓ Branch 145 → 173 taken 30961 times.
30961 if (matches.size() > 1) {
351 std::stringstream errorMessage;
352 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
353 for (const Function *match : matches)
354 errorMessage << "\n " << match->getSignature();
355 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
356 }
357 30961 Function *matchedFunction = matches.front();
358 30961 matchedFunction->isNewlyInserted = false;
359
360 // Insert into cache
361
1/2
✓ Branch 174 → 175 taken 30961 times.
✗ Branch 174 → 219 not taken.
30961 lookupCache[cacheKey] = matchedFunction;
362
363 // Trigger revisit in type checker if required
364
1/2
✓ Branch 175 → 176 taken 30961 times.
✗ Branch 175 → 219 not taken.
30961 TypeChecker::requestRevisitIfRequired(matchedFunction);
365
366 // Return the very match
367 30961 return matchedFunction;
368 405653 }
369
370 4656832 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
371 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
372 bool strictQualifierMatching, bool &forceSubstantiation,
373 const ASTNode *callNode) {
374 // Check name requirement
375
2/2
✓ Branch 3 → 4 taken 4419458 times.
✓ Branch 3 → 5 taken 237374 times.
4656832 if (!matchName(candidate, reqName))
376 4419458 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
377
378 // Check 'this' type requirement
379
2/4
✓ Branch 5 → 6 taken 237374 times.
✗ Branch 5 → 29 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 237374 times.
237374 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
380 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
381
382 // Check arg types requirement
383
4/4
✓ Branch 8 → 9 taken 237373 times.
✓ Branch 8 → 29 taken 1 time.
✓ Branch 9 → 10 taken 195390 times.
✓ Branch 9 → 11 taken 41983 times.
237374 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
384 195390 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
385
386 // Check if there are unresolved generic types. Only template slots that actually are generic have to be resolved into
387 // the type mapping - a slot that already holds a concrete type (as for the default members of an already-concrete
388 // struct manifestation, e.g. HashEntry<int, String>::dtor()) never ends up there and must not count as unresolved.
389 18964 const auto isGeneric = [](const GenericType &templateType) { return templateType.is(TY_GENERIC); };
390
3/4
✓ Branch 12 → 13 taken 41983 times.
✗ Branch 12 → 29 not taken.
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 41981 times.
41983 if (typeMapping.size() < static_cast<size_t>(std::ranges::count_if(candidate.templateTypes, isGeneric)))
391 2 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
392
393 // Substantiate return type
394
1/2
✓ Branch 15 → 16 taken 41981 times.
✗ Branch 15 → 29 not taken.
41981 substantiateReturnType(candidate, typeMapping, callNode);
395
396 // Set the match scope to the scope of the concrete substantiation
397 41981 const QualType &thisType = candidate.thisType;
398
3/4
✓ Branch 16 → 17 taken 41981 times.
✗ Branch 16 → 29 not taken.
✓ Branch 17 → 18 taken 31278 times.
✓ Branch 17 → 25 taken 10703 times.
41981 if (!thisType.is(TY_DYN)) {
399 // If we only have the generic struct scope, lookup the concrete manifestation scope
400
2/2
✓ Branch 18 → 19 taken 15 times.
✓ Branch 18 → 23 taken 31263 times.
31278 if (matchScope->isGenericScope) {
401
1/2
✓ Branch 19 → 20 taken 15 times.
✗ Branch 19 → 29 not taken.
15 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
402
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 15 times.
15 assert(spiceStruct != nullptr);
403 15 matchScope = spiceStruct->scope;
404 }
405
1/2
✓ Branch 23 → 24 taken 31278 times.
✗ Branch 23 → 28 not taken.
31278 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
406 }
407
408 41981 return MatchResult::MATCHED;
409 }
410
411 /**
412 * Checks if the matching candidate fulfills the name requirement
413 *
414 * @param candidate Matching candidate function
415 * @param reqName Requested function name
416 * @return Fulfilled or not
417 */
418 4656832 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
419
420 /**
421 * Checks if the matching candidate fulfills the 'this' type requirement
422 *
423 * @param candidate Matching candidate function
424 * @param reqThisType Requested 'this' type
425 * @param typeMapping Concrete template type mapping
426 * @param strictQualifierMatching Match qualifiers strictly
427 * @param callNode Call AST node for printing error messages
428 * @return Fulfilled or not
429 */
430 237374 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
431 bool strictQualifierMatching, const ASTNode *callNode) {
432 237374 QualType &candidateThisType = candidate.thisType;
433
434 // Shortcut for procedures
435
7/10
✓ Branch 2 → 3 taken 237374 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 151697 times.
✓ Branch 3 → 7 taken 85677 times.
✓ Branch 4 → 5 taken 151697 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 151697 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 151697 times.
✓ Branch 8 → 10 taken 85677 times.
237374 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
436 151697 return true;
437
438 // Give the type matcher a way to retrieve instances of GenericType by their name
439 184816 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
440 13462 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
441 85677 };
442
443 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
444
2/4
✓ Branch 11 → 12 taken 85677 times.
✗ Branch 11 → 21 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 85677 times.
85677 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
445 strictQualifierMatching))
446 return false;
447
448 // Substantiate the candidate param type, based on the type mapping
449
3/4
✓ Branch 14 → 15 taken 85677 times.
✗ Branch 14 → 21 not taken.
✓ Branch 15 → 16 taken 14859 times.
✓ Branch 15 → 17 taken 70818 times.
85677 if (candidateThisType.hasAnyGenericParts())
450
1/2
✓ Branch 16 → 17 taken 14859 times.
✗ Branch 16 → 21 not taken.
14859 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
451
452 85677 return true;
453 85677 }
454
455 /**
456 * Checks if the matching candidate fulfills the argument types requirement
457 *
458 * @param candidate Matching candidate function
459 * @param reqArgs Requested argument types
460 * @param typeMapping Concrete template type mapping
461 * @param strictQualifierMatching Match qualifiers strictly
462 * @param needsSubstantiation We want to create a substantiation after successfully matching
463 * @param callNode Call AST node for printing error messages
464 * @return Fulfilled or not
465 */
466 237374 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
467 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
468 237374 std::vector<Param> &candidateParamList = candidate.paramList;
469
470 // If the number of arguments does not match with the number of params, the matching fails
471
6/6
✓ Branch 2 → 3 taken 237133 times.
✓ Branch 2 → 7 taken 241 times.
✓ Branch 5 → 6 taken 37316 times.
✓ Branch 5 → 7 taken 199817 times.
✓ Branch 8 → 9 taken 37316 times.
✓ Branch 8 → 10 taken 200058 times.
237374 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
472 37316 return false;
473 // In the case of a vararg function, we only disallow fewer arguments than parameters
474
4/6
✓ Branch 10 → 11 taken 241 times.
✓ Branch 10 → 15 taken 199817 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 241 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 200058 times.
200058 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
475 return false;
476
477 // Give the type matcher a way to retrieve instances of GenericType by their name
478 412542 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
479 12426 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
480 200058 };
481
482 // Loop over all parameters
483
2/2
✓ Branch 66 → 20 taken 203139 times.
✓ Branch 66 → 67 taken 41983 times.
245122 for (size_t i = 0; i < reqArgs.size(); i++) {
484 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
485 // but we need to modify the candidate param list to accept them
486
6/6
✓ Branch 20 → 21 taken 983 times.
✓ Branch 20 → 24 taken 202156 times.
✓ Branch 22 → 23 taken 263 times.
✓ Branch 22 → 24 taken 720 times.
✓ Branch 25 → 26 taken 263 times.
✓ Branch 25 → 29 taken 202876 times.
203139 if (candidate.isVararg && i >= candidateParamList.size()) {
487
2/4
✓ Branch 26 → 27 taken 263 times.
✗ Branch 26 → 71 not taken.
✓ Branch 27 → 28 taken 263 times.
✗ Branch 27 → 71 not taken.
263 candidateParamList.push_back(Param(reqArgs.at(i).first, false));
488 263 needsSubstantiation = true; // We need to modify the candidate param types
489 263 continue;
490 }
491
492 // Retrieve actual and requested types
493
2/4
✓ Branch 29 → 30 taken 202876 times.
✗ Branch 29 → 84 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 202876 times.
202876 assert(!candidateParamList.at(i).isOptional);
494
1/2
✓ Branch 32 → 33 taken 202876 times.
✗ Branch 32 → 84 not taken.
202876 QualType &candidateType = candidateParamList.at(i).qualType;
495
1/2
✓ Branch 33 → 34 taken 202876 times.
✗ Branch 33 → 84 not taken.
202876 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
496
497 // Check if the requested param type matches the candidate param type. The type mapping may be extended
498
3/4
✓ Branch 36 → 37 taken 202876 times.
✗ Branch 36 → 84 not taken.
✓ Branch 37 → 38 taken 158074 times.
✓ Branch 37 → 39 taken 44802 times.
202876 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
499 strictQualifierMatching))
500 158074 return false;
501
502 // Substantiate the candidate param type, based on the type mapping
503
3/4
✓ Branch 39 → 40 taken 44802 times.
✗ Branch 39 → 84 not taken.
✓ Branch 40 → 41 taken 10777 times.
✓ Branch 40 → 42 taken 34025 times.
44802 if (candidateType.hasAnyGenericParts())
504
1/2
✓ Branch 41 → 42 taken 10777 times.
✗ Branch 41 → 84 not taken.
10777 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
505
506 // Check if we try to bind a non-ref temporary to a non-const ref parameter
507
3/4
✓ Branch 42 → 43 taken 44802 times.
✗ Branch 42 → 84 not taken.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 54 taken 44801 times.
44802 if (!candidateType.canBind(requestedType, isArgTemporary)) {
508
1/2
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 53 not taken.
1 if (callNode)
509
2/4
✓ Branch 48 → 49 taken 1 time.
✗ Branch 48 → 75 not taken.
✓ Branch 49 → 50 taken 1 time.
✗ Branch 49 → 72 not taken.
3 throw SemanticError(callNode, TEMP_TO_NON_CONST_REF, "Temporary values can only be bound to const reference parameters");
510 return false;
511 }
512
513 // If we have a function/procedure type we need to take care of the information, if it takes captures
514
9/12
✓ Branch 54 → 55 taken 44801 times.
✗ Branch 54 → 81 not taken.
✓ Branch 55 → 56 taken 44801 times.
✗ Branch 55 → 81 not taken.
✓ Branch 56 → 57 taken 64 times.
✓ Branch 56 → 60 taken 44737 times.
✓ Branch 57 → 58 taken 64 times.
✗ Branch 57 → 81 not taken.
✓ Branch 58 → 59 taken 16 times.
✓ Branch 58 → 60 taken 48 times.
✓ Branch 61 → 62 taken 16 times.
✓ Branch 61 → 64 taken 44785 times.
44801 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
515
1/2
✓ Branch 62 → 63 taken 16 times.
✗ Branch 62 → 83 not taken.
16 candidateType = candidateType.getWithLambdaCaptures();
516 16 needsSubstantiation = true;
517 }
518 }
519
520 41983 return true;
521 200058 }
522
523 /**
524 * Substantiates the candidate return type, based on the given type mapping
525 *
526 * @param candidate Matching candidate function
527 * @param typeMapping Concrete template type mapping
528 * @param callNode AST node for error messages
529 */
530 41981 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
531
2/2
✓ Branch 3 → 4 taken 3951 times.
✓ Branch 3 → 5 taken 38030 times.
41981 if (candidate.returnType.hasAnyGenericParts())
532 3951 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
533 41981 }
534
535 /**
536 * Searches the candidate template types for a generic type object with a certain name and return it
537 *
538 * @param candidate Matching candidate function
539 * @param templateTypeName Template type name
540 * @return Generic type object
541 */
542 25888 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
543 const std::string &templateTypeName) {
544
1/2
✓ Branch 19 → 4 taken 29128 times.
✗ Branch 19 → 20 not taken.
55016 for (const GenericType &templateType : candidate.templateTypes) {
545
3/4
✓ Branch 6 → 7 taken 29128 times.
✗ Branch 6 → 22 not taken.
✓ Branch 8 → 9 taken 25888 times.
✓ Branch 8 → 10 taken 3240 times.
29128 if (templateType.getSubType() == templateTypeName)
546 25888 return &templateType;
547 }
548 return nullptr;
549 }
550
551 /**
552 * Narrow a multi-match overload set by preferring the candidate whose parameter qualifiers most closely match
553 * the argument qualifiers. This resolves the typical copy-vs-move ctor ambiguity where both a `const T&`
554 * (copy) and a `T&` (move) ctor match a non-const lvalue argument - we prefer the non-const-ref candidate
555 * (move) since it requires no constification. When the argument is const, we prefer the const-ref candidate
556 * (copy) since binding to a non-const ref would require const-loss. As a secondary criterion, an explicitly
557 * declared (non-generic) overload is preferred over a generic substitution that matches equally well.
558 *
559 * Modifies `matches` in place, removing any candidate that scores worse than the best one. A no-op if there
560 * are fewer than two candidates.
561 *
562 * @param matches Candidate list to narrow
563 * @param reqArgs Argument list from the call site
564 */
565 30961 void FunctionManager::breakOverloadTie(std::vector<Function *> &matches, const ArgList &reqArgs) {
566
2/2
✓ Branch 3 → 4 taken 30891 times.
✓ Branch 3 → 5 taken 70 times.
30961 if (matches.size() < 2)
567 30891 return;
568
569 70 constexpr int CONSTIFY_PENALTY = 1;
570 70 constexpr int CONST_LOSS_PENALTY = 100;
571 // An exact type match is always preferred over a match that required an implicit upcast (struct to
572 // implemented interface, or struct to composed base struct). This keeps e.g. 'f(Derived)' preferred
573 // over 'f(Base)' when called with a Derived, instead of reporting an ambiguity.
574 70 constexpr int UPCAST_PENALTY = 1000;
575 350 const auto scoreSpecificity = [&](const Function *f) {
576 280 int penalty = 0;
577
2/2
✓ Branch 23 → 3 taken 280 times.
✓ Branch 23 → 24 taken 280 times.
560 for (size_t i = 0; i < std::min(reqArgs.size(), f->paramList.size()); i++) {
578
1/2
✓ Branch 3 → 4 taken 280 times.
✗ Branch 3 → 28 not taken.
280 const QualType &paramType = f->paramList.at(i).qualType;
579
1/2
✓ Branch 4 → 5 taken 280 times.
✗ Branch 4 → 28 not taken.
280 const QualType &argType = reqArgs.at(i).first;
580
2/4
✓ Branch 5 → 6 taken 280 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 280 times.
✗ Branch 6 → 26 not taken.
280 const bool paramIsConst = paramType.removeReferenceWrapper().isConst();
581
2/4
✓ Branch 7 → 8 taken 280 times.
✗ Branch 7 → 27 not taken.
✓ Branch 8 → 9 taken 280 times.
✗ Branch 8 → 27 not taken.
280 const bool argIsConst = argType.removeReferenceWrapper().isConst();
582
4/4
✓ Branch 9 → 10 taken 142 times.
✓ Branch 9 → 12 taken 138 times.
✓ Branch 10 → 11 taken 50 times.
✓ Branch 10 → 12 taken 92 times.
280 if (paramIsConst && !argIsConst)
583 50 penalty += CONSTIFY_PENALTY;
584
4/4
✓ Branch 12 → 13 taken 138 times.
✓ Branch 12 → 15 taken 92 times.
✓ Branch 13 → 14 taken 84 times.
✓ Branch 13 → 15 taken 54 times.
230 else if (!paramIsConst && argIsConst)
585 84 penalty += CONST_LOSS_PENALTY;
586 // Penalize matches that were only possible through an implicit upcast
587 280 QualType paramBase = paramType;
588 280 QualType argBase = argType;
589
1/2
✓ Branch 15 → 16 taken 280 times.
✗ Branch 15 → 28 not taken.
280 QualType::unwrapBothWithRefWrappers(paramBase, argBase);
590
2/4
✓ Branch 16 → 17 taken 280 times.
✗ Branch 16 → 28 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 280 times.
280 if (!paramBase.matches(argBase, true, true, true))
591 penalty += UPCAST_PENALTY;
592 }
593 280 return penalty;
594 70 };
595
596 70 int bestScore = std::numeric_limits<int>::max();
597
2/2
✓ Branch 20 → 7 taken 140 times.
✓ Branch 20 → 21 taken 70 times.
280 for (const Function *m : matches)
598
1/2
✓ Branch 9 → 10 taken 140 times.
✗ Branch 9 → 76 not taken.
140 bestScore = std::min(bestScore, scoreSpecificity(m));
599 70 std::vector<Function *> filtered;
600
1/2
✓ Branch 22 → 23 taken 70 times.
✗ Branch 22 → 80 not taken.
70 filtered.reserve(matches.size());
601
2/2
✓ Branch 39 → 25 taken 140 times.
✓ Branch 39 → 40 taken 70 times.
280 for (Function *m : matches)
602
3/4
✓ Branch 27 → 28 taken 140 times.
✗ Branch 27 → 78 not taken.
✓ Branch 28 → 29 taken 73 times.
✓ Branch 28 → 30 taken 67 times.
140 if (scoreSpecificity(m) == bestScore)
603
1/2
✓ Branch 29 → 30 taken 73 times.
✗ Branch 29 → 78 not taken.
73 filtered.push_back(m);
604 70 matches = std::move(filtered);
605
606 // Secondary tie-break: prefer an explicitly declared (non-generic) overload over a generic substitution
607 // when both match equally well, mirroring C++ overload resolution where a non-template wins over a
608 // template specialization. This resolves e.g. the copy ctor 'Any.ctor(const Any&)' vs. the value ctor
609 // 'Any.ctor<Any>(const Any&)' ambiguity when copy-constructing from another value of the same type. It is
610 // applied after the qualifier-specificity narrowing above, so a more specific generic match still wins.
611 // A generic substitution that loses here and was only inserted for this very match is removed from its
612 // declaration's manifestation list again, so the IR generator never emits a manifestation that was never
613 // type-checked (and so we leave no dead code behind).
614
6/8
✓ Branch 44 → 45 taken 3 times.
✓ Branch 44 → 48 taken 67 times.
✓ Branch 45 → 46 taken 3 times.
✗ Branch 45 → 80 not taken.
✓ Branch 46 → 47 taken 3 times.
✗ Branch 46 → 48 not taken.
✓ Branch 49 → 50 taken 3 times.
✓ Branch 49 → 73 taken 67 times.
75 if (matches.size() > 1 && std::ranges::any_of(matches, [](const Function *m) { return !m->isGenericSubstantiation(); })) {
615
2/2
✓ Branch 71 → 52 taken 6 times.
✓ Branch 71 → 72 taken 3 times.
12 for (Function *m : matches)
616
6/8
✓ Branch 54 → 55 taken 6 times.
✗ Branch 54 → 79 not taken.
✓ Branch 55 → 56 taken 3 times.
✓ Branch 55 → 58 taken 3 times.
✓ Branch 56 → 57 taken 3 times.
✗ Branch 56 → 58 not taken.
✓ Branch 59 → 60 taken 3 times.
✓ Branch 59 → 62 taken 3 times.
6 if (m->isGenericSubstantiation() && m->isNewlyInserted)
617
2/4
✓ Branch 60 → 61 taken 3 times.
✗ Branch 60 → 79 not taken.
✓ Branch 61 → 62 taken 3 times.
✗ Branch 61 → 79 not taken.
3 std::erase(*m->declNode->getFctManifestations(m->name), m);
618
1/2
✓ Branch 72 → 73 taken 3 times.
✗ Branch 72 → 80 not taken.
9 std::erase_if(matches, [](const Function *m) { return m->isGenericSubstantiation(); });
619 }
620 70 }
621
622 /**
623 * Calculate the cache key for the function lookup cache
624 *
625 * @param scope Scope to match against
626 * @param name Function name requirement
627 * @param thisType This type requirement
628 * @param args Argument requirement
629 * @param templateTypes Template type requirement
630 * @return Cache key
631 */
632 516801 uint64_t FunctionManager::getCacheKey(const Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
633 const QualTypeList &templateTypes) {
634 516801 uint64_t hash = 0;
635 516801 hashCombine64(hash, hashPointer(scope));
636 516801 hashCombine64(hash, std::hash<std::string>{}(name));
637 516801 hashCombine64(hash, std::hash<QualType>{}(thisType));
638
2/2
✓ Branch 27 → 10 taken 815547 times.
✓ Branch 27 → 28 taken 516801 times.
2664696 for (const auto &[first, second] : args) {
639 815547 hashCombine64(hash, std::hash<QualType>{}(first));
640 815547 hashCombine64(hash, std::hash<bool>{}(second));
641 }
642 516801 hashCombine64(hash, hashVector(templateTypes));
643 516801 return hash;
644 }
645
646 18224 bool FunctionManager::hasCtor(const Scope *matchScope, CtorKind kind) {
647
5/8
✓ Branch 2 → 3 taken 18224 times.
✗ Branch 2 → 60 not taken.
✓ Branch 3 → 4 taken 18224 times.
✗ Branch 3 → 60 not taken.
✓ Branch 4 → 5 taken 18224 times.
✗ Branch 4 → 60 not taken.
✓ Branch 55 → 6 taken 77419 times.
✓ Branch 55 → 56 taken 11837 times.
89256 for (const auto &manifestations : matchScope->functions | std::views::values) {
648
5/8
✓ Branch 7 → 8 taken 77419 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 77419 times.
✗ Branch 8 → 59 not taken.
✓ Branch 9 → 10 taken 77419 times.
✗ Branch 9 → 59 not taken.
✓ Branch 51 → 11 taken 85053 times.
✓ Branch 51 → 52 taken 71032 times.
156085 for (const auto &function : manifestations | std::views::values) {
649 // If it is no ctor, skip it
650
3/4
✓ Branch 12 → 13 taken 85053 times.
✗ Branch 12 → 59 not taken.
✓ Branch 13 → 14 taken 51002 times.
✓ Branch 13 → 15 taken 34051 times.
85053 if (function.name != CTOR_FUNCTION_NAME)
651 51002 continue;
652 // Classify the ctor based on its parameter list
653
6/8
✓ Branch 16 → 17 taken 18854 times.
✓ Branch 16 → 25 taken 15197 times.
✓ Branch 17 → 18 taken 18854 times.
✗ Branch 17 → 58 not taken.
✓ Branch 18 → 19 taken 18854 times.
✗ Branch 18 → 58 not taken.
✓ Branch 19 → 20 taken 9003 times.
✓ Branch 19 → 25 taken 9851 times.
43054 const bool singleSelfRefParam = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
654
5/8
✓ Branch 20 → 21 taken 9003 times.
✗ Branch 20 → 58 not taken.
✓ Branch 21 → 22 taken 9003 times.
✗ Branch 21 → 58 not taken.
✓ Branch 22 → 23 taken 9003 times.
✗ Branch 22 → 58 not taken.
✓ Branch 23 → 24 taken 7258 times.
✓ Branch 23 → 25 taken 1745 times.
9003 function.paramList.at(0).qualType.getBase() == function.thisType;
655
6/8
✓ Branch 26 → 27 taken 7258 times.
✓ Branch 26 → 31 taken 26793 times.
✓ Branch 27 → 28 taken 7258 times.
✗ Branch 27 → 59 not taken.
✓ Branch 28 → 29 taken 7258 times.
✗ Branch 28 → 59 not taken.
✓ Branch 29 → 30 taken 7245 times.
✓ Branch 29 → 31 taken 13 times.
34051 const bool isCopyCtor = singleSelfRefParam && function.paramList.at(0).qualType.isConstRef();
656
6/8
✓ Branch 32 → 33 taken 7258 times.
✓ Branch 32 → 37 taken 26793 times.
✓ Branch 33 → 34 taken 7258 times.
✗ Branch 33 → 59 not taken.
✓ Branch 34 → 35 taken 7258 times.
✗ Branch 34 → 59 not taken.
✓ Branch 35 → 36 taken 13 times.
✓ Branch 35 → 37 taken 7245 times.
34051 const bool isMoveCtor = singleSelfRefParam && !function.paramList.at(0).qualType.isConstRef();
657
3/4
✓ Branch 38 → 39 taken 25154 times.
✓ Branch 38 → 42 taken 5234 times.
✓ Branch 38 → 45 taken 3663 times.
✗ Branch 38 → 49 not taken.
34051 switch (kind) {
658 25154 case CtorKind::COPY:
659
2/2
✓ Branch 39 → 40 taken 4086 times.
✓ Branch 39 → 41 taken 21068 times.
25154 if (isCopyCtor)
660 6387 return true;
661 21068 break;
662 5234 case CtorKind::MOVE:
663
2/2
✓ Branch 42 → 43 taken 4 times.
✓ Branch 42 → 44 taken 5230 times.
5234 if (isMoveCtor)
664 4 return true;
665 5230 break;
666 3663 case CtorKind::ANY_NON_COPY_NON_MOVE:
667
4/4
✓ Branch 45 → 46 taken 2299 times.
✓ Branch 45 → 48 taken 1364 times.
✓ Branch 46 → 47 taken 2297 times.
✓ Branch 46 → 48 taken 2 times.
3663 if (!isCopyCtor && !isMoveCtor)
668 2297 return true;
669 1366 break;
670 }
671 }
672 }
673 11837 return false;
674 }
675
676 363 bool FunctionManager::hasAnyCtor(const Scope *matchScope) {
677
5/8
✓ Branch 2 → 3 taken 363 times.
✗ Branch 2 → 24 not taken.
✓ Branch 3 → 4 taken 363 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 363 times.
✗ Branch 4 → 24 not taken.
✓ Branch 20 → 6 taken 640 times.
✓ Branch 20 → 21 taken 363 times.
1003 for (const auto &manifestations : matchScope->functions | std::views::values)
678
5/8
✓ Branch 7 → 8 taken 640 times.
✗ Branch 7 → 23 not taken.
✓ Branch 8 → 9 taken 640 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 640 times.
✗ Branch 9 → 23 not taken.
✓ Branch 17 → 11 taken 657 times.
✓ Branch 17 → 18 taken 640 times.
1297 for (const auto &function : manifestations | std::views::values)
679
2/4
✓ Branch 12 → 13 taken 657 times.
✗ Branch 12 → 23 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 657 times.
657 if (function.name == CTOR_FUNCTION_NAME)
680 return true;
681 363 return false;
682 }
683
684 2409 bool FunctionManager::hasAnyNonCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::ANY_NON_COPY_NON_MOVE); }
685
686 13421 bool FunctionManager::hasCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::COPY); }
687
688 2390 bool FunctionManager::hasUserCopyCtor(const Scope *matchScope) {
689
5/8
✓ Branch 2 → 3 taken 2390 times.
✗ Branch 2 → 42 not taken.
✓ Branch 3 → 4 taken 2390 times.
✗ Branch 3 → 42 not taken.
✓ Branch 4 → 5 taken 2390 times.
✗ Branch 4 → 42 not taken.
✓ Branch 37 → 6 taken 10747 times.
✓ Branch 37 → 38 taken 2163 times.
12910 for (const auto &manifestations : matchScope->functions | std::views::values) {
690
5/8
✓ Branch 7 → 8 taken 10747 times.
✗ Branch 7 → 41 not taken.
✓ Branch 8 → 9 taken 10747 times.
✗ Branch 8 → 41 not taken.
✓ Branch 9 → 10 taken 10747 times.
✗ Branch 9 → 41 not taken.
✓ Branch 34 → 11 taken 11603 times.
✓ Branch 34 → 35 taken 10520 times.
22123 for (const auto &function : manifestations | std::views::values) {
691
7/8
✓ Branch 12 → 13 taken 11603 times.
✗ Branch 12 → 41 not taken.
✓ Branch 13 → 14 taken 5223 times.
✓ Branch 13 → 15 taken 6380 times.
✓ Branch 14 → 15 taken 1677 times.
✓ Branch 14 → 16 taken 3546 times.
✓ Branch 17 → 18 taken 8057 times.
✓ Branch 17 → 19 taken 3546 times.
11603 if (function.name != CTOR_FUNCTION_NAME || function.implicitDefault)
692 8057 continue;
693
6/8
✓ Branch 20 → 21 taken 2548 times.
✓ Branch 20 → 29 taken 998 times.
✓ Branch 21 → 22 taken 2548 times.
✗ Branch 21 → 40 not taken.
✓ Branch 22 → 23 taken 2548 times.
✗ Branch 22 → 40 not taken.
✓ Branch 23 → 24 taken 366 times.
✓ Branch 23 → 29 taken 2182 times.
3912 const bool isCopyCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isConstRef() &&
694
5/8
✓ Branch 24 → 25 taken 366 times.
✗ Branch 24 → 40 not taken.
✓ Branch 25 → 26 taken 366 times.
✗ Branch 25 → 40 not taken.
✓ Branch 26 → 27 taken 366 times.
✗ Branch 26 → 40 not taken.
✓ Branch 27 → 28 taken 227 times.
✓ Branch 27 → 29 taken 139 times.
366 function.paramList.at(0).qualType.getBase() == function.thisType;
695
2/2
✓ Branch 30 → 31 taken 227 times.
✓ Branch 30 → 32 taken 3319 times.
3546 if (isCopyCtor)
696 227 return true;
697 }
698 }
699 2163 return false;
700 }
701
702 2394 bool FunctionManager::hasMoveCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::MOVE); }
703
704 15532 Function *FunctionManager::findMoveCtor(Scope *matchScope) {
705
5/8
✓ Branch 2 → 3 taken 15532 times.
✗ Branch 2 → 41 not taken.
✓ Branch 3 → 4 taken 15532 times.
✗ Branch 3 → 41 not taken.
✓ Branch 4 → 5 taken 15532 times.
✗ Branch 4 → 41 not taken.
✓ Branch 36 → 6 taken 136523 times.
✓ Branch 36 → 37 taken 15051 times.
151574 for (auto &manifestations : matchScope->functions | std::views::values) {
706
5/8
✓ Branch 7 → 8 taken 136523 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 136523 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 136523 times.
✗ Branch 9 → 40 not taken.
✓ Branch 33 → 11 taken 177220 times.
✓ Branch 33 → 34 taken 136042 times.
313262 for (auto &function : manifestations | std::views::values) {
707
3/4
✓ Branch 12 → 13 taken 177220 times.
✗ Branch 12 → 40 not taken.
✓ Branch 13 → 14 taken 131657 times.
✓ Branch 13 → 15 taken 45563 times.
177220 if (function.name != CTOR_FUNCTION_NAME)
708 131657 continue;
709
4/6
✓ Branch 17 → 18 taken 33671 times.
✗ Branch 17 → 39 not taken.
✓ Branch 18 → 19 taken 33671 times.
✗ Branch 18 → 39 not taken.
✓ Branch 19 → 20 taken 21737 times.
✓ Branch 19 → 28 taken 11934 times.
79234 const bool isMoveCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
710
6/8
✓ Branch 16 → 17 taken 33671 times.
✓ Branch 16 → 28 taken 11892 times.
✓ Branch 20 → 21 taken 21737 times.
✗ Branch 20 → 39 not taken.
✓ Branch 21 → 22 taken 21737 times.
✗ Branch 21 → 39 not taken.
✓ Branch 22 → 23 taken 4054 times.
✓ Branch 22 → 28 taken 17683 times.
83288 !function.paramList.at(0).qualType.isConstRef() &&
711
5/8
✓ Branch 23 → 24 taken 4054 times.
✗ Branch 23 → 39 not taken.
✓ Branch 24 → 25 taken 4054 times.
✗ Branch 24 → 39 not taken.
✓ Branch 25 → 26 taken 4054 times.
✗ Branch 25 → 39 not taken.
✓ Branch 26 → 27 taken 481 times.
✓ Branch 26 → 28 taken 3573 times.
4054 function.paramList.at(0).qualType.getBase() == function.thisType;
712
2/2
✓ Branch 29 → 30 taken 481 times.
✓ Branch 29 → 31 taken 45082 times.
45563 if (isMoveCtor)
713 481 return &function;
714 }
715 }
716 15051 return nullptr;
717 }
718
719 9 bool FunctionManager::hasDefaultCtor(const Scope *matchScope) {
720
4/8
✓ Branch 2 → 3 taken 9 times.
✗ Branch 2 → 29 not taken.
✓ Branch 3 → 4 taken 9 times.
✗ Branch 3 → 29 not taken.
✓ Branch 4 → 5 taken 9 times.
✗ Branch 4 → 29 not taken.
✓ Branch 25 → 6 taken 34 times.
✗ Branch 25 → 26 not taken.
34 for (const auto &manifestations : matchScope->functions | std::views::values)
721
5/8
✓ Branch 7 → 8 taken 34 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 34 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 34 times.
✗ Branch 9 → 28 not taken.
✓ Branch 22 → 11 taken 34 times.
✓ Branch 22 → 23 taken 25 times.
59 for (const auto &function : manifestations | std::views::values)
722
6/8
✓ Branch 12 → 13 taken 34 times.
✗ Branch 12 → 28 not taken.
✓ Branch 13 → 14 taken 9 times.
✓ Branch 13 → 17 taken 25 times.
✓ Branch 15 → 16 taken 9 times.
✗ Branch 15 → 17 not taken.
✓ Branch 18 → 19 taken 9 times.
✓ Branch 18 → 20 taken 25 times.
34 if (function.name == CTOR_FUNCTION_NAME && function.paramList.empty())
723 9 return true;
724 return false;
725 }
726
727 23548 bool FunctionManager::hasDtor(const Scope *matchScope) {
728
5/8
✓ Branch 2 → 3 taken 23548 times.
✗ Branch 2 → 24 not taken.
✓ Branch 3 → 4 taken 23548 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 23548 times.
✗ Branch 4 → 24 not taken.
✓ Branch 20 → 6 taken 166122 times.
✓ Branch 20 → 21 taken 13151 times.
179273 for (const auto &manifestations : matchScope->functions | std::views::values)
729
5/8
✓ Branch 7 → 8 taken 166122 times.
✗ Branch 7 → 23 not taken.
✓ Branch 8 → 9 taken 166122 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 166122 times.
✗ Branch 9 → 23 not taken.
✓ Branch 17 → 11 taken 186267 times.
✓ Branch 17 → 18 taken 155725 times.
341992 for (const auto &function : manifestations | std::views::values)
730
3/4
✓ Branch 12 → 13 taken 186267 times.
✗ Branch 12 → 23 not taken.
✓ Branch 13 → 14 taken 10397 times.
✓ Branch 13 → 15 taken 175870 times.
186267 if (function.name == DTOR_FUNCTION_NAME)
731 10397 return true;
732 13151 return false;
733 }
734
735 /**
736 * Clear the lookup cache
737 */
738 584 void FunctionManager::cleanup() {
739 584 lookupCache.clear();
740 584 lookupCacheHits = 0;
741 584 lookupCacheMisses = 0;
742 584 }
743
744 /**
745 * Dump usage statistics for the lookup cache
746 */
747 337 std::string FunctionManager::dumpLookupCacheStatistics() {
748
1/2
✓ Branch 2 → 3 taken 337 times.
✗ Branch 2 → 22 not taken.
337 std::stringstream stats;
749
2/4
✓ Branch 3 → 4 taken 337 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 337 times.
✗ Branch 4 → 20 not taken.
337 stats << "FunctionManager lookup cache statistics:" << std::endl;
750
3/6
✓ Branch 5 → 6 taken 337 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 337 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 337 times.
✗ Branch 8 → 20 not taken.
337 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
751
3/6
✓ Branch 9 → 10 taken 337 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 337 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 337 times.
✗ Branch 11 → 20 not taken.
337 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
752
3/6
✓ Branch 12 → 13 taken 337 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 337 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 337 times.
✗ Branch 14 → 20 not taken.
337 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
753
1/2
✓ Branch 15 → 16 taken 337 times.
✗ Branch 15 → 20 not taken.
674 return stats.str();
754 337 }
755
756 } // namespace spice::compiler
757