GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 98.3% 340 / 0 / 346
Functions: 100.0% 27 / 0 / 27
Branches: 66.8% 442 / 0 / 662

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 <limits>
6
7 #include <ast/ASTNodes.h>
8 #include <exception/SemanticError.h>
9 #include <model/GenericType.h>
10 #include <symboltablebuilder/Scope.h>
11 #include <symboltablebuilder/SymbolTableBuilder.h>
12 #include <typechecker/TypeChecker.h>
13 #include <typechecker/TypeMatcher.h>
14 #include <util/CodeLoc.h>
15 #include <util/CustomHashFunctions.h>
16
17 namespace spice::compiler {
18
19 // Static member initialization
20 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
21 size_t FunctionManager::lookupCacheHits = 0;
22 size_t FunctionManager::lookupCacheMisses = 0;
23
24 33143 Function *FunctionManager::insert(Scope *insertScope, const Function &baseFunction, std::vector<Function *> *nodeFunctionList) {
25 // Open a new manifestation list for the function definition
26
3/6
✓ Branch 2 → 3 taken 33143 times.
✗ Branch 2 → 49 not taken.
✓ Branch 3 → 4 taken 33143 times.
✗ Branch 3 → 46 not taken.
✓ Branch 4 → 5 taken 33143 times.
✗ Branch 4 → 44 not taken.
33143 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
27
1/2
✓ Branch 8 → 9 taken 33143 times.
✗ Branch 8 → 50 not taken.
33143 insertScope->functions.emplace(fctId, FunctionManifestationList());
28
29 // Collect substantiations
30 33143 std::vector<Function> manifestations;
31
1/2
✓ Branch 10 → 11 taken 33143 times.
✗ Branch 10 → 54 not taken.
33143 substantiateOptionalParams(baseFunction, manifestations);
32
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 33143 times.
33143 assert(!manifestations.empty());
33
34 // Save substantiations in declaration node
35 33143 Function *manifestationPtr = nullptr;
36
2/2
✓ Branch 32 → 16 taken 35303 times.
✓ Branch 32 → 33 taken 33141 times.
101587 for (const Function &manifestation : manifestations) {
37
2/2
✓ Branch 18 → 19 taken 35301 times.
✓ Branch 18 → 53 taken 2 times.
35303 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
38
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 35301 times.
35301 assert(manifestationPtr != nullptr);
39
1/2
✓ Branch 21 → 22 taken 35301 times.
✗ Branch 21 → 23 not taken.
35301 if (nodeFunctionList)
40
1/2
✓ Branch 22 → 23 taken 35301 times.
✗ Branch 22 → 53 not taken.
35301 nodeFunctionList->push_back(manifestationPtr);
41 }
42
43
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 33141 times.
33141 if (!nodeFunctionList)
44 return manifestationPtr;
45
46
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 33141 times.
33141 assert(!nodeFunctionList->empty());
47 33141 return nodeFunctionList->front();
48 33145 }
49
50 /**
51 * Create definite functions from ambiguous ones, in regard to optional arguments.
52 *
53 * Example:
54 * int testFunc(string, int?, double?)
55 * gets
56 * int testFunc(string)
57 * int testFunc(string, int)
58 * int testFunc(string, int, double)
59 *
60 * This method also accepts functions, that are already definite, but does nothing to them.
61 *
62 * @param baseFunction Ambiguous base function
63 * @param manifestations Vector to store the definite manifestations
64 * @return True, if there were optional arguments found
65 */
66 33143 void FunctionManager::substantiateOptionalParams(const Function &baseFunction, std::vector<Function> &manifestations) {
67 // Handle the case of no parameters -> simply return the base function
68
2/2
✓ Branch 3 → 4 taken 9512 times.
✓ Branch 3 → 6 taken 23631 times.
33143 if (baseFunction.paramList.empty()) {
69
1/2
✓ Branch 4 → 5 taken 9512 times.
✗ Branch 4 → 47 not taken.
9512 manifestations.push_back(baseFunction);
70 9512 return;
71 }
72
73 23631 ParamList currentFunctionParamTypes;
74
1/2
✓ Branch 7 → 8 taken 23631 times.
✗ Branch 7 → 45 not taken.
23631 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
75 23631 bool metFirstOptionalParam = false;
76
1/2
✓ Branch 8 → 9 taken 23631 times.
✗ Branch 8 → 45 not taken.
23631 Function manifestation = baseFunction;
77
78 // Loop over all parameters
79
2/2
✓ Branch 32 → 11 taken 37069 times.
✓ Branch 32 → 33 taken 23631 times.
84331 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
80 // Check if we have a mandatory parameter
81
2/2
✓ Branch 13 → 14 taken 34909 times.
✓ Branch 13 → 16 taken 2160 times.
37069 if (!isOptional) {
82
1/2
✓ Branch 14 → 15 taken 34909 times.
✗ Branch 14 → 40 not taken.
34909 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
83 34909 continue;
84 }
85
86 // Add substantiation without the optional parameter
87
2/2
✓ Branch 16 → 17 taken 2048 times.
✓ Branch 16 → 20 taken 112 times.
2160 if (!metFirstOptionalParam) {
88
1/2
✓ Branch 17 → 18 taken 2048 times.
✗ Branch 17 → 42 not taken.
2048 manifestation.paramList = currentFunctionParamTypes;
89
1/2
✓ Branch 18 → 19 taken 2048 times.
✗ Branch 18 → 42 not taken.
2048 manifestations.push_back(manifestation);
90 // Now we cannot accept mandatory parameters anymore
91 2048 metFirstOptionalParam = true;
92 }
93
94 // Add substantiation with the optional parameter
95
1/2
✓ Branch 20 → 21 taken 2160 times.
✗ Branch 20 → 41 not taken.
2160 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
96
1/2
✓ Branch 21 → 22 taken 2160 times.
✗ Branch 21 → 42 not taken.
2160 manifestation.paramList = currentFunctionParamTypes;
97
1/2
✓ Branch 22 → 23 taken 2160 times.
✗ Branch 22 → 42 not taken.
2160 manifestations.push_back(manifestation);
98 }
99
100 // Ensure at least once manifestation
101
2/2
✓ Branch 34 → 35 taken 21583 times.
✓ Branch 34 → 36 taken 2048 times.
23631 if (manifestations.empty())
102
1/2
✓ Branch 35 → 36 taken 21583 times.
✗ Branch 35 → 43 not taken.
21583 manifestations.push_back(baseFunction);
103 23631 }
104
105 6 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
106 6 ParamList paramList;
107
2/2
✓ Branch 16 → 4 taken 2 times.
✓ Branch 16 → 17 taken 6 times.
14 for (const QualType &paramType : paramTypes)
108
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 33 not taken.
2 paramList.push_back({paramType, false});
109
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};
110 6 }
111
112 45629 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
113
2/4
✓ Branch 2 → 3 taken 45629 times.
✗ Branch 2 → 70 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 45629 times.
45629 assert(newManifestation.hasSubstantiatedParams());
114
115
1/2
✓ Branch 5 → 6 taken 45629 times.
✗ Branch 5 → 70 not taken.
45629 const std::string signature = newManifestation.getSignature(true, true, false, true);
116
117 // Check if the function exists already
118
5/8
✓ Branch 6 → 7 taken 45629 times.
✗ Branch 6 → 59 not taken.
✓ Branch 7 → 8 taken 45629 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 45629 times.
✗ Branch 8 → 59 not taken.
✓ Branch 30 → 10 taken 787203 times.
✓ Branch 30 → 31 taken 45627 times.
832830 for (const auto &manifestations : insertScope->functions | std::views::values) {
119
3/4
✓ Branch 11 → 12 taken 787203 times.
✗ Branch 11 → 59 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 28 taken 787201 times.
787203 if (manifestations.contains(signature)) {
120
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;
121
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");
122 }
123 }
124
125 // Retrieve the matching manifestation list of the scope
126
3/6
✓ Branch 31 → 32 taken 45627 times.
✗ Branch 31 → 65 not taken.
✓ Branch 32 → 33 taken 45627 times.
✗ Branch 32 → 62 not taken.
✓ Branch 33 → 34 taken 45627 times.
✗ Branch 33 → 60 not taken.
45627 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
127
2/4
✓ Branch 36 → 37 taken 45627 times.
✗ Branch 36 → 66 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 45627 times.
45627 assert(insertScope->functions.contains(fctId));
128
1/2
✓ Branch 39 → 40 taken 45627 times.
✗ Branch 39 → 66 not taken.
45627 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
129
130 // Add substantiated function
131
1/2
✓ Branch 40 → 41 taken 45627 times.
✗ Branch 40 → 66 not taken.
45627 manifestationList.emplace(signature, newManifestation);
132
1/2
✓ Branch 41 → 42 taken 45627 times.
✗ Branch 41 → 66 not taken.
91254 return &manifestationList.at(signature);
133 45629 }
134
135 /**
136 * Checks if a function exists by matching it, but not setting it to used
137 *
138 * @param matchScope Scope to match against
139 * @param reqName Function name requirement
140 * @param reqThisType This type requirement
141 * @param reqArgs Argument requirement
142 * @param strictQualifierMatching Match argument and this type qualifiers strictly
143 * @return Found function or nullptr
144 */
145 37718 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
146 const ArgList &reqArgs, bool strictQualifierMatching) {
147
2/4
✓ Branch 2 → 3 taken 37718 times.
✗ Branch 2 → 64 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 37718 times.
37718 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
148
149 // Do cache lookup
150 37718 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
151
3/4
✓ Branch 8 → 9 taken 37718 times.
✗ Branch 8 → 65 not taken.
✓ Branch 11 → 12 taken 7033 times.
✓ Branch 11 → 14 taken 30685 times.
37718 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
152 7033 lookupCacheHits++;
153 7033 return it->second;
154 }
155 30685 lookupCacheMisses++;
156
157 11637 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
158
5/8
✓ Branch 14 → 15 taken 30685 times.
✗ Branch 14 → 74 not taken.
✓ Branch 15 → 16 taken 30095 times.
✓ Branch 15 → 19 taken 590 times.
✓ Branch 16 → 17 taken 30095 times.
✗ Branch 16 → 74 not taken.
✓ Branch 17 → 18 taken 30095 times.
✗ Branch 17 → 19 not taken.
30685 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
159
160 // Loop over function registry to find functions, that match the requirements of the call
161 30685 std::vector<const Function *> matches;
162
2/2
✓ Branch 54 → 22 taken 206851 times.
✓ Branch 54 → 55 taken 30685 times.
237536 for (const auto &[defCodeLocStr, manifestations] : matchScope->functions) {
163
2/2
✓ Branch 51 → 27 taken 241858 times.
✓ Branch 51 → 52 taken 95529 times.
337387 for (const auto &[signature, presetFunction] : manifestations) {
164
2/4
✓ Branch 30 → 31 taken 241858 times.
✗ Branch 30 → 69 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 241858 times.
241858 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
165
166 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
167 // - search for generic fct: Only match against generic preset functions
168
3/4
✓ Branch 33 → 34 taken 241858 times.
✗ Branch 33 → 69 not taken.
✓ Branch 34 → 35 taken 106814 times.
✓ Branch 34 → 36 taken 135044 times.
241858 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
169 130536 continue;
170
171 // Copy the function to be able to substantiate types
172
1/2
✓ Branch 36 → 37 taken 135044 times.
✗ Branch 36 → 69 not taken.
135044 Function candidate = presetFunction;
173
174 // Create empty type mapping
175 135044 TypeMapping &typeMapping = candidate.typeMapping;
176
177 135044 bool forceSubstantiation = false;
178
1/2
✓ Branch 37 → 38 taken 135044 times.
✗ Branch 37 → 67 not taken.
135044 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
179 strictQualifierMatching, forceSubstantiation, nullptr);
180
2/2
✓ Branch 38 → 39 taken 106377 times.
✓ Branch 38 → 40 taken 28667 times.
135044 if (matchResult == MatchResult::SKIP_FUNCTION)
181 106377 break; // Leave the whole function
182
2/2
✓ Branch 40 → 41 taken 23722 times.
✓ Branch 40 → 42 taken 4945 times.
28667 if (matchResult == MatchResult::SKIP_MANIFESTATION)
183 23722 continue; // Leave this manifestation and try the next one
184
185 // Add to matches
186
3/6
✓ Branch 42 → 43 taken 4945 times.
✗ Branch 42 → 66 not taken.
✓ Branch 43 → 44 taken 4945 times.
✗ Branch 43 → 66 not taken.
✓ Branch 44 → 45 taken 4945 times.
✗ Branch 44 → 66 not taken.
4945 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
187
188 4945 break; // Leave the whole manifestation list to not double-match the manifestation
189
2/2
✓ Branch 47 → 48 taken 23722 times.
✓ Branch 47 → 49 taken 111322 times.
135044 }
190 }
191
192 // Return the very match or a nullptr
193
2/2
✓ Branch 56 → 57 taken 4945 times.
✓ Branch 56 → 59 taken 25740 times.
30685 return !matches.empty() ? matches.front() : nullptr;
194 30685 }
195
196 /**
197 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
198 * If more than one function matches the requirement, an error gets thrown.
199 *
200 * @param matchScope Scope to match against
201 * @param reqName Function name requirement
202 * @param reqThisType This type requirement
203 * @param reqArgs Argument requirement
204 * @param templateTypeHints Template type requirement
205 * @param strictQualifierMatching Match argument and this type qualifiers strictly
206 * @param callNode Call AST node for printing error messages
207 * @return Matched function or nullptr
208 */
209 352294 Function *FunctionManager::match(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
210 const ArgList &reqArgs, const QualTypeList &templateTypeHints, bool strictQualifierMatching,
211 const ASTNode *callNode) {
212
2/4
✓ Branch 2 → 3 taken 352294 times.
✗ Branch 2 → 181 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 352294 times.
352294 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
213
214 // Do cache lookup
215 352294 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
216
3/4
✓ Branch 6 → 7 taken 352294 times.
✗ Branch 6 → 182 not taken.
✓ Branch 9 → 10 taken 48484 times.
✓ Branch 9 → 12 taken 303810 times.
352294 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
217 48484 lookupCacheHits++;
218 48484 return it->second;
219 }
220 303810 lookupCacheMisses++;
221
222 // Loop over function registry to find functions, that match the requirements of the call
223 303810 std::vector<Function *> matches;
224
2/2
✓ Branch 140 → 14 taken 3471548 times.
✓ Branch 140 → 141 taken 303809 times.
3775357 for (auto &[fctId, manifestations] : matchScope->functions) {
225
2/2
✓ Branch 137 → 19 taken 3570698 times.
✓ Branch 137 → 138 taken 130458 times.
3701156 for (const auto &[signature, presetFunction] : manifestations) {
226
2/4
✓ Branch 22 → 23 taken 3570698 times.
✗ Branch 22 → 205 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 3570698 times.
3570698 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
227
228 // Skip generic and newly inserted substantiations to prevent double matching of a function
229
6/8
✓ Branch 25 → 26 taken 3570698 times.
✗ Branch 25 → 205 not taken.
✓ Branch 26 → 27 taken 3475842 times.
✓ Branch 26 → 28 taken 94856 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 3475842 times.
✓ Branch 30 → 31 taken 94856 times.
✓ Branch 30 → 32 taken 3475842 times.
3570698 if (presetFunction.isGenericSubstantiation() || presetFunction.isNewlyInserted)
230 229608 continue;
231
232 // Copy the function to be able to substantiate types
233
1/2
✓ Branch 32 → 33 taken 3475842 times.
✗ Branch 32 → 205 not taken.
3475842 Function candidate = presetFunction;
234
235 // Prepare type mapping, based on the given initial type mapping
236 3475842 TypeMapping &typeMapping = candidate.typeMapping;
237 3475842 typeMapping.clear();
238
2/2
✓ Branch 43 → 35 taken 54150 times.
✓ Branch 43 → 44 taken 3475842 times.
3529992 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
239
2/4
✓ Branch 35 → 36 taken 54150 times.
✗ Branch 35 → 203 not taken.
✓ Branch 36 → 37 taken 54150 times.
✗ Branch 36 → 203 not taken.
54150 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
240
1/2
✓ Branch 37 → 38 taken 54150 times.
✗ Branch 37 → 203 not taken.
54150 const QualType &templateType = templateTypeHints.at(i);
241
1/2
✓ Branch 38 → 39 taken 54150 times.
✗ Branch 38 → 203 not taken.
54150 typeMapping.emplace(typeName, templateType);
242 }
243
244 3475842 bool forceSubstantiation = false;
245
2/2
✓ Branch 44 → 45 taken 3475841 times.
✓ Branch 44 → 203 taken 1 time.
3475842 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
246 strictQualifierMatching, forceSubstantiation, callNode);
247
2/2
✓ Branch 45 → 46 taken 3329743 times.
✓ Branch 45 → 47 taken 146098 times.
3475841 if (matchResult == MatchResult::SKIP_FUNCTION)
248 3329743 break; // Leave the whole function
249
2/2
✓ Branch 47 → 48 taken 120546 times.
✓ Branch 47 → 49 taken 25552 times.
146098 if (matchResult == MatchResult::SKIP_MANIFESTATION)
250 120546 continue; // Leave this manifestation and try the next one
251
252 // We found a match! -> Set the actual candidate and its entry to used
253 25552 candidate.used = true;
254 25552 candidate.entry->used = true;
255
256 // Check if the function is generic needs to be substantiated
257
6/6
✓ Branch 50 → 51 taken 14431 times.
✓ Branch 50 → 53 taken 11121 times.
✓ Branch 51 → 52 taken 14206 times.
✓ Branch 51 → 53 taken 225 times.
✓ Branch 54 → 55 taken 14206 times.
✓ Branch 54 → 67 taken 11346 times.
25552 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
258
5/10
✓ Branch 55 → 56 taken 14206 times.
✗ Branch 55 → 183 not taken.
✓ Branch 56 → 57 taken 14206 times.
✗ Branch 56 → 61 not taken.
✓ Branch 57 → 58 taken 14206 times.
✗ Branch 57 → 183 not taken.
✓ Branch 58 → 59 taken 14206 times.
✗ Branch 58 → 183 not taken.
✓ Branch 59 → 60 taken 14206 times.
✗ Branch 59 → 61 not taken.
14206 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
259
2/4
✓ Branch 62 → 63 taken 14206 times.
✗ Branch 62 → 183 not taken.
✓ Branch 63 → 64 taken 14206 times.
✗ Branch 63 → 183 not taken.
14206 Function *match = &matchScope->functions.at(fctId).at(signature);
260 14206 match->used = true;
261
1/2
✓ Branch 64 → 65 taken 14206 times.
✗ Branch 64 → 183 not taken.
14206 matches.push_back(match);
262 14206 continue; // Match was successful -> match the next function
263 14206 }
264
265 // Check if we already have this manifestation and can simply re-use it
266
1/2
✓ Branch 67 → 68 taken 11346 times.
✗ Branch 67 → 203 not taken.
11346 const std::string newSignature = candidate.getSignature(true, true, false, true);
267
3/4
✓ Branch 68 → 69 taken 11346 times.
✗ Branch 68 → 185 not taken.
✓ Branch 71 → 72 taken 1020 times.
✓ Branch 71 → 76 taken 10326 times.
11346 if (const auto it = manifestations.find(newSignature); it != manifestations.end()) {
268 1020 it->second.used = true;
269
1/2
✓ Branch 74 → 75 taken 1020 times.
✗ Branch 74 → 184 not taken.
1020 matches.push_back(&it->second);
270 1020 break; // Leave the whole manifestation list to not double-match the manifestation
271 }
272
273 // Insert the substantiated version if required
274
1/2
✓ Branch 76 → 78 taken 10326 times.
✗ Branch 76 → 201 not taken.
10326 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
275
2/4
✓ Branch 78 → 79 taken 10326 times.
✗ Branch 78 → 201 not taken.
✓ Branch 79 → 80 taken 10326 times.
✗ Branch 79 → 201 not taken.
10326 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
276 10326 substantiatedFunction->alreadyTypeChecked = false;
277
2/4
✓ Branch 80 → 81 taken 10326 times.
✗ Branch 80 → 201 not taken.
✓ Branch 81 → 82 taken 10326 times.
✗ Branch 81 → 201 not taken.
10326 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
278 10326 substantiatedFunction->isNewlyInserted = true; // To not iterate over it in the same matching
279
280 // Copy function entry
281
1/2
✓ Branch 82 → 83 taken 10326 times.
✗ Branch 82 → 201 not taken.
10326 const std::string newScopeName = substantiatedFunction->getScopeName();
282
1/2
✓ Branch 83 → 84 taken 10326 times.
✗ Branch 83 → 199 not taken.
10326 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
283
1/2
✓ Branch 86 → 87 taken 10326 times.
✗ Branch 86 → 199 not taken.
10326 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newScopeName);
284
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 10326 times.
10326 assert(substantiatedFunction->entry != nullptr);
285
286 // Copy function scope
287
1/2
✓ Branch 89 → 90 taken 10326 times.
✗ Branch 89 → 199 not taken.
10326 const std::string oldScopeName = presetFunction.getScopeName();
288
1/2
✓ Branch 90 → 91 taken 10326 times.
✗ Branch 90 → 197 not taken.
10326 Scope *childScope = matchScope->copyChildScope(oldScopeName, newScopeName);
289
1/2
✗ Branch 91 → 92 not taken.
✓ Branch 91 → 93 taken 10326 times.
10326 assert(childScope != nullptr);
290 10326 childScope->isGenericScope = false;
291 10326 substantiatedFunction->bodyScope = childScope;
292
293 // Insert symbols for generic type names with concrete types into the child block
294
2/2
✓ Branch 103 → 95 taken 13124 times.
✓ Branch 103 → 104 taken 10326 times.
23450 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
295
2/4
✓ Branch 98 → 99 taken 13124 times.
✗ Branch 98 → 188 not taken.
✓ Branch 99 → 100 taken 13124 times.
✗ Branch 99 → 186 not taken.
13124 childScope->insertGenericType(typeName, GenericType(concreteType));
296
297 // Substantiate the 'this' entry in the new function scope
298
6/6
✓ Branch 107 → 108 taken 8503 times.
✓ Branch 107 → 111 taken 1823 times.
✓ Branch 109 → 110 taken 8501 times.
✓ Branch 109 → 111 taken 2 times.
✓ Branch 112 → 113 taken 8501 times.
✓ Branch 112 → 126 taken 1825 times.
10326 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
299
1/2
✓ Branch 115 → 116 taken 8501 times.
✗ Branch 115 → 192 not taken.
25503 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
300
1/2
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 123 taken 8501 times.
8501 assert(thisEntry != nullptr);
301
2/4
✓ Branch 123 → 124 taken 8501 times.
✗ Branch 123 → 196 not taken.
✓ Branch 124 → 125 taken 8501 times.
✗ Branch 124 → 196 not taken.
8501 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
302 }
303
304 // Add to matched functions
305
1/2
✓ Branch 126 → 127 taken 10326 times.
✗ Branch 126 → 197 not taken.
10326 matches.push_back(substantiatedFunction);
306
307 10326 break; // Leave the whole manifestation list to not double-match the manifestation
308
2/2
✓ Branch 133 → 134 taken 134752 times.
✓ Branch 133 → 135 taken 3341089 times.
3487188 }
309 }
310
311 // If no matches were found, return a nullptr
312
2/2
✓ Branch 142 → 143 taken 278266 times.
✓ Branch 142 → 144 taken 25543 times.
303809 if (matches.empty())
313 278266 return nullptr;
314
315 // Tie-breaking: if multiple candidates match, narrow them by qualifier specificity (see breakOverloadTie).
316
1/2
✓ Branch 144 → 145 taken 25543 times.
✗ Branch 144 → 221 not taken.
25543 breakOverloadTie(matches, reqArgs);
317
318 // Check if more than one function matches the requirements
319
2/2
✓ Branch 146 → 147 taken 1 time.
✓ Branch 146 → 174 taken 25542 times.
25543 if (matches.size() > 1) {
320
1/2
✓ Branch 147 → 148 taken 1 time.
✗ Branch 147 → 220 not taken.
1 std::stringstream errorMessage;
321
3/6
✓ Branch 148 → 149 taken 1 time.
✗ Branch 148 → 218 not taken.
✓ Branch 149 → 150 taken 1 time.
✗ Branch 149 → 218 not taken.
✓ Branch 150 → 151 taken 1 time.
✗ Branch 150 → 218 not taken.
1 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
322
2/2
✓ Branch 168 → 153 taken 2 times.
✓ Branch 168 → 169 taken 1 time.
4 for (const Function *match : matches)
323
3/6
✓ Branch 155 → 156 taken 2 times.
✗ Branch 155 → 211 not taken.
✓ Branch 156 → 157 taken 2 times.
✗ Branch 156 → 210 not taken.
✓ Branch 157 → 158 taken 2 times.
✗ Branch 157 → 208 not taken.
2 errorMessage << "\n " << match->getSignature();
324
2/4
✓ Branch 170 → 171 taken 1 time.
✗ Branch 170 → 215 not taken.
✓ Branch 171 → 172 taken 1 time.
✗ Branch 171 → 212 not taken.
1 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
325 1 }
326 25542 Function *matchedFunction = matches.front();
327 25542 matchedFunction->isNewlyInserted = false;
328
329 // Insert into cache
330
1/2
✓ Branch 175 → 176 taken 25542 times.
✗ Branch 175 → 221 not taken.
25542 lookupCache[cacheKey] = matchedFunction;
331
332 // Trigger revisit in type checker if required
333
1/2
✓ Branch 176 → 177 taken 25542 times.
✗ Branch 176 → 221 not taken.
25542 TypeChecker::requestRevisitIfRequired(matchedFunction);
334
335 // Return the very match
336 25542 return matchedFunction;
337 303810 }
338
339 3610886 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 3 → 4 taken 3436120 times.
✓ Branch 3 → 5 taken 174766 times.
3610886 if (!matchName(candidate, reqName))
345 3436120 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 6 → 7 not taken.
✓ Branch 6 → 8 taken 174766 times.
174766 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 9 → 10 taken 144267 times.
✓ Branch 9 → 11 taken 30498 times.
174766 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
353 144267 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 13 → 14 taken 1 time.
✓ Branch 13 → 15 taken 30497 times.
30498 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 30497 substantiateReturnType(candidate, typeMapping, callNode);
361
362 // Set the match scope to the scope of the concrete substantiation
363 30497 const QualType &thisType = candidate.thisType;
364
2/2
✓ Branch 17 → 18 taken 21231 times.
✓ Branch 17 → 25 taken 9266 times.
30497 if (!thisType.is(TY_DYN)) {
365 // If we only have the generic struct scope, lookup the concrete manifestation scope
366
2/2
✓ Branch 18 → 19 taken 199 times.
✓ Branch 18 → 23 taken 21032 times.
21231 if (matchScope->isGenericScope) {
367 199 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
368
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 199 times.
199 assert(spiceStruct != nullptr);
369 199 matchScope = spiceStruct->scope;
370 }
371
1/2
✓ Branch 23 → 24 taken 21231 times.
✗ Branch 23 → 27 not taken.
21231 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
372 }
373
374 30497 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 3610886 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 174766 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
397 bool strictQualifierMatching, const ASTNode *callNode) {
398 174766 QualType &candidateThisType = candidate.thisType;
399
400 // Shortcut for procedures
401
7/10
✓ Branch 2 → 3 taken 174766 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 112784 times.
✓ Branch 3 → 7 taken 61982 times.
✓ Branch 4 → 5 taken 112784 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 112784 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 112784 times.
✓ Branch 8 → 10 taken 61982 times.
174766 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
402 112784 return true;
403
404 // Give the type matcher a way to retrieve instances of GenericType by their name
405 136784 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
406 12820 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
407 61982 };
408
409 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
410
2/4
✓ Branch 11 → 12 taken 61982 times.
✗ Branch 11 → 21 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 61982 times.
61982 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 14 → 15 taken 61982 times.
✗ Branch 14 → 21 not taken.
✓ Branch 15 → 16 taken 13542 times.
✓ Branch 15 → 17 taken 48440 times.
61982 if (candidateThisType.hasAnyGenericParts())
416
1/2
✓ Branch 16 → 17 taken 13542 times.
✗ Branch 16 → 21 not taken.
13542 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
417
418 61982 return true;
419 61982 }
420
421 /**
422 * Checks if the matching candidate fulfills the argument types requirement
423 *
424 * @param candidate Matching candidate function
425 * @param reqArgs Requested argument types
426 * @param typeMapping Concrete template type mapping
427 * @param strictQualifierMatching Match qualifiers strictly
428 * @param needsSubstantiation We want to create a substantiation after successfully matching
429 * @param callNode Call AST node for printing error messages
430 * @return Fulfilled or not
431 */
432 174766 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
433 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
434 174766 std::vector<Param> &candidateParamList = candidate.paramList;
435
436 // If the number of arguments does not match with the number of params, the matching fails
437
6/6
✓ Branch 2 → 3 taken 174545 times.
✓ Branch 2 → 7 taken 221 times.
✓ Branch 5 → 6 taken 24835 times.
✓ Branch 5 → 7 taken 149710 times.
✓ Branch 8 → 9 taken 24835 times.
✓ Branch 8 → 10 taken 149931 times.
174766 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
438 24835 return false;
439 // In the case of a vararg function, we only disallow fewer arguments than parameters
440
4/6
✓ Branch 10 → 11 taken 221 times.
✓ Branch 10 → 15 taken 149710 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 221 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 149931 times.
149931 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
441 return false;
442
443 // Give the type matcher a way to retrieve instances of GenericType by their name
444 310886 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
445 11024 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
446 149931 };
447
448 // Loop over all parameters
449
2/2
✓ Branch 66 → 20 taken 154852 times.
✓ Branch 66 → 67 taken 30498 times.
185350 for (size_t i = 0; i < reqArgs.size(); i++) {
450 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
451 // but we need to modify the candidate param list to accept them
452
6/6
✓ Branch 20 → 21 taken 903 times.
✓ Branch 20 → 24 taken 153949 times.
✓ Branch 22 → 23 taken 243 times.
✓ Branch 22 → 24 taken 660 times.
✓ Branch 25 → 26 taken 243 times.
✓ Branch 25 → 29 taken 154609 times.
154852 if (candidate.isVararg && i >= candidateParamList.size()) {
453
2/4
✓ Branch 26 → 27 taken 243 times.
✗ Branch 26 → 71 not taken.
✓ Branch 27 → 28 taken 243 times.
✗ Branch 27 → 71 not taken.
243 candidateParamList.push_back(Param(reqArgs.at(i).first, false));
454 243 needsSubstantiation = true; // We need to modify the candidate param types
455 243 continue;
456 }
457
458 // Retrieve actual and requested types
459
2/4
✓ Branch 29 → 30 taken 154609 times.
✗ Branch 29 → 84 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 154609 times.
154609 assert(!candidateParamList.at(i).isOptional);
460
1/2
✓ Branch 32 → 33 taken 154609 times.
✗ Branch 32 → 84 not taken.
154609 QualType &candidateType = candidateParamList.at(i).qualType;
461
1/2
✓ Branch 33 → 34 taken 154609 times.
✗ Branch 33 → 84 not taken.
154609 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
462
463 // Check if the requested param type matches the candidate param type. The type mapping may be extended
464
3/4
✓ Branch 36 → 37 taken 154609 times.
✗ Branch 36 → 84 not taken.
✓ Branch 37 → 38 taken 119432 times.
✓ Branch 37 → 39 taken 35177 times.
154609 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
465 strictQualifierMatching))
466 119432 return false;
467
468 // Substantiate the candidate param type, based on the type mapping
469
3/4
✓ Branch 39 → 40 taken 35177 times.
✗ Branch 39 → 84 not taken.
✓ Branch 40 → 41 taken 9501 times.
✓ Branch 40 → 42 taken 25676 times.
35177 if (candidateType.hasAnyGenericParts())
470
1/2
✓ Branch 41 → 42 taken 9501 times.
✗ Branch 41 → 84 not taken.
9501 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
471
472 // Check if we try to bind a non-ref temporary to a non-const ref parameter
473
3/4
✓ Branch 42 → 43 taken 35177 times.
✗ Branch 42 → 84 not taken.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 54 taken 35176 times.
35177 if (!candidateType.canBind(requestedType, isArgTemporary)) {
474
1/2
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 53 not taken.
1 if (callNode)
475
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");
476 return false;
477 }
478
479 // If we have a function/procedure type we need to take care of the information, if it takes captures
480
9/12
✓ Branch 54 → 55 taken 35176 times.
✗ Branch 54 → 81 not taken.
✓ Branch 55 → 56 taken 35176 times.
✗ Branch 55 → 81 not taken.
✓ Branch 56 → 57 taken 43 times.
✓ Branch 56 → 60 taken 35133 times.
✓ Branch 57 → 58 taken 43 times.
✗ Branch 57 → 81 not taken.
✓ Branch 58 → 59 taken 7 times.
✓ Branch 58 → 60 taken 36 times.
✓ Branch 61 → 62 taken 7 times.
✓ Branch 61 → 64 taken 35169 times.
35176 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
481
1/2
✓ Branch 62 → 63 taken 7 times.
✗ Branch 62 → 83 not taken.
7 candidateType = candidateType.getWithLambdaCaptures();
482 7 needsSubstantiation = true;
483 }
484 }
485
486 30498 return true;
487 149931 }
488
489 /**
490 * Substantiates the candidate return type, based on the given type mapping
491 *
492 * @param candidate Matching candidate function
493 * @param typeMapping Concrete template type mapping
494 * @param callNode AST node for error messages
495 */
496 30497 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
497
2/2
✓ Branch 3 → 4 taken 3373 times.
✓ Branch 3 → 5 taken 27124 times.
30497 if (candidate.returnType.hasAnyGenericParts())
498 3373 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
499 30497 }
500
501 /**
502 * Searches the candidate template types for a generic type object with a certain name and return it
503 *
504 * @param candidate Matching candidate function
505 * @param templateTypeName Template type name
506 * @return Generic type object
507 */
508 23844 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
509 const std::string &templateTypeName) {
510
1/2
✓ Branch 19 → 4 taken 27059 times.
✗ Branch 19 → 20 not taken.
50903 for (const GenericType &templateType : candidate.templateTypes) {
511
3/4
✓ Branch 6 → 7 taken 27059 times.
✗ Branch 6 → 22 not taken.
✓ Branch 8 → 9 taken 23844 times.
✓ Branch 8 → 10 taken 3215 times.
27059 if (templateType.getSubType() == templateTypeName)
512 23844 return &templateType;
513 }
514 return nullptr;
515 }
516
517 /**
518 * Narrow a multi-match overload set by preferring the candidate whose parameter qualifiers most closely match
519 * the argument qualifiers. This resolves the typical copy-vs-move ctor ambiguity where both a `const T&`
520 * (copy) and a `T&` (move) ctor match a non-const lvalue argument - we prefer the non-const-ref candidate
521 * (move) since it requires no constification. When the argument is const, we prefer the const-ref candidate
522 * (copy) since binding to a non-const ref would require const-loss.
523 *
524 * Modifies `matches` in place, removing any candidate that scores worse than the best one. A no-op if there
525 * are fewer than two candidates.
526 *
527 * @param matches Candidate list to narrow
528 * @param reqArgs Argument list from the call site
529 */
530 25543 void FunctionManager::breakOverloadTie(std::vector<Function *> &matches, const ArgList &reqArgs) {
531
2/2
✓ Branch 3 → 4 taken 25534 times.
✓ Branch 3 → 5 taken 9 times.
25543 if (matches.size() < 2)
532 25534 return;
533
534 9 constexpr int CONSTIFY_PENALTY = 1;
535 9 constexpr int CONST_LOSS_PENALTY = 100;
536 // An exact type match is always preferred over a match that required an implicit upcast (struct to
537 // implemented interface, or struct to composed base struct). This keeps e.g. 'f(Derived)' preferred
538 // over 'f(Base)' when called with a Derived, instead of reporting an ambiguity.
539 9 constexpr int UPCAST_PENALTY = 1000;
540 45 const auto scoreSpecificity = [&](const Function *f) {
541 36 int penalty = 0;
542
2/2
✓ Branch 23 → 3 taken 36 times.
✓ Branch 23 → 24 taken 36 times.
72 for (size_t i = 0; i < std::min(reqArgs.size(), f->paramList.size()); i++) {
543
1/2
✓ Branch 3 → 4 taken 36 times.
✗ Branch 3 → 28 not taken.
36 const QualType &paramType = f->paramList.at(i).qualType;
544
1/2
✓ Branch 4 → 5 taken 36 times.
✗ Branch 4 → 28 not taken.
36 const QualType &argType = reqArgs.at(i).first;
545
2/4
✓ Branch 5 → 6 taken 36 times.
✗ Branch 5 → 26 not taken.
✓ Branch 6 → 7 taken 36 times.
✗ Branch 6 → 26 not taken.
36 const bool paramIsConst = paramType.removeReferenceWrapper().isConst();
546
2/4
✓ Branch 7 → 8 taken 36 times.
✗ Branch 7 → 27 not taken.
✓ Branch 8 → 9 taken 36 times.
✗ Branch 8 → 27 not taken.
36 const bool argIsConst = argType.removeReferenceWrapper().isConst();
547
4/4
✓ Branch 9 → 10 taken 12 times.
✓ Branch 9 → 12 taken 24 times.
✓ Branch 10 → 11 taken 8 times.
✓ Branch 10 → 12 taken 4 times.
36 if (paramIsConst && !argIsConst)
548 8 penalty += CONSTIFY_PENALTY;
549
4/4
✓ Branch 12 → 13 taken 24 times.
✓ Branch 12 → 15 taken 4 times.
✓ Branch 13 → 14 taken 4 times.
✓ Branch 13 → 15 taken 20 times.
28 else if (!paramIsConst && argIsConst)
550 4 penalty += CONST_LOSS_PENALTY;
551 // Penalize matches that were only possible through an implicit upcast
552 36 QualType paramBase = paramType;
553 36 QualType argBase = argType;
554
1/2
✓ Branch 15 → 16 taken 36 times.
✗ Branch 15 → 28 not taken.
36 QualType::unwrapBothWithRefWrappers(paramBase, argBase);
555
3/4
✓ Branch 16 → 17 taken 36 times.
✗ Branch 16 → 28 not taken.
✓ Branch 17 → 18 taken 4 times.
✓ Branch 17 → 19 taken 32 times.
36 if (!paramBase.matches(argBase, true, true, true))
556 4 penalty += UPCAST_PENALTY;
557 }
558 36 return penalty;
559 9 };
560
561 9 int bestScore = std::numeric_limits<int>::max();
562
2/2
✓ Branch 20 → 7 taken 18 times.
✓ Branch 20 → 21 taken 9 times.
36 for (const Function *m : matches)
563
1/2
✓ Branch 9 → 10 taken 18 times.
✗ Branch 9 → 46 not taken.
18 bestScore = std::min(bestScore, scoreSpecificity(m));
564 9 std::vector<Function *> filtered;
565
1/2
✓ Branch 22 → 23 taken 9 times.
✗ Branch 22 → 49 not taken.
9 filtered.reserve(matches.size());
566
2/2
✓ Branch 39 → 25 taken 18 times.
✓ Branch 39 → 40 taken 9 times.
36 for (Function *m : matches)
567
3/4
✓ Branch 27 → 28 taken 18 times.
✗ Branch 27 → 48 not taken.
✓ Branch 28 → 29 taken 10 times.
✓ Branch 28 → 30 taken 8 times.
18 if (scoreSpecificity(m) == bestScore)
568
1/2
✓ Branch 29 → 30 taken 10 times.
✗ Branch 29 → 48 not taken.
10 filtered.push_back(m);
569 9 matches = std::move(filtered);
570 9 }
571
572 /**
573 * Calculate the cache key for the function lookup cache
574 *
575 * @param scope Scope to match against
576 * @param name Function name requirement
577 * @param thisType This type requirement
578 * @param args Argument requirement
579 * @param templateTypes Template type requirement
580 * @return Cache key
581 */
582 390012 uint64_t FunctionManager::getCacheKey(const Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
583 const QualTypeList &templateTypes) {
584 390012 uint64_t hash = 0;
585 390012 hashCombine64(hash, hashPointer(scope));
586 390012 hashCombine64(hash, std::hash<std::string>{}(name));
587 390012 hashCombine64(hash, std::hash<QualType>{}(thisType));
588
2/2
✓ Branch 27 → 10 taken 611570 times.
✓ Branch 27 → 28 taken 390012 times.
2003164 for (const auto &[first, second] : args) {
589 611570 hashCombine64(hash, std::hash<QualType>{}(first));
590 611570 hashCombine64(hash, std::hash<bool>{}(second));
591 }
592 390012 hashCombine64(hash, hashVector(templateTypes));
593 390012 return hash;
594 }
595
596 10043 bool FunctionManager::hasCtor(const Scope *matchScope, CtorKind kind) {
597
5/8
✓ Branch 2 → 3 taken 10043 times.
✗ Branch 2 → 60 not taken.
✓ Branch 3 → 4 taken 10043 times.
✗ Branch 3 → 60 not taken.
✓ Branch 4 → 5 taken 10043 times.
✗ Branch 4 → 60 not taken.
✓ Branch 55 → 6 taken 47185 times.
✓ Branch 55 → 56 taken 6541 times.
53726 for (const auto &manifestations : matchScope->functions | std::views::values) {
598
5/8
✓ Branch 7 → 8 taken 47185 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 47185 times.
✗ Branch 8 → 59 not taken.
✓ Branch 9 → 10 taken 47185 times.
✗ Branch 9 → 59 not taken.
✓ Branch 51 → 11 taken 52115 times.
✓ Branch 51 → 52 taken 43683 times.
95798 for (const auto &function : manifestations | std::views::values) {
599 // If it is no ctor, skip it
600
3/4
✓ Branch 12 → 13 taken 52115 times.
✗ Branch 12 → 59 not taken.
✓ Branch 13 → 14 taken 33209 times.
✓ Branch 13 → 15 taken 18906 times.
52115 if (function.name != CTOR_FUNCTION_NAME)
601 33209 continue;
602 // Classify the ctor based on its parameter list
603
6/8
✓ Branch 16 → 17 taken 11603 times.
✓ Branch 16 → 25 taken 7303 times.
✓ Branch 17 → 18 taken 11603 times.
✗ Branch 17 → 58 not taken.
✓ Branch 18 → 19 taken 11603 times.
✗ Branch 18 → 58 not taken.
✓ Branch 19 → 20 taken 3946 times.
✓ Branch 19 → 25 taken 7657 times.
22852 const bool singleSelfRefParam = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
604
5/8
✓ Branch 20 → 21 taken 3946 times.
✗ Branch 20 → 58 not taken.
✓ Branch 21 → 22 taken 3946 times.
✗ Branch 21 → 58 not taken.
✓ Branch 22 → 23 taken 3946 times.
✗ Branch 22 → 58 not taken.
✓ Branch 23 → 24 taken 2208 times.
✓ Branch 23 → 25 taken 1738 times.
3946 function.paramList.at(0).qualType.getBase() == function.thisType;
605
6/8
✓ Branch 26 → 27 taken 2208 times.
✓ Branch 26 → 31 taken 16698 times.
✓ Branch 27 → 28 taken 2208 times.
✗ Branch 27 → 59 not taken.
✓ Branch 28 → 29 taken 2208 times.
✗ Branch 28 → 59 not taken.
✓ Branch 29 → 30 taken 2197 times.
✓ Branch 29 → 31 taken 11 times.
18906 const bool isCopyCtor = singleSelfRefParam && function.paramList.at(0).qualType.isConstRef();
606
6/8
✓ Branch 32 → 33 taken 2208 times.
✓ Branch 32 → 37 taken 16698 times.
✓ Branch 33 → 34 taken 2208 times.
✗ Branch 33 → 59 not taken.
✓ Branch 34 → 35 taken 2208 times.
✗ Branch 34 → 59 not taken.
✓ Branch 35 → 36 taken 11 times.
✓ Branch 35 → 37 taken 2197 times.
18906 const bool isMoveCtor = singleSelfRefParam && !function.paramList.at(0).qualType.isConstRef();
607
3/4
✓ Branch 38 → 39 taken 13714 times.
✓ Branch 38 → 42 taken 3283 times.
✓ Branch 38 → 45 taken 1909 times.
✗ Branch 38 → 49 not taken.
18906 switch (kind) {
608 13714 case CtorKind::COPY:
609
2/2
✓ Branch 39 → 40 taken 1674 times.
✓ Branch 39 → 41 taken 12040 times.
13714 if (isCopyCtor)
610 3502 return true;
611 12040 break;
612 3283 case CtorKind::MOVE:
613
2/2
✓ Branch 42 → 43 taken 4 times.
✓ Branch 42 → 44 taken 3279 times.
3283 if (isMoveCtor)
614 4 return true;
615 3279 break;
616 1909 case CtorKind::ANY_NON_COPY_NON_MOVE:
617
4/4
✓ Branch 45 → 46 taken 1826 times.
✓ Branch 45 → 48 taken 83 times.
✓ Branch 46 → 47 taken 1824 times.
✓ Branch 46 → 48 taken 2 times.
1909 if (!isCopyCtor && !isMoveCtor)
618 1824 return true;
619 85 break;
620 }
621 }
622 }
623 6541 return false;
624 }
625
626 1917 bool FunctionManager::hasAnyNonCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::ANY_NON_COPY_NON_MOVE); }
627
628 6194 bool FunctionManager::hasCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::COPY); }
629
630 1928 bool FunctionManager::hasUserCopyCtor(const Scope *matchScope) {
631
5/8
✓ Branch 2 → 3 taken 1928 times.
✗ Branch 2 → 42 not taken.
✓ Branch 3 → 4 taken 1928 times.
✗ Branch 3 → 42 not taken.
✓ Branch 4 → 5 taken 1928 times.
✗ Branch 4 → 42 not taken.
✓ Branch 37 → 6 taken 7037 times.
✓ Branch 37 → 38 taken 1740 times.
8777 for (const auto &manifestations : matchScope->functions | std::views::values) {
632
5/8
✓ Branch 7 → 8 taken 7037 times.
✗ Branch 7 → 41 not taken.
✓ Branch 8 → 9 taken 7037 times.
✗ Branch 8 → 41 not taken.
✓ Branch 9 → 10 taken 7037 times.
✗ Branch 9 → 41 not taken.
✓ Branch 34 → 11 taken 7835 times.
✓ Branch 34 → 35 taken 6849 times.
14684 for (const auto &function : manifestations | std::views::values) {
633
7/8
✓ Branch 12 → 13 taken 7835 times.
✗ Branch 12 → 41 not taken.
✓ Branch 13 → 14 taken 3129 times.
✓ Branch 13 → 15 taken 4706 times.
✓ Branch 14 → 15 taken 342 times.
✓ Branch 14 → 16 taken 2787 times.
✓ Branch 17 → 18 taken 5048 times.
✓ Branch 17 → 19 taken 2787 times.
7835 if (function.name != CTOR_FUNCTION_NAME || function.implicitDefault)
634 5048 continue;
635
6/8
✓ Branch 20 → 21 taken 1891 times.
✓ Branch 20 → 29 taken 896 times.
✓ Branch 21 → 22 taken 1891 times.
✗ Branch 21 → 40 not taken.
✓ Branch 22 → 23 taken 1891 times.
✗ Branch 22 → 40 not taken.
✓ Branch 23 → 24 taken 298 times.
✓ Branch 23 → 29 taken 1593 times.
3085 const bool isCopyCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isConstRef() &&
636
5/8
✓ Branch 24 → 25 taken 298 times.
✗ Branch 24 → 40 not taken.
✓ Branch 25 → 26 taken 298 times.
✗ Branch 25 → 40 not taken.
✓ Branch 26 → 27 taken 298 times.
✗ Branch 26 → 40 not taken.
✓ Branch 27 → 28 taken 188 times.
✓ Branch 27 → 29 taken 110 times.
298 function.paramList.at(0).qualType.getBase() == function.thisType;
637
2/2
✓ Branch 30 → 31 taken 188 times.
✓ Branch 30 → 32 taken 2599 times.
2787 if (isCopyCtor)
638 188 return true;
639 }
640 }
641 1740 return false;
642 }
643
644 1932 bool FunctionManager::hasMoveCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::MOVE); }
645
646 11970 Function *FunctionManager::findMoveCtor(Scope *matchScope) {
647
5/8
✓ Branch 2 → 3 taken 11970 times.
✗ Branch 2 → 41 not taken.
✓ Branch 3 → 4 taken 11970 times.
✗ Branch 3 → 41 not taken.
✓ Branch 4 → 5 taken 11970 times.
✗ Branch 4 → 41 not taken.
✓ Branch 36 → 6 taken 107650 times.
✓ Branch 36 → 37 taken 11923 times.
119573 for (auto &manifestations : matchScope->functions | std::views::values) {
648
5/8
✓ Branch 7 → 8 taken 107650 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 107650 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 107650 times.
✗ Branch 9 → 40 not taken.
✓ Branch 33 → 11 taken 141700 times.
✓ Branch 33 → 34 taken 107603 times.
249303 for (auto &function : manifestations | std::views::values) {
649
3/4
✓ Branch 12 → 13 taken 141700 times.
✗ Branch 12 → 40 not taken.
✓ Branch 13 → 14 taken 111510 times.
✓ Branch 13 → 15 taken 30190 times.
141700 if (function.name != CTOR_FUNCTION_NAME)
650 111510 continue;
651
4/6
✓ Branch 17 → 18 taken 20338 times.
✗ Branch 17 → 39 not taken.
✓ Branch 18 → 19 taken 20338 times.
✗ Branch 18 → 39 not taken.
✓ Branch 19 → 20 taken 10654 times.
✓ Branch 19 → 28 taken 9684 times.
50528 const bool isMoveCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
652
6/8
✓ Branch 16 → 17 taken 20338 times.
✓ Branch 16 → 28 taken 9852 times.
✓ Branch 20 → 21 taken 10654 times.
✗ Branch 20 → 39 not taken.
✓ Branch 21 → 22 taken 10654 times.
✗ Branch 21 → 39 not taken.
✓ Branch 22 → 23 taken 2808 times.
✓ Branch 22 → 28 taken 7846 times.
53336 !function.paramList.at(0).qualType.isConstRef() &&
653
5/8
✓ Branch 23 → 24 taken 2808 times.
✗ Branch 23 → 39 not taken.
✓ Branch 24 → 25 taken 2808 times.
✗ Branch 24 → 39 not taken.
✓ Branch 25 → 26 taken 2808 times.
✗ Branch 25 → 39 not taken.
✓ Branch 26 → 27 taken 47 times.
✓ Branch 26 → 28 taken 2761 times.
2808 function.paramList.at(0).qualType.getBase() == function.thisType;
654
2/2
✓ Branch 29 → 30 taken 47 times.
✓ Branch 29 → 31 taken 30143 times.
30190 if (isMoveCtor)
655 47 return &function;
656 }
657 }
658 11923 return nullptr;
659 }
660
661 15109 bool FunctionManager::hasDtor(const Scope *matchScope) {
662
5/8
✓ Branch 2 → 3 taken 15109 times.
✗ Branch 2 → 24 not taken.
✓ Branch 3 → 4 taken 15109 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 15109 times.
✗ Branch 4 → 24 not taken.
✓ Branch 20 → 6 taken 119357 times.
✓ Branch 20 → 21 taken 9122 times.
128479 for (const auto &manifestations : matchScope->functions | std::views::values)
663
5/8
✓ Branch 7 → 8 taken 119357 times.
✗ Branch 7 → 23 not taken.
✓ Branch 8 → 9 taken 119357 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 119357 times.
✗ Branch 9 → 23 not taken.
✓ Branch 17 → 11 taken 137018 times.
✓ Branch 17 → 18 taken 113370 times.
250388 for (const auto &function : manifestations | std::views::values)
664
3/4
✓ Branch 12 → 13 taken 137018 times.
✗ Branch 12 → 23 not taken.
✓ Branch 13 → 14 taken 5987 times.
✓ Branch 13 → 15 taken 131031 times.
137018 if (function.name == DTOR_FUNCTION_NAME)
665 5987 return true;
666 9122 return false;
667 }
668
669 /**
670 * Clear the lookup cache
671 */
672 533 void FunctionManager::cleanup() {
673 533 lookupCache.clear();
674 533 lookupCacheHits = 0;
675 533 lookupCacheMisses = 0;
676 533 }
677
678 /**
679 * Dump usage statistics for the lookup cache
680 */
681 296 std::string FunctionManager::dumpLookupCacheStatistics() {
682
1/2
✓ Branch 2 → 3 taken 296 times.
✗ Branch 2 → 22 not taken.
296 std::stringstream stats;
683
2/4
✓ Branch 3 → 4 taken 296 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 296 times.
✗ Branch 4 → 20 not taken.
296 stats << "FunctionManager lookup cache statistics:" << std::endl;
684
3/6
✓ Branch 5 → 6 taken 296 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 296 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 296 times.
✗ Branch 8 → 20 not taken.
296 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
685
3/6
✓ Branch 9 → 10 taken 296 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 296 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 296 times.
✗ Branch 11 → 20 not taken.
296 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
686
3/6
✓ Branch 12 → 13 taken 296 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 296 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 296 times.
✗ Branch 14 → 20 not taken.
296 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
687
1/2
✓ Branch 15 → 16 taken 296 times.
✗ Branch 15 → 20 not taken.
592 return stats.str();
688 296 }
689
690 } // namespace spice::compiler
691