GCC Code Coverage Report


Directory: ../
File: src/typechecker/FunctionManager.cpp
Date: 2024-11-22 23:10:59
Exec Total Coverage
Lines: 241 244 98.8%
Functions: 19 19 100.0%
Branches: 272 424 64.2%

Line Branch Exec Source
1 // Copyright (c) 2021-2024 ChilliBits. All rights reserved.
2
3 #include "FunctionManager.h"
4 #include "TypeChecker.h"
5
6 #include <ast/ASTNodes.h>
7 #include <exception/SemanticError.h>
8 #include <model/GenericType.h>
9 #include <symboltablebuilder/Scope.h>
10 #include <symboltablebuilder/SymbolTableBuilder.h>
11 #include <typechecker/TypeMatcher.h>
12 #include <util/CodeLoc.h>
13 #include <util/CustomHashFunctions.h>
14
15 namespace spice::compiler {
16
17 // Static member initialization
18 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
19
20 8071 Function *FunctionManager::insert(Scope *insertScope, const Function &baseFunction, std::vector<Function *> *nodeFunctionList) {
21 // Open a new manifestation list for the function definition
22
3/6
✓ Branch 1 taken 8071 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8071 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 8071 times.
✗ Branch 8 not taken.
8071 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
23
2/4
✓ Branch 2 taken 8071 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 8071 times.
✗ Branch 6 not taken.
8071 insertScope->functions.insert({fctId, FunctionManifestationList()});
24
25 // Collect substantiations
26 8071 std::vector<Function> manifestations;
27
1/2
✓ Branch 1 taken 8071 times.
✗ Branch 2 not taken.
8071 substantiateOptionalParams(baseFunction, manifestations);
28
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8071 times.
8071 assert(!manifestations.empty());
29
30 // Save substantiations in declaration node
31 8071 Function *manifestationPtr = nullptr;
32
2/2
✓ Branch 5 taken 8731 times.
✓ Branch 6 taken 8069 times.
16800 for (const Function &manifestation : manifestations) {
33
2/2
✓ Branch 1 taken 8729 times.
✓ Branch 2 taken 2 times.
8731 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
34
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8729 times.
8729 assert(manifestationPtr != nullptr);
35
1/2
✓ Branch 0 taken 8729 times.
✗ Branch 1 not taken.
8729 if (nodeFunctionList)
36
1/2
✓ Branch 1 taken 8729 times.
✗ Branch 2 not taken.
8729 nodeFunctionList->push_back(manifestationPtr);
37 }
38
39
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8069 times.
8069 if (!nodeFunctionList)
40 return manifestationPtr;
41
42
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8069 times.
8069 assert(!nodeFunctionList->empty());
43 8069 return nodeFunctionList->front();
44 8073 }
45
46 /**
47 * Create definite functions from ambiguous ones, in regard to optional arguments.
48 *
49 * Example:
50 * int testFunc(string, int?, double?)
51 * gets
52 * int testFunc(string)
53 * int testFunc(string, int)
54 * int testFunc(string, int, double)
55 *
56 * This method also accepts functions, that are already definite, but does nothing to them.
57 *
58 * @param baseFunction Ambiguous base function
59 * @param manifestations Vector to store the definite manifestations
60 * @return True, if there were optional arguments found
61 */
62 8071 void FunctionManager::substantiateOptionalParams(const Function &baseFunction, std::vector<Function> &manifestations) {
63 // Handle the case of no parameters -> simply return the base function
64
2/2
✓ Branch 1 taken 2322 times.
✓ Branch 2 taken 5749 times.
8071 if (baseFunction.paramList.empty()) {
65
1/2
✓ Branch 1 taken 2322 times.
✗ Branch 2 not taken.
2322 manifestations.push_back(baseFunction);
66 2322 return;
67 }
68
69 5749 ParamList currentFunctionParamTypes;
70
1/2
✓ Branch 2 taken 5749 times.
✗ Branch 3 not taken.
5749 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
71 5749 bool metFirstOptionalParam = false;
72
1/2
✓ Branch 1 taken 5749 times.
✗ Branch 2 not taken.
5749 Function manifestation = baseFunction;
73
74 // Loop over all parameters
75
2/2
✓ Branch 5 taken 9198 times.
✓ Branch 6 taken 5749 times.
14947 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
76 // Check if we have a mandatory parameter
77
2/2
✓ Branch 0 taken 8538 times.
✓ Branch 1 taken 660 times.
9198 if (!isOptional) {
78
1/2
✓ Branch 1 taken 8538 times.
✗ Branch 2 not taken.
8538 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
79 8538 continue;
80 }
81
82 // Add substantiation without the optional parameter
83
2/2
✓ Branch 0 taken 656 times.
✓ Branch 1 taken 4 times.
660 if (!metFirstOptionalParam) {
84
1/2
✓ Branch 1 taken 656 times.
✗ Branch 2 not taken.
656 manifestation.paramList = currentFunctionParamTypes;
85
1/2
✓ Branch 1 taken 656 times.
✗ Branch 2 not taken.
656 manifestations.push_back(manifestation);
86 // Now we cannot accept mandatory parameters anymore
87 656 metFirstOptionalParam = true;
88 }
89
90 // Add substantiation with the optional parameter
91
1/2
✓ Branch 1 taken 660 times.
✗ Branch 2 not taken.
660 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
92
1/2
✓ Branch 1 taken 660 times.
✗ Branch 2 not taken.
660 manifestation.paramList = currentFunctionParamTypes;
93
1/2
✓ Branch 1 taken 660 times.
✗ Branch 2 not taken.
660 manifestations.push_back(manifestation);
94 }
95
96 // Ensure at least once manifestation
97
2/2
✓ Branch 1 taken 5093 times.
✓ Branch 2 taken 656 times.
5749 if (manifestations.empty())
98
1/2
✓ Branch 1 taken 5093 times.
✗ Branch 2 not taken.
5093 manifestations.push_back(baseFunction);
99 5749 }
100
101 2 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
102 2 ParamList paramList;
103
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 2 times.
4 for (const QualType &paramType : paramTypes)
104
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 paramList.push_back({paramType, false});
105
4/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
4 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
106 2 }
107
108 10211 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
109
2/4
✓ Branch 1 taken 10211 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10211 times.
10211 assert(newManifestation.hasSubstantiatedParams());
110
111
1/2
✓ Branch 1 taken 10211 times.
✗ Branch 2 not taken.
10211 const std::string signature = newManifestation.getSignature();
112
113 // Check if the function exists already
114
5/8
✓ Branch 1 taken 10211 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10211 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10211 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 113245 times.
✓ Branch 13 taken 10209 times.
123454 for (const auto &manifestations : insertScope->functions | std::views::values) {
115
3/4
✓ Branch 1 taken 113245 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 113243 times.
113245 if (manifestations.contains(signature)) {
116
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (newManifestation.isFunction())
117
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 throw SemanticError(declNode, FUNCTION_DECLARED_TWICE, "'" + signature + "' is declared twice");
118 else
119
3/6
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
1 throw SemanticError(declNode, PROCEDURE_DECLARED_TWICE, "'" + signature + "' is declared twice");
120 }
121 }
122
123 // Retrieve the matching manifestation list of the scope
124
3/6
✓ Branch 1 taken 10209 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 10209 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 10209 times.
✗ Branch 8 not taken.
10209 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
125
2/4
✓ Branch 1 taken 10209 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 10209 times.
10209 assert(insertScope->functions.contains(fctId));
126
1/2
✓ Branch 1 taken 10209 times.
✗ Branch 2 not taken.
10209 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
127
128 // Add substantiated function
129
1/2
✓ Branch 1 taken 10209 times.
✗ Branch 2 not taken.
10209 manifestationList.emplace(signature, newManifestation);
130
1/2
✓ Branch 1 taken 10209 times.
✗ Branch 2 not taken.
20418 return &manifestationList.at(signature);
131 10211 }
132
133 /**
134 * Checks if a function exists by matching it, but not setting it to used
135 *
136 * @param matchScope Scope to match against
137 * @param reqName Function name requirement
138 * @param reqThisType This type requirement
139 * @param reqArgs Argument requirement
140 * @param strictSpecifierMatching Match argument and this type specifiers strictly
141 * @return Found function or nullptr
142 */
143 16669 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
144 const ArgList &reqArgs, bool strictSpecifierMatching) {
145
2/4
✓ Branch 1 taken 16669 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 16669 times.
16669 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
146
147 // Do cache lookup
148
1/2
✓ Branch 2 taken 16669 times.
✗ Branch 3 not taken.
16669 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
149
3/4
✓ Branch 1 taken 16669 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3416 times.
✓ Branch 4 taken 13253 times.
16669 if (lookupCache.contains(cacheKey))
150
1/2
✓ Branch 1 taken 3416 times.
✗ Branch 2 not taken.
3416 return lookupCache.at(cacheKey);
151
152 5190 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
153
5/8
✓ Branch 1 taken 13253 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 13082 times.
✓ Branch 4 taken 171 times.
✓ Branch 6 taken 13082 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 13082 times.
✗ Branch 9 not taken.
13253 const bool requestedFullySubstantiated = !reqThisType.hasAnyGenericParts() && std::ranges::none_of(reqArgs, pred);
154
155 // Copy the registry to prevent iterating over items, that are created within the loop
156
1/2
✓ Branch 1 taken 13253 times.
✗ Branch 2 not taken.
13253 FunctionRegistry functionRegistry = matchScope->functions;
157 // Loop over function registry to find functions, that match the requirements of the call
158 13253 std::vector<const Function *> matches;
159
2/2
✓ Branch 7 taken 92455 times.
✓ Branch 8 taken 13253 times.
105708 for (const auto &[defCodeLocStr, m] : functionRegistry) {
160 // Copy the manifestation list to prevent iterating over items, that are created within the loop
161
1/2
✓ Branch 1 taken 92455 times.
✗ Branch 2 not taken.
92455 const FunctionManifestationList manifestations = m;
162
2/2
✓ Branch 6 taken 100191 times.
✓ Branch 7 taken 41649 times.
141840 for (const auto &[signature, presetFunction] : manifestations) {
163
2/4
✓ Branch 1 taken 100191 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 100191 times.
100191 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
164
165 // - search for concrete fct: Only match against fully substantiated versions to prevent double matching of a function
166 // - search for generic fct: Only match against generic preset functions
167
3/4
✓ Branch 1 taken 100191 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 35087 times.
✓ Branch 4 taken 65104 times.
100191 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
168 49385 continue;
169
170 // Copy the function to be able to substantiate types
171
1/2
✓ Branch 1 taken 65104 times.
✗ Branch 2 not taken.
65104 Function candidate = presetFunction;
172
173 // Create empty type mapping
174 65104 TypeMapping &typeMapping = candidate.typeMapping;
175
176 65104 bool forceSubstantiation = false;
177
1/2
✓ Branch 1 taken 65104 times.
✗ Branch 2 not taken.
65104 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
178 strictSpecifierMatching, forceSubstantiation, nullptr);
179
2/2
✓ Branch 0 taken 48958 times.
✓ Branch 1 taken 16146 times.
65104 if (matchResult == MatchResult::SKIP_FUNCTION)
180 48958 break; // Leave the whole function
181
2/2
✓ Branch 0 taken 14298 times.
✓ Branch 1 taken 1848 times.
16146 if (matchResult == MatchResult::SKIP_MANIFESTATION)
182 14298 continue; // Leave this manifestation and try the next one
183
184 // Add to matches
185
3/6
✓ Branch 1 taken 1848 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1848 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1848 times.
✗ Branch 8 not taken.
1848 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
186
187 1848 break; // Leave the whole manifestation list to not double-match the manifestation
188
2/2
✓ Branch 1 taken 14298 times.
✓ Branch 2 taken 50806 times.
65104 }
189 92455 }
190
191 // Return the very match or a nullptr
192
2/2
✓ Branch 1 taken 1848 times.
✓ Branch 2 taken 11405 times.
13253 return !matches.empty() ? matches.front() : nullptr;
193 13253 }
194
195 /**
196 * Check if there is a function in the scope, fulfilling all given requirements and if found, return it.
197 * If more than one function matches the requirement, an error gets thrown.
198 *
199 * @param typeChecker Type Checker
200 * @param matchScope Scope to match against
201 * @param reqName Function name requirement
202 * @param reqThisType This type requirement
203 * @param reqArgs Argument requirement
204 * @param templateTypeHints Template type requirement
205 * @param strictSpecifierMatching Match argument and this type specifiers strictly
206 * @param callNode Call AST node for printing error messages
207 * @return Matched function or nullptr
208 */
209 42480 Function *FunctionManager::match(TypeChecker *typeChecker, Scope *matchScope, const std::string &reqName,
210 const QualType &reqThisType, const ArgList &reqArgs, const QualTypeList &templateTypeHints,
211 bool strictSpecifierMatching, const ASTNode *callNode) {
212
2/4
✓ Branch 1 taken 42480 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 42480 times.
42480 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
213
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42480 times.
42480 assert(typeChecker != nullptr && "The match() function must be called from the TypeChecker");
214
215 // Do cache lookup
216
1/2
✓ Branch 1 taken 42480 times.
✗ Branch 2 not taken.
42480 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
217
3/4
✓ Branch 1 taken 42480 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8555 times.
✓ Branch 4 taken 33925 times.
42480 if (lookupCache.contains(cacheKey))
218
1/2
✓ Branch 1 taken 8555 times.
✗ Branch 2 not taken.
8555 return lookupCache.at(cacheKey);
219
220 // Copy the registry to prevent iterating over items, that are created within the loop
221
1/2
✓ Branch 1 taken 33925 times.
✗ Branch 2 not taken.
33925 FunctionRegistry functionRegistry = matchScope->functions;
222 // Loop over function registry to find functions, that match the requirements of the call
223 33925 std::vector<Function *> matches;
224
2/2
✓ Branch 7 taken 300686 times.
✓ Branch 8 taken 33924 times.
334610 for (const auto &[fctId, m] : functionRegistry) {
225 // Copy the manifestation list to prevent iterating over items, that are created within the loop
226
1/2
✓ Branch 1 taken 300686 times.
✗ Branch 2 not taken.
300686 const FunctionManifestationList manifestations = m;
227
2/2
✓ Branch 6 taken 321965 times.
✓ Branch 7 taken 25936 times.
347901 for (const auto &[signature, presetFunction] : manifestations) {
228
2/4
✓ Branch 1 taken 321965 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 321965 times.
321965 assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point
229
230 // Skip generic substantiations to prevent double matching of a function
231
3/4
✓ Branch 1 taken 321965 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 19740 times.
✓ Branch 4 taken 302225 times.
321965 if (presetFunction.isGenericSubstantiation())
232 47215 continue;
233
234 // Copy the function to be able to substantiate types
235
1/2
✓ Branch 1 taken 302225 times.
✗ Branch 2 not taken.
302225 Function candidate = presetFunction;
236
237 // Prepare type mapping, based on the given initial type mapping
238 302225 TypeMapping &typeMapping = candidate.typeMapping;
239 302225 typeMapping.clear();
240
2/2
✓ Branch 3 taken 2694 times.
✓ Branch 4 taken 302225 times.
304919 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
241
2/4
✓ Branch 1 taken 2694 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2694 times.
✗ Branch 5 not taken.
2694 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
242
1/2
✓ Branch 1 taken 2694 times.
✗ Branch 2 not taken.
2694 const QualType &templateType = templateTypeHints.at(i);
243
2/4
✓ Branch 1 taken 2694 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2694 times.
✗ Branch 5 not taken.
2694 typeMapping.insert({typeName, templateType});
244 }
245
246 302225 bool forceSubstantiation = false;
247
2/2
✓ Branch 1 taken 302224 times.
✓ Branch 2 taken 1 times.
302225 MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
248 strictSpecifierMatching, forceSubstantiation, callNode);
249
2/2
✓ Branch 0 taken 273178 times.
✓ Branch 1 taken 29046 times.
302224 if (matchResult == MatchResult::SKIP_FUNCTION)
250 273178 break; // Leave the whole function
251
2/2
✓ Branch 0 taken 23791 times.
✓ Branch 1 taken 5255 times.
29046 if (matchResult == MatchResult::SKIP_MANIFESTATION)
252 23791 continue; // Leave this manifestation and try the next one
253
254 // We found a match! -> Set the actual candidate and its entry to used
255 5255 candidate.used = true;
256 5255 candidate.entry->used = true;
257
258 // Check if the function is generic needs to be substantiated
259
6/6
✓ Branch 1 taken 3687 times.
✓ Branch 2 taken 1568 times.
✓ Branch 3 taken 3684 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3684 times.
✓ Branch 6 taken 1571 times.
5255 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
260
5/10
✓ Branch 1 taken 3684 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3684 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3684 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 3684 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 3684 times.
✗ Branch 12 not taken.
3684 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
261
3/6
✓ Branch 1 taken 3684 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3684 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3684 times.
✗ Branch 8 not taken.
3684 matches.push_back(&matchScope->functions.at(fctId).at(signature));
262 3684 matches.back()->used = true;
263 3684 continue; // Match was successful -> match the next function
264 }
265
266 // Check if we already have this manifestation and can simply re-use it
267
1/2
✓ Branch 1 taken 1571 times.
✗ Branch 2 not taken.
1571 const std::string nonGenericSignature = candidate.getSignature();
268
4/6
✓ Branch 1 taken 1571 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1571 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 91 times.
✓ Branch 7 taken 1480 times.
1571 if (matchScope->functions.at(fctId).contains(nonGenericSignature)) {
269
3/6
✓ Branch 1 taken 91 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 91 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 91 times.
✗ Branch 8 not taken.
91 matches.push_back(&matchScope->functions.at(fctId).at(nonGenericSignature));
270 91 break; // Leave the whole manifestation list to not double-match the manifestation
271 }
272
273 // Insert the substantiated version if required
274
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
275
2/4
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1480 times.
✗ Branch 5 not taken.
1480 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
276 1480 substantiatedFunction->alreadyTypeChecked = false;
277
2/4
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1480 times.
✗ Branch 5 not taken.
1480 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
278
279 // Copy function entry
280
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 const std::string newSignature = substantiatedFunction->getSignature(false);
281
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
282
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newSignature);
283
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1480 times.
1480 assert(substantiatedFunction->entry != nullptr);
284
285 // Copy function scope
286
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 const std::string oldSignature = presetFunction.getSignature(false);
287
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 Scope *childScope = matchScope->copyChildScope(oldSignature, newSignature);
288
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1480 times.
1480 assert(childScope != nullptr);
289 1480 childScope->isGenericScope = false;
290 1480 substantiatedFunction->bodyScope = childScope;
291
292 // Insert symbols for generic type names with concrete types into the child block
293
2/2
✓ Branch 6 taken 1767 times.
✓ Branch 7 taken 1480 times.
3247 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
294
2/4
✓ Branch 1 taken 1767 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1767 times.
✗ Branch 5 not taken.
1767 childScope->insertGenericType(typeName, GenericType(concreteType));
295
296 // Substantiate the 'this' entry in the new function scope
297
6/6
✓ Branch 0 taken 1229 times.
✓ Branch 1 taken 251 times.
✓ Branch 3 taken 1228 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1228 times.
✓ Branch 6 taken 252 times.
1480 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
298
1/2
✓ Branch 1 taken 1228 times.
✗ Branch 2 not taken.
3684 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1228 times.
1228 assert(thisEntry != nullptr);
300
2/4
✓ Branch 1 taken 1228 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1228 times.
✗ Branch 5 not taken.
1228 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
301 }
302
303 // Add to matched functions
304
1/2
✓ Branch 1 taken 1480 times.
✗ Branch 2 not taken.
1480 matches.push_back(substantiatedFunction);
305
306 1480 break; // Leave the whole manifestation list to not double-match the manifestation
307
2/2
✓ Branch 4 taken 27475 times.
✓ Branch 5 taken 274749 times.
303796 }
308 300686 }
309
310 // If no matches were found, return a nullptr
311
2/2
✓ Branch 1 taken 28670 times.
✓ Branch 2 taken 5254 times.
33924 if (matches.empty())
312 28670 return nullptr;
313
314 // Check if more than one function matches the requirements
315
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5253 times.
5254 if (matches.size() > 1) {
316
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::stringstream errorMessage;
317
3/6
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
1 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
318
2/2
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
3 for (const Function *match : matches)
319
3/6
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
2 errorMessage << "\n " << match->getSignature();
320
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
1 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
321 1 }
322
323 // Insert into cache
324
1/2
✓ Branch 2 taken 5253 times.
✗ Branch 3 not taken.
5253 lookupCache[cacheKey] = matches.front();
325
326 // Trigger revisit in type checker if required
327
1/2
✓ Branch 2 taken 5253 times.
✗ Branch 3 not taken.
5253 typeChecker->requestRevisitIfRequired(matches.front());
328
329 // Return the very match
330 5253 return matches.front();
331 33927 }
332
333 367329 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
334 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
335 bool strictSpecifierMatching, bool &forceSubstantiation,
336 const ASTNode *callNode) {
337 // Check name requirement
338
2/2
✓ Branch 1 taken 322136 times.
✓ Branch 2 taken 45193 times.
367329 if (!matchName(candidate, reqName))
339 322136 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
340
341 // Check 'this' type requirement
342
2/2
✓ Branch 1 taken 296 times.
✓ Branch 2 taken 44897 times.
45193 if (!matchThisType(candidate, reqThisType, typeMapping, strictSpecifierMatching, callNode))
343 296 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
344
345 // Check arg types requirement
346
2/2
✓ Branch 1 taken 37792 times.
✓ Branch 2 taken 7104 times.
44897 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictSpecifierMatching, forceSubstantiation, callNode))
347 37792 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
348
349 // Check if there are unresolved generic types
350
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 7103 times.
7104 if (typeMapping.size() < candidate.templateTypes.size())
351 1 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
352
353 // Substantiate return type
354 7103 substantiateReturnType(candidate, typeMapping, callNode);
355
356 7103 const QualType &thisType = candidate.thisType;
357
2/2
✓ Branch 1 taken 5017 times.
✓ Branch 2 taken 2086 times.
7103 if (!thisType.is(TY_DYN)) {
358 // If we only have the generic struct scope, lookup the concrete manifestation scope
359
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 4974 times.
5017 if (matchScope->isGenericScope) {
360 43 const std::string &structName = thisType.getSubType();
361 43 Scope *scope = thisType.getBodyScope()->parent;
362 43 Struct *spiceStruct = StructManager::match(scope, structName, thisType.getTemplateTypes(), candidate.declNode);
363
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 assert(spiceStruct != nullptr);
364 43 matchScope = spiceStruct->scope;
365 }
366
1/2
✓ Branch 1 taken 5017 times.
✗ Branch 2 not taken.
5017 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
367 }
368
369 7103 return MatchResult::MATCHED;
370 }
371
372 /**
373 * Checks if the matching candidate fulfills the name requirement
374 *
375 * @param candidate Matching candidate function
376 * @param reqName Requested function name
377 * @return Fulfilled or not
378 */
379 367329 bool FunctionManager::matchName(const Function &candidate, const std::string &reqName) { return candidate.name == reqName; }
380
381 /**
382 * Checks if the matching candidate fulfills the 'this' type requirement
383 *
384 * @param candidate Matching candidate function
385 * @param reqThisType Requested 'this' type
386 * @param typeMapping Concrete template type mapping
387 * @param strictSpecifierMatching Match specifiers strictly
388 * @param callNode Call AST node for printing error messages
389 * @return Fulfilled or not
390 */
391 45193 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
392 bool strictSpecifierMatching, const ASTNode *callNode) {
393 45193 QualType &candidateThisType = candidate.thisType;
394
395 // Shortcut for procedures
396
7/10
✓ Branch 1 taken 45193 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 21108 times.
✓ Branch 4 taken 24085 times.
✓ Branch 6 taken 21108 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 21108 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 21108 times.
✓ Branch 11 taken 24085 times.
45193 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
397 21108 return true;
398
399 // Give the type matcher a way to retrieve instances of GenericType by their name
400 50142 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
401 1972 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
402 24085 };
403
404 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
405
3/4
✓ Branch 1 taken 24085 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 296 times.
✓ Branch 4 taken 23789 times.
24085 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
406 strictSpecifierMatching))
407 296 return false;
408
409 // Substantiate the candidate param type, based on the type mapping
410
3/4
✓ Branch 1 taken 23789 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2214 times.
✓ Branch 4 taken 21575 times.
23789 if (candidateThisType.hasAnyGenericParts())
411
1/2
✓ Branch 1 taken 2214 times.
✗ Branch 2 not taken.
2214 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
412
413 23789 return true;
414 24085 }
415
416 /**
417 * Checks if the matching candidate fulfills the argument types requirement
418 *
419 * @param candidate Matching candidate function
420 * @param reqArgs Requested argument types
421 * @param typeMapping Concrete template type mapping
422 * @param strictSpecifierMatching Match specifiers strictly
423 * @param needsSubstantiation Do we want to create a substantiation after successfully matching
424 * @param callNode Call AST node for printing error messages
425 * @return Fulfilled or not
426 */
427 44897 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
428 bool strictSpecifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
429 44897 std::vector<Param> &candidateParamList = candidate.paramList;
430
431 // If the number of arguments does not match with the number of params, the matching fails
432
2/2
✓ Branch 2 taken 12474 times.
✓ Branch 3 taken 32423 times.
44897 if (reqArgs.size() != candidateParamList.size())
433 12474 return false;
434
435 // Give the type matcher a way to retrieve instances of GenericType by their name
436 67897 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
437 3051 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
438 32423 };
439
440 // Loop over all parameters
441
2/2
✓ Branch 1 taken 34141 times.
✓ Branch 2 taken 7104 times.
41245 for (size_t i = 0; i < reqArgs.size(); i++) {
442 // Retrieve actual and requested types
443
2/4
✓ Branch 1 taken 34141 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 34141 times.
34141 assert(!candidateParamList.at(i).isOptional);
444
1/2
✓ Branch 1 taken 34141 times.
✗ Branch 2 not taken.
34141 QualType &candidateParamType = candidateParamList.at(i).qualType;
445
1/2
✓ Branch 1 taken 34141 times.
✗ Branch 2 not taken.
34141 const auto &[requestedType, isArgTemporary] = reqArgs.at(i);
446
447 // Check if the requested param type matches the candidate param type. The type mapping may be extended
448
3/4
✓ Branch 1 taken 34141 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 25318 times.
✓ Branch 4 taken 8823 times.
34141 if (!TypeMatcher::matchRequestedToCandidateType(candidateParamType, requestedType, typeMapping, genericTypeResolver,
449 strictSpecifierMatching))
450 25318 return false;
451
452 // Substantiate the candidate param type, based on the type mapping
453
3/4
✓ Branch 1 taken 8823 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1349 times.
✓ Branch 4 taken 7474 times.
8823 if (candidateParamType.hasAnyGenericParts())
454
1/2
✓ Branch 1 taken 1349 times.
✗ Branch 2 not taken.
1349 TypeMatcher::substantiateTypeWithTypeMapping(candidateParamType, typeMapping, callNode);
455
456 // Check if we try to bind a non-ref temporary to a non-const ref parameter
457
3/4
✓ Branch 1 taken 8823 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 8822 times.
8823 if (!candidateParamType.canBind(requestedType, isArgTemporary)) {
458
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (callNode)
459
2/4
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
3 throw SemanticError(callNode, TEMP_TO_NON_CONST_REF, "Temporary values can only be bound to const reference parameters");
460 return false;
461 }
462
463 // If we have a function/procedure type we need to take care of the information, if it takes captures
464
9/12
✓ Branch 1 taken 8822 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8822 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 29 times.
✓ Branch 7 taken 8793 times.
✓ Branch 9 taken 29 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 4 times.
✓ Branch 12 taken 25 times.
✓ Branch 13 taken 4 times.
✓ Branch 14 taken 8818 times.
8822 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
465
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 candidateParamType = candidateParamType.getWithLambdaCaptures();
466 4 needsSubstantiation = true;
467 }
468 }
469
470 7104 return true;
471 32423 }
472
473 /**
474 * Substantiates the candidate return type, based on the given type mapping
475 *
476 * @param candidate Matching candidate function
477 * @param typeMapping Concrete template type mapping
478 * @param callNode AST node for error messages
479 */
480 7103 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
481
2/2
✓ Branch 1 taken 493 times.
✓ Branch 2 taken 6610 times.
7103 if (candidate.returnType.hasAnyGenericParts())
482 493 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
483 7103 }
484
485 /**
486 * Searches the candidate template types for a generic type object with a certain name and return it
487 *
488 * @param candidate Matching candidate function
489 * @param templateTypeName Template type name
490 * @return Generic type object
491 */
492 5023 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
493 const std::string &templateTypeName) {
494
1/2
✓ Branch 5 taken 5380 times.
✗ Branch 6 not taken.
5380 for (const GenericType &templateType : candidate.templateTypes) {
495
3/4
✓ Branch 1 taken 5380 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 5023 times.
✓ Branch 5 taken 357 times.
5380 if (templateType.getSubType() == templateTypeName)
496 5023 return &templateType;
497 }
498 return nullptr;
499 }
500
501 /**
502 * Calculate the cache key for the function lookup cache
503 *
504 * @param scope Scope to match against
505 * @param name Function name requirement
506 * @param thisType This type requirement
507 * @param args Argument requirement
508 * @param templateTypes Template type requirement
509 * @return Cache key
510 */
511 59149 uint64_t FunctionManager::getCacheKey(Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
512 const QualTypeList &templateTypes) {
513 72203 const auto pred1 = [](size_t acc, const Arg &val) {
514 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
515 72203 const uint64_t typeHash = std::hash<QualType>{}(val.first);
516 72203 const uint64_t temporaryHash = std::hash<bool>{}(val.second);
517 72203 const uint64_t newHash = typeHash ^ (temporaryHash << 1);
518 72203 return acc * 31 + newHash;
519 };
520 499 const auto pred2 = [](size_t acc, const QualType &val) {
521 // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions
522 499 return acc * 31 + std::hash<QualType>{}(val);
523 };
524 // Calculate the cache key
525 59149 const uint64_t scopeHash = std::hash<Scope *>{}(scope);
526 59149 const uint64_t hashName = std::hash<std::string>{}(name);
527 59149 const uint64_t hashThisType = std::hash<QualType>{}(thisType);
528 59149 const uint64_t hashArgs = std::accumulate(args.begin(), args.end(), 0u, pred1);
529 59149 const uint64_t hashTemplateTypes = std::accumulate(templateTypes.begin(), templateTypes.end(), 0u, pred2);
530 59149 return scopeHash ^ (hashName << 1) ^ (hashThisType << 2) ^ (hashArgs << 3) ^ (hashTemplateTypes << 4);
531 }
532
533 /**
534 * Clear all statics
535 */
536 390 void FunctionManager::clear() { lookupCache.clear(); }
537
538 } // namespace spice::compiler
539