GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.1% 344 / 0 / 358
Functions: 100.0% 30 / 0 / 30
Branches: 64.7% 461 / 0 / 712

src/typechecker/FunctionManager.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "FunctionManager.h"
4
5 #include <algorithm>
6 #include <limits>
7
8 #include <ast/ASTNodes.h>
9 #include <exception/SemanticError.h>
10 #include <model/GenericType.h>
11 #include <symboltablebuilder/Scope.h>
12 #include <symboltablebuilder/SymbolTableBuilder.h>
13 #include <typechecker/TypeChecker.h>
14 #include <typechecker/TypeMatcher.h>
15 #include <util/CodeLoc.h>
16 #include <util/Concurrency.h>
17 #include <util/CustomHashFunctions.h>
18
19 namespace spice::compiler {
20
21 // Static member initialization
22 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
23 size_t FunctionManager::lookupCacheHits = 0;
24 size_t FunctionManager::lookupCacheMisses = 0;
25
26 40259 Function *FunctionManager::insert(Scope *insertScope, const Function &baseFunction, std::vector<Function *> *nodeFunctionList) {
27 // Open a new manifestation list for the function definition
28
3/6
✓ Branch 2 → 3 taken 40259 times.
✗ Branch 2 → 49 not taken.
✓ Branch 3 → 4 taken 40259 times.
✗ Branch 3 → 46 not taken.
✓ Branch 4 → 5 taken 40259 times.
✗ Branch 4 → 44 not taken.
40259 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
29
1/2
✓ Branch 8 → 9 taken 40259 times.
✗ Branch 8 → 50 not taken.
40259 insertScope->functions.emplace(fctId, FunctionManifestationList());
30
31 // Collect substantiations
32 40259 std::vector<Function> manifestations;
33
1/2
✓ Branch 10 → 11 taken 40259 times.
✗ Branch 10 → 54 not taken.
40259 substantiateOptionalParams(baseFunction, manifestations);
34
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 40259 times.
40259 assert(!manifestations.empty());
35
36 // Save substantiations in declaration node
37 40259 Function *manifestationPtr = nullptr;
38
2/2
✓ Branch 32 → 16 taken 42529 times.
✓ Branch 32 → 33 taken 40257 times.
123045 for (const Function &manifestation : manifestations) {
39
2/2
✓ Branch 18 → 19 taken 42527 times.
✓ Branch 18 → 53 taken 2 times.
42529 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
40
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 42527 times.
42527 assert(manifestationPtr != nullptr);
41
1/2
✓ Branch 21 → 22 taken 42527 times.
✗ Branch 21 → 23 not taken.
42527 if (nodeFunctionList)
42
1/2
✓ Branch 22 → 23 taken 42527 times.
✗ Branch 22 → 53 not taken.
42527 nodeFunctionList->push_back(manifestationPtr);
43 }
44
45
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 40257 times.
40257 if (!nodeFunctionList)
46 return manifestationPtr;
47
48
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 40257 times.
40257 assert(!nodeFunctionList->empty());
49 40257 return nodeFunctionList->front();
50 40261 }
51
52 /**
53 * Create definite functions from ambiguous ones, in regard to optional arguments.
54 *
55 * Example:
56 * int testFunc(string, int?, double?)
57 * gets
58 * int testFunc(string)
59 * int testFunc(string, int)
60 * int testFunc(string, int, double)
61 *
62 * This method also accepts functions, that are already definite, but does nothing to them.
63 *
64 * @param baseFunction Ambiguous base function
65 * @param manifestations Vector to store the definite manifestations
66 * @return True, if there were optional arguments found
67 */
68 40259 void FunctionManager::substantiateOptionalParams(const Function &baseFunction, std::vector<Function> &manifestations) {
69 // Handle the case of no parameters -> simply return the base function
70
2/2
✓ Branch 3 → 4 taken 10569 times.
✓ Branch 3 → 6 taken 29690 times.
40259 if (baseFunction.paramList.empty()) {
71
1/2
✓ Branch 4 → 5 taken 10569 times.
✗ Branch 4 → 47 not taken.
10569 manifestations.push_back(baseFunction);
72 10569 return;
73 }
74
75 29690 ParamList currentFunctionParamTypes;
76
1/2
✓ Branch 7 → 8 taken 29690 times.
✗ Branch 7 → 45 not taken.
29690 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
77 29690 bool metFirstOptionalParam = false;
78
1/2
✓ Branch 8 → 9 taken 29690 times.
✗ Branch 8 → 45 not taken.
29690 Function manifestation = baseFunction;
79
80 // Loop over all parameters
81
2/2
✓ Branch 32 → 11 taken 43976 times.
✓ Branch 32 → 33 taken 29690 times.
103356 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
82 // Check if we have a mandatory parameter
83
2/2
✓ Branch 13 → 14 taken 41706 times.
✓ Branch 13 → 16 taken 2270 times.
43976 if (!isOptional) {
84
1/2
✓ Branch 14 → 15 taken 41706 times.
✗ Branch 14 → 40 not taken.
41706 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
85 41706 continue;
86 }
87
88 // Add substantiation without the optional parameter
89
2/2
✓ Branch 16 → 17 taken 2156 times.
✓ Branch 16 → 20 taken 114 times.
2270 if (!metFirstOptionalParam) {
90
1/2
✓ Branch 17 → 18 taken 2156 times.
✗ Branch 17 → 42 not taken.
2156 manifestation.paramList = currentFunctionParamTypes;
91
1/2
✓ Branch 18 → 19 taken 2156 times.
✗ Branch 18 → 42 not taken.
2156 manifestations.push_back(manifestation);
92 // Now we cannot accept mandatory parameters anymore
93 2156 metFirstOptionalParam = true;
94 }
95
96 // Add substantiation with the optional parameter
97
1/2
✓ Branch 20 → 21 taken 2270 times.
✗ Branch 20 → 41 not taken.
2270 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
98
1/2
✓ Branch 21 → 22 taken 2270 times.
✗ Branch 21 → 42 not taken.
2270 manifestation.paramList = currentFunctionParamTypes;
99
1/2
✓ Branch 22 → 23 taken 2270 times.
✗ Branch 22 → 42 not taken.
2270 manifestations.push_back(manifestation);
100 }
101
102 // Ensure at least once manifestation
103
2/2
✓ Branch 34 → 35 taken 27534 times.
✓ Branch 34 → 36 taken 2156 times.
29690 if (manifestations.empty())
104
1/2
✓ Branch 35 → 36 taken 27534 times.
✗ Branch 35 → 43 not taken.
27534 manifestations.push_back(baseFunction);
105 29690 }
106
107 6 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
108 6 ParamList paramList;
109
2/2
✓ Branch 16 → 4 taken 2 times.
✓ Branch 16 → 17 taken 6 times.
14 for (const QualType &paramType : paramTypes)
110
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 33 not taken.
2 paramList.push_back({paramType, false});
111
5/10
✓ Branch 19 → 20 taken 6 times.
✗ Branch 19 → 45 not taken.
✓ Branch 20 → 21 taken 6 times.
✗ Branch 20 → 42 not taken.
✓ Branch 21 → 22 taken 6 times.
✗ Branch 21 → 41 not taken.
✓ Branch 22 → 23 taken 6 times.
✗ Branch 22 → 40 not taken.
✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 35 not taken.
18 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
112 6 }
113
114 53547 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
115
2/4
✓ Branch 2 → 3 taken 53547 times.
✗ Branch 2 → 70 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 53547 times.
53547 assert(newManifestation.hasSubstantiatedParams());
116
117
1/2
✓ Branch 5 → 6 taken 53547 times.
✗ Branch 5 → 70 not taken.
53547 const std::string signature = newManifestation.getSignature(true, true, false, true);
118
119 // Check if the function exists already
120
5/8
✓ Branch 6 → 7 taken 53547 times.
✗ Branch 6 → 59 not taken.
✓ Branch 7 → 8 taken 53547 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 53547 times.
✗ Branch 8 → 59 not taken.
✓ Branch 30 → 10 taken 896811 times.
✓ Branch 30 → 31 taken 53545 times.
950356 for (const auto &manifestations : insertScope->functions | std::views::values) {
121
3/4
✓ Branch 11 → 12 taken 896811 times.
✗ Branch 11 → 59 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 28 taken 896809 times.
896811 if (manifestations.contains(signature)) {
122
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;
123
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");
124 }
125 }
126
127 // Retrieve the matching manifestation list of the scope
128
3/6
✓ Branch 31 → 32 taken 53545 times.
✗ Branch 31 → 65 not taken.
✓ Branch 32 → 33 taken 53545 times.
✗ Branch 32 → 62 not taken.
✓ Branch 33 → 34 taken 53545 times.
✗ Branch 33 → 60 not taken.
53545 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
129
2/4
✓ Branch 36 → 37 taken 53545 times.
✗ Branch 36 → 66 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 53545 times.
53545 assert(insertScope->functions.contains(fctId));
130
1/2
✓ Branch 39 → 40 taken 53545 times.
✗ Branch 39 → 66 not taken.
53545 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
131
132 // Add substantiated function
133
1/2
✓ Branch 40 → 41 taken 53545 times.
✗ Branch 40 → 66 not taken.
53545 manifestationList.emplace(signature, newManifestation);
134
1/2
✓ Branch 41 → 42 taken 53545 times.
✗ Branch 41 → 66 not taken.
107090 return &manifestationList.at(signature);
135 53547 }
136
137 /**
138 * Checks if a function exists by matching it, but not setting it to used
139 *
140 * @param matchScope Scope to match against
141 * @param reqName Function name requirement
142 * @param reqThisType This type requirement
143 * @param reqArgs Argument requirement
144 * @param strictQualifierMatching Match argument and this type qualifiers strictly
145 * @return Found function or nullptr
146 */
147 41270 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
148 const ArgList &reqArgs, bool strictQualifierMatching) {
149
2/4
✓ Branch 2 → 3 taken 41270 times.
✗ Branch 2 → 66 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 41270 times.
41270 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
150
151 // Unlike match(), lookup() is also called from the IR generator (e.g. to find a copy ctor), so it can run on multiple
152 // threads at once and has to guard the process-wide lookup machinery.
153
1/2
✓ Branch 5 → 6 taken 41270 times.
✗ Branch 5 → 78 not taken.
41270 const ConditionalLock lock(symbolRegistryMutex);
154
155 // Do cache lookup
156 41270 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
157
3/4
✓ Branch 9 → 10 taken 41270 times.
✗ Branch 9 → 67 not taken.
✓ Branch 12 → 13 taken 8463 times.
✓ Branch 12 → 15 taken 32807 times.
41270 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
158 8463 lookupCacheHits++;
159 8463 return it->second;
160 }
161 32807 lookupCacheMisses++;
162
163 11027 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
164
4/8
✓ Branch 15 → 16 taken 32807 times.
✗ Branch 15 → 76 not taken.
✓ Branch 16 → 17 taken 32807 times.
✗ Branch 16 → 20 not taken.
✓ Branch 17 → 18 taken 32807 times.
✗ Branch 17 → 76 not taken.
✓ Branch 18 → 19 taken 32807 times.
✗ Branch 18 → 20 not taken.
32807 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
165
166 // Loop over function registry to find functions, that match the requirements of the call
167 32807 std::vector<const Function *> matches;
168
2/2
✓ Branch 55 → 23 taken 229891 times.
✓ Branch 55 → 56 taken 32807 times.
262698 for (const auto &[defCodeLocStr, manifestations] : matchScope->functions) {
169
2/2
✓ Branch 52 → 28 taken 264921 times.
✓ Branch 52 → 53 taken 102905 times.
367826 for (const auto &[signature, presetFunction] : manifestations) {
170
2/4
✓ Branch 31 → 32 taken 264921 times.
✗ Branch 31 → 71 not taken.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 264921 times.
264921 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
171
172 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
173 // - search for generic fct: Only match against generic preset functions
174
3/4
✓ Branch 34 → 35 taken 264921 times.
✗ Branch 34 → 71 not taken.
✓ Branch 35 → 36 taken 109629 times.
✓ Branch 35 → 37 taken 155292 times.
264921 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
175 137935 continue;
176
177 // Copy the function to be able to substantiate types
178
1/2
✓ Branch 37 → 38 taken 155292 times.
✗ Branch 37 → 71 not taken.
155292 Function candidate = presetFunction;
179
180 // Create empty type mapping
181 155292 TypeMapping &typeMapping = candidate.typeMapping;
182
183 155292 bool forceSubstantiation = false;
184
1/2
✓ Branch 38 → 39 taken 155292 times.
✗ Branch 38 → 69 not taken.
155292 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
185 strictQualifierMatching, forceSubstantiation, nullptr);
186
2/2
✓ Branch 39 → 40 taken 117247 times.
✓ Branch 39 → 41 taken 38045 times.
155292 if (matchResult == MatchResult::SKIP_FUNCTION)
187 117247 break; // Leave the whole function
188
2/2
✓ Branch 41 → 42 taken 28306 times.
✓ Branch 41 → 43 taken 9739 times.
38045 if (matchResult == MatchResult::SKIP_MANIFESTATION)
189 28306 continue; // Leave this manifestation and try the next one
190
191 // Add to matches
192
3/6
✓ Branch 43 → 44 taken 9739 times.
✗ Branch 43 → 68 not taken.
✓ Branch 44 → 45 taken 9739 times.
✗ Branch 44 → 68 not taken.
✓ Branch 45 → 46 taken 9739 times.
✗ Branch 45 → 68 not taken.
9739 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
193
194 9739 break; // Leave the whole manifestation list to not double-match the manifestation
195
2/2
✓ Branch 48 → 49 taken 28306 times.
✓ Branch 48 → 50 taken 126986 times.
155292 }
196 }
197
198 // Return the very match or a nullptr
199
2/2
✓ Branch 57 → 58 taken 9739 times.
✓ Branch 57 → 60 taken 23068 times.
32807 return !matches.empty() ? matches.front() : nullptr;
200 41270 }
201
202 /**
203 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
204 * If more than one function matches the requirement, an error gets thrown.
205 *
206 * @param matchScope Scope to match against
207 * @param reqName Function name requirement
208 * @param reqThisType This type requirement
209 * @param reqArgs Argument requirement
210 * @param templateTypeHints Template type requirement
211 * @param strictQualifierMatching Match argument and this type qualifiers strictly
212 * @param callNode Call AST node for printing error messages
213 * @return Matched function or nullptr
214 */
215 431124 Function *FunctionManager::match(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
216 const ArgList &reqArgs, const QualTypeList &templateTypeHints, bool strictQualifierMatching,
217 const ASTNode *callNode) {
218
2/4
✓ Branch 2 → 3 taken 431124 times.
✗ Branch 2 → 181 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 431124 times.
431124 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
219
220 // Do cache lookup
221 431124 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
222
3/4
✓ Branch 6 → 7 taken 431124 times.
✗ Branch 6 → 182 not taken.
✓ Branch 9 → 10 taken 59525 times.
✓ Branch 9 → 12 taken 371599 times.
431124 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
223 59525 lookupCacheHits++;
224 59525 return it->second;
225 }
226 371599 lookupCacheMisses++;
227
228 // Loop over function registry to find functions, that match the requirements of the call
229 371599 std::vector<Function *> matches;
230
2/2
✓ Branch 140 → 14 taken 4121451 times.
✓ Branch 140 → 141 taken 371598 times.
4493049 for (auto &[fctId, manifestations] : matchScope->functions) {
231
2/2
✓ Branch 137 → 19 taken 4226436 times.
✓ Branch 137 → 138 taken 162748 times.
4389184 for (const auto &[signature, presetFunction] : manifestations) {
232
2/4
✓ Branch 22 → 23 taken 4226436 times.
✗ Branch 22 → 205 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 4226436 times.
4226436 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
233
234 // Skip generic and newly inserted substantiations to prevent double matching of a function
235
6/8
✓ Branch 25 → 26 taken 4226436 times.
✗ Branch 25 → 205 not taken.
✓ Branch 26 → 27 taken 4126074 times.
✓ Branch 26 → 28 taken 100362 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 4126074 times.
✓ Branch 30 → 31 taken 100362 times.
✓ Branch 30 → 32 taken 4126074 times.
4226436 if (presetFunction.isGenericSubstantiation() || presetFunction.isNewlyInserted)
236 267733 continue;
237
238 // Copy the function to be able to substantiate types
239
1/2
✓ Branch 32 → 33 taken 4126074 times.
✗ Branch 32 → 205 not taken.
4126074 Function candidate = presetFunction;
240
241 // Prepare type mapping, based on the given initial type mapping
242 4126074 TypeMapping &typeMapping = candidate.typeMapping;
243 4126074 typeMapping.clear();
244
2/2
✓ Branch 43 → 35 taken 53809 times.
✓ Branch 43 → 44 taken 4126074 times.
4179883 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
245
2/4
✓ Branch 35 → 36 taken 53809 times.
✗ Branch 35 → 203 not taken.
✓ Branch 36 → 37 taken 53809 times.
✗ Branch 36 → 203 not taken.
53809 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
246
1/2
✓ Branch 37 → 38 taken 53809 times.
✗ Branch 37 → 203 not taken.
53809 const QualType &templateType = templateTypeHints.at(i);
247
1/2
✓ Branch 38 → 39 taken 53809 times.
✗ Branch 38 → 203 not taken.
53809 typeMapping.emplace(typeName, templateType);
248 }
249
250 4126074 bool forceSubstantiation = false;
251
2/2
✓ Branch 44 → 45 taken 4126073 times.
✓ Branch 44 → 203 taken 1 time.
4126074 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
252 strictQualifierMatching, forceSubstantiation, callNode);
253
2/2
✓ Branch 45 → 46 taken 3946680 times.
✓ Branch 45 → 47 taken 179393 times.
4126073 if (matchResult == MatchResult::SKIP_FUNCTION)
254 3946680 break; // Leave the whole function
255
2/2
✓ Branch 47 → 48 taken 150527 times.
✓ Branch 47 → 49 taken 28866 times.
179393 if (matchResult == MatchResult::SKIP_MANIFESTATION)
256 150527 continue; // Leave this manifestation and try the next one
257
258 // We found a match! -> Set the actual candidate and its entry to used
259 28866 candidate.used = true;
260 28866 candidate.entry->used = true;
261
262 // Check if the function is generic needs to be substantiated
263
6/6
✓ Branch 50 → 51 taken 17089 times.
✓ Branch 50 → 53 taken 11777 times.
✓ Branch 51 → 52 taken 16844 times.
✓ Branch 51 → 53 taken 245 times.
✓ Branch 54 → 55 taken 16844 times.
✓ Branch 54 → 67 taken 12022 times.
28866 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
264
5/10
✓ Branch 55 → 56 taken 16844 times.
✗ Branch 55 → 183 not taken.
✓ Branch 56 → 57 taken 16844 times.
✗ Branch 56 → 61 not taken.
✓ Branch 57 → 58 taken 16844 times.
✗ Branch 57 → 183 not taken.
✓ Branch 58 → 59 taken 16844 times.
✗ Branch 58 → 183 not taken.
✓ Branch 59 → 60 taken 16844 times.
✗ Branch 59 → 61 not taken.
16844 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
265
2/4
✓ Branch 62 → 63 taken 16844 times.
✗ Branch 62 → 183 not taken.
✓ Branch 63 → 64 taken 16844 times.
✗ Branch 63 → 183 not taken.
16844 Function *match = &matchScope->functions.at(fctId).at(signature);
266 16844 match->used = true;
267
1/2
✓ Branch 64 → 65 taken 16844 times.
✗ Branch 64 → 183 not taken.
16844 matches.push_back(match);
268 16844 continue; // Match was successful -> match the next function
269 16844 }
270
271 // Check if we already have this manifestation and can simply re-use it
272
1/2
✓ Branch 67 → 68 taken 12022 times.
✗ Branch 67 → 203 not taken.
12022 const std::string newSignature = candidate.getSignature(true, true, false, true);
273
3/4
✓ Branch 68 → 69 taken 12022 times.
✗ Branch 68 → 185 not taken.
✓ Branch 71 → 72 taken 1004 times.
✓ Branch 71 → 76 taken 11018 times.
12022 if (const auto it = manifestations.find(newSignature); it != manifestations.end()) {
274 1004 it->second.used = true;
275
1/2
✓ Branch 74 → 75 taken 1004 times.
✗ Branch 74 → 184 not taken.
1004 matches.push_back(&it->second);
276 1004 break; // Leave the whole manifestation list to not double-match the manifestation
277 }
278
279 // Insert the substantiated version if required
280
1/2
✓ Branch 76 → 78 taken 11018 times.
✗ Branch 76 → 201 not taken.
11018 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
281
2/4
✓ Branch 78 → 79 taken 11018 times.
✗ Branch 78 → 201 not taken.
✓ Branch 79 → 80 taken 11018 times.
✗ Branch 79 → 201 not taken.
11018 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
282 11018 substantiatedFunction->alreadyTypeChecked = false;
283
2/4
✓ Branch 80 → 81 taken 11018 times.
✗ Branch 80 → 201 not taken.
✓ Branch 81 → 82 taken 11018 times.
✗ Branch 81 → 201 not taken.
11018 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
284 11018 substantiatedFunction->isNewlyInserted = true; // To not iterate over it in the same matching
285
286 // Copy function entry
287
1/2
✓ Branch 82 → 83 taken 11018 times.
✗ Branch 82 → 201 not taken.
11018 const std::string newScopeName = substantiatedFunction->getScopeName();
288
1/2
✓ Branch 83 → 84 taken 11018 times.
✗ Branch 83 → 199 not taken.
11018 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
289
1/2
✓ Branch 86 → 87 taken 11018 times.
✗ Branch 86 → 199 not taken.
11018 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newScopeName);
290
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 11018 times.
11018 assert(substantiatedFunction->entry != nullptr);
291
292 // Copy function scope
293
1/2
✓ Branch 89 → 90 taken 11018 times.
✗ Branch 89 → 199 not taken.
11018 const std::string oldScopeName = presetFunction.getScopeName();
294
1/2
✓ Branch 90 → 91 taken 11018 times.
✗ Branch 90 → 197 not taken.
11018 Scope *childScope = matchScope->copyChildScope(oldScopeName, newScopeName);
295
1/2
✗ Branch 91 → 92 not taken.
✓ Branch 91 → 93 taken 11018 times.
11018 assert(childScope != nullptr);
296 11018 childScope->isGenericScope = false;
297 11018 substantiatedFunction->bodyScope = childScope;
298
299 // Insert symbols for generic type names with concrete types into the child block
300
2/2
✓ Branch 103 → 95 taken 13898 times.
✓ Branch 103 → 104 taken 11018 times.
24916 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
301
2/4
✓ Branch 98 → 99 taken 13898 times.
✗ Branch 98 → 188 not taken.
✓ Branch 99 → 100 taken 13898 times.
✗ Branch 99 → 186 not taken.
13898 childScope->insertGenericType(typeName, GenericType(concreteType));
302
303 // Substantiate the 'this' entry in the new function scope
304
6/6
✓ Branch 107 → 108 taken 9105 times.
✓ Branch 107 → 111 taken 1913 times.
✓ Branch 109 → 110 taken 9103 times.
✓ Branch 109 → 111 taken 2 times.
✓ Branch 112 → 113 taken 9103 times.
✓ Branch 112 → 126 taken 1915 times.
11018 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
305
1/2
✓ Branch 115 → 116 taken 9103 times.
✗ Branch 115 → 192 not taken.
27309 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
306
1/2
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 123 taken 9103 times.
9103 assert(thisEntry != nullptr);
307
2/4
✓ Branch 123 → 124 taken 9103 times.
✗ Branch 123 → 196 not taken.
✓ Branch 124 → 125 taken 9103 times.
✗ Branch 124 → 196 not taken.
9103 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
308 }
309
310 // Add to matched functions
311
1/2
✓ Branch 126 → 127 taken 11018 times.
✗ Branch 126 → 197 not taken.
11018 matches.push_back(substantiatedFunction);
312
313 11018 break; // Leave the whole manifestation list to not double-match the manifestation
314
2/2
✓ Branch 133 → 134 taken 167371 times.
✓ Branch 133 → 135 taken 3958702 times.
4138096 }
315 }
316
317 // If no matches were found, return a nullptr
318
2/2
✓ Branch 142 → 143 taken 342741 times.
✓ Branch 142 → 144 taken 28857 times.
371598 if (matches.empty())
319 342741 return nullptr;
320
321 // Tie-breaking: if multiple candidates match, narrow them by qualifier specificity and by preferring
322 // explicitly declared overloads over generic substitutions (see breakOverloadTie).
323
1/2
✓ Branch 144 → 145 taken 28857 times.
✗ Branch 144 → 221 not taken.
28857 breakOverloadTie(matches, reqArgs);
324
325 // Check if more than one function matches the requirements
326
1/2
✗ Branch 146 → 147 not taken.
✓ Branch 146 → 174 taken 28857 times.
28857 if (matches.size() > 1) {
327 std::stringstream errorMessage;
328 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
329 for (const Function *match : matches)
330 errorMessage << "\n " << match->getSignature();
331 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
332 }
333 28857 Function *matchedFunction = matches.front();
334 28857 matchedFunction->isNewlyInserted = false;
335
336 // Insert into cache
337
1/2
✓ Branch 175 → 176 taken 28857 times.
✗ Branch 175 → 221 not taken.
28857 lookupCache[cacheKey] = matchedFunction;
338
339 // Trigger revisit in type checker if required
340
1/2
✓ Branch 176 → 177 taken 28857 times.
✗ Branch 176 → 221 not taken.
28857 TypeChecker::requestRevisitIfRequired(matchedFunction);
341
342 // Return the very match
343 28857 return matchedFunction;
344 371599 }
345
346 4281366 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
347 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
348 bool strictQualifierMatching, bool &forceSubstantiation,
349 const ASTNode *callNode) {
350 // Check name requirement
351
2/2
✓ Branch 3 → 4 taken 4063927 times.
✓ Branch 3 → 5 taken 217439 times.
4281366 if (!matchName(candidate, reqName))
352 4063927 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
353
354 // Check 'this' type requirement
355
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 217439 times.
217439 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
356 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
357
358 // Check arg types requirement
359
2/2
✓ Branch 9 → 10 taken 178831 times.
✓ Branch 9 → 11 taken 38607 times.
217439 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
360 178831 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
361
362 // Check if there are unresolved generic types
363
2/2
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 38605 times.
38607 if (typeMapping.size() < candidate.templateTypes.size())
364 2 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
365
366 // Substantiate return type
367 38605 substantiateReturnType(candidate, typeMapping, callNode);
368
369 // Set the match scope to the scope of the concrete substantiation
370 38605 const QualType &thisType = candidate.thisType;
371
2/2
✓ Branch 17 → 18 taken 28686 times.
✓ Branch 17 → 25 taken 9919 times.
38605 if (!thisType.is(TY_DYN)) {
372 // If we only have the generic struct scope, lookup the concrete manifestation scope
373
2/2
✓ Branch 18 → 19 taken 18 times.
✓ Branch 18 → 23 taken 28668 times.
28686 if (matchScope->isGenericScope) {
374 18 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
375
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 18 times.
18 assert(spiceStruct != nullptr);
376 18 matchScope = spiceStruct->scope;
377 }
378
1/2
✓ Branch 23 → 24 taken 28686 times.
✗ Branch 23 → 27 not taken.
28686 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
379 }
380
381 38605 return MatchResult::MATCHED;
382 }
383
384 /**
385 * Checks if the matching candidate fulfills the name requirement
386 *
387 * @param candidate Matching candidate function
388 * @param reqName Requested function name
389 * @return Fulfilled or not
390 */
391 4281366 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
392
393 /**
394 * Checks if the matching candidate fulfills the 'this' type requirement
395 *
396 * @param candidate Matching candidate function
397 * @param reqThisType Requested 'this' type
398 * @param typeMapping Concrete template type mapping
399 * @param strictQualifierMatching Match qualifiers strictly
400 * @param callNode Call AST node for printing error messages
401 * @return Fulfilled or not
402 */
403 217439 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
404 bool strictQualifierMatching, const ASTNode *callNode) {
405 217439 QualType &candidateThisType = candidate.thisType;
406
407 // Shortcut for procedures
408
7/10
✓ Branch 2 → 3 taken 217439 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 140846 times.
✓ Branch 3 → 7 taken 76593 times.
✓ Branch 4 → 5 taken 140846 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 140846 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 140846 times.
✓ Branch 8 → 10 taken 76593 times.
217439 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
409 140846 return true;
410
411 // Give the type matcher a way to retrieve instances of GenericType by their name
412 165415 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
413 12229 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
414 76593 };
415
416 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
417
2/4
✓ Branch 11 → 12 taken 76593 times.
✗ Branch 11 → 21 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 76593 times.
76593 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
418 strictQualifierMatching))
419 return false;
420
421 // Substantiate the candidate param type, based on the type mapping
422
3/4
✓ Branch 14 → 15 taken 76593 times.
✗ Branch 14 → 21 not taken.
✓ Branch 15 → 16 taken 13227 times.
✓ Branch 15 → 17 taken 63366 times.
76593 if (candidateThisType.hasAnyGenericParts())
423
1/2
✓ Branch 16 → 17 taken 13227 times.
✗ Branch 16 → 21 not taken.
13227 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
424
425 76593 return true;
426 76593 }
427
428 /**
429 * Checks if the matching candidate fulfills the argument types requirement
430 *
431 * @param candidate Matching candidate function
432 * @param reqArgs Requested argument types
433 * @param typeMapping Concrete template type mapping
434 * @param strictQualifierMatching Match qualifiers strictly
435 * @param needsSubstantiation We want to create a substantiation after successfully matching
436 * @param callNode Call AST node for printing error messages
437 * @return Fulfilled or not
438 */
439 217439 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
440 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
441 217439 std::vector<Param> &candidateParamList = candidate.paramList;
442
443 // If the number of arguments does not match with the number of params, the matching fails
444
6/6
✓ Branch 2 → 3 taken 217198 times.
✓ Branch 2 → 7 taken 241 times.
✓ Branch 5 → 6 taken 33093 times.
✓ Branch 5 → 7 taken 184105 times.
✓ Branch 8 → 9 taken 33093 times.
✓ Branch 8 → 10 taken 184346 times.
217439 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
445 33093 return false;
446 // In the case of a vararg function, we only disallow fewer arguments than parameters
447
4/6
✓ Branch 10 → 11 taken 241 times.
✓ Branch 10 → 15 taken 184105 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 241 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 184346 times.
184346 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
448 return false;
449
450 // Give the type matcher a way to retrieve instances of GenericType by their name
451 380603 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
452 11911 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
453 184346 };
454
455 // Loop over all parameters
456
2/2
✓ Branch 66 → 20 taken 188635 times.
✓ Branch 66 → 67 taken 38607 times.
227242 for (size_t i = 0; i < reqArgs.size(); i++) {
457 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
458 // but we need to modify the candidate param list to accept them
459
6/6
✓ Branch 20 → 21 taken 983 times.
✓ Branch 20 → 24 taken 187652 times.
✓ Branch 22 → 23 taken 263 times.
✓ Branch 22 → 24 taken 720 times.
✓ Branch 25 → 26 taken 263 times.
✓ Branch 25 → 29 taken 188372 times.
188635 if (candidate.isVararg && i >= candidateParamList.size()) {
460
2/4
✓ Branch 26 → 27 taken 263 times.
✗ Branch 26 → 71 not taken.
✓ Branch 27 → 28 taken 263 times.
✗ Branch 27 → 71 not taken.
263 candidateParamList.push_back(Param(reqArgs.at(i).first, false));
461 263 needsSubstantiation = true; // We need to modify the candidate param types
462 263 continue;
463 }
464
465 // Retrieve actual and requested types
466
2/4
✓ Branch 29 → 30 taken 188372 times.
✗ Branch 29 → 84 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 188372 times.
188372 assert(!candidateParamList.at(i).isOptional);
467
1/2
✓ Branch 32 → 33 taken 188372 times.
✗ Branch 32 → 84 not taken.
188372 QualType &candidateType = candidateParamList.at(i).qualType;
468
1/2
✓ Branch 33 → 34 taken 188372 times.
✗ Branch 33 → 84 not taken.
188372 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
469
470 // Check if the requested param type matches the candidate param type. The type mapping may be extended
471
3/4
✓ Branch 36 → 37 taken 188372 times.
✗ Branch 36 → 84 not taken.
✓ Branch 37 → 38 taken 145738 times.
✓ Branch 37 → 39 taken 42634 times.
188372 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
472 strictQualifierMatching))
473 145738 return false;
474
475 // Substantiate the candidate param type, based on the type mapping
476
3/4
✓ Branch 39 → 40 taken 42634 times.
✗ Branch 39 → 84 not taken.
✓ Branch 40 → 41 taken 9938 times.
✓ Branch 40 → 42 taken 32696 times.
42634 if (candidateType.hasAnyGenericParts())
477
1/2
✓ Branch 41 → 42 taken 9938 times.
✗ Branch 41 → 84 not taken.
9938 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
478
479 // Check if we try to bind a non-ref temporary to a non-const ref parameter
480
3/4
✓ Branch 42 → 43 taken 42634 times.
✗ Branch 42 → 84 not taken.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 54 taken 42633 times.
42634 if (!candidateType.canBind(requestedType, isArgTemporary)) {
481
1/2
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 53 not taken.
1 if (callNode)
482
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");
483 return false;
484 }
485
486 // If we have a function/procedure type we need to take care of the information, if it takes captures
487
9/12
✓ Branch 54 → 55 taken 42633 times.
✗ Branch 54 → 81 not taken.
✓ Branch 55 → 56 taken 42633 times.
✗ Branch 55 → 81 not taken.
✓ Branch 56 → 57 taken 64 times.
✓ Branch 56 → 60 taken 42569 times.
✓ Branch 57 → 58 taken 64 times.
✗ Branch 57 → 81 not taken.
✓ Branch 58 → 59 taken 16 times.
✓ Branch 58 → 60 taken 48 times.
✓ Branch 61 → 62 taken 16 times.
✓ Branch 61 → 64 taken 42617 times.
42633 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
488
1/2
✓ Branch 62 → 63 taken 16 times.
✗ Branch 62 → 83 not taken.
16 candidateType = candidateType.getWithLambdaCaptures();
489 16 needsSubstantiation = true;
490 }
491 }
492
493 38607 return true;
494 184346 }
495
496 /**
497 * Substantiates the candidate return type, based on the given type mapping
498 *
499 * @param candidate Matching candidate function
500 * @param typeMapping Concrete template type mapping
501 * @param callNode AST node for error messages
502 */
503 38605 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
504
2/2
✓ Branch 3 → 4 taken 3557 times.
✓ Branch 3 → 5 taken 35048 times.
38605 if (candidate.returnType.hasAnyGenericParts())
505 3557 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
506 38605 }
507
508 /**
509 * Searches the candidate template types for a generic type object with a certain name and return it
510 *
511 * @param candidate Matching candidate function
512 * @param templateTypeName Template type name
513 * @return Generic type object
514 */
515 24140 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
516 const std::string &templateTypeName) {
517
1/2
✓ Branch 19 → 4 taken 27172 times.
✗ Branch 19 → 20 not taken.
51312 for (const GenericType &templateType : candidate.templateTypes) {
518
3/4
✓ Branch 6 → 7 taken 27172 times.
✗ Branch 6 → 22 not taken.
✓ Branch 8 → 9 taken 24140 times.
✓ Branch 8 → 10 taken 3032 times.
27172 if (templateType.getSubType() == templateTypeName)
519 24140 return &templateType;
520 }
521 return nullptr;
522 }
523
524 /**
525 * Narrow a multi-match overload set by preferring the candidate whose parameter qualifiers most closely match
526 * the argument qualifiers. This resolves the typical copy-vs-move ctor ambiguity where both a `const T&`
527 * (copy) and a `T&` (move) ctor match a non-const lvalue argument - we prefer the non-const-ref candidate
528 * (move) since it requires no constification. When the argument is const, we prefer the const-ref candidate
529 * (copy) since binding to a non-const ref would require const-loss. As a secondary criterion, an explicitly
530 * declared (non-generic) overload is preferred over a generic substitution that matches equally well.
531 *
532 * Modifies `matches` in place, removing any candidate that scores worse than the best one. A no-op if there
533 * are fewer than two candidates.
534 *
535 * @param matches Candidate list to narrow
536 * @param reqArgs Argument list from the call site
537 */
538 28857 void FunctionManager::breakOverloadTie(std::vector<Function *> &matches, const ArgList &reqArgs) {
539
2/2
✓ Branch 3 → 4 taken 28848 times.
✓ Branch 3 → 5 taken 9 times.
28857 if (matches.size() < 2)
540 28848 return;
541
542 9 constexpr int CONSTIFY_PENALTY = 1;
543 9 constexpr int CONST_LOSS_PENALTY = 100;
544 // An exact type match is always preferred over a match that required an implicit upcast (struct to
545 // implemented interface, or struct to composed base struct). This keeps e.g. 'f(Derived)' preferred
546 // over 'f(Base)' when called with a Derived, instead of reporting an ambiguity.
547 9 constexpr int UPCAST_PENALTY = 1000;
548 45 const auto scoreSpecificity = [&](const Function *f) {
549 36 int penalty = 0;
550
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++) {
551
1/2
✓ Branch 3 → 4 taken 36 times.
✗ Branch 3 → 28 not taken.
36 const QualType &paramType = f->paramList.at(i).qualType;
552
1/2
✓ Branch 4 → 5 taken 36 times.
✗ Branch 4 → 28 not taken.
36 const QualType &argType = reqArgs.at(i).first;
553
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();
554
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();
555
4/4
✓ Branch 9 → 10 taken 20 times.
✓ Branch 9 → 12 taken 16 times.
✓ Branch 10 → 11 taken 8 times.
✓ Branch 10 → 12 taken 12 times.
36 if (paramIsConst && !argIsConst)
556 8 penalty += CONSTIFY_PENALTY;
557
4/4
✓ Branch 12 → 13 taken 16 times.
✓ Branch 12 → 15 taken 12 times.
✓ Branch 13 → 14 taken 4 times.
✓ Branch 13 → 15 taken 12 times.
28 else if (!paramIsConst && argIsConst)
558 4 penalty += CONST_LOSS_PENALTY;
559 // Penalize matches that were only possible through an implicit upcast
560 36 QualType paramBase = paramType;
561 36 QualType argBase = argType;
562
1/2
✓ Branch 15 → 16 taken 36 times.
✗ Branch 15 → 28 not taken.
36 QualType::unwrapBothWithRefWrappers(paramBase, argBase);
563
2/4
✓ Branch 16 → 17 taken 36 times.
✗ Branch 16 → 28 not taken.
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 19 taken 36 times.
36 if (!paramBase.matches(argBase, true, true, true))
564 penalty += UPCAST_PENALTY;
565 }
566 36 return penalty;
567 9 };
568
569 9 int bestScore = std::numeric_limits<int>::max();
570
2/2
✓ Branch 20 → 7 taken 18 times.
✓ Branch 20 → 21 taken 9 times.
36 for (const Function *m : matches)
571
1/2
✓ Branch 9 → 10 taken 18 times.
✗ Branch 9 → 76 not taken.
18 bestScore = std::min(bestScore, scoreSpecificity(m));
572 9 std::vector<Function *> filtered;
573
1/2
✓ Branch 22 → 23 taken 9 times.
✗ Branch 22 → 80 not taken.
9 filtered.reserve(matches.size());
574
2/2
✓ Branch 39 → 25 taken 18 times.
✓ Branch 39 → 40 taken 9 times.
36 for (Function *m : matches)
575
3/4
✓ Branch 27 → 28 taken 18 times.
✗ Branch 27 → 78 not taken.
✓ Branch 28 → 29 taken 12 times.
✓ Branch 28 → 30 taken 6 times.
18 if (scoreSpecificity(m) == bestScore)
576
1/2
✓ Branch 29 → 30 taken 12 times.
✗ Branch 29 → 78 not taken.
12 filtered.push_back(m);
577 9 matches = std::move(filtered);
578
579 // Secondary tie-break: prefer an explicitly declared (non-generic) overload over a generic substitution
580 // when both match equally well, mirroring C++ overload resolution where a non-template wins over a
581 // template specialization. This resolves e.g. the copy ctor 'Any.ctor(const Any&)' vs. the value ctor
582 // 'Any.ctor<Any>(const Any&)' ambiguity when copy-constructing from another value of the same type. It is
583 // applied after the qualifier-specificity narrowing above, so a more specific generic match still wins.
584 // A generic substitution that loses here and was only inserted for this very match is removed from its
585 // declaration's manifestation list again, so the IR generator never emits a manifestation that was never
586 // type-checked (and so we leave no dead code behind).
587
6/8
✓ Branch 44 → 45 taken 3 times.
✓ Branch 44 → 48 taken 6 times.
✓ Branch 45 → 46 taken 3 times.
✗ Branch 45 → 80 not taken.
✓ Branch 46 → 47 taken 3 times.
✗ Branch 46 → 48 not taken.
✓ Branch 49 → 50 taken 3 times.
✓ Branch 49 → 73 taken 6 times.
14 if (matches.size() > 1 && std::ranges::any_of(matches, [](const Function *m) { return !m->isGenericSubstantiation(); })) {
588
2/2
✓ Branch 71 → 52 taken 6 times.
✓ Branch 71 → 72 taken 3 times.
12 for (Function *m : matches)
589
6/8
✓ Branch 54 → 55 taken 6 times.
✗ Branch 54 → 79 not taken.
✓ Branch 55 → 56 taken 3 times.
✓ Branch 55 → 58 taken 3 times.
✓ Branch 56 → 57 taken 3 times.
✗ Branch 56 → 58 not taken.
✓ Branch 59 → 60 taken 3 times.
✓ Branch 59 → 62 taken 3 times.
6 if (m->isGenericSubstantiation() && m->isNewlyInserted)
590
2/4
✓ Branch 60 → 61 taken 3 times.
✗ Branch 60 → 79 not taken.
✓ Branch 61 → 62 taken 3 times.
✗ Branch 61 → 79 not taken.
3 std::erase(*m->declNode->getFctManifestations(m->name), m);
591
1/2
✓ Branch 72 → 73 taken 3 times.
✗ Branch 72 → 80 not taken.
9 std::erase_if(matches, [](const Function *m) { return m->isGenericSubstantiation(); });
592 }
593 9 }
594
595 /**
596 * Calculate the cache key for the function lookup cache
597 *
598 * @param scope Scope to match against
599 * @param name Function name requirement
600 * @param thisType This type requirement
601 * @param args Argument requirement
602 * @param templateTypes Template type requirement
603 * @return Cache key
604 */
605 472394 uint64_t FunctionManager::getCacheKey(const Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
606 const QualTypeList &templateTypes) {
607 472394 uint64_t hash = 0;
608 472394 hashCombine64(hash, hashPointer(scope));
609 472394 hashCombine64(hash, std::hash<std::string>{}(name));
610 472394 hashCombine64(hash, std::hash<QualType>{}(thisType));
611
2/2
✓ Branch 27 → 10 taken 753315 times.
✓ Branch 27 → 28 taken 472394 times.
2451418 for (const auto &[first, second] : args) {
612 753315 hashCombine64(hash, std::hash<QualType>{}(first));
613 753315 hashCombine64(hash, std::hash<bool>{}(second));
614 }
615 472394 hashCombine64(hash, hashVector(templateTypes));
616 472394 return hash;
617 }
618
619 11957 bool FunctionManager::hasCtor(const Scope *matchScope, CtorKind kind) {
620
5/8
✓ Branch 2 → 3 taken 11957 times.
✗ Branch 2 → 60 not taken.
✓ Branch 3 → 4 taken 11957 times.
✗ Branch 3 → 60 not taken.
✓ Branch 4 → 5 taken 11957 times.
✗ Branch 4 → 60 not taken.
✓ Branch 55 → 6 taken 53447 times.
✓ Branch 55 → 56 taken 7045 times.
60492 for (const auto &manifestations : matchScope->functions | std::views::values) {
621
5/8
✓ Branch 7 → 8 taken 53447 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 53447 times.
✗ Branch 8 → 59 not taken.
✓ Branch 9 → 10 taken 53447 times.
✗ Branch 9 → 59 not taken.
✓ Branch 51 → 11 taken 58868 times.
✓ Branch 51 → 52 taken 48535 times.
107403 for (const auto &function : manifestations | std::views::values) {
622 // If it is no ctor, skip it
623
3/4
✓ Branch 12 → 13 taken 58868 times.
✗ Branch 12 → 59 not taken.
✓ Branch 13 → 14 taken 36160 times.
✓ Branch 13 → 15 taken 22708 times.
58868 if (function.name != CTOR_FUNCTION_NAME)
624 36160 continue;
625 // Classify the ctor based on its parameter list
626
6/8
✓ Branch 16 → 17 taken 14835 times.
✓ Branch 16 → 25 taken 7873 times.
✓ Branch 17 → 18 taken 14835 times.
✗ Branch 17 → 58 not taken.
✓ Branch 18 → 19 taken 14835 times.
✗ Branch 18 → 58 not taken.
✓ Branch 19 → 20 taken 6907 times.
✓ Branch 19 → 25 taken 7928 times.
29615 const bool singleSelfRefParam = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
627
5/8
✓ Branch 20 → 21 taken 6907 times.
✗ Branch 20 → 58 not taken.
✓ Branch 21 → 22 taken 6907 times.
✗ Branch 21 → 58 not taken.
✓ Branch 22 → 23 taken 6907 times.
✗ Branch 22 → 58 not taken.
✓ Branch 23 → 24 taken 5794 times.
✓ Branch 23 → 25 taken 1113 times.
6907 function.paramList.at(0).qualType.getBase() == function.thisType;
628
6/8
✓ Branch 26 → 27 taken 5794 times.
✓ Branch 26 → 31 taken 16914 times.
✓ Branch 27 → 28 taken 5794 times.
✗ Branch 27 → 59 not taken.
✓ Branch 28 → 29 taken 5794 times.
✗ Branch 28 → 59 not taken.
✓ Branch 29 → 30 taken 5781 times.
✓ Branch 29 → 31 taken 13 times.
22708 const bool isCopyCtor = singleSelfRefParam && function.paramList.at(0).qualType.isConstRef();
629
6/8
✓ Branch 32 → 33 taken 5794 times.
✓ Branch 32 → 37 taken 16914 times.
✓ Branch 33 → 34 taken 5794 times.
✗ Branch 33 → 59 not taken.
✓ Branch 34 → 35 taken 5794 times.
✗ Branch 34 → 59 not taken.
✓ Branch 35 → 36 taken 13 times.
✓ Branch 35 → 37 taken 5781 times.
22708 const bool isMoveCtor = singleSelfRefParam && !function.paramList.at(0).qualType.isConstRef();
630
3/4
✓ Branch 38 → 39 taken 13932 times.
✓ Branch 38 → 42 taken 5141 times.
✓ Branch 38 → 45 taken 3635 times.
✗ Branch 38 → 49 not taken.
22708 switch (kind) {
631 13932 case CtorKind::COPY:
632
2/2
✓ Branch 39 → 40 taken 2637 times.
✓ Branch 39 → 41 taken 11295 times.
13932 if (isCopyCtor)
633 4912 return true;
634 11295 break;
635 5141 case CtorKind::MOVE:
636
2/2
✓ Branch 42 → 43 taken 4 times.
✓ Branch 42 → 44 taken 5137 times.
5141 if (isMoveCtor)
637 4 return true;
638 5137 break;
639 3635 case CtorKind::ANY_NON_COPY_NON_MOVE:
640
4/4
✓ Branch 45 → 46 taken 2273 times.
✓ Branch 45 → 48 taken 1362 times.
✓ Branch 46 → 47 taken 2271 times.
✓ Branch 46 → 48 taken 2 times.
3635 if (!isCopyCtor && !isMoveCtor)
641 2271 return true;
642 1364 break;
643 }
644 }
645 }
646 7045 return false;
647 }
648
649 2380 bool FunctionManager::hasAnyNonCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::ANY_NON_COPY_NON_MOVE); }
650
651 7217 bool FunctionManager::hasCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::COPY); }
652
653 2356 bool FunctionManager::hasUserCopyCtor(const Scope *matchScope) {
654
5/8
✓ Branch 2 → 3 taken 2356 times.
✗ Branch 2 → 42 not taken.
✓ Branch 3 → 4 taken 2356 times.
✗ Branch 3 → 42 not taken.
✓ Branch 4 → 5 taken 2356 times.
✗ Branch 4 → 42 not taken.
✓ Branch 37 → 6 taken 10595 times.
✓ Branch 37 → 38 taken 2137 times.
12732 for (const auto &manifestations : matchScope->functions | std::views::values) {
655
5/8
✓ Branch 7 → 8 taken 10595 times.
✗ Branch 7 → 41 not taken.
✓ Branch 8 → 9 taken 10595 times.
✗ Branch 8 → 41 not taken.
✓ Branch 9 → 10 taken 10595 times.
✗ Branch 9 → 41 not taken.
✓ Branch 34 → 11 taken 11420 times.
✓ Branch 34 → 35 taken 10376 times.
21796 for (const auto &function : manifestations | std::views::values) {
656
7/8
✓ Branch 12 → 13 taken 11420 times.
✗ Branch 12 → 41 not taken.
✓ Branch 13 → 14 taken 5130 times.
✓ Branch 13 → 15 taken 6290 times.
✓ Branch 14 → 15 taken 1659 times.
✓ Branch 14 → 16 taken 3471 times.
✓ Branch 17 → 18 taken 7949 times.
✓ Branch 17 → 19 taken 3471 times.
11420 if (function.name != CTOR_FUNCTION_NAME || function.implicitDefault)
657 7949 continue;
658
6/8
✓ Branch 20 → 21 taken 2497 times.
✓ Branch 20 → 29 taken 974 times.
✓ Branch 21 → 22 taken 2497 times.
✗ Branch 21 → 40 not taken.
✓ Branch 22 → 23 taken 2497 times.
✗ Branch 22 → 40 not taken.
✓ Branch 23 → 24 taken 358 times.
✓ Branch 23 → 29 taken 2139 times.
3829 const bool isCopyCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isConstRef() &&
659
5/8
✓ Branch 24 → 25 taken 358 times.
✗ Branch 24 → 40 not taken.
✓ Branch 25 → 26 taken 358 times.
✗ Branch 25 → 40 not taken.
✓ Branch 26 → 27 taken 358 times.
✗ Branch 26 → 40 not taken.
✓ Branch 27 → 28 taken 219 times.
✓ Branch 27 → 29 taken 139 times.
358 function.paramList.at(0).qualType.getBase() == function.thisType;
660
2/2
✓ Branch 30 → 31 taken 219 times.
✓ Branch 30 → 32 taken 3252 times.
3471 if (isCopyCtor)
661 219 return true;
662 }
663 }
664 2137 return false;
665 }
666
667 2360 bool FunctionManager::hasMoveCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::MOVE); }
668
669 14107 Function *FunctionManager::findMoveCtor(Scope *matchScope) {
670
5/8
✓ Branch 2 → 3 taken 14107 times.
✗ Branch 2 → 41 not taken.
✓ Branch 3 → 4 taken 14107 times.
✗ Branch 3 → 41 not taken.
✓ Branch 4 → 5 taken 14107 times.
✗ Branch 4 → 41 not taken.
✓ Branch 36 → 6 taken 128888 times.
✓ Branch 36 → 37 taken 14060 times.
142948 for (auto &manifestations : matchScope->functions | std::views::values) {
671
5/8
✓ Branch 7 → 8 taken 128888 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 128888 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 128888 times.
✗ Branch 9 → 40 not taken.
✓ Branch 33 → 11 taken 165749 times.
✓ Branch 33 → 34 taken 128841 times.
294590 for (auto &function : manifestations | std::views::values) {
672
3/4
✓ Branch 12 → 13 taken 165749 times.
✗ Branch 12 → 40 not taken.
✓ Branch 13 → 14 taken 125759 times.
✓ Branch 13 → 15 taken 39990 times.
165749 if (function.name != CTOR_FUNCTION_NAME)
673 125759 continue;
674
4/6
✓ Branch 17 → 18 taken 29182 times.
✗ Branch 17 → 39 not taken.
✓ Branch 18 → 19 taken 29182 times.
✗ Branch 18 → 39 not taken.
✓ Branch 19 → 20 taken 17703 times.
✓ Branch 19 → 28 taken 11479 times.
69172 const bool isMoveCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
675
6/8
✓ Branch 16 → 17 taken 29182 times.
✓ Branch 16 → 28 taken 10808 times.
✓ Branch 20 → 21 taken 17703 times.
✗ Branch 20 → 39 not taken.
✓ Branch 21 → 22 taken 17703 times.
✗ Branch 21 → 39 not taken.
✓ Branch 22 → 23 taken 2986 times.
✓ Branch 22 → 28 taken 14717 times.
72158 !function.paramList.at(0).qualType.isConstRef() &&
676
5/8
✓ Branch 23 → 24 taken 2986 times.
✗ Branch 23 → 39 not taken.
✓ Branch 24 → 25 taken 2986 times.
✗ Branch 24 → 39 not taken.
✓ Branch 25 → 26 taken 2986 times.
✗ Branch 25 → 39 not taken.
✓ Branch 26 → 27 taken 47 times.
✓ Branch 26 → 28 taken 2939 times.
2986 function.paramList.at(0).qualType.getBase() == function.thisType;
677
2/2
✓ Branch 29 → 30 taken 47 times.
✓ Branch 29 → 31 taken 39943 times.
39990 if (isMoveCtor)
678 47 return &function;
679 }
680 }
681 14060 return nullptr;
682 }
683
684 9 bool FunctionManager::hasDefaultCtor(const Scope *matchScope) {
685
4/8
✓ Branch 2 → 3 taken 9 times.
✗ Branch 2 → 29 not taken.
✓ Branch 3 → 4 taken 9 times.
✗ Branch 3 → 29 not taken.
✓ Branch 4 → 5 taken 9 times.
✗ Branch 4 → 29 not taken.
✓ Branch 25 → 6 taken 26 times.
✗ Branch 25 → 26 not taken.
26 for (const auto &manifestations : matchScope->functions | std::views::values)
686
5/8
✓ Branch 7 → 8 taken 26 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 26 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 26 times.
✗ Branch 9 → 28 not taken.
✓ Branch 22 → 11 taken 26 times.
✓ Branch 22 → 23 taken 17 times.
43 for (const auto &function : manifestations | std::views::values)
687
6/8
✓ Branch 12 → 13 taken 26 times.
✗ Branch 12 → 28 not taken.
✓ Branch 13 → 14 taken 9 times.
✓ Branch 13 → 17 taken 17 times.
✓ Branch 15 → 16 taken 9 times.
✗ Branch 15 → 17 not taken.
✓ Branch 18 → 19 taken 9 times.
✓ Branch 18 → 20 taken 17 times.
26 if (function.name == CTOR_FUNCTION_NAME && function.paramList.empty())
688 9 return true;
689 return false;
690 }
691
692 18263 bool FunctionManager::hasDtor(const Scope *matchScope) {
693
5/8
✓ Branch 2 → 3 taken 18263 times.
✗ Branch 2 → 24 not taken.
✓ Branch 3 → 4 taken 18263 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 18263 times.
✗ Branch 4 → 24 not taken.
✓ Branch 20 → 6 taken 141664 times.
✓ Branch 20 → 21 taken 9657 times.
151321 for (const auto &manifestations : matchScope->functions | std::views::values)
694
5/8
✓ Branch 7 → 8 taken 141664 times.
✗ Branch 7 → 23 not taken.
✓ Branch 8 → 9 taken 141664 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 141664 times.
✗ Branch 9 → 23 not taken.
✓ Branch 17 → 11 taken 161302 times.
✓ Branch 17 → 18 taken 133058 times.
294360 for (const auto &function : manifestations | std::views::values)
695
3/4
✓ Branch 12 → 13 taken 161302 times.
✗ Branch 12 → 23 not taken.
✓ Branch 13 → 14 taken 8606 times.
✓ Branch 13 → 15 taken 152696 times.
161302 if (function.name == DTOR_FUNCTION_NAME)
696 8606 return true;
697 9657 return false;
698 }
699
700 /**
701 * Clear the lookup cache
702 */
703 572 void FunctionManager::cleanup() {
704 572 lookupCache.clear();
705 572 lookupCacheHits = 0;
706 572 lookupCacheMisses = 0;
707 572 }
708
709 /**
710 * Dump usage statistics for the lookup cache
711 */
712 327 std::string FunctionManager::dumpLookupCacheStatistics() {
713
1/2
✓ Branch 2 → 3 taken 327 times.
✗ Branch 2 → 22 not taken.
327 std::stringstream stats;
714
2/4
✓ Branch 3 → 4 taken 327 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 327 times.
✗ Branch 4 → 20 not taken.
327 stats << "FunctionManager lookup cache statistics:" << std::endl;
715
3/6
✓ Branch 5 → 6 taken 327 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 327 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 327 times.
✗ Branch 8 → 20 not taken.
327 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
716
3/6
✓ Branch 9 → 10 taken 327 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 327 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 327 times.
✗ Branch 11 → 20 not taken.
327 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
717
3/6
✓ Branch 12 → 13 taken 327 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 327 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 327 times.
✗ Branch 14 → 20 not taken.
327 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
718
1/2
✓ Branch 15 → 16 taken 327 times.
✗ Branch 15 → 20 not taken.
654 return stats.str();
719 327 }
720
721 } // namespace spice::compiler
722