GCC Code Coverage Report


Directory: ../
File: src/typechecker/FunctionManager.cpp
Date: 2025-03-05 01:50:32
Exec Total Coverage
Lines: 252 257 98.1%
Functions: 20 20 100.0%
Branches: 280 444 63.1%

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "FunctionManager.h"
4 #include "TypeChecker.h"
5
6 #include <ast/ASTNodes.h>
7 #include <exception/SemanticError.h>
8 #include <model/GenericType.h>
9 #include <symboltablebuilder/Scope.h>
10 #include <symboltablebuilder/SymbolTableBuilder.h>
11 #include <typechecker/TypeMatcher.h>
12 #include <util/CodeLoc.h>
13 #include <util/CustomHashFunctions.h>
14
15 namespace spice::compiler {
16
17 // Static member initialization
18 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
19 size_t FunctionManager::lookupCacheHits = 0;
20 size_t FunctionManager::lookupCacheMisses = 0;
21
22 10974 Function *FunctionManager::insert(Scope *insertScope, const Function &baseFunction, std::vector<Function *> *nodeFunctionList) {
23 // Open a new manifestation list for the function definition
24
3/6
✓ Branch 0 (2→3) taken 10974 times.
✗ Branch 1 (2→43) not taken.
✓ Branch 2 (3→4) taken 10974 times.
✗ Branch 3 (3→40) not taken.
✓ Branch 4 (4→5) taken 10974 times.
✗ Branch 5 (4→38) not taken.
10974 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
25
2/4
✓ Branch 0 (8→9) taken 10974 times.
✗ Branch 1 (8→46) not taken.
✓ Branch 2 (9→10) taken 10974 times.
✗ Branch 3 (9→44) not taken.
10974 insertScope->functions.insert({fctId, FunctionManifestationList()});
26
27 // Collect substantiations
28 10974 std::vector<Function> manifestations;
29
1/2
✓ Branch 0 (12→13) taken 10974 times.
✗ Branch 1 (12→51) not taken.
10974 substantiateOptionalParams(baseFunction, manifestations);
30
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 10974 times.
10974 assert(!manifestations.empty());
31
32 // Save substantiations in declaration node
33 10974 Function *manifestationPtr = nullptr;
34
2/2
✓ Branch 0 (26→18) taken 11740 times.
✓ Branch 1 (26→27) taken 10972 times.
22712 for (const Function &manifestation : manifestations) {
35
2/2
✓ Branch 0 (19→20) taken 11738 times.
✓ Branch 1 (19→50) taken 2 times.
11740 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
36
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 11738 times.
11738 assert(manifestationPtr != nullptr);
37
1/2
✓ Branch 0 (22→23) taken 11738 times.
✗ Branch 1 (22→24) not taken.
11738 if (nodeFunctionList)
38
1/2
✓ Branch 0 (23→24) taken 11738 times.
✗ Branch 1 (23→50) not taken.
11738 nodeFunctionList->push_back(manifestationPtr);
39 }
40
41
1/2
✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→29) taken 10972 times.
10972 if (!nodeFunctionList)
42 return manifestationPtr;
43
44
1/2
✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 10972 times.
10972 assert(!nodeFunctionList->empty());
45 10972 return nodeFunctionList->front();
46 10976 }
47
48 /**
49 * Create definite functions from ambiguous ones, in regard to optional arguments.
50 *
51 * Example:
52 * int testFunc(string, int?, double?)
53 * gets
54 * int testFunc(string)
55 * int testFunc(string, int)
56 * int testFunc(string, int, double)
57 *
58 * This method also accepts functions, that are already definite, but does nothing to them.
59 *
60 * @param baseFunction Ambiguous base function
61 * @param manifestations Vector to store the definite manifestations
62 * @return True, if there were optional arguments found
63 */
64 10974 void FunctionManager::substantiateOptionalParams(const Function &baseFunction, std::vector<Function> &manifestations) {
65 // Handle the case of no parameters -> simply return the base function
66
2/2
✓ Branch 0 (3→4) taken 2723 times.
✓ Branch 1 (3→6) taken 8251 times.
10974 if (baseFunction.paramList.empty()) {
67
1/2
✓ Branch 0 (4→5) taken 2723 times.
✗ Branch 1 (4→39) not taken.
2723 manifestations.push_back(baseFunction);
68 2723 return;
69 }
70
71 8251 ParamList currentFunctionParamTypes;
72
1/2
✓ Branch 0 (7→8) taken 8251 times.
✗ Branch 1 (7→37) not taken.
8251 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
73 8251 bool metFirstOptionalParam = false;
74
1/2
✓ Branch 0 (8→9) taken 8251 times.
✗ Branch 1 (8→37) not taken.
8251 Function manifestation = baseFunction;
75
76 // Loop over all parameters
77
2/2
✓ Branch 0 (24→11) taken 13128 times.
✓ Branch 1 (24→25) taken 8251 times.
21379 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
78 // Check if we have a mandatory parameter
79
2/2
✓ Branch 0 (12→13) taken 12362 times.
✓ Branch 1 (12→15) taken 766 times.
13128 if (!isOptional) {
80
1/2
✓ Branch 0 (13→14) taken 12362 times.
✗ Branch 1 (13→32) not taken.
12362 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
81 12362 continue;
82 }
83
84 // Add substantiation without the optional parameter
85
2/2
✓ Branch 0 (15→16) taken 760 times.
✓ Branch 1 (15→19) taken 6 times.
766 if (!metFirstOptionalParam) {
86
1/2
✓ Branch 0 (16→17) taken 760 times.
✗ Branch 1 (16→34) not taken.
760 manifestation.paramList = currentFunctionParamTypes;
87
1/2
✓ Branch 0 (17→18) taken 760 times.
✗ Branch 1 (17→34) not taken.
760 manifestations.push_back(manifestation);
88 // Now we cannot accept mandatory parameters anymore
89 760 metFirstOptionalParam = true;
90 }
91
92 // Add substantiation with the optional parameter
93
1/2
✓ Branch 0 (19→20) taken 766 times.
✗ Branch 1 (19→33) not taken.
766 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
94
1/2
✓ Branch 0 (20→21) taken 766 times.
✗ Branch 1 (20→34) not taken.
766 manifestation.paramList = currentFunctionParamTypes;
95
1/2
✓ Branch 0 (21→22) taken 766 times.
✗ Branch 1 (21→34) not taken.
766 manifestations.push_back(manifestation);
96 }
97
98 // Ensure at least once manifestation
99
2/2
✓ Branch 0 (26→27) taken 7491 times.
✓ Branch 1 (26→28) taken 760 times.
8251 if (manifestations.empty())
100
1/2
✓ Branch 0 (27→28) taken 7491 times.
✗ Branch 1 (27→35) not taken.
7491 manifestations.push_back(baseFunction);
101 8251 }
102
103 2 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
104 2 ParamList paramList;
105
2/2
✓ Branch 0 (8→4) taken 2 times.
✓ Branch 1 (8→9) taken 2 times.
4 for (const QualType &paramType : paramTypes)
106
1/2
✓ Branch 0 (5→6) taken 2 times.
✗ Branch 1 (5→24) not taken.
2 paramList.push_back({paramType, false});
107
4/8
✓ Branch 0 (11→12) taken 2 times.
✗ Branch 1 (11→31) not taken.
✓ Branch 2 (12→13) taken 2 times.
✗ Branch 3 (12→28) not taken.
✓ Branch 4 (13→14) taken 2 times.
✗ Branch 5 (13→27) not taken.
✓ Branch 6 (14→15) taken 2 times.
✗ Branch 7 (14→26) not taken.
4 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
108 2 }
109
110 13508 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
111
2/4
✓ Branch 0 (2→3) taken 13508 times.
✗ Branch 1 (2→65) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 13508 times.
13508 assert(newManifestation.hasSubstantiatedParams());
112
113
1/2
✓ Branch 0 (5→6) taken 13508 times.
✗ Branch 1 (5→65) not taken.
13508 const std::string signature = newManifestation.getSignature();
114
115 // Check if the function exists already
116
5/8
✓ Branch 0 (6→7) taken 13508 times.
✗ Branch 1 (6→54) not taken.
✓ Branch 2 (7→8) taken 13508 times.
✗ Branch 3 (7→54) not taken.
✓ Branch 4 (8→9) taken 13508 times.
✗ Branch 5 (8→54) not taken.
✓ Branch 6 (28→10) taken 172520 times.
✓ Branch 7 (28→29) taken 13506 times.
186026 for (const auto &manifestations : insertScope->functions | std::views::values) {
117
3/4
✓ Branch 0 (11→12) taken 172520 times.
✗ Branch 1 (11→54) not taken.
✓ Branch 2 (12→13) taken 2 times.
✓ Branch 3 (12→26) taken 172518 times.
172520 if (manifestations.contains(signature)) {
118
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→18) taken 1 times.
2 const SemanticErrorType errorType = newManifestation.isFunction() ? FUNCTION_DECLARED_TWICE : PROCEDURE_DECLARED_TWICE;
119
3/6
✓ Branch 0 (20→21) taken 2 times.
✗ Branch 1 (20→49) not taken.
✓ Branch 2 (21→22) taken 2 times.
✗ Branch 3 (21→47) not taken.
✓ Branch 4 (22→23) taken 2 times.
✗ Branch 5 (22→45) not taken.
2 throw SemanticError(declNode, errorType, "'" + signature + "' is declared twice");
120 }
121 }
122
123 // Retrieve the matching manifestation list of the scope
124
3/6
✓ Branch 0 (29→30) taken 13506 times.
✗ Branch 1 (29→60) not taken.
✓ Branch 2 (30→31) taken 13506 times.
✗ Branch 3 (30→57) not taken.
✓ Branch 4 (31→32) taken 13506 times.
✗ Branch 5 (31→55) not taken.
13506 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
125
2/4
✓ Branch 0 (34→35) taken 13506 times.
✗ Branch 1 (34→61) not taken.
✗ Branch 2 (35→36) not taken.
✓ Branch 3 (35→37) taken 13506 times.
13506 assert(insertScope->functions.contains(fctId));
126
1/2
✓ Branch 0 (37→38) taken 13506 times.
✗ Branch 1 (37→61) not taken.
13506 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
127
128 // Add substantiated function
129
1/2
✓ Branch 0 (38→39) taken 13506 times.
✗ Branch 1 (38→61) not taken.
13506 manifestationList.emplace(signature, newManifestation);
130
1/2
✓ Branch 0 (39→40) taken 13506 times.
✗ Branch 1 (39→61) not taken.
27012 return &manifestationList.at(signature);
131 13508 }
132
133 /**
134 * Checks if a function exists by matching it, but not setting it to used
135 *
136 * @param matchScope Scope to match against
137 * @param reqName Function name requirement
138 * @param reqThisType This type requirement
139 * @param reqArgs Argument requirement
140 * @param strictQualifierMatching Match argument and this type qualifiers strictly
141 * @return Found function or nullptr
142 */
143 10362 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
144 const ArgList &reqArgs, bool strictQualifierMatching) {
145
2/4
✓ Branch 0 (2→3) taken 10362 times.
✗ Branch 1 (2→66) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 10362 times.
10362 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
146
147 // Do cache lookup
148
1/2
✓ Branch 0 (6→7) taken 10362 times.
✗ Branch 1 (6→67) not taken.
10362 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
149
3/4
✓ Branch 0 (8→9) taken 10362 times.
✗ Branch 1 (8→83) not taken.
✓ Branch 2 (9→10) taken 2965 times.
✓ Branch 3 (9→12) taken 7397 times.
10362 if (lookupCache.contains(cacheKey)) {
150 2965 lookupCacheHits++;
151
1/2
✓ Branch 0 (10→11) taken 2965 times.
✗ Branch 1 (10→83) not taken.
2965 return lookupCache.at(cacheKey);
152 }
153 7397 lookupCacheMisses++;
154
155 2233 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
156
5/8
✓ Branch 0 (12→13) taken 7397 times.
✗ Branch 1 (12→83) not taken.
✓ Branch 2 (13→14) taken 7206 times.
✓ Branch 3 (13→17) taken 191 times.
✓ Branch 4 (14→15) taken 7206 times.
✗ Branch 5 (14→83) not taken.
✓ Branch 6 (15→16) taken 7206 times.
✗ Branch 7 (15→17) not taken.
7397 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
157
158 // Copy the registry to prevent iterating over items, that are created within the loop
159
1/2
✓ Branch 0 (18→19) taken 7397 times.
✗ Branch 1 (18→83) not taken.
7397 FunctionRegistry functionRegistry = matchScope->functions;
160 // Loop over function registry to find functions, that match the requirements of the call
161 7397 std::vector<const Function *> matches;
162
2/2
✓ Branch 0 (55→21) taken 61247 times.
✓ Branch 1 (55→56) taken 7397 times.
68644 for (const auto &[defCodeLocStr, m] : functionRegistry) {
163 // Copy the manifestation list to prevent iterating over items, that are created within the loop
164
1/2
✓ Branch 0 (24→25) taken 61247 times.
✗ Branch 1 (24→77) not taken.
61247 const FunctionManifestationList manifestations = m;
165
2/2
✓ Branch 0 (51→27) taken 63985 times.
✓ Branch 1 (51→52) taken 21859 times.
85844 for (const auto &[signature, presetFunction] : manifestations) {
166
2/4
✓ Branch 0 (30→31) taken 63985 times.
✗ Branch 1 (30→73) not taken.
✗ Branch 2 (31→32) not taken.
✓ Branch 3 (31→33) taken 63985 times.
63985 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
167
168 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
169 // - search for generic fct: Only match against generic preset functions
170
3/4
✓ Branch 0 (33→34) taken 63985 times.
✗ Branch 1 (33→73) not taken.
✓ Branch 2 (34→35) taken 19261 times.
✓ Branch 3 (34→36) taken 44724 times.
63985 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
171 24597 continue;
172
173 // Copy the function to be able to substantiate types
174
1/2
✓ Branch 0 (36→37) taken 44724 times.
✗ Branch 1 (36→73) not taken.
44724 Function candidate = presetFunction;
175
176 // Create empty type mapping
177 44724 TypeMapping &typeMapping = candidate.typeMapping;
178
179 44724 bool forceSubstantiation = false;
180
1/2
✓ Branch 0 (37→38) taken 44724 times.
✗ Branch 1 (37→71) not taken.
44724 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
181 strictQualifierMatching, forceSubstantiation, nullptr);
182
2/2
✓ Branch 0 (38→39) taken 38207 times.
✓ Branch 1 (38→40) taken 6517 times.
44724 if (matchResult == MatchResult::SKIP_FUNCTION)
183 38207 break; // Leave the whole function
184
2/2
✓ Branch 0 (40→41) taken 5336 times.
✓ Branch 1 (40→42) taken 1181 times.
6517 if (matchResult == MatchResult::SKIP_MANIFESTATION)
185 5336 continue; // Leave this manifestation and try the next one
186
187 // Add to matches
188
3/6
✓ Branch 0 (42→43) taken 1181 times.
✗ Branch 1 (42→70) not taken.
✓ Branch 2 (43→44) taken 1181 times.
✗ Branch 3 (43→70) not taken.
✓ Branch 4 (44→45) taken 1181 times.
✗ Branch 5 (44→70) not taken.
1181 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
189
190 1181 break; // Leave the whole manifestation list to not double-match the manifestation
191
2/2
✓ Branch 0 (47→48) taken 5336 times.
✓ Branch 1 (47→49) taken 39388 times.
44724 }
192 61247 }
193
194 // Return the very match or a nullptr
195
2/2
✓ Branch 0 (57→58) taken 1181 times.
✓ Branch 1 (57→60) taken 6216 times.
7397 return !matches.empty() ? matches.front() : nullptr;
196 7397 }
197
198 /**
199 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
200 * If more than one function matches the requirement, an error gets thrown.
201 *
202 * @param typeChecker Type Checker
203 * @param matchScope Scope to match against
204 * @param reqName Function name requirement
205 * @param reqThisType This type requirement
206 * @param reqArgs Argument requirement
207 * @param templateTypeHints Template type requirement
208 * @param strictQualifierMatching Match argument and this type qualifiers strictly
209 * @param callNode Call AST node for printing error messages
210 * @return Matched function or nullptr
211 */
212 63927 Function *FunctionManager::match(TypeChecker *typeChecker, Scope *matchScope, const std::string &reqName,
213 const QualType &reqThisType, const ArgList &reqArgs, const QualTypeList &templateTypeHints,
214 bool strictQualifierMatching, const ASTNode *callNode) {
215
2/4
✓ Branch 0 (2→3) taken 63927 times.
✗ Branch 1 (2→174) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 63927 times.
63927 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
216
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 63927 times.
63927 assert(typeChecker != nullptr && "The match() function must be called from the TypeChecker");
217
218 // Do cache lookup
219
1/2
✓ Branch 0 (7→8) taken 63927 times.
✗ Branch 1 (7→222) not taken.
63927 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
220
3/4
✓ Branch 0 (8→9) taken 63927 times.
✗ Branch 1 (8→222) not taken.
✓ Branch 2 (9→10) taken 10367 times.
✓ Branch 3 (9→12) taken 53560 times.
63927 if (lookupCache.contains(cacheKey)) {
221 10367 lookupCacheHits++;
222
1/2
✓ Branch 0 (10→11) taken 10367 times.
✗ Branch 1 (10→222) not taken.
10367 return lookupCache.at(cacheKey);
223 }
224 53560 lookupCacheMisses++;
225
226 // Copy the registry to prevent iterating over items, that are created within the loop
227
1/2
✓ Branch 0 (12→13) taken 53560 times.
✗ Branch 1 (12→222) not taken.
53560 FunctionRegistry functionRegistry = matchScope->functions;
228 // Loop over function registry to find functions, that match the requirements of the call
229 53560 std::vector<Function *> matches;
230
2/2
✓ Branch 0 (139→15) taken 538830 times.
✓ Branch 1 (139→140) taken 53559 times.
592389 for (const auto &[fctId, m] : functionRegistry) {
231 // Copy the manifestation list to prevent iterating over items, that are created within the loop
232
1/2
✓ Branch 0 (18→19) taken 538830 times.
✗ Branch 1 (18→203) not taken.
538830 const FunctionManifestationList manifestations = m;
233
2/2
✓ Branch 0 (135→21) taken 570242 times.
✓ Branch 1 (135→136) taken 39988 times.
610230 for (const auto &[signature, presetFunction] : manifestations) {
234
2/4
✓ Branch 0 (24→25) taken 570242 times.
✗ Branch 1 (24→199) not taken.
✗ Branch 2 (25→26) not taken.
✓ Branch 3 (25→27) taken 570242 times.
570242 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
235
236 // Skip generic substantiations to prevent double matching of a function
237
3/4
✓ Branch 0 (27→28) taken 570242 times.
✗ Branch 1 (27→199) not taken.
✓ Branch 2 (28→29) taken 29614 times.
✓ Branch 3 (28→30) taken 540628 times.
570242 if (presetFunction.isGenericSubstantiation())
238 71400 continue;
239
240 // Copy the function to be able to substantiate types
241
1/2
✓ Branch 0 (30→31) taken 540628 times.
✗ Branch 1 (30→199) not taken.
540628 Function candidate = presetFunction;
242
243 // Prepare type mapping, based on the given initial type mapping
244 540628 TypeMapping &typeMapping = candidate.typeMapping;
245 540628 typeMapping.clear();
246
2/2
✓ Branch 0 (43→33) taken 2887 times.
✓ Branch 1 (43→44) taken 540628 times.
543515 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
247
2/4
✓ Branch 0 (33→34) taken 2887 times.
✗ Branch 1 (33→197) not taken.
✓ Branch 2 (34→35) taken 2887 times.
✗ Branch 3 (34→197) not taken.
2887 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
248
1/2
✓ Branch 0 (35→36) taken 2887 times.
✗ Branch 1 (35→197) not taken.
2887 const QualType &templateType = templateTypeHints.at(i);
249
2/4
✓ Branch 0 (36→37) taken 2887 times.
✗ Branch 1 (36→177) not taken.
✓ Branch 2 (37→38) taken 2887 times.
✗ Branch 3 (37→175) not taken.
2887 typeMapping.insert({typeName, templateType});
250 }
251
252 540628 bool forceSubstantiation = false;
253
2/2
✓ Branch 0 (44→45) taken 540627 times.
✓ Branch 1 (44→197) taken 1 times.
540628 MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
254 strictQualifierMatching, forceSubstantiation, callNode);
255
2/2
✓ Branch 0 (45→46) taken 496948 times.
✓ Branch 1 (45→47) taken 43679 times.
540627 if (matchResult == MatchResult::SKIP_FUNCTION)
256 496948 break; // Leave the whole function
257
2/2
✓ Branch 0 (47→48) taken 36741 times.
✓ Branch 1 (47→49) taken 6938 times.
43679 if (matchResult == MatchResult::SKIP_MANIFESTATION)
258 36741 continue; // Leave this manifestation and try the next one
259
260 // We found a match! -> Set the actual candidate and its entry to used
261 6938 candidate.used = true;
262 6938 candidate.entry->used = true;
263
264 // Check if the function is generic needs to be substantiated
265
6/6
✓ Branch 0 (50→51) taken 5048 times.
✓ Branch 1 (50→53) taken 1890 times.
✓ Branch 2 (51→52) taken 5045 times.
✓ Branch 3 (51→53) taken 3 times.
✓ Branch 4 (54→55) taken 5045 times.
✓ Branch 5 (54→67) taken 1893 times.
6938 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
266
5/10
✓ Branch 0 (55→56) taken 5045 times.
✗ Branch 1 (55→197) not taken.
✓ Branch 2 (56→57) taken 5045 times.
✗ Branch 3 (56→61) not taken.
✓ Branch 4 (57→58) taken 5045 times.
✗ Branch 5 (57→197) not taken.
✓ Branch 6 (58→59) taken 5045 times.
✗ Branch 7 (58→197) not taken.
✓ Branch 8 (59→60) taken 5045 times.
✗ Branch 9 (59→61) not taken.
5045 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
267
3/6
✓ Branch 0 (62→63) taken 5045 times.
✗ Branch 1 (62→178) not taken.
✓ Branch 2 (63→64) taken 5045 times.
✗ Branch 3 (63→178) not taken.
✓ Branch 4 (64→65) taken 5045 times.
✗ Branch 5 (64→178) not taken.
5045 matches.push_back(&matchScope->functions.at(fctId).at(signature));
268 5045 matches.back()->used = true;
269 5045 continue; // Match was successful -> match the next function
270 }
271
272 // Check if we already have this manifestation and can simply re-use it
273
1/2
✓ Branch 0 (67→68) taken 1893 times.
✗ Branch 1 (67→197) not taken.
1893 const std::string nonGenericSignature = candidate.getSignature();
274
4/6
✓ Branch 0 (68→69) taken 1893 times.
✗ Branch 1 (68→195) not taken.
✓ Branch 2 (69→70) taken 1893 times.
✗ Branch 3 (69→195) not taken.
✓ Branch 4 (70→71) taken 125 times.
✓ Branch 5 (70→75) taken 1768 times.
1893 if (matchScope->functions.at(fctId).contains(nonGenericSignature)) {
275
3/6
✓ Branch 0 (71→72) taken 125 times.
✗ Branch 1 (71→179) not taken.
✓ Branch 2 (72→73) taken 125 times.
✗ Branch 3 (72→179) not taken.
✓ Branch 4 (73→74) taken 125 times.
✗ Branch 5 (73→179) not taken.
125 matches.push_back(&matchScope->functions.at(fctId).at(nonGenericSignature));
276 125 break; // Leave the whole manifestation list to not double-match the manifestation
277 }
278
279 // Insert the substantiated version if required
280
1/2
✓ Branch 0 (75→76) taken 1768 times.
✗ Branch 1 (75→195) not taken.
1768 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
281
2/4
✓ Branch 0 (76→77) taken 1768 times.
✗ Branch 1 (76→195) not taken.
✓ Branch 2 (77→78) taken 1768 times.
✗ Branch 3 (77→195) not taken.
1768 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
282 1768 substantiatedFunction->alreadyTypeChecked = false;
283
2/4
✓ Branch 0 (78→79) taken 1768 times.
✗ Branch 1 (78→195) not taken.
✓ Branch 2 (79→80) taken 1768 times.
✗ Branch 3 (79→195) not taken.
1768 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
284
285 // Copy function entry
286
1/2
✓ Branch 0 (80→81) taken 1768 times.
✗ Branch 1 (80→195) not taken.
1768 const std::string newSignature = substantiatedFunction->getSignature(false);
287
1/2
✓ Branch 0 (81→82) taken 1768 times.
✗ Branch 1 (81→193) not taken.
1768 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
288
1/2
✓ Branch 0 (84→85) taken 1768 times.
✗ Branch 1 (84→193) not taken.
1768 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newSignature);
289
1/2
✗ Branch 0 (85→86) not taken.
✓ Branch 1 (85→87) taken 1768 times.
1768 assert(substantiatedFunction->entry != nullptr);
290
291 // Copy function scope
292
1/2
✓ Branch 0 (87→88) taken 1768 times.
✗ Branch 1 (87→193) not taken.
1768 const std::string oldSignature = presetFunction.getSignature(false);
293
1/2
✓ Branch 0 (88→89) taken 1768 times.
✗ Branch 1 (88→191) not taken.
1768 Scope *childScope = matchScope->copyChildScope(oldSignature, newSignature);
294
1/2
✗ Branch 0 (89→90) not taken.
✓ Branch 1 (89→91) taken 1768 times.
1768 assert(childScope != nullptr);
295 1768 childScope->isGenericScope = false;
296 1768 substantiatedFunction->bodyScope = childScope;
297
298 // Insert symbols for generic type names with concrete types into the child block
299
2/2
✓ Branch 0 (101→93) taken 2111 times.
✓ Branch 1 (101→102) taken 1768 times.
3879 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
300
2/4
✓ Branch 0 (96→97) taken 2111 times.
✗ Branch 1 (96→182) not taken.
✓ Branch 2 (97→98) taken 2111 times.
✗ Branch 3 (97→180) not taken.
2111 childScope->insertGenericType(typeName, GenericType(concreteType));
301
302 // Substantiate the 'this' entry in the new function scope
303
6/6
✓ Branch 0 (105→106) taken 1481 times.
✓ Branch 1 (105→109) taken 287 times.
✓ Branch 2 (107→108) taken 1480 times.
✓ Branch 3 (107→109) taken 1 times.
✓ Branch 4 (110→111) taken 1480 times.
✓ Branch 5 (110→124) taken 288 times.
1768 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
304
1/2
✓ Branch 0 (113→114) taken 1480 times.
✗ Branch 1 (113→186) not taken.
4440 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
305
1/2
✗ Branch 0 (119→120) not taken.
✓ Branch 1 (119→121) taken 1480 times.
1480 assert(thisEntry != nullptr);
306
2/4
✓ Branch 0 (121→122) taken 1480 times.
✗ Branch 1 (121→190) not taken.
✓ Branch 2 (122→123) taken 1480 times.
✗ Branch 3 (122→190) not taken.
1480 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
307 }
308
309 // Add to matched functions
310
1/2
✓ Branch 0 (124→125) taken 1768 times.
✗ Branch 1 (124→191) not taken.
1768 matches.push_back(substantiatedFunction);
311
312 1768 break; // Leave the whole manifestation list to not double-match the manifestation
313
2/2
✓ Branch 0 (131→132) taken 41786 times.
✓ Branch 1 (131→133) taken 498841 times.
542521 }
314 538830 }
315
316 // If no matches were found, return a nullptr
317
2/2
✓ Branch 0 (141→142) taken 46622 times.
✓ Branch 1 (141→143) taken 6937 times.
53559 if (matches.empty())
318 46622 return nullptr;
319
320 // Check if more than one function matches the requirements
321
2/2
✓ Branch 0 (144→145) taken 1 times.
✓ Branch 1 (144→164) taken 6936 times.
6937 if (matches.size() > 1) {
322
1/2
✓ Branch 0 (145→146) taken 1 times.
✗ Branch 1 (145→217) not taken.
1 std::stringstream errorMessage;
323
3/6
✓ Branch 0 (146→147) taken 1 times.
✗ Branch 1 (146→215) not taken.
✓ Branch 2 (147→148) taken 1 times.
✗ Branch 3 (147→215) not taken.
✓ Branch 4 (148→149) taken 1 times.
✗ Branch 5 (148→215) not taken.
1 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
324
2/2
✓ Branch 0 (158→151) taken 2 times.
✓ Branch 1 (158→159) taken 1 times.
3 for (const Function *match : matches)
325
3/6
✓ Branch 0 (152→153) taken 2 times.
✗ Branch 1 (152→208) not taken.
✓ Branch 2 (153→154) taken 2 times.
✗ Branch 3 (153→207) not taken.
✓ Branch 4 (154→155) taken 2 times.
✗ Branch 5 (154→205) not taken.
2 errorMessage << "\n " << match->getSignature();
326
2/4
✓ Branch 0 (160→161) taken 1 times.
✗ Branch 1 (160→212) not taken.
✓ Branch 2 (161→162) taken 1 times.
✗ Branch 3 (161→209) not taken.
1 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
327 1 }
328
329 // Insert into cache
330
1/2
✓ Branch 0 (165→166) taken 6936 times.
✗ Branch 1 (165→218) not taken.
6936 lookupCache[cacheKey] = matches.front();
331
332 // Trigger revisit in type checker if required
333
1/2
✓ Branch 0 (167→168) taken 6936 times.
✗ Branch 1 (167→218) not taken.
6936 typeChecker->requestRevisitIfRequired(matches.front());
334
335 // Return the very match
336 6936 return matches.front();
337 53562 }
338
339 585352 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
340 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
341 bool strictQualifierMatching, bool &forceSubstantiation,
342 const ASTNode *callNode) {
343 // Check name requirement
344
2/2
✓ Branch 0 (3→4) taken 535155 times.
✓ Branch 1 (3→5) taken 50197 times.
585352 if (!matchName(candidate, reqName))
345 535155 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
346
347 // Check 'this' type requirement
348
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 50197 times.
50197 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
349 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
350
351 // Check arg types requirement
352
2/2
✓ Branch 0 (9→10) taken 42076 times.
✓ Branch 1 (9→11) taken 8120 times.
50197 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
353 42076 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
354
355 // Check if there are unresolved generic types
356
2/2
✓ Branch 0 (13→14) taken 1 times.
✓ Branch 1 (13→15) taken 8119 times.
8120 if (typeMapping.size() < candidate.templateTypes.size())
357 1 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
358
359 // Substantiate return type
360 8119 substantiateReturnType(candidate, typeMapping, callNode);
361
362 // Set the match scope to the scope of the concrete substantiation
363 8119 const QualType &thisType = candidate.thisType;
364
2/2
✓ Branch 0 (17→18) taken 5006 times.
✓ Branch 1 (17→25) taken 3113 times.
8119 if (!thisType.is(TY_DYN)) {
365 // If we only have the generic struct scope, lookup the concrete manifestation scope
366
2/2
✓ Branch 0 (18→19) taken 50 times.
✓ Branch 1 (18→23) taken 4956 times.
5006 if (matchScope->isGenericScope) {
367 50 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
368
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 50 times.
50 assert(spiceStruct != nullptr);
369 50 matchScope = spiceStruct->scope;
370 }
371
1/2
✓ Branch 0 (23→24) taken 5006 times.
✗ Branch 1 (23→27) not taken.
5006 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
372 }
373
374 8119 return MatchResult::MATCHED;
375 }
376
377 /**
378 * Checks if the matching candidate fulfills the name requirement
379 *
380 * @param candidate Matching candidate function
381 * @param reqName Requested function name
382 * @return Fulfilled or not
383 */
384 585352 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
385
386 /**
387 * Checks if the matching candidate fulfills the 'this' type requirement
388 *
389 * @param candidate Matching candidate function
390 * @param reqThisType Requested 'this' type
391 * @param typeMapping Concrete template type mapping
392 * @param strictQualifierMatching Match qualifiers strictly
393 * @param callNode Call AST node for printing error messages
394 * @return Fulfilled or not
395 */
396 50197 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
397 bool strictQualifierMatching, const ASTNode *callNode) {
398 50197 QualType &candidateThisType = candidate.thisType;
399
400 // Shortcut for procedures
401
7/10
✓ Branch 0 (2→3) taken 50197 times.
✗ Branch 1 (2→23) not taken.
✓ Branch 2 (3→4) taken 34230 times.
✓ Branch 3 (3→7) taken 15967 times.
✓ Branch 4 (4→5) taken 34230 times.
✗ Branch 5 (4→23) not taken.
✓ Branch 6 (5→6) taken 34230 times.
✗ Branch 7 (5→7) not taken.
✓ Branch 8 (8→9) taken 34230 times.
✓ Branch 9 (8→10) taken 15967 times.
50197 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
402 34230 return true;
403
404 // Give the type matcher a way to retrieve instances of GenericType by their name
405 34154 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
406 2220 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
407 15967 };
408
409 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
410
2/4
✓ Branch 0 (11→12) taken 15967 times.
✗ Branch 1 (11→21) not taken.
✗ Branch 2 (12→13) not taken.
✓ Branch 3 (12→14) taken 15967 times.
15967 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
411 strictQualifierMatching))
412 return false;
413
414 // Substantiate the candidate param type, based on the type mapping
415
3/4
✓ Branch 0 (14→15) taken 15967 times.
✗ Branch 1 (14→21) not taken.
✓ Branch 2 (15→16) taken 2412 times.
✓ Branch 3 (15→17) taken 13555 times.
15967 if (candidateThisType.hasAnyGenericParts())
416
1/2
✓ Branch 0 (16→17) taken 2412 times.
✗ Branch 1 (16→21) not taken.
2412 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
417
418 15967 return true;
419 15967 }
420
421 /**
422 * Checks if the matching candidate fulfills the argument types requirement
423 *
424 * @param candidate Matching candidate function
425 * @param reqArgs Requested argument types
426 * @param typeMapping Concrete template type mapping
427 * @param strictQualifierMatching Match qualifiers strictly
428 * @param needsSubstantiation Do we want to create a substantiation after successfully matching
429 * @param callNode Call AST node for printing error messages
430 * @return Fulfilled or not
431 */
432 50197 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
433 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
434 50197 std::vector<Param> &candidateParamList = candidate.paramList;
435
436 // If the number of arguments does not match with the number of params, the matching fails
437
2/2
✓ Branch 0 (4→5) taken 6557 times.
✓ Branch 1 (4→6) taken 43640 times.
50197 if (reqArgs.size() != candidateParamList.size())
438 6557 return false;
439
440 // Give the type matcher a way to retrieve instances of GenericType by their name
441 91133 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
442 3853 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
443 43640 };
444
445 // Loop over all parameters
446
2/2
✓ Branch 0 (45→8) taken 46247 times.
✓ Branch 1 (45→46) taken 8120 times.
54367 for (size_t i = 0; i < reqArgs.size(); i++) {
447 // Retrieve actual and requested types
448
2/4
✓ Branch 0 (8→9) taken 46247 times.
✗ Branch 1 (8→62) not taken.
✗ Branch 2 (9→10) not taken.
✓ Branch 3 (9→11) taken 46247 times.
46247 assert(!candidateParamList.at(i).isOptional);
449
1/2
✓ Branch 0 (11→12) taken 46247 times.
✗ Branch 1 (11→62) not taken.
46247 QualType &candidateType = candidateParamList.at(i).qualType;
450
1/2
✓ Branch 0 (12→13) taken 46247 times.
✗ Branch 1 (12→62) not taken.
46247 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
451
452 // Check if the requested param type matches the candidate param type. The type mapping may be extended
453
3/4
✓ Branch 0 (15→16) taken 46247 times.
✗ Branch 1 (15→62) not taken.
✓ Branch 2 (16→17) taken 35519 times.
✓ Branch 3 (16→18) taken 10728 times.
46247 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
454 strictQualifierMatching))
455 35519 return false;
456
457 // Substantiate the candidate param type, based on the type mapping
458
3/4
✓ Branch 0 (18→19) taken 10728 times.
✗ Branch 1 (18→62) not taken.
✓ Branch 2 (19→20) taken 1691 times.
✓ Branch 3 (19→21) taken 9037 times.
10728 if (candidateType.hasAnyGenericParts())
459
1/2
✓ Branch 0 (20→21) taken 1691 times.
✗ Branch 1 (20→62) not taken.
1691 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
460
461 // Check if we try to bind a non-ref temporary to a non-const ref parameter
462
3/4
✓ Branch 0 (21→22) taken 10728 times.
✗ Branch 1 (21→62) not taken.
✓ Branch 2 (22→23) taken 1 times.
✓ Branch 3 (22→33) taken 10727 times.
10728 if (!candidateType.canBind(requestedType, isArgTemporary)) {
463
1/2
✓ Branch 0 (23→24) taken 1 times.
✗ Branch 1 (23→32) not taken.
1 if (callNode)
464
2/4
✓ Branch 0 (27→28) taken 1 times.
✗ Branch 1 (27→53) not taken.
✓ Branch 2 (28→29) taken 1 times.
✗ Branch 3 (28→50) not taken.
3 throw SemanticError(callNode, TEMP_TO_NON_CONST_REF, "Temporary values can only be bound to const reference parameters");
465 return false;
466 }
467
468 // If we have a function/procedure type we need to take care of the information, if it takes captures
469
9/12
✓ Branch 0 (33→34) taken 10727 times.
✗ Branch 1 (33→59) not taken.
✓ Branch 2 (34→35) taken 10727 times.
✗ Branch 3 (34→59) not taken.
✓ Branch 4 (35→36) taken 19 times.
✓ Branch 5 (35→39) taken 10708 times.
✓ Branch 6 (36→37) taken 19 times.
✗ Branch 7 (36→59) not taken.
✓ Branch 8 (37→38) taken 4 times.
✓ Branch 9 (37→39) taken 15 times.
✓ Branch 10 (40→41) taken 4 times.
✓ Branch 11 (40→43) taken 10723 times.
10727 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
470
1/2
✓ Branch 0 (41→42) taken 4 times.
✗ Branch 1 (41→61) not taken.
4 candidateType = candidateType.getWithLambdaCaptures();
471 4 needsSubstantiation = true;
472 }
473 }
474
475 8120 return true;
476 43640 }
477
478 /**
479 * Substantiates the candidate return type, based on the given type mapping
480 *
481 * @param candidate Matching candidate function
482 * @param typeMapping Concrete template type mapping
483 * @param callNode AST node for error messages
484 */
485 8119 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
486
2/2
✓ Branch 0 (3→4) taken 553 times.
✓ Branch 1 (3→5) taken 7566 times.
8119 if (candidate.returnType.hasAnyGenericParts())
487 553 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
488 8119 }
489
490 /**
491 * Searches the candidate template types for a generic type object with a certain name and return it
492 *
493 * @param candidate Matching candidate function
494 * @param templateTypeName Template type name
495 * @return Generic type object
496 */
497 6073 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
498 const std::string &templateTypeName) {
499
1/2
✓ Branch 0 (11→4) taken 6509 times.
✗ Branch 1 (11→12) not taken.
6509 for (const GenericType &templateType : candidate.templateTypes) {
500
3/4
✓ Branch 0 (5→6) taken 6509 times.
✗ Branch 1 (5→14) not taken.
✓ Branch 2 (7→8) taken 6073 times.
✓ Branch 3 (7→9) taken 436 times.
6509 if (templateType.getSubType() == templateTypeName)
501 6073 return &templateType;
502 }
503 return nullptr;
504 }
505
506 /**
507 * Calculate the cache key for the function lookup cache
508 *
509 * @param scope Scope to match against
510 * @param name Function name requirement
511 * @param thisType This type requirement
512 * @param args Argument requirement
513 * @param templateTypes Template type requirement
514 * @return Cache key
515 */
516 74289 uint64_t FunctionManager::getCacheKey(Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
517 const QualTypeList &templateTypes) {
518 110961 const auto pred1 = [](size_t acc, const Arg &val) {
519 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
520 110961 const uint64_t typeHash = std::hash<QualType>{}(val.first);
521 110961 const uint64_t temporaryHash = std::hash<bool>{}(val.second);
522 110961 const uint64_t newHash = typeHash ^ (temporaryHash << 1);
523 110961 return acc * 31 + newHash;
524 };
525 601 const auto pred2 = [](size_t acc, const QualType &val) {
526 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
527 601 return acc * 31 + std::hash<QualType>{}(val);
528 };
529 // Calculate the cache key
530 74289 const uint64_t scopeHash = std::hash<Scope *>{}(scope);
531 74289 const uint64_t hashName = std::hash<std::string>{}(name);
532 74289 const uint64_t hashThisType = std::hash<QualType>{}(thisType);
533 74289 const uint64_t hashArgs = std::accumulate(args.begin(), args.end(), 0u, pred1);
534 74289 const uint64_t hashTemplateTypes = std::accumulate(templateTypes.begin(), templateTypes.end(), 0u, pred2);
535 74289 return scopeHash ^ (hashName << 1) ^ (hashThisType << 2) ^ (hashArgs << 3) ^ (hashTemplateTypes << 4);
536 }
537
538 /**
539 * Clear the lookup cache
540 */
541 407 void FunctionManager::cleanup() {
542 407 lookupCache.clear();
543 407 lookupCacheHits = 0;
544 407 lookupCacheMisses = 0;
545 407 }
546
547 /**
548 * Dump usage statistics for the lookup cache
549 */
550 191 std::string FunctionManager::dumpLookupCacheStatistics() {
551
1/2
✓ Branch 0 (2→3) taken 191 times.
✗ Branch 1 (2→22) not taken.
191 std::stringstream stats;
552
2/4
✓ Branch 0 (3→4) taken 191 times.
✗ Branch 1 (3→20) not taken.
✓ Branch 2 (4→5) taken 191 times.
✗ Branch 3 (4→20) not taken.
191 stats << "FunctionManager lookup cache statistics:" << std::endl;
553
3/6
✓ Branch 0 (5→6) taken 191 times.
✗ Branch 1 (5→20) not taken.
✓ Branch 2 (7→8) taken 191 times.
✗ Branch 3 (7→20) not taken.
✓ Branch 4 (8→9) taken 191 times.
✗ Branch 5 (8→20) not taken.
191 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
554
3/6
✓ Branch 0 (9→10) taken 191 times.
✗ Branch 1 (9→20) not taken.
✓ Branch 2 (10→11) taken 191 times.
✗ Branch 3 (10→20) not taken.
✓ Branch 4 (11→12) taken 191 times.
✗ Branch 5 (11→20) not taken.
191 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
555
3/6
✓ Branch 0 (12→13) taken 191 times.
✗ Branch 1 (12→20) not taken.
✓ Branch 2 (13→14) taken 191 times.
✗ Branch 3 (13→20) not taken.
✓ Branch 4 (14→15) taken 191 times.
✗ Branch 5 (14→20) not taken.
191 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
556
1/2
✓ Branch 0 (15→16) taken 191 times.
✗ Branch 1 (15→20) not taken.
382 return stats.str();
557 191 }
558
559 } // namespace spice::compiler
560