GCC Code Coverage Report


Directory: ../
File: src/typechecker/FunctionManager.cpp
Date: 2025-09-17 23:16:16
Exec Total Coverage
Lines: 252 258 97.7%
Functions: 20 20 100.0%
Branches: 285 444 64.2%

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 12769 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 12769 times.
✗ Branch 1 (2→43) not taken.
✓ Branch 2 (3→4) taken 12769 times.
✗ Branch 3 (3→40) not taken.
✓ Branch 4 (4→5) taken 12769 times.
✗ Branch 5 (4→38) not taken.
12769 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
25
2/4
✓ Branch 0 (8→9) taken 12769 times.
✗ Branch 1 (8→46) not taken.
✓ Branch 2 (9→10) taken 12769 times.
✗ Branch 3 (9→44) not taken.
12769 insertScope->functions.insert({fctId, FunctionManifestationList()});
26
27 // Collect substantiations
28 12769 std::vector<Function> manifestations;
29
1/2
✓ Branch 0 (12→13) taken 12769 times.
✗ Branch 1 (12→51) not taken.
12769 substantiateOptionalParams(baseFunction, manifestations);
30
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 12769 times.
12769 assert(!manifestations.empty());
31
32 // Save substantiations in declaration node
33 12769 Function *manifestationPtr = nullptr;
34
2/2
✓ Branch 0 (26→18) taken 13637 times.
✓ Branch 1 (26→27) taken 12767 times.
26404 for (const Function &manifestation : manifestations) {
35
2/2
✓ Branch 0 (19→20) taken 13635 times.
✓ Branch 1 (19→50) taken 2 times.
13637 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
36
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 13635 times.
13635 assert(manifestationPtr != nullptr);
37
1/2
✓ Branch 0 (22→23) taken 13635 times.
✗ Branch 1 (22→24) not taken.
13635 if (nodeFunctionList)
38
1/2
✓ Branch 0 (23→24) taken 13635 times.
✗ Branch 1 (23→50) not taken.
13635 nodeFunctionList->push_back(manifestationPtr);
39 }
40
41
1/2
✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→29) taken 12767 times.
12767 if (!nodeFunctionList)
42 return manifestationPtr;
43
44
1/2
✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 12767 times.
12767 assert(!nodeFunctionList->empty());
45 12767 return nodeFunctionList->front();
46 12771 }
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 12769 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 3068 times.
✓ Branch 1 (3→6) taken 9701 times.
12769 if (baseFunction.paramList.empty()) {
67
1/2
✓ Branch 0 (4→5) taken 3068 times.
✗ Branch 1 (4→39) not taken.
3068 manifestations.push_back(baseFunction);
68 3068 return;
69 }
70
71 9701 ParamList currentFunctionParamTypes;
72
1/2
✓ Branch 0 (7→8) taken 9701 times.
✗ Branch 1 (7→37) not taken.
9701 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
73 9701 bool metFirstOptionalParam = false;
74
1/2
✓ Branch 0 (8→9) taken 9701 times.
✗ Branch 1 (8→37) not taken.
9701 Function manifestation = baseFunction;
75
76 // Loop over all parameters
77
2/2
✓ Branch 0 (24→11) taken 15015 times.
✓ Branch 1 (24→25) taken 9701 times.
24716 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
78 // Check if we have a mandatory parameter
79
2/2
✓ Branch 0 (12→13) taken 14147 times.
✓ Branch 1 (12→15) taken 868 times.
15015 if (!isOptional) {
80
1/2
✓ Branch 0 (13→14) taken 14147 times.
✗ Branch 1 (13→32) not taken.
14147 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
81 14147 continue;
82 }
83
84 // Add substantiation without the optional parameter
85
2/2
✓ Branch 0 (15→16) taken 862 times.
✓ Branch 1 (15→19) taken 6 times.
868 if (!metFirstOptionalParam) {
86
1/2
✓ Branch 0 (16→17) taken 862 times.
✗ Branch 1 (16→34) not taken.
862 manifestation.paramList = currentFunctionParamTypes;
87
1/2
✓ Branch 0 (17→18) taken 862 times.
✗ Branch 1 (17→34) not taken.
862 manifestations.push_back(manifestation);
88 // Now we cannot accept mandatory parameters anymore
89 862 metFirstOptionalParam = true;
90 }
91
92 // Add substantiation with the optional parameter
93
1/2
✓ Branch 0 (19→20) taken 868 times.
✗ Branch 1 (19→33) not taken.
868 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
94
1/2
✓ Branch 0 (20→21) taken 868 times.
✗ Branch 1 (20→34) not taken.
868 manifestation.paramList = currentFunctionParamTypes;
95
1/2
✓ Branch 0 (21→22) taken 868 times.
✗ Branch 1 (21→34) not taken.
868 manifestations.push_back(manifestation);
96 }
97
98 // Ensure at least once manifestation
99
2/2
✓ Branch 0 (26→27) taken 8839 times.
✓ Branch 1 (26→28) taken 862 times.
9701 if (manifestations.empty())
100
1/2
✓ Branch 0 (27→28) taken 8839 times.
✗ Branch 1 (27→35) not taken.
8839 manifestations.push_back(baseFunction);
101 9701 }
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 15797 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
111
2/4
✓ Branch 0 (2→3) taken 15797 times.
✗ Branch 1 (2→65) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 15797 times.
15797 assert(newManifestation.hasSubstantiatedParams());
112
113
1/2
✓ Branch 0 (5→6) taken 15797 times.
✗ Branch 1 (5→65) not taken.
15797 const std::string signature = newManifestation.getSignature(true, false);
114
115 // Check if the function exists already
116
5/8
✓ Branch 0 (6→7) taken 15797 times.
✗ Branch 1 (6→54) not taken.
✓ Branch 2 (7→8) taken 15797 times.
✗ Branch 3 (7→54) not taken.
✓ Branch 4 (8→9) taken 15797 times.
✗ Branch 5 (8→54) not taken.
✓ Branch 6 (28→10) taken 216998 times.
✓ Branch 7 (28→29) taken 15794 times.
232792 for (const auto &manifestations : insertScope->functions | std::views::values) {
117
3/4
✓ Branch 0 (11→12) taken 216998 times.
✗ Branch 1 (11→54) not taken.
✓ Branch 2 (12→13) taken 3 times.
✓ Branch 3 (12→26) taken 216995 times.
216998 if (manifestations.contains(signature)) {
118
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→18) taken 2 times.
3 const SemanticErrorType errorType = newManifestation.isFunction() ? FUNCTION_DECLARED_TWICE : PROCEDURE_DECLARED_TWICE;
119
3/6
✓ Branch 0 (20→21) taken 3 times.
✗ Branch 1 (20→49) not taken.
✓ Branch 2 (21→22) taken 3 times.
✗ Branch 3 (21→47) not taken.
✓ Branch 4 (22→23) taken 3 times.
✗ Branch 5 (22→45) not taken.
3 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 15794 times.
✗ Branch 1 (29→60) not taken.
✓ Branch 2 (30→31) taken 15794 times.
✗ Branch 3 (30→57) not taken.
✓ Branch 4 (31→32) taken 15794 times.
✗ Branch 5 (31→55) not taken.
15794 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
125
2/4
✓ Branch 0 (34→35) taken 15794 times.
✗ Branch 1 (34→61) not taken.
✗ Branch 2 (35→36) not taken.
✓ Branch 3 (35→37) taken 15794 times.
15794 assert(insertScope->functions.contains(fctId));
126
1/2
✓ Branch 0 (37→38) taken 15794 times.
✗ Branch 1 (37→61) not taken.
15794 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
127
128 // Add substantiated function
129
1/2
✓ Branch 0 (38→39) taken 15794 times.
✗ Branch 1 (38→61) not taken.
15794 manifestationList.emplace(signature, newManifestation);
130
1/2
✓ Branch 0 (39→40) taken 15794 times.
✗ Branch 1 (39→61) not taken.
31588 return &manifestationList.at(signature);
131 15797 }
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 11654 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 11654 times.
✗ Branch 1 (2→66) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 11654 times.
11654 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
146
147 // Do cache lookup
148
1/2
✓ Branch 0 (6→7) taken 11654 times.
✗ Branch 1 (6→67) not taken.
11654 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
149
3/4
✓ Branch 0 (8→9) taken 11654 times.
✗ Branch 1 (8→83) not taken.
✓ Branch 2 (9→10) taken 3268 times.
✓ Branch 3 (9→12) taken 8386 times.
11654 if (lookupCache.contains(cacheKey)) {
150 3268 lookupCacheHits++;
151
1/2
✓ Branch 0 (10→11) taken 3268 times.
✗ Branch 1 (10→83) not taken.
3268 return lookupCache.at(cacheKey);
152 }
153 8386 lookupCacheMisses++;
154
155 2482 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
156
5/8
✓ Branch 0 (12→13) taken 8386 times.
✗ Branch 1 (12→83) not taken.
✓ Branch 2 (13→14) taken 8148 times.
✓ Branch 3 (13→17) taken 238 times.
✓ Branch 4 (14→15) taken 8148 times.
✗ Branch 5 (14→83) not taken.
✓ Branch 6 (15→16) taken 8148 times.
✗ Branch 7 (15→17) not taken.
8386 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 8386 times.
✗ Branch 1 (18→83) not taken.
8386 FunctionRegistry functionRegistry = matchScope->functions;
160 // Loop over function registry to find functions, that match the requirements of the call
161 8386 std::vector<const Function *> matches;
162
2/2
✓ Branch 0 (55→21) taken 69459 times.
✓ Branch 1 (55→56) taken 8386 times.
77845 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 69459 times.
✗ Branch 1 (24→77) not taken.
69459 const FunctionManifestationList manifestations = m;
165
2/2
✓ Branch 0 (51→27) taken 72635 times.
✓ Branch 1 (51→52) taken 25050 times.
97685 for (const auto &[signature, presetFunction] : manifestations) {
166
2/4
✓ Branch 0 (30→31) taken 72635 times.
✗ Branch 1 (30→73) not taken.
✗ Branch 2 (31→32) not taken.
✓ Branch 3 (31→33) taken 72635 times.
72635 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 72635 times.
✗ Branch 1 (33→73) not taken.
✓ Branch 2 (34→35) taken 22300 times.
✓ Branch 3 (34→36) taken 50335 times.
72635 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
171 28226 continue;
172
173 // Copy the function to be able to substantiate types
174
1/2
✓ Branch 0 (36→37) taken 50335 times.
✗ Branch 1 (36→73) not taken.
50335 Function candidate = presetFunction;
175
176 // Create empty type mapping
177 50335 TypeMapping &typeMapping = candidate.typeMapping;
178
179 50335 bool forceSubstantiation = false;
180
1/2
✓ Branch 0 (37→38) taken 50335 times.
✗ Branch 1 (37→71) not taken.
50335 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
181 strictQualifierMatching, forceSubstantiation, nullptr);
182
2/2
✓ Branch 0 (38→39) taken 43135 times.
✓ Branch 1 (38→40) taken 7200 times.
50335 if (matchResult == MatchResult::SKIP_FUNCTION)
183 43135 break; // Leave the whole function
184
2/2
✓ Branch 0 (40→41) taken 5926 times.
✓ Branch 1 (40→42) taken 1274 times.
7200 if (matchResult == MatchResult::SKIP_MANIFESTATION)
185 5926 continue; // Leave this manifestation and try the next one
186
187 // Add to matches
188
3/6
✓ Branch 0 (42→43) taken 1274 times.
✗ Branch 1 (42→70) not taken.
✓ Branch 2 (43→44) taken 1274 times.
✗ Branch 3 (43→70) not taken.
✓ Branch 4 (44→45) taken 1274 times.
✗ Branch 5 (44→70) not taken.
1274 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
189
190 1274 break; // Leave the whole manifestation list to not double-match the manifestation
191
2/2
✓ Branch 0 (47→48) taken 5926 times.
✓ Branch 1 (47→49) taken 44409 times.
50335 }
192 69459 }
193
194 // Return the very match or a nullptr
195
2/2
✓ Branch 0 (57→58) taken 1274 times.
✓ Branch 1 (57→60) taken 7112 times.
8386 return !matches.empty() ? matches.front() : nullptr;
196 8386 }
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 71288 Function *FunctionManager::match(const 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 71288 times.
✗ Branch 1 (2→154) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 71288 times.
71288 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
216
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 71288 times.
71288 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 71288 times.
✗ Branch 1 (7→189) not taken.
71288 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
220
3/4
✓ Branch 0 (8→9) taken 71288 times.
✗ Branch 1 (8→189) not taken.
✓ Branch 2 (9→10) taken 11494 times.
✓ Branch 3 (9→12) taken 59794 times.
71288 if (lookupCache.contains(cacheKey)) {
221 11494 lookupCacheHits++;
222
1/2
✓ Branch 0 (10→11) taken 11494 times.
✗ Branch 1 (10→189) not taken.
11494 return lookupCache.at(cacheKey);
223 }
224 59794 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 59794 times.
✗ Branch 1 (12→189) not taken.
59794 FunctionRegistry functionRegistry = matchScope->functions;
228 // Loop over function registry to find functions, that match the requirements of the call
229 59794 std::vector<Function *> matches;
230
2/2
✓ Branch 0 (139→15) taken 718130 times.
✓ Branch 1 (139→140) taken 59792 times.
777922 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 718130 times.
✗ Branch 1 (18→183) not taken.
718130 const FunctionManifestationList manifestations = m;
233
2/2
✓ Branch 0 (135→21) taken 762378 times.
✓ Branch 1 (135→136) taken 44050 times.
806428 for (const auto &[signature, presetFunction] : manifestations) {
234
2/4
✓ Branch 0 (24→25) taken 762378 times.
✗ Branch 1 (24→179) not taken.
✗ Branch 2 (25→26) not taken.
✓ Branch 3 (25→27) taken 762378 times.
762378 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 762378 times.
✗ Branch 1 (27→179) not taken.
✓ Branch 2 (28→29) taken 42271 times.
✓ Branch 3 (28→30) taken 720107 times.
762378 if (presetFunction.isGenericSubstantiation())
238 88298 continue;
239
240 // Copy the function to be able to substantiate types
241
1/2
✓ Branch 0 (30→31) taken 720107 times.
✗ Branch 1 (30→179) not taken.
720107 Function candidate = presetFunction;
242
243 // Prepare type mapping, based on the given initial type mapping
244 720107 TypeMapping &typeMapping = candidate.typeMapping;
245 720107 typeMapping.clear();
246
2/2
✓ Branch 0 (43→33) taken 3447 times.
✓ Branch 1 (43→44) taken 720107 times.
723554 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
247
2/4
✓ Branch 0 (33→34) taken 3447 times.
✗ Branch 1 (33→177) not taken.
✓ Branch 2 (34→35) taken 3447 times.
✗ Branch 3 (34→177) not taken.
3447 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
248
1/2
✓ Branch 0 (35→36) taken 3447 times.
✗ Branch 1 (35→177) not taken.
3447 const QualType &templateType = templateTypeHints.at(i);
249
2/4
✓ Branch 0 (36→37) taken 3447 times.
✗ Branch 1 (36→157) not taken.
✓ Branch 2 (37→38) taken 3447 times.
✗ Branch 3 (37→155) not taken.
3447 typeMapping.insert({typeName, templateType});
250 }
251
252 720107 bool forceSubstantiation = false;
253
2/2
✓ Branch 0 (44→45) taken 720106 times.
✓ Branch 1 (44→177) taken 1 times.
720107 MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
254 strictQualifierMatching, forceSubstantiation, callNode);
255
2/2
✓ Branch 0 (45→46) taken 671769 times.
✓ Branch 1 (45→47) taken 48337 times.
720106 if (matchResult == MatchResult::SKIP_FUNCTION)
256 671769 break; // Leave the whole function
257
2/2
✓ Branch 0 (47→48) taken 40636 times.
✓ Branch 1 (47→49) taken 7701 times.
48337 if (matchResult == MatchResult::SKIP_MANIFESTATION)
258 40636 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 7701 candidate.used = true;
262 7701 candidate.entry->used = true;
263
264 // Check if the function is generic needs to be substantiated
265
6/6
✓ Branch 0 (50→51) taken 5469 times.
✓ Branch 1 (50→53) taken 2232 times.
✓ Branch 2 (51→52) taken 5391 times.
✓ Branch 3 (51→53) taken 78 times.
✓ Branch 4 (54→55) taken 5391 times.
✓ Branch 5 (54→67) taken 2310 times.
7701 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
266
5/10
✓ Branch 0 (55→56) taken 5391 times.
✗ Branch 1 (55→177) not taken.
✓ Branch 2 (56→57) taken 5391 times.
✗ Branch 3 (56→61) not taken.
✓ Branch 4 (57→58) taken 5391 times.
✗ Branch 5 (57→177) not taken.
✓ Branch 6 (58→59) taken 5391 times.
✗ Branch 7 (58→177) not taken.
✓ Branch 8 (59→60) taken 5391 times.
✗ Branch 9 (59→61) not taken.
5391 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
267
3/6
✓ Branch 0 (62→63) taken 5391 times.
✗ Branch 1 (62→158) not taken.
✓ Branch 2 (63→64) taken 5391 times.
✗ Branch 3 (63→158) not taken.
✓ Branch 4 (64→65) taken 5391 times.
✗ Branch 5 (64→158) not taken.
5391 matches.push_back(&matchScope->functions.at(fctId).at(signature));
268 5391 matches.back()->used = true;
269 5391 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 2310 times.
✗ Branch 1 (67→177) not taken.
2310 const std::string nonGenericSignature = candidate.getSignature(true, false);
274
4/6
✓ Branch 0 (68→69) taken 2310 times.
✗ Branch 1 (68→175) not taken.
✓ Branch 2 (69→70) taken 2310 times.
✗ Branch 3 (69→175) not taken.
✓ Branch 4 (70→71) taken 150 times.
✓ Branch 5 (70→75) taken 2160 times.
2310 if (matchScope->functions.at(fctId).contains(nonGenericSignature)) {
275
3/6
✓ Branch 0 (71→72) taken 150 times.
✗ Branch 1 (71→159) not taken.
✓ Branch 2 (72→73) taken 150 times.
✗ Branch 3 (72→159) not taken.
✓ Branch 4 (73→74) taken 150 times.
✗ Branch 5 (73→159) not taken.
150 matches.push_back(&matchScope->functions.at(fctId).at(nonGenericSignature));
276 150 break; // Leave the whole manifestation list to not double-match the manifestation
277 }
278
279 // Insert the substantiated version if required
280
2/2
✓ Branch 0 (75→76) taken 2159 times.
✓ Branch 1 (75→175) taken 1 times.
2160 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
281
2/4
✓ Branch 0 (76→77) taken 2159 times.
✗ Branch 1 (76→175) not taken.
✓ Branch 2 (77→78) taken 2159 times.
✗ Branch 3 (77→175) not taken.
2159 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
282 2159 substantiatedFunction->alreadyTypeChecked = false;
283
2/4
✓ Branch 0 (78→79) taken 2159 times.
✗ Branch 1 (78→175) not taken.
✓ Branch 2 (79→80) taken 2159 times.
✗ Branch 3 (79→175) not taken.
2159 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
284
285 // Copy function entry
286
1/2
✓ Branch 0 (80→81) taken 2159 times.
✗ Branch 1 (80→175) not taken.
2159 const std::string newSignature = substantiatedFunction->getSignature(false);
287
1/2
✓ Branch 0 (81→82) taken 2159 times.
✗ Branch 1 (81→173) not taken.
2159 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
288
1/2
✓ Branch 0 (84→85) taken 2159 times.
✗ Branch 1 (84→173) not taken.
2159 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newSignature);
289
1/2
✗ Branch 0 (85→86) not taken.
✓ Branch 1 (85→87) taken 2159 times.
2159 assert(substantiatedFunction->entry != nullptr);
290
291 // Copy function scope
292
1/2
✓ Branch 0 (87→88) taken 2159 times.
✗ Branch 1 (87→173) not taken.
2159 const std::string oldSignature = presetFunction.getSignature(false);
293
1/2
✓ Branch 0 (88→89) taken 2159 times.
✗ Branch 1 (88→171) not taken.
2159 Scope *childScope = matchScope->copyChildScope(oldSignature, newSignature);
294
1/2
✗ Branch 0 (89→90) not taken.
✓ Branch 1 (89→91) taken 2159 times.
2159 assert(childScope != nullptr);
295 2159 childScope->isGenericScope = false;
296 2159 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 2548 times.
✓ Branch 1 (101→102) taken 2159 times.
4707 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
300
2/4
✓ Branch 0 (96→97) taken 2548 times.
✗ Branch 1 (96→162) not taken.
✓ Branch 2 (97→98) taken 2548 times.
✗ Branch 3 (97→160) not taken.
2548 childScope->insertGenericType(typeName, GenericType(concreteType));
301
302 // Substantiate the 'this' entry in the new function scope
303
6/6
✓ Branch 0 (105→106) taken 1754 times.
✓ Branch 1 (105→109) taken 405 times.
✓ Branch 2 (107→108) taken 1753 times.
✓ Branch 3 (107→109) taken 1 times.
✓ Branch 4 (110→111) taken 1753 times.
✓ Branch 5 (110→124) taken 406 times.
2159 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
304
1/2
✓ Branch 0 (113→114) taken 1753 times.
✗ Branch 1 (113→166) not taken.
5259 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
305
1/2
✗ Branch 0 (119→120) not taken.
✓ Branch 1 (119→121) taken 1753 times.
1753 assert(thisEntry != nullptr);
306
2/4
✓ Branch 0 (121→122) taken 1753 times.
✗ Branch 1 (121→170) not taken.
✓ Branch 2 (122→123) taken 1753 times.
✗ Branch 3 (122→170) not taken.
1753 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
307 }
308
309 // Add to matched functions
310
1/2
✓ Branch 0 (124→125) taken 2159 times.
✗ Branch 1 (124→171) not taken.
2159 matches.push_back(substantiatedFunction);
311
312 2159 break; // Leave the whole manifestation list to not double-match the manifestation
313
2/2
✓ Branch 0 (131→132) taken 46027 times.
✓ Branch 1 (131→133) taken 674078 times.
722417 }
314 718130 }
315
316 // If no matches were found, return a nullptr
317
2/2
✓ Branch 0 (141→142) taken 52093 times.
✓ Branch 1 (141→143) taken 7699 times.
59792 if (matches.empty())
318 52093 return nullptr;
319
320
1/2
✗ Branch 0 (144→145) not taken.
✓ Branch 1 (144→146) taken 7699 times.
7699 assert(matches.size() == 1);
321 7699 Function *matchedFunction = matches.front();
322
323 // Insert into cache
324
1/2
✓ Branch 0 (147→148) taken 7699 times.
✗ Branch 1 (147→185) not taken.
7699 lookupCache[cacheKey] = matchedFunction;
325
326 // Trigger revisit in type checker if required
327
1/2
✓ Branch 0 (148→149) taken 7699 times.
✗ Branch 1 (148→185) not taken.
7699 TypeChecker::requestRevisitIfRequired(matchedFunction);
328
329 // Return the very match
330 7699 return matchedFunction;
331 59796 }
332
333 770442 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
334 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
335 bool strictQualifierMatching, bool &forceSubstantiation,
336 const ASTNode *callNode) {
337 // Check name requirement
338
2/2
✓ Branch 0 (3→4) taken 714904 times.
✓ Branch 1 (3→5) taken 55538 times.
770442 if (!matchName(candidate, reqName))
339 714904 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
340
341 // Check 'this' type requirement
342
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 55538 times.
55538 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
343 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
344
345 // Check arg types requirement
346
2/2
✓ Branch 0 (9→10) taken 46561 times.
✓ Branch 1 (9→11) taken 8976 times.
55538 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
347 46561 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
348
349 // Check if there are unresolved generic types
350
2/2
✓ Branch 0 (13→14) taken 1 times.
✓ Branch 1 (13→15) taken 8975 times.
8976 if (typeMapping.size() < candidate.templateTypes.size())
351 1 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
352
353 // Substantiate return type
354 8975 substantiateReturnType(candidate, typeMapping, callNode);
355
356 // Set the match scope to the scope of the concrete substantiation
357 8975 const QualType &thisType = candidate.thisType;
358
2/2
✓ Branch 0 (17→18) taken 5548 times.
✓ Branch 1 (17→25) taken 3427 times.
8975 if (!thisType.is(TY_DYN)) {
359 // If we only have the generic struct scope, lookup the concrete manifestation scope
360
2/2
✓ Branch 0 (18→19) taken 62 times.
✓ Branch 1 (18→23) taken 5486 times.
5548 if (matchScope->isGenericScope) {
361 62 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
362
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 62 times.
62 assert(spiceStruct != nullptr);
363 62 matchScope = spiceStruct->scope;
364 }
365
1/2
✓ Branch 0 (23→24) taken 5548 times.
✗ Branch 1 (23→27) not taken.
5548 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
366 }
367
368 8975 return MatchResult::MATCHED;
369 }
370
371 /**
372 * Checks if the matching candidate fulfills the name requirement
373 *
374 * @param candidate Matching candidate function
375 * @param reqName Requested function name
376 * @return Fulfilled or not
377 */
378 770442 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
379
380 /**
381 * Checks if the matching candidate fulfills the 'this' type requirement
382 *
383 * @param candidate Matching candidate function
384 * @param reqThisType Requested 'this' type
385 * @param typeMapping Concrete template type mapping
386 * @param strictQualifierMatching Match qualifiers strictly
387 * @param callNode Call AST node for printing error messages
388 * @return Fulfilled or not
389 */
390 55538 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
391 bool strictQualifierMatching, const ASTNode *callNode) {
392 55538 QualType &candidateThisType = candidate.thisType;
393
394 // Shortcut for procedures
395
7/10
✓ Branch 0 (2→3) taken 55538 times.
✗ Branch 1 (2→23) not taken.
✓ Branch 2 (3→4) taken 38000 times.
✓ Branch 3 (3→7) taken 17538 times.
✓ Branch 4 (4→5) taken 38000 times.
✗ Branch 5 (4→23) not taken.
✓ Branch 6 (5→6) taken 38000 times.
✗ Branch 7 (5→7) not taken.
✓ Branch 8 (8→9) taken 38000 times.
✓ Branch 9 (8→10) taken 17538 times.
55538 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
396 38000 return true;
397
398 // Give the type matcher a way to retrieve instances of GenericType by their name
399 37832 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
400 2756 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
401 17538 };
402
403 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
404
2/4
✓ Branch 0 (11→12) taken 17538 times.
✗ Branch 1 (11→21) not taken.
✗ Branch 2 (12→13) not taken.
✓ Branch 3 (12→14) taken 17538 times.
17538 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
405 strictQualifierMatching))
406 return false;
407
408 // Substantiate the candidate param type, based on the type mapping
409
3/4
✓ Branch 0 (14→15) taken 17538 times.
✗ Branch 1 (14→21) not taken.
✓ Branch 2 (15→16) taken 2867 times.
✓ Branch 3 (15→17) taken 14671 times.
17538 if (candidateThisType.hasAnyGenericParts())
410
1/2
✓ Branch 0 (16→17) taken 2867 times.
✗ Branch 1 (16→21) not taken.
2867 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
411
412 17538 return true;
413 17538 }
414
415 /**
416 * Checks if the matching candidate fulfills the argument types requirement
417 *
418 * @param candidate Matching candidate function
419 * @param reqArgs Requested argument types
420 * @param typeMapping Concrete template type mapping
421 * @param strictQualifierMatching Match qualifiers strictly
422 * @param needsSubstantiation We want to create a substantiation after successfully matching
423 * @param callNode Call AST node for printing error messages
424 * @return Fulfilled or not
425 */
426 55538 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
427 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
428 55538 std::vector<Param> &candidateParamList = candidate.paramList;
429
430 // If the number of arguments does not match with the number of params, the matching fails
431
6/6
✓ Branch 0 (2→3) taken 55463 times.
✓ Branch 1 (2→7) taken 75 times.
✓ Branch 2 (5→6) taken 7240 times.
✓ Branch 3 (5→7) taken 48223 times.
✓ Branch 4 (8→9) taken 7240 times.
✓ Branch 5 (8→10) taken 48298 times.
55538 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
432 7240 return false;
433 // In the case of a vararg function, we only disallow fewer arguments than parameters
434
4/6
✓ Branch 0 (10→11) taken 75 times.
✓ Branch 1 (10→15) taken 48223 times.
✗ Branch 2 (13→14) not taken.
✓ Branch 3 (13→15) taken 75 times.
✗ Branch 4 (16→17) not taken.
✓ Branch 5 (16→18) taken 48298 times.
48298 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
435 return false;
436
437 // Give the type matcher a way to retrieve instances of GenericType by their name
438 100829 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
439 4233 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
440 48298 };
441
442 // Loop over all parameters
443
2/2
✓ Branch 0 (66→20) taken 51168 times.
✓ Branch 1 (66→67) taken 8976 times.
60144 for (size_t i = 0; i < reqArgs.size(); i++) {
444 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
445 // but we need to modify the candidate param list to accept them
446
6/6
✓ Branch 0 (20→21) taken 300 times.
✓ Branch 1 (20→24) taken 50868 times.
✓ Branch 2 (22→23) taken 75 times.
✓ Branch 3 (22→24) taken 225 times.
✓ Branch 4 (25→26) taken 75 times.
✓ Branch 5 (25→29) taken 51093 times.
51168 if (candidate.isVararg && i >= candidateParamList.size()) {
447
2/4
✓ Branch 0 (26→27) taken 75 times.
✗ Branch 1 (26→71) not taken.
✓ Branch 2 (27→28) taken 75 times.
✗ Branch 3 (27→71) not taken.
75 candidateParamList.push_back(Param(reqArgs.at(i).first, false));
448 75 needsSubstantiation = true; // We need to modify the candidate param types
449 75 continue;
450 }
451
452 // Retrieve actual and requested types
453
2/4
✓ Branch 0 (29→30) taken 51093 times.
✗ Branch 1 (29→84) not taken.
✗ Branch 2 (30→31) not taken.
✓ Branch 3 (30→32) taken 51093 times.
51093 assert(!candidateParamList.at(i).isOptional);
454
1/2
✓ Branch 0 (32→33) taken 51093 times.
✗ Branch 1 (32→84) not taken.
51093 QualType &candidateType = candidateParamList.at(i).qualType;
455
1/2
✓ Branch 0 (33→34) taken 51093 times.
✗ Branch 1 (33→84) not taken.
51093 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
456
457 // Check if the requested param type matches the candidate param type. The type mapping may be extended
458
3/4
✓ Branch 0 (36→37) taken 51093 times.
✗ Branch 1 (36→84) not taken.
✓ Branch 2 (37→38) taken 39321 times.
✓ Branch 3 (37→39) taken 11772 times.
51093 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
459 strictQualifierMatching))
460 39321 return false;
461
462 // Substantiate the candidate param type, based on the type mapping
463
3/4
✓ Branch 0 (39→40) taken 11772 times.
✗ Branch 1 (39→84) not taken.
✓ Branch 2 (40→41) taken 1902 times.
✓ Branch 3 (40→42) taken 9870 times.
11772 if (candidateType.hasAnyGenericParts())
464
1/2
✓ Branch 0 (41→42) taken 1902 times.
✗ Branch 1 (41→84) not taken.
1902 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
465
466 // Check if we try to bind a non-ref temporary to a non-const ref parameter
467
3/4
✓ Branch 0 (42→43) taken 11772 times.
✗ Branch 1 (42→84) not taken.
✓ Branch 2 (43→44) taken 1 times.
✓ Branch 3 (43→54) taken 11771 times.
11772 if (!candidateType.canBind(requestedType, isArgTemporary)) {
468
1/2
✓ Branch 0 (44→45) taken 1 times.
✗ Branch 1 (44→53) not taken.
1 if (callNode)
469
2/4
✓ Branch 0 (48→49) taken 1 times.
✗ Branch 1 (48→75) not taken.
✓ Branch 2 (49→50) taken 1 times.
✗ Branch 3 (49→72) not taken.
3 throw SemanticError(callNode, TEMP_TO_NON_CONST_REF, "Temporary values can only be bound to const reference parameters");
470 return false;
471 }
472
473 // If we have a function/procedure type we need to take care of the information, if it takes captures
474
9/12
✓ Branch 0 (54→55) taken 11771 times.
✗ Branch 1 (54→81) not taken.
✓ Branch 2 (55→56) taken 11771 times.
✗ Branch 3 (55→81) not taken.
✓ Branch 4 (56→57) taken 19 times.
✓ Branch 5 (56→60) taken 11752 times.
✓ Branch 6 (57→58) taken 19 times.
✗ Branch 7 (57→81) not taken.
✓ Branch 8 (58→59) taken 4 times.
✓ Branch 9 (58→60) taken 15 times.
✓ Branch 10 (61→62) taken 4 times.
✓ Branch 11 (61→64) taken 11767 times.
11771 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
475
1/2
✓ Branch 0 (62→63) taken 4 times.
✗ Branch 1 (62→83) not taken.
4 candidateType = candidateType.getWithLambdaCaptures();
476 4 needsSubstantiation = true;
477 }
478 }
479
480 8976 return true;
481 48298 }
482
483 /**
484 * Substantiates the candidate return type, based on the given type mapping
485 *
486 * @param candidate Matching candidate function
487 * @param typeMapping Concrete template type mapping
488 * @param callNode AST node for error messages
489 */
490 8975 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
491
2/2
✓ Branch 0 (3→4) taken 658 times.
✓ Branch 1 (3→5) taken 8317 times.
8975 if (candidate.returnType.hasAnyGenericParts())
492 658 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
493 8975 }
494
495 /**
496 * Searches the candidate template types for a generic type object with a certain name and return it
497 *
498 * @param candidate Matching candidate function
499 * @param templateTypeName Template type name
500 * @return Generic type object
501 */
502 6989 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
503 const std::string &templateTypeName) {
504
1/2
✓ Branch 0 (11→4) taken 7598 times.
✗ Branch 1 (11→12) not taken.
7598 for (const GenericType &templateType : candidate.templateTypes) {
505
3/4
✓ Branch 0 (5→6) taken 7598 times.
✗ Branch 1 (5→14) not taken.
✓ Branch 2 (7→8) taken 6989 times.
✓ Branch 3 (7→9) taken 609 times.
7598 if (templateType.getSubType() == templateTypeName)
506 6989 return &templateType;
507 }
508 return nullptr;
509 }
510
511 /**
512 * Calculate the cache key for the function lookup cache
513 *
514 * @param scope Scope to match against
515 * @param name Function name requirement
516 * @param thisType This type requirement
517 * @param args Argument requirement
518 * @param templateTypes Template type requirement
519 * @return Cache key
520 */
521 82942 uint64_t FunctionManager::getCacheKey(Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
522 const QualTypeList &templateTypes) {
523 123778 const auto pred1 = [](size_t acc, const Arg &val) {
524 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
525 123778 const uint64_t typeHash = std::hash<QualType>{}(val.first);
526 123778 const uint64_t temporaryHash = std::hash<bool>{}(val.second);
527 123778 const uint64_t newHash = typeHash ^ (temporaryHash << 1);
528 123778 return acc * 31 + newHash;
529 };
530 716 const auto pred2 = [](size_t acc, const QualType &val) {
531 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
532 716 return acc * 31 + std::hash<QualType>{}(val);
533 };
534 // Calculate the cache key
535 82942 const uint64_t scopeHash = std::hash<Scope *>{}(scope);
536 82942 const uint64_t hashName = std::hash<std::string>{}(name);
537 82942 const uint64_t hashThisType = std::hash<QualType>{}(thisType);
538 82942 const uint64_t hashArgs = std::accumulate(args.begin(), args.end(), 0u, pred1);
539 82942 const uint64_t hashTemplateTypes = std::accumulate(templateTypes.begin(), templateTypes.end(), 0u, pred2);
540 82942 return scopeHash ^ (hashName << 1) ^ (hashThisType << 2) ^ (hashArgs << 3) ^ (hashTemplateTypes << 4);
541 }
542
543 /**
544 * Clear the lookup cache
545 */
546 415 void FunctionManager::cleanup() {
547 415 lookupCache.clear();
548 415 lookupCacheHits = 0;
549 415 lookupCacheMisses = 0;
550 415 }
551
552 /**
553 * Dump usage statistics for the lookup cache
554 */
555 198 std::string FunctionManager::dumpLookupCacheStatistics() {
556
1/2
✓ Branch 0 (2→3) taken 198 times.
✗ Branch 1 (2→22) not taken.
198 std::stringstream stats;
557
2/4
✓ Branch 0 (3→4) taken 198 times.
✗ Branch 1 (3→20) not taken.
✓ Branch 2 (4→5) taken 198 times.
✗ Branch 3 (4→20) not taken.
198 stats << "FunctionManager lookup cache statistics:" << std::endl;
558
3/6
✓ Branch 0 (5→6) taken 198 times.
✗ Branch 1 (5→20) not taken.
✓ Branch 2 (7→8) taken 198 times.
✗ Branch 3 (7→20) not taken.
✓ Branch 4 (8→9) taken 198 times.
✗ Branch 5 (8→20) not taken.
198 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
559
3/6
✓ Branch 0 (9→10) taken 198 times.
✗ Branch 1 (9→20) not taken.
✓ Branch 2 (10→11) taken 198 times.
✗ Branch 3 (10→20) not taken.
✓ Branch 4 (11→12) taken 198 times.
✗ Branch 5 (11→20) not taken.
198 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
560
3/6
✓ Branch 0 (12→13) taken 198 times.
✗ Branch 1 (12→20) not taken.
✓ Branch 2 (13→14) taken 198 times.
✗ Branch 3 (13→20) not taken.
✓ Branch 4 (14→15) taken 198 times.
✗ Branch 5 (14→20) not taken.
198 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
561
1/2
✓ Branch 0 (15→16) taken 198 times.
✗ Branch 1 (15→20) not taken.
396 return stats.str();
562 198 }
563
564 } // namespace spice::compiler
565