GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 96.3% 338 / 0 / 351
Functions: 100.0% 29 / 0 / 29
Branches: 64.9% 445 / 0 / 686

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/CustomHashFunctions.h>
17
18 namespace spice::compiler {
19
20 // Static member initialization
21 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
22 size_t FunctionManager::lookupCacheHits = 0;
23 size_t FunctionManager::lookupCacheMisses = 0;
24
25 38159 Function *FunctionManager::insert(Scope *insertScope, const Function &baseFunction, std::vector<Function *> *nodeFunctionList) {
26 // Open a new manifestation list for the function definition
27
3/6
✓ Branch 2 → 3 taken 38159 times.
✗ Branch 2 → 49 not taken.
✓ Branch 3 → 4 taken 38159 times.
✗ Branch 3 → 46 not taken.
✓ Branch 4 → 5 taken 38159 times.
✗ Branch 4 → 44 not taken.
38159 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
28
1/2
✓ Branch 8 → 9 taken 38159 times.
✗ Branch 8 → 50 not taken.
38159 insertScope->functions.emplace(fctId, FunctionManifestationList());
29
30 // Collect substantiations
31 38159 std::vector<Function> manifestations;
32
1/2
✓ Branch 10 → 11 taken 38159 times.
✗ Branch 10 → 54 not taken.
38159 substantiateOptionalParams(baseFunction, manifestations);
33
1/2
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 38159 times.
38159 assert(!manifestations.empty());
34
35 // Save substantiations in declaration node
36 38159 Function *manifestationPtr = nullptr;
37
2/2
✓ Branch 32 → 16 taken 40407 times.
✓ Branch 32 → 33 taken 38157 times.
116723 for (const Function &manifestation : manifestations) {
38
2/2
✓ Branch 18 → 19 taken 40405 times.
✓ Branch 18 → 53 taken 2 times.
40407 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
39
1/2
✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 40405 times.
40405 assert(manifestationPtr != nullptr);
40
1/2
✓ Branch 21 → 22 taken 40405 times.
✗ Branch 21 → 23 not taken.
40405 if (nodeFunctionList)
41
1/2
✓ Branch 22 → 23 taken 40405 times.
✗ Branch 22 → 53 not taken.
40405 nodeFunctionList->push_back(manifestationPtr);
42 }
43
44
1/2
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 38157 times.
38157 if (!nodeFunctionList)
45 return manifestationPtr;
46
47
1/2
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 38157 times.
38157 assert(!nodeFunctionList->empty());
48 38157 return nodeFunctionList->front();
49 38161 }
50
51 /**
52 * Create definite functions from ambiguous ones, in regard to optional arguments.
53 *
54 * Example:
55 * int testFunc(string, int?, double?)
56 * gets
57 * int testFunc(string)
58 * int testFunc(string, int)
59 * int testFunc(string, int, double)
60 *
61 * This method also accepts functions, that are already definite, but does nothing to them.
62 *
63 * @param baseFunction Ambiguous base function
64 * @param manifestations Vector to store the definite manifestations
65 * @return True, if there were optional arguments found
66 */
67 38159 void FunctionManager::substantiateOptionalParams(const Function &baseFunction, std::vector<Function> &manifestations) {
68 // Handle the case of no parameters -> simply return the base function
69
2/2
✓ Branch 3 → 4 taken 10346 times.
✓ Branch 3 → 6 taken 27813 times.
38159 if (baseFunction.paramList.empty()) {
70
1/2
✓ Branch 4 → 5 taken 10346 times.
✗ Branch 4 → 47 not taken.
10346 manifestations.push_back(baseFunction);
71 10346 return;
72 }
73
74 27813 ParamList currentFunctionParamTypes;
75
1/2
✓ Branch 7 → 8 taken 27813 times.
✗ Branch 7 → 45 not taken.
27813 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
76 27813 bool metFirstOptionalParam = false;
77
1/2
✓ Branch 8 → 9 taken 27813 times.
✗ Branch 8 → 45 not taken.
27813 Function manifestation = baseFunction;
78
79 // Loop over all parameters
80
2/2
✓ Branch 32 → 11 taken 41904 times.
✓ Branch 32 → 33 taken 27813 times.
97530 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
81 // Check if we have a mandatory parameter
82
2/2
✓ Branch 13 → 14 taken 39656 times.
✓ Branch 13 → 16 taken 2248 times.
41904 if (!isOptional) {
83
1/2
✓ Branch 14 → 15 taken 39656 times.
✗ Branch 14 → 40 not taken.
39656 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
84 39656 continue;
85 }
86
87 // Add substantiation without the optional parameter
88
2/2
✓ Branch 16 → 17 taken 2134 times.
✓ Branch 16 → 20 taken 114 times.
2248 if (!metFirstOptionalParam) {
89
1/2
✓ Branch 17 → 18 taken 2134 times.
✗ Branch 17 → 42 not taken.
2134 manifestation.paramList = currentFunctionParamTypes;
90
1/2
✓ Branch 18 → 19 taken 2134 times.
✗ Branch 18 → 42 not taken.
2134 manifestations.push_back(manifestation);
91 // Now we cannot accept mandatory parameters anymore
92 2134 metFirstOptionalParam = true;
93 }
94
95 // Add substantiation with the optional parameter
96
1/2
✓ Branch 20 → 21 taken 2248 times.
✗ Branch 20 → 41 not taken.
2248 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
97
1/2
✓ Branch 21 → 22 taken 2248 times.
✗ Branch 21 → 42 not taken.
2248 manifestation.paramList = currentFunctionParamTypes;
98
1/2
✓ Branch 22 → 23 taken 2248 times.
✗ Branch 22 → 42 not taken.
2248 manifestations.push_back(manifestation);
99 }
100
101 // Ensure at least once manifestation
102
2/2
✓ Branch 34 → 35 taken 25679 times.
✓ Branch 34 → 36 taken 2134 times.
27813 if (manifestations.empty())
103
1/2
✓ Branch 35 → 36 taken 25679 times.
✗ Branch 35 → 43 not taken.
25679 manifestations.push_back(baseFunction);
104 27813 }
105
106 6 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
107 6 ParamList paramList;
108
2/2
✓ Branch 16 → 4 taken 2 times.
✓ Branch 16 → 17 taken 6 times.
14 for (const QualType &paramType : paramTypes)
109
1/2
✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 33 not taken.
2 paramList.push_back({paramType, false});
110
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};
111 6 }
112
113 51516 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
114
2/4
✓ Branch 2 → 3 taken 51516 times.
✗ Branch 2 → 70 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 51516 times.
51516 assert(newManifestation.hasSubstantiatedParams());
115
116
1/2
✓ Branch 5 → 6 taken 51516 times.
✗ Branch 5 → 70 not taken.
51516 const std::string signature = newManifestation.getSignature(true, true, false, true);
117
118 // Check if the function exists already
119
5/8
✓ Branch 6 → 7 taken 51516 times.
✗ Branch 6 → 59 not taken.
✓ Branch 7 → 8 taken 51516 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 51516 times.
✗ Branch 8 → 59 not taken.
✓ Branch 30 → 10 taken 852456 times.
✓ Branch 30 → 31 taken 51514 times.
903970 for (const auto &manifestations : insertScope->functions | std::views::values) {
120
3/4
✓ Branch 11 → 12 taken 852456 times.
✗ Branch 11 → 59 not taken.
✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 28 taken 852454 times.
852456 if (manifestations.contains(signature)) {
121
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;
122
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");
123 }
124 }
125
126 // Retrieve the matching manifestation list of the scope
127
3/6
✓ Branch 31 → 32 taken 51514 times.
✗ Branch 31 → 65 not taken.
✓ Branch 32 → 33 taken 51514 times.
✗ Branch 32 → 62 not taken.
✓ Branch 33 → 34 taken 51514 times.
✗ Branch 33 → 60 not taken.
51514 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
128
2/4
✓ Branch 36 → 37 taken 51514 times.
✗ Branch 36 → 66 not taken.
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 51514 times.
51514 assert(insertScope->functions.contains(fctId));
129
1/2
✓ Branch 39 → 40 taken 51514 times.
✗ Branch 39 → 66 not taken.
51514 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
130
131 // Add substantiated function
132
1/2
✓ Branch 40 → 41 taken 51514 times.
✗ Branch 40 → 66 not taken.
51514 manifestationList.emplace(signature, newManifestation);
133
1/2
✓ Branch 41 → 42 taken 51514 times.
✗ Branch 41 → 66 not taken.
103028 return &manifestationList.at(signature);
134 51516 }
135
136 /**
137 * Checks if a function exists by matching it, but not setting it to used
138 *
139 * @param matchScope Scope to match against
140 * @param reqName Function name requirement
141 * @param reqThisType This type requirement
142 * @param reqArgs Argument requirement
143 * @param strictQualifierMatching Match argument and this type qualifiers strictly
144 * @return Found function or nullptr
145 */
146 38875 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
147 const ArgList &reqArgs, bool strictQualifierMatching) {
148
2/4
✓ Branch 2 → 3 taken 38875 times.
✗ Branch 2 → 64 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 38875 times.
38875 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
149
150 // Do cache lookup
151 38875 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
152
3/4
✓ Branch 8 → 9 taken 38875 times.
✗ Branch 8 → 65 not taken.
✓ Branch 11 → 12 taken 8247 times.
✓ Branch 11 → 14 taken 30628 times.
38875 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
153 8247 lookupCacheHits++;
154 8247 return it->second;
155 }
156 30628 lookupCacheMisses++;
157
158 10329 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
159
4/8
✓ Branch 14 → 15 taken 30628 times.
✗ Branch 14 → 74 not taken.
✓ Branch 15 → 16 taken 30628 times.
✗ Branch 15 → 19 not taken.
✓ Branch 16 → 17 taken 30628 times.
✗ Branch 16 → 74 not taken.
✓ Branch 17 → 18 taken 30628 times.
✗ Branch 17 → 19 not taken.
30628 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
160
161 // Loop over function registry to find functions, that match the requirements of the call
162 30628 std::vector<const Function *> matches;
163
2/2
✓ Branch 54 → 22 taken 221941 times.
✓ Branch 54 → 55 taken 30628 times.
252569 for (const auto &[defCodeLocStr, manifestations] : matchScope->functions) {
164
2/2
✓ Branch 51 → 27 taken 256624 times.
✓ Branch 51 → 52 taken 100211 times.
356835 for (const auto &[signature, presetFunction] : manifestations) {
165
2/4
✓ Branch 30 → 31 taken 256624 times.
✗ Branch 30 → 69 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 33 taken 256624 times.
256624 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
166
167 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
168 // - search for generic fct: Only match against generic preset functions
169
3/4
✓ Branch 33 → 34 taken 256624 times.
✗ Branch 33 → 69 not taken.
✓ Branch 34 → 35 taken 108683 times.
✓ Branch 34 → 36 taken 147941 times.
256624 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
170 134894 continue;
171
172 // Copy the function to be able to substantiate types
173
1/2
✓ Branch 36 → 37 taken 147941 times.
✗ Branch 36 → 69 not taken.
147941 Function candidate = presetFunction;
174
175 // Create empty type mapping
176 147941 TypeMapping &typeMapping = candidate.typeMapping;
177
178 147941 bool forceSubstantiation = false;
179
1/2
✓ Branch 37 → 38 taken 147941 times.
✗ Branch 37 → 67 not taken.
147941 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
180 strictQualifierMatching, forceSubstantiation, nullptr);
181
2/2
✓ Branch 38 → 39 taken 113083 times.
✓ Branch 38 → 40 taken 34858 times.
147941 if (matchResult == MatchResult::SKIP_FUNCTION)
182 113083 break; // Leave the whole function
183
2/2
✓ Branch 40 → 41 taken 26211 times.
✓ Branch 40 → 42 taken 8647 times.
34858 if (matchResult == MatchResult::SKIP_MANIFESTATION)
184 26211 continue; // Leave this manifestation and try the next one
185
186 // Add to matches
187
3/6
✓ Branch 42 → 43 taken 8647 times.
✗ Branch 42 → 66 not taken.
✓ Branch 43 → 44 taken 8647 times.
✗ Branch 43 → 66 not taken.
✓ Branch 44 → 45 taken 8647 times.
✗ Branch 44 → 66 not taken.
8647 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
188
189 8647 break; // Leave the whole manifestation list to not double-match the manifestation
190
2/2
✓ Branch 47 → 48 taken 26211 times.
✓ Branch 47 → 49 taken 121730 times.
147941 }
191 }
192
193 // Return the very match or a nullptr
194
2/2
✓ Branch 56 → 57 taken 8647 times.
✓ Branch 56 → 59 taken 21981 times.
30628 return !matches.empty() ? matches.front() : nullptr;
195 30628 }
196
197 /**
198 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
199 * If more than one function matches the requirement, an error gets thrown.
200 *
201 * @param matchScope Scope to match against
202 * @param reqName Function name requirement
203 * @param reqThisType This type requirement
204 * @param reqArgs Argument requirement
205 * @param templateTypeHints Template type requirement
206 * @param strictQualifierMatching Match argument and this type qualifiers strictly
207 * @param callNode Call AST node for printing error messages
208 * @return Matched function or nullptr
209 */
210 394384 Function *FunctionManager::match(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
211 const ArgList &reqArgs, const QualTypeList &templateTypeHints, bool strictQualifierMatching,
212 const ASTNode *callNode) {
213
2/4
✓ Branch 2 → 3 taken 394384 times.
✗ Branch 2 → 181 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 394384 times.
394384 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
214
215 // Do cache lookup
216 394384 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
217
3/4
✓ Branch 6 → 7 taken 394384 times.
✗ Branch 6 → 182 not taken.
✓ Branch 9 → 10 taken 59001 times.
✓ Branch 9 → 12 taken 335383 times.
394384 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
218 59001 lookupCacheHits++;
219 59001 return it->second;
220 }
221 335383 lookupCacheMisses++;
222
223 // Loop over function registry to find functions, that match the requirements of the call
224 335383 std::vector<Function *> matches;
225
2/2
✓ Branch 140 → 14 taken 3707615 times.
✓ Branch 140 → 141 taken 335382 times.
4042997 for (auto &[fctId, manifestations] : matchScope->functions) {
226
2/2
✓ Branch 137 → 19 taken 3813449 times.
✓ Branch 137 → 138 taken 142027 times.
3955476 for (const auto &[signature, presetFunction] : manifestations) {
227
2/4
✓ Branch 22 → 23 taken 3813449 times.
✗ Branch 22 → 205 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 25 taken 3813449 times.
3813449 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
228
229 // Skip generic and newly inserted substantiations to prevent double matching of a function
230
6/8
✓ Branch 25 → 26 taken 3813449 times.
✗ Branch 25 → 205 not taken.
✓ Branch 26 → 27 taken 3712264 times.
✓ Branch 26 → 28 taken 101185 times.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 3712264 times.
✓ Branch 30 → 31 taken 101185 times.
✓ Branch 30 → 32 taken 3712264 times.
3813449 if (presetFunction.isGenericSubstantiation() || presetFunction.isNewlyInserted)
231 247861 continue;
232
233 // Copy the function to be able to substantiate types
234
1/2
✓ Branch 32 → 33 taken 3712264 times.
✗ Branch 32 → 205 not taken.
3712264 Function candidate = presetFunction;
235
236 // Prepare type mapping, based on the given initial type mapping
237 3712264 TypeMapping &typeMapping = candidate.typeMapping;
238 3712264 typeMapping.clear();
239
2/2
✓ Branch 43 → 35 taken 55290 times.
✓ Branch 43 → 44 taken 3712264 times.
3767554 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
240
2/4
✓ Branch 35 → 36 taken 55290 times.
✗ Branch 35 → 203 not taken.
✓ Branch 36 → 37 taken 55290 times.
✗ Branch 36 → 203 not taken.
55290 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
241
1/2
✓ Branch 37 → 38 taken 55290 times.
✗ Branch 37 → 203 not taken.
55290 const QualType &templateType = templateTypeHints.at(i);
242
1/2
✓ Branch 38 → 39 taken 55290 times.
✗ Branch 38 → 203 not taken.
55290 typeMapping.emplace(typeName, templateType);
243 }
244
245 3712264 bool forceSubstantiation = false;
246
2/2
✓ Branch 44 → 45 taken 3712263 times.
✓ Branch 44 → 203 taken 1 time.
3712264 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
247 strictQualifierMatching, forceSubstantiation, callNode);
248
2/2
✓ Branch 45 → 46 taken 3553387 times.
✓ Branch 45 → 47 taken 158876 times.
3712263 if (matchResult == MatchResult::SKIP_FUNCTION)
249 3553387 break; // Leave the whole function
250
2/2
✓ Branch 47 → 48 taken 131299 times.
✓ Branch 47 → 49 taken 27577 times.
158876 if (matchResult == MatchResult::SKIP_MANIFESTATION)
251 131299 continue; // Leave this manifestation and try the next one
252
253 // We found a match! -> Set the actual candidate and its entry to used
254 27577 candidate.used = true;
255 27577 candidate.entry->used = true;
256
257 // Check if the function is generic needs to be substantiated
258
6/6
✓ Branch 50 → 51 taken 15622 times.
✓ Branch 50 → 53 taken 11955 times.
✓ Branch 51 → 52 taken 15377 times.
✓ Branch 51 → 53 taken 245 times.
✓ Branch 54 → 55 taken 15377 times.
✓ Branch 54 → 67 taken 12200 times.
27577 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
259
5/10
✓ Branch 55 → 56 taken 15377 times.
✗ Branch 55 → 183 not taken.
✓ Branch 56 → 57 taken 15377 times.
✗ Branch 56 → 61 not taken.
✓ Branch 57 → 58 taken 15377 times.
✗ Branch 57 → 183 not taken.
✓ Branch 58 → 59 taken 15377 times.
✗ Branch 58 → 183 not taken.
✓ Branch 59 → 60 taken 15377 times.
✗ Branch 59 → 61 not taken.
15377 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
260
2/4
✓ Branch 62 → 63 taken 15377 times.
✗ Branch 62 → 183 not taken.
✓ Branch 63 → 64 taken 15377 times.
✗ Branch 63 → 183 not taken.
15377 Function *match = &matchScope->functions.at(fctId).at(signature);
261 15377 match->used = true;
262
1/2
✓ Branch 64 → 65 taken 15377 times.
✗ Branch 64 → 183 not taken.
15377 matches.push_back(match);
263 15377 continue; // Match was successful -> match the next function
264 15377 }
265
266 // Check if we already have this manifestation and can simply re-use it
267
1/2
✓ Branch 67 → 68 taken 12200 times.
✗ Branch 67 → 203 not taken.
12200 const std::string newSignature = candidate.getSignature(true, true, false, true);
268
3/4
✓ Branch 68 → 69 taken 12200 times.
✗ Branch 68 → 185 not taken.
✓ Branch 71 → 72 taken 1091 times.
✓ Branch 71 → 76 taken 11109 times.
12200 if (const auto it = manifestations.find(newSignature); it != manifestations.end()) {
269 1091 it->second.used = true;
270
1/2
✓ Branch 74 → 75 taken 1091 times.
✗ Branch 74 → 184 not taken.
1091 matches.push_back(&it->second);
271 1091 break; // Leave the whole manifestation list to not double-match the manifestation
272 }
273
274 // Insert the substantiated version if required
275
1/2
✓ Branch 76 → 78 taken 11109 times.
✗ Branch 76 → 201 not taken.
11109 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
276
2/4
✓ Branch 78 → 79 taken 11109 times.
✗ Branch 78 → 201 not taken.
✓ Branch 79 → 80 taken 11109 times.
✗ Branch 79 → 201 not taken.
11109 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
277 11109 substantiatedFunction->alreadyTypeChecked = false;
278
2/4
✓ Branch 80 → 81 taken 11109 times.
✗ Branch 80 → 201 not taken.
✓ Branch 81 → 82 taken 11109 times.
✗ Branch 81 → 201 not taken.
11109 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
279 11109 substantiatedFunction->isNewlyInserted = true; // To not iterate over it in the same matching
280
281 // Copy function entry
282
1/2
✓ Branch 82 → 83 taken 11109 times.
✗ Branch 82 → 201 not taken.
11109 const std::string newScopeName = substantiatedFunction->getScopeName();
283
1/2
✓ Branch 83 → 84 taken 11109 times.
✗ Branch 83 → 199 not taken.
11109 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
284
1/2
✓ Branch 86 → 87 taken 11109 times.
✗ Branch 86 → 199 not taken.
11109 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newScopeName);
285
1/2
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 11109 times.
11109 assert(substantiatedFunction->entry != nullptr);
286
287 // Copy function scope
288
1/2
✓ Branch 89 → 90 taken 11109 times.
✗ Branch 89 → 199 not taken.
11109 const std::string oldScopeName = presetFunction.getScopeName();
289
1/2
✓ Branch 90 → 91 taken 11109 times.
✗ Branch 90 → 197 not taken.
11109 Scope *childScope = matchScope->copyChildScope(oldScopeName, newScopeName);
290
1/2
✗ Branch 91 → 92 not taken.
✓ Branch 91 → 93 taken 11109 times.
11109 assert(childScope != nullptr);
291 11109 childScope->isGenericScope = false;
292 11109 substantiatedFunction->bodyScope = childScope;
293
294 // Insert symbols for generic type names with concrete types into the child block
295
2/2
✓ Branch 103 → 95 taken 13962 times.
✓ Branch 103 → 104 taken 11109 times.
25071 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
296
2/4
✓ Branch 98 → 99 taken 13962 times.
✗ Branch 98 → 188 not taken.
✓ Branch 99 → 100 taken 13962 times.
✗ Branch 99 → 186 not taken.
13962 childScope->insertGenericType(typeName, GenericType(concreteType));
297
298 // Substantiate the 'this' entry in the new function scope
299
6/6
✓ Branch 107 → 108 taken 9197 times.
✓ Branch 107 → 111 taken 1912 times.
✓ Branch 109 → 110 taken 9195 times.
✓ Branch 109 → 111 taken 2 times.
✓ Branch 112 → 113 taken 9195 times.
✓ Branch 112 → 126 taken 1914 times.
11109 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
300
1/2
✓ Branch 115 → 116 taken 9195 times.
✗ Branch 115 → 192 not taken.
27585 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
301
1/2
✗ Branch 121 → 122 not taken.
✓ Branch 121 → 123 taken 9195 times.
9195 assert(thisEntry != nullptr);
302
2/4
✓ Branch 123 → 124 taken 9195 times.
✗ Branch 123 → 196 not taken.
✓ Branch 124 → 125 taken 9195 times.
✗ Branch 124 → 196 not taken.
9195 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
303 }
304
305 // Add to matched functions
306
1/2
✓ Branch 126 → 127 taken 11109 times.
✗ Branch 126 → 197 not taken.
11109 matches.push_back(substantiatedFunction);
307
308 11109 break; // Leave the whole manifestation list to not double-match the manifestation
309
2/2
✓ Branch 133 → 134 taken 146676 times.
✓ Branch 133 → 135 taken 3565587 times.
3724464 }
310 }
311
312 // If no matches were found, return a nullptr
313
2/2
✓ Branch 142 → 143 taken 307814 times.
✓ Branch 142 → 144 taken 27568 times.
335382 if (matches.empty())
314 307814 return nullptr;
315
316 // Tie-breaking: if multiple candidates match, narrow them by qualifier specificity and by preferring
317 // explicitly declared overloads over generic substitutions (see breakOverloadTie).
318
1/2
✓ Branch 144 → 145 taken 27568 times.
✗ Branch 144 → 221 not taken.
27568 breakOverloadTie(matches, reqArgs);
319
320 // Check if more than one function matches the requirements
321
1/2
✗ Branch 146 → 147 not taken.
✓ Branch 146 → 174 taken 27568 times.
27568 if (matches.size() > 1) {
322 std::stringstream errorMessage;
323 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
324 for (const Function *match : matches)
325 errorMessage << "\n " << match->getSignature();
326 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
327 }
328 27568 Function *matchedFunction = matches.front();
329 27568 matchedFunction->isNewlyInserted = false;
330
331 // Insert into cache
332
1/2
✓ Branch 175 → 176 taken 27568 times.
✗ Branch 175 → 221 not taken.
27568 lookupCache[cacheKey] = matchedFunction;
333
334 // Trigger revisit in type checker if required
335
1/2
✓ Branch 176 → 177 taken 27568 times.
✗ Branch 176 → 221 not taken.
27568 TypeChecker::requestRevisitIfRequired(matchedFunction);
336
337 // Return the very match
338 27568 return matchedFunction;
339 335383 }
340
341 3860205 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
342 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
343 bool strictQualifierMatching, bool &forceSubstantiation,
344 const ASTNode *callNode) {
345 // Check name requirement
346
2/2
✓ Branch 3 → 4 taken 3666470 times.
✓ Branch 3 → 5 taken 193735 times.
3860205 if (!matchName(candidate, reqName))
347 3666470 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
348
349 // Check 'this' type requirement
350
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 193735 times.
193735 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
351 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
352
353 // Check arg types requirement
354
2/2
✓ Branch 9 → 10 taken 157508 times.
✓ Branch 9 → 11 taken 36226 times.
193735 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
355 157508 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
356
357 // Check if there are unresolved generic types
358
2/2
✓ Branch 13 → 14 taken 2 times.
✓ Branch 13 → 15 taken 36224 times.
36226 if (typeMapping.size() < candidate.templateTypes.size())
359 2 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
360
361 // Substantiate return type
362 36224 substantiateReturnType(candidate, typeMapping, callNode);
363
364 // Set the match scope to the scope of the concrete substantiation
365 36224 const QualType &thisType = candidate.thisType;
366
2/2
✓ Branch 17 → 18 taken 26435 times.
✓ Branch 17 → 25 taken 9789 times.
36224 if (!thisType.is(TY_DYN)) {
367 // If we only have the generic struct scope, lookup the concrete manifestation scope
368
2/2
✓ Branch 18 → 19 taken 118 times.
✓ Branch 18 → 23 taken 26317 times.
26435 if (matchScope->isGenericScope) {
369 118 const Struct *spiceStruct = thisType.getStruct(candidate.declNode);
370
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 118 times.
118 assert(spiceStruct != nullptr);
371 118 matchScope = spiceStruct->scope;
372 }
373
1/2
✓ Branch 23 → 24 taken 26435 times.
✗ Branch 23 → 27 not taken.
26435 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
374 }
375
376 36224 return MatchResult::MATCHED;
377 }
378
379 /**
380 * Checks if the matching candidate fulfills the name requirement
381 *
382 * @param candidate Matching candidate function
383 * @param reqName Requested function name
384 * @return Fulfilled or not
385 */
386 3860205 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
387
388 /**
389 * Checks if the matching candidate fulfills the 'this' type requirement
390 *
391 * @param candidate Matching candidate function
392 * @param reqThisType Requested 'this' type
393 * @param typeMapping Concrete template type mapping
394 * @param strictQualifierMatching Match qualifiers strictly
395 * @param callNode Call AST node for printing error messages
396 * @return Fulfilled or not
397 */
398 193735 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
399 bool strictQualifierMatching, const ASTNode *callNode) {
400 193735 QualType &candidateThisType = candidate.thisType;
401
402 // Shortcut for procedures
403
7/10
✓ Branch 2 → 3 taken 193735 times.
✗ Branch 2 → 23 not taken.
✓ Branch 3 → 4 taken 121660 times.
✓ Branch 3 → 7 taken 72075 times.
✓ Branch 4 → 5 taken 121660 times.
✗ Branch 4 → 23 not taken.
✓ Branch 5 → 6 taken 121660 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 121660 times.
✓ Branch 8 → 10 taken 72075 times.
193735 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
404 121660 return true;
405
406 // Give the type matcher a way to retrieve instances of GenericType by their name
407 156881 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
408 12731 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
409 72075 };
410
411 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
412
2/4
✓ Branch 11 → 12 taken 72075 times.
✗ Branch 11 → 21 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 72075 times.
72075 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
413 strictQualifierMatching))
414 return false;
415
416 // Substantiate the candidate param type, based on the type mapping
417
3/4
✓ Branch 14 → 15 taken 72075 times.
✗ Branch 14 → 21 not taken.
✓ Branch 15 → 16 taken 13840 times.
✓ Branch 15 → 17 taken 58235 times.
72075 if (candidateThisType.hasAnyGenericParts())
418
1/2
✓ Branch 16 → 17 taken 13840 times.
✗ Branch 16 → 21 not taken.
13840 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
419
420 72075 return true;
421 72075 }
422
423 /**
424 * Checks if the matching candidate fulfills the argument types requirement
425 *
426 * @param candidate Matching candidate function
427 * @param reqArgs Requested argument types
428 * @param typeMapping Concrete template type mapping
429 * @param strictQualifierMatching Match qualifiers strictly
430 * @param needsSubstantiation We want to create a substantiation after successfully matching
431 * @param callNode Call AST node for printing error messages
432 * @return Fulfilled or not
433 */
434 193735 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
435 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
436 193735 std::vector<Param> &candidateParamList = candidate.paramList;
437
438 // If the number of arguments does not match with the number of params, the matching fails
439
6/6
✓ Branch 2 → 3 taken 193494 times.
✓ Branch 2 → 7 taken 241 times.
✓ Branch 5 → 6 taken 31258 times.
✓ Branch 5 → 7 taken 162236 times.
✓ Branch 8 → 9 taken 31258 times.
✓ Branch 8 → 10 taken 162477 times.
193735 if (!candidate.isVararg && reqArgs.size() != candidateParamList.size())
440 31258 return false;
441 // In the case of a vararg function, we only disallow fewer arguments than parameters
442
4/6
✓ Branch 10 → 11 taken 241 times.
✓ Branch 10 → 15 taken 162236 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 241 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 162477 times.
162477 if (candidate.isVararg && reqArgs.size() < candidateParamList.size())
443 return false;
444
445 // Give the type matcher a way to retrieve instances of GenericType by their name
446 336787 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
447 11833 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
448 162477 };
449
450 // Loop over all parameters
451
2/2
✓ Branch 66 → 20 taken 167082 times.
✓ Branch 66 → 67 taken 36226 times.
203308 for (size_t i = 0; i < reqArgs.size(); i++) {
452 // In the case of a vararg function candidate, we can accept additional arguments, that are not defined in the candidate,
453 // but we need to modify the candidate param list to accept them
454
6/6
✓ Branch 20 → 21 taken 983 times.
✓ Branch 20 → 24 taken 166099 times.
✓ Branch 22 → 23 taken 263 times.
✓ Branch 22 → 24 taken 720 times.
✓ Branch 25 → 26 taken 263 times.
✓ Branch 25 → 29 taken 166819 times.
167082 if (candidate.isVararg && i >= candidateParamList.size()) {
455
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));
456 263 needsSubstantiation = true; // We need to modify the candidate param types
457 263 continue;
458 }
459
460 // Retrieve actual and requested types
461
2/4
✓ Branch 29 → 30 taken 166819 times.
✗ Branch 29 → 84 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 166819 times.
166819 assert(!candidateParamList.at(i).isOptional);
462
1/2
✓ Branch 32 → 33 taken 166819 times.
✗ Branch 32 → 84 not taken.
166819 QualType &candidateType = candidateParamList.at(i).qualType;
463
1/2
✓ Branch 33 → 34 taken 166819 times.
✗ Branch 33 → 84 not taken.
166819 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
464
465 // Check if the requested param type matches the candidate param type. The type mapping may be extended
466
3/4
✓ Branch 36 → 37 taken 166819 times.
✗ Branch 36 → 84 not taken.
✓ Branch 37 → 38 taken 126250 times.
✓ Branch 37 → 39 taken 40569 times.
166819 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, requestedType, typeMapping, genericTypeResolver,
467 strictQualifierMatching))
468 126250 return false;
469
470 // Substantiate the candidate param type, based on the type mapping
471
3/4
✓ Branch 39 → 40 taken 40569 times.
✗ Branch 39 → 84 not taken.
✓ Branch 40 → 41 taken 9998 times.
✓ Branch 40 → 42 taken 30571 times.
40569 if (candidateType.hasAnyGenericParts())
472
1/2
✓ Branch 41 → 42 taken 9998 times.
✗ Branch 41 → 84 not taken.
9998 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, callNode);
473
474 // Check if we try to bind a non-ref temporary to a non-const ref parameter
475
3/4
✓ Branch 42 → 43 taken 40569 times.
✗ Branch 42 → 84 not taken.
✓ Branch 43 → 44 taken 1 time.
✓ Branch 43 → 54 taken 40568 times.
40569 if (!candidateType.canBind(requestedType, isArgTemporary)) {
476
1/2
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 53 not taken.
1 if (callNode)
477
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");
478 return false;
479 }
480
481 // If we have a function/procedure type we need to take care of the information, if it takes captures
482
9/12
✓ Branch 54 → 55 taken 40568 times.
✗ Branch 54 → 81 not taken.
✓ Branch 55 → 56 taken 40568 times.
✗ Branch 55 → 81 not taken.
✓ Branch 56 → 57 taken 59 times.
✓ Branch 56 → 60 taken 40509 times.
✓ Branch 57 → 58 taken 59 times.
✗ Branch 57 → 81 not taken.
✓ Branch 58 → 59 taken 9 times.
✓ Branch 58 → 60 taken 50 times.
✓ Branch 61 → 62 taken 9 times.
✓ Branch 61 → 64 taken 40559 times.
40568 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
483
1/2
✓ Branch 62 → 63 taken 9 times.
✗ Branch 62 → 83 not taken.
9 candidateType = candidateType.getWithLambdaCaptures();
484 9 needsSubstantiation = true;
485 }
486 }
487
488 36226 return true;
489 162477 }
490
491 /**
492 * Substantiates the candidate return type, based on the given type mapping
493 *
494 * @param candidate Matching candidate function
495 * @param typeMapping Concrete template type mapping
496 * @param callNode AST node for error messages
497 */
498 36224 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
499
2/2
✓ Branch 3 → 4 taken 3539 times.
✓ Branch 3 → 5 taken 32685 times.
36224 if (candidate.returnType.hasAnyGenericParts())
500 3539 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
501 36224 }
502
503 /**
504 * Searches the candidate template types for a generic type object with a certain name and return it
505 *
506 * @param candidate Matching candidate function
507 * @param templateTypeName Template type name
508 * @return Generic type object
509 */
510 24564 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
511 const std::string &templateTypeName) {
512
1/2
✓ Branch 19 → 4 taken 27689 times.
✗ Branch 19 → 20 not taken.
52253 for (const GenericType &templateType : candidate.templateTypes) {
513
3/4
✓ Branch 6 → 7 taken 27689 times.
✗ Branch 6 → 22 not taken.
✓ Branch 8 → 9 taken 24564 times.
✓ Branch 8 → 10 taken 3125 times.
27689 if (templateType.getSubType() == templateTypeName)
514 24564 return &templateType;
515 }
516 return nullptr;
517 }
518
519 /**
520 * Narrow a multi-match overload set by preferring the candidate whose parameter qualifiers most closely match
521 * the argument qualifiers. This resolves the typical copy-vs-move ctor ambiguity where both a `const T&`
522 * (copy) and a `T&` (move) ctor match a non-const lvalue argument - we prefer the non-const-ref candidate
523 * (move) since it requires no constification. When the argument is const, we prefer the const-ref candidate
524 * (copy) since binding to a non-const ref would require const-loss. As a secondary criterion, an explicitly
525 * declared (non-generic) overload is preferred over a generic substitution that matches equally well.
526 *
527 * Modifies `matches` in place, removing any candidate that scores worse than the best one. A no-op if there
528 * are fewer than two candidates.
529 *
530 * @param matches Candidate list to narrow
531 * @param reqArgs Argument list from the call site
532 */
533 27568 void FunctionManager::breakOverloadTie(std::vector<Function *> &matches, const ArgList &reqArgs) {
534
2/2
✓ Branch 3 → 4 taken 27559 times.
✓ Branch 3 → 5 taken 9 times.
27568 if (matches.size() < 2)
535 27559 return;
536
537 9 constexpr int CONSTIFY_PENALTY = 1;
538 9 constexpr int CONST_LOSS_PENALTY = 100;
539 // An exact type match is always preferred over a match that required an implicit upcast (struct to
540 // implemented interface, or struct to composed base struct). This keeps e.g. 'f(Derived)' preferred
541 // over 'f(Base)' when called with a Derived, instead of reporting an ambiguity.
542 9 constexpr int UPCAST_PENALTY = 1000;
543 45 const auto scoreSpecificity = [&](const Function *f) {
544 36 int penalty = 0;
545
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++) {
546
1/2
✓ Branch 3 → 4 taken 36 times.
✗ Branch 3 → 28 not taken.
36 const QualType &paramType = f->paramList.at(i).qualType;
547
1/2
✓ Branch 4 → 5 taken 36 times.
✗ Branch 4 → 28 not taken.
36 const QualType &argType = reqArgs.at(i).first;
548
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();
549
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();
550
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)
551 8 penalty += CONSTIFY_PENALTY;
552
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)
553 4 penalty += CONST_LOSS_PENALTY;
554 // Penalize matches that were only possible through an implicit upcast
555 36 QualType paramBase = paramType;
556 36 QualType argBase = argType;
557
1/2
✓ Branch 15 → 16 taken 36 times.
✗ Branch 15 → 28 not taken.
36 QualType::unwrapBothWithRefWrappers(paramBase, argBase);
558
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))
559 penalty += UPCAST_PENALTY;
560 }
561 36 return penalty;
562 9 };
563
564 9 int bestScore = std::numeric_limits<int>::max();
565
2/2
✓ Branch 20 → 7 taken 18 times.
✓ Branch 20 → 21 taken 9 times.
36 for (const Function *m : matches)
566
1/2
✓ Branch 9 → 10 taken 18 times.
✗ Branch 9 → 76 not taken.
18 bestScore = std::min(bestScore, scoreSpecificity(m));
567 9 std::vector<Function *> filtered;
568
1/2
✓ Branch 22 → 23 taken 9 times.
✗ Branch 22 → 80 not taken.
9 filtered.reserve(matches.size());
569
2/2
✓ Branch 39 → 25 taken 18 times.
✓ Branch 39 → 40 taken 9 times.
36 for (Function *m : matches)
570
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)
571
1/2
✓ Branch 29 → 30 taken 12 times.
✗ Branch 29 → 78 not taken.
12 filtered.push_back(m);
572 9 matches = std::move(filtered);
573
574 // Secondary tie-break: prefer an explicitly declared (non-generic) overload over a generic substitution
575 // when both match equally well, mirroring C++ overload resolution where a non-template wins over a
576 // template specialization. This resolves e.g. the copy ctor 'Any.ctor(const Any&)' vs. the value ctor
577 // 'Any.ctor<Any>(const Any&)' ambiguity when copy-constructing from another value of the same type. It is
578 // applied after the qualifier-specificity narrowing above, so a more specific generic match still wins.
579 // A generic substitution that loses here and was only inserted for this very match is removed from its
580 // declaration's manifestation list again, so the IR generator never emits a manifestation that was never
581 // type-checked (and so we leave no dead code behind).
582
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(); })) {
583
2/2
✓ Branch 71 → 52 taken 6 times.
✓ Branch 71 → 72 taken 3 times.
12 for (Function *m : matches)
584
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)
585
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);
586
1/2
✓ Branch 72 → 73 taken 3 times.
✗ Branch 72 → 80 not taken.
9 std::erase_if(matches, [](const Function *m) { return m->isGenericSubstantiation(); });
587 }
588 9 }
589
590 /**
591 * Calculate the cache key for the function lookup cache
592 *
593 * @param scope Scope to match against
594 * @param name Function name requirement
595 * @param thisType This type requirement
596 * @param args Argument requirement
597 * @param templateTypes Template type requirement
598 * @return Cache key
599 */
600 433259 uint64_t FunctionManager::getCacheKey(const Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
601 const QualTypeList &templateTypes) {
602 433259 uint64_t hash = 0;
603 433259 hashCombine64(hash, hashPointer(scope));
604 433259 hashCombine64(hash, std::hash<std::string>{}(name));
605 433259 hashCombine64(hash, std::hash<QualType>{}(thisType));
606
2/2
✓ Branch 27 → 10 taken 679351 times.
✓ Branch 27 → 28 taken 433259 times.
2225220 for (const auto &[first, second] : args) {
607 679351 hashCombine64(hash, std::hash<QualType>{}(first));
608 679351 hashCombine64(hash, std::hash<bool>{}(second));
609 }
610 433259 hashCombine64(hash, hashVector(templateTypes));
611 433259 return hash;
612 }
613
614 11270 bool FunctionManager::hasCtor(const Scope *matchScope, CtorKind kind) {
615
5/8
✓ Branch 2 → 3 taken 11270 times.
✗ Branch 2 → 60 not taken.
✓ Branch 3 → 4 taken 11270 times.
✗ Branch 3 → 60 not taken.
✓ Branch 4 → 5 taken 11270 times.
✗ Branch 4 → 60 not taken.
✓ Branch 55 → 6 taken 51687 times.
✓ Branch 55 → 56 taken 6483 times.
58170 for (const auto &manifestations : matchScope->functions | std::views::values) {
616
5/8
✓ Branch 7 → 8 taken 51687 times.
✗ Branch 7 → 59 not taken.
✓ Branch 8 → 9 taken 51687 times.
✗ Branch 8 → 59 not taken.
✓ Branch 9 → 10 taken 51687 times.
✗ Branch 9 → 59 not taken.
✓ Branch 51 → 11 taken 56916 times.
✓ Branch 51 → 52 taken 46900 times.
103816 for (const auto &function : manifestations | std::views::values) {
617 // If it is no ctor, skip it
618
3/4
✓ Branch 12 → 13 taken 56916 times.
✗ Branch 12 → 59 not taken.
✓ Branch 13 → 14 taken 35427 times.
✓ Branch 13 → 15 taken 21489 times.
56916 if (function.name != CTOR_FUNCTION_NAME)
619 35427 continue;
620 // Classify the ctor based on its parameter list
621
6/8
✓ Branch 16 → 17 taken 13817 times.
✓ Branch 16 → 25 taken 7672 times.
✓ Branch 17 → 18 taken 13817 times.
✗ Branch 17 → 58 not taken.
✓ Branch 18 → 19 taken 13817 times.
✗ Branch 18 → 58 not taken.
✓ Branch 19 → 20 taken 6111 times.
✓ Branch 19 → 25 taken 7706 times.
27600 const bool singleSelfRefParam = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
622
5/8
✓ Branch 20 → 21 taken 6111 times.
✗ Branch 20 → 58 not taken.
✓ Branch 21 → 22 taken 6111 times.
✗ Branch 21 → 58 not taken.
✓ Branch 22 → 23 taken 6111 times.
✗ Branch 22 → 58 not taken.
✓ Branch 23 → 24 taken 5064 times.
✓ Branch 23 → 25 taken 1047 times.
6111 function.paramList.at(0).qualType.getBase() == function.thisType;
623
6/8
✓ Branch 26 → 27 taken 5064 times.
✓ Branch 26 → 31 taken 16425 times.
✓ Branch 27 → 28 taken 5064 times.
✗ Branch 27 → 59 not taken.
✓ Branch 28 → 29 taken 5064 times.
✗ Branch 28 → 59 not taken.
✓ Branch 29 → 30 taken 5051 times.
✓ Branch 29 → 31 taken 13 times.
21489 const bool isCopyCtor = singleSelfRefParam && function.paramList.at(0).qualType.isConstRef();
624
6/8
✓ Branch 32 → 33 taken 5064 times.
✓ Branch 32 → 37 taken 16425 times.
✓ Branch 33 → 34 taken 5064 times.
✗ Branch 33 → 59 not taken.
✓ Branch 34 → 35 taken 5064 times.
✗ Branch 34 → 59 not taken.
✓ Branch 35 → 36 taken 13 times.
✓ Branch 35 → 37 taken 5051 times.
21489 const bool isMoveCtor = singleSelfRefParam && !function.paramList.at(0).qualType.isConstRef();
625
3/4
✓ Branch 38 → 39 taken 13408 times.
✓ Branch 38 → 42 taken 4762 times.
✓ Branch 38 → 45 taken 3319 times.
✗ Branch 38 → 49 not taken.
21489 switch (kind) {
626 13408 case CtorKind::COPY:
627
2/2
✓ Branch 39 → 40 taken 2455 times.
✓ Branch 39 → 41 taken 10953 times.
13408 if (isCopyCtor)
628 4787 return true;
629 10953 break;
630 4762 case CtorKind::MOVE:
631
2/2
✓ Branch 42 → 43 taken 4 times.
✓ Branch 42 → 44 taken 4758 times.
4762 if (isMoveCtor)
632 4 return true;
633 4758 break;
634 3319 case CtorKind::ANY_NON_COPY_NON_MOVE:
635
4/4
✓ Branch 45 → 46 taken 2330 times.
✓ Branch 45 → 48 taken 989 times.
✓ Branch 46 → 47 taken 2328 times.
✓ Branch 46 → 48 taken 2 times.
3319 if (!isCopyCtor && !isMoveCtor)
636 2328 return true;
637 991 break;
638 }
639 }
640 }
641 6483 return false;
642 }
643
644 2430 bool FunctionManager::hasAnyNonCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::ANY_NON_COPY_NON_MOVE); }
645
646 6666 bool FunctionManager::hasCopyCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::COPY); }
647
648 2170 bool FunctionManager::hasUserCopyCtor(const Scope *matchScope) {
649
5/8
✓ Branch 2 → 3 taken 2170 times.
✗ Branch 2 → 42 not taken.
✓ Branch 3 → 4 taken 2170 times.
✗ Branch 3 → 42 not taken.
✓ Branch 4 → 5 taken 2170 times.
✗ Branch 4 → 42 not taken.
✓ Branch 37 → 6 taken 10040 times.
✓ Branch 37 → 38 taken 1956 times.
11996 for (const auto &manifestations : matchScope->functions | std::views::values) {
650
5/8
✓ Branch 7 → 8 taken 10040 times.
✗ Branch 7 → 41 not taken.
✓ Branch 8 → 9 taken 10040 times.
✗ Branch 8 → 41 not taken.
✓ Branch 9 → 10 taken 10040 times.
✗ Branch 9 → 41 not taken.
✓ Branch 34 → 11 taken 10860 times.
✓ Branch 34 → 35 taken 9826 times.
20686 for (const auto &function : manifestations | std::views::values) {
651
7/8
✓ Branch 12 → 13 taken 10860 times.
✗ Branch 12 → 41 not taken.
✓ Branch 13 → 14 taken 4751 times.
✓ Branch 13 → 15 taken 6109 times.
✓ Branch 14 → 15 taken 1488 times.
✓ Branch 14 → 16 taken 3263 times.
✓ Branch 17 → 18 taken 7597 times.
✓ Branch 17 → 19 taken 3263 times.
10860 if (function.name != CTOR_FUNCTION_NAME || function.implicitDefault)
652 7597 continue;
653
6/8
✓ Branch 20 → 21 taken 2304 times.
✓ Branch 20 → 29 taken 959 times.
✓ Branch 21 → 22 taken 2304 times.
✗ Branch 21 → 40 not taken.
✓ Branch 22 → 23 taken 2304 times.
✗ Branch 22 → 40 not taken.
✓ Branch 23 → 24 taken 350 times.
✓ Branch 23 → 29 taken 1954 times.
3613 const bool isCopyCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isConstRef() &&
654
5/8
✓ Branch 24 → 25 taken 350 times.
✗ Branch 24 → 40 not taken.
✓ Branch 25 → 26 taken 350 times.
✗ Branch 25 → 40 not taken.
✓ Branch 26 → 27 taken 350 times.
✗ Branch 26 → 40 not taken.
✓ Branch 27 → 28 taken 214 times.
✓ Branch 27 → 29 taken 136 times.
350 function.paramList.at(0).qualType.getBase() == function.thisType;
655
2/2
✓ Branch 30 → 31 taken 214 times.
✓ Branch 30 → 32 taken 3049 times.
3263 if (isCopyCtor)
656 214 return true;
657 }
658 }
659 1956 return false;
660 }
661
662 2174 bool FunctionManager::hasMoveCtor(const Scope *matchScope) { return hasCtor(matchScope, CtorKind::MOVE); }
663
664 13216 Function *FunctionManager::findMoveCtor(Scope *matchScope) {
665
5/8
✓ Branch 2 → 3 taken 13216 times.
✗ Branch 2 → 41 not taken.
✓ Branch 3 → 4 taken 13216 times.
✗ Branch 3 → 41 not taken.
✓ Branch 4 → 5 taken 13216 times.
✗ Branch 4 → 41 not taken.
✓ Branch 36 → 6 taken 124887 times.
✓ Branch 36 → 37 taken 13169 times.
138056 for (auto &manifestations : matchScope->functions | std::views::values) {
666
5/8
✓ Branch 7 → 8 taken 124887 times.
✗ Branch 7 → 40 not taken.
✓ Branch 8 → 9 taken 124887 times.
✗ Branch 8 → 40 not taken.
✓ Branch 9 → 10 taken 124887 times.
✗ Branch 9 → 40 not taken.
✓ Branch 33 → 11 taken 161497 times.
✓ Branch 33 → 34 taken 124840 times.
286337 for (auto &function : manifestations | std::views::values) {
667
3/4
✓ Branch 12 → 13 taken 161497 times.
✗ Branch 12 → 40 not taken.
✓ Branch 13 → 14 taken 123314 times.
✓ Branch 13 → 15 taken 38183 times.
161497 if (function.name != CTOR_FUNCTION_NAME)
668 123314 continue;
669
4/6
✓ Branch 17 → 18 taken 27621 times.
✗ Branch 17 → 39 not taken.
✓ Branch 18 → 19 taken 27621 times.
✗ Branch 18 → 39 not taken.
✓ Branch 19 → 20 taken 16746 times.
✓ Branch 19 → 28 taken 10875 times.
65804 const bool isMoveCtor = function.paramList.size() == 1 && function.paramList.at(0).qualType.isRef() &&
670
6/8
✓ Branch 16 → 17 taken 27621 times.
✓ Branch 16 → 28 taken 10562 times.
✓ Branch 20 → 21 taken 16746 times.
✗ Branch 20 → 39 not taken.
✓ Branch 21 → 22 taken 16746 times.
✗ Branch 21 → 39 not taken.
✓ Branch 22 → 23 taken 2929 times.
✓ Branch 22 → 28 taken 13817 times.
68733 !function.paramList.at(0).qualType.isConstRef() &&
671
5/8
✓ Branch 23 → 24 taken 2929 times.
✗ Branch 23 → 39 not taken.
✓ Branch 24 → 25 taken 2929 times.
✗ Branch 24 → 39 not taken.
✓ Branch 25 → 26 taken 2929 times.
✗ Branch 25 → 39 not taken.
✓ Branch 26 → 27 taken 47 times.
✓ Branch 26 → 28 taken 2882 times.
2929 function.paramList.at(0).qualType.getBase() == function.thisType;
672
2/2
✓ Branch 29 → 30 taken 47 times.
✓ Branch 29 → 31 taken 38136 times.
38183 if (isMoveCtor)
673 47 return &function;
674 }
675 }
676 13169 return nullptr;
677 }
678
679 17773 bool FunctionManager::hasDtor(const Scope *matchScope) {
680
5/8
✓ Branch 2 → 3 taken 17773 times.
✗ Branch 2 → 24 not taken.
✓ Branch 3 → 4 taken 17773 times.
✗ Branch 3 → 24 not taken.
✓ Branch 4 → 5 taken 17773 times.
✗ Branch 4 → 24 not taken.
✓ Branch 20 → 6 taken 138835 times.
✓ Branch 20 → 21 taken 9614 times.
148449 for (const auto &manifestations : matchScope->functions | std::views::values)
681
5/8
✓ Branch 7 → 8 taken 138835 times.
✗ Branch 7 → 23 not taken.
✓ Branch 8 → 9 taken 138835 times.
✗ Branch 8 → 23 not taken.
✓ Branch 9 → 10 taken 138835 times.
✗ Branch 9 → 23 not taken.
✓ Branch 17 → 11 taken 158610 times.
✓ Branch 17 → 18 taken 130676 times.
289286 for (const auto &function : manifestations | std::views::values)
682
3/4
✓ Branch 12 → 13 taken 158610 times.
✗ Branch 12 → 23 not taken.
✓ Branch 13 → 14 taken 8159 times.
✓ Branch 13 → 15 taken 150451 times.
158610 if (function.name == DTOR_FUNCTION_NAME)
683 8159 return true;
684 9614 return false;
685 }
686
687 /**
688 * Clear the lookup cache
689 */
690 557 void FunctionManager::cleanup() {
691 557 lookupCache.clear();
692 557 lookupCacheHits = 0;
693 557 lookupCacheMisses = 0;
694 557 }
695
696 /**
697 * Dump usage statistics for the lookup cache
698 */
699 310 std::string FunctionManager::dumpLookupCacheStatistics() {
700
1/2
✓ Branch 2 → 3 taken 310 times.
✗ Branch 2 → 22 not taken.
310 std::stringstream stats;
701
2/4
✓ Branch 3 → 4 taken 310 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 310 times.
✗ Branch 4 → 20 not taken.
310 stats << "FunctionManager lookup cache statistics:" << std::endl;
702
3/6
✓ Branch 5 → 6 taken 310 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 310 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 310 times.
✗ Branch 8 → 20 not taken.
310 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
703
3/6
✓ Branch 9 → 10 taken 310 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 310 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 310 times.
✗ Branch 11 → 20 not taken.
310 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
704
3/6
✓ Branch 12 → 13 taken 310 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 310 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 310 times.
✗ Branch 14 → 20 not taken.
310 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
705
1/2
✓ Branch 15 → 16 taken 310 times.
✗ Branch 15 → 20 not taken.
620 return stats.str();
706 310 }
707
708 } // namespace spice::compiler
709