GCC Code Coverage Report


Directory: ../
File: src/typechecker/FunctionManager.cpp
Date: 2025-02-05 01:09:36
Exec Total Coverage
Lines: 239 244 98.0%
Functions: 19 19 100.0%
Branches: 270 424 63.7%

Line Branch Exec Source
1 // Copyright (c) 2021-2025 ChilliBits. All rights reserved.
2
3 #include "FunctionManager.h"
4 #include "TypeChecker.h"
5
6 #include <ast/ASTNodes.h>
7 #include <exception/SemanticError.h>
8 #include <model/GenericType.h>
9 #include <symboltablebuilder/Scope.h>
10 #include <symboltablebuilder/SymbolTableBuilder.h>
11 #include <typechecker/TypeMatcher.h>
12 #include <util/CodeLoc.h>
13 #include <util/CustomHashFunctions.h>
14
15 namespace spice::compiler {
16
17 // Static member initialization
18 std::unordered_map<uint64_t, Function *> FunctionManager::lookupCache = {};
19
20 10260 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 0 (2→3) taken 10260 times.
✗ Branch 1 (2→43) not taken.
✓ Branch 2 (3→4) taken 10260 times.
✗ Branch 3 (3→40) not taken.
✓ Branch 4 (4→5) taken 10260 times.
✗ Branch 5 (4→38) not taken.
10260 const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn();
23
2/4
✓ Branch 0 (8→9) taken 10260 times.
✗ Branch 1 (8→46) not taken.
✓ Branch 2 (9→10) taken 10260 times.
✗ Branch 3 (9→44) not taken.
10260 insertScope->functions.insert({fctId, FunctionManifestationList()});
24
25 // Collect substantiations
26 10260 std::vector<Function> manifestations;
27
1/2
✓ Branch 0 (12→13) taken 10260 times.
✗ Branch 1 (12→51) not taken.
10260 substantiateOptionalParams(baseFunction, manifestations);
28
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 10260 times.
10260 assert(!manifestations.empty());
29
30 // Save substantiations in declaration node
31 10260 Function *manifestationPtr = nullptr;
32
2/2
✓ Branch 0 (26→18) taken 11002 times.
✓ Branch 1 (26→27) taken 10258 times.
21260 for (const Function &manifestation : manifestations) {
33
2/2
✓ Branch 0 (19→20) taken 11000 times.
✓ Branch 1 (19→50) taken 2 times.
11002 manifestationPtr = insertSubstantiation(insertScope, manifestation, baseFunction.declNode);
34
1/2
✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 11000 times.
11000 assert(manifestationPtr != nullptr);
35
1/2
✓ Branch 0 (22→23) taken 11000 times.
✗ Branch 1 (22→24) not taken.
11000 if (nodeFunctionList)
36
1/2
✓ Branch 0 (23→24) taken 11000 times.
✗ Branch 1 (23→50) not taken.
11000 nodeFunctionList->push_back(manifestationPtr);
37 }
38
39
1/2
✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→29) taken 10258 times.
10258 if (!nodeFunctionList)
40 return manifestationPtr;
41
42
1/2
✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 10258 times.
10258 assert(!nodeFunctionList->empty());
43 10258 return nodeFunctionList->front();
44 10262 }
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 10260 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 0 (3→4) taken 2610 times.
✓ Branch 1 (3→6) taken 7650 times.
10260 if (baseFunction.paramList.empty()) {
65
1/2
✓ Branch 0 (4→5) taken 2610 times.
✗ Branch 1 (4→39) not taken.
2610 manifestations.push_back(baseFunction);
66 2610 return;
67 }
68
69 7650 ParamList currentFunctionParamTypes;
70
1/2
✓ Branch 0 (7→8) taken 7650 times.
✗ Branch 1 (7→37) not taken.
7650 currentFunctionParamTypes.reserve(baseFunction.paramList.size());
71 7650 bool metFirstOptionalParam = false;
72
1/2
✓ Branch 0 (8→9) taken 7650 times.
✗ Branch 1 (8→37) not taken.
7650 Function manifestation = baseFunction;
73
74 // Loop over all parameters
75
2/2
✓ Branch 0 (24→11) taken 12133 times.
✓ Branch 1 (24→25) taken 7650 times.
19783 for (const auto &[qualType, isOptional] : baseFunction.paramList) {
76 // Check if we have a mandatory parameter
77
2/2
✓ Branch 0 (12→13) taken 11391 times.
✓ Branch 1 (12→15) taken 742 times.
12133 if (!isOptional) {
78
1/2
✓ Branch 0 (13→14) taken 11391 times.
✗ Branch 1 (13→32) not taken.
11391 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
79 11391 continue;
80 }
81
82 // Add substantiation without the optional parameter
83
2/2
✓ Branch 0 (15→16) taken 736 times.
✓ Branch 1 (15→19) taken 6 times.
742 if (!metFirstOptionalParam) {
84
1/2
✓ Branch 0 (16→17) taken 736 times.
✗ Branch 1 (16→34) not taken.
736 manifestation.paramList = currentFunctionParamTypes;
85
1/2
✓ Branch 0 (17→18) taken 736 times.
✗ Branch 1 (17→34) not taken.
736 manifestations.push_back(manifestation);
86 // Now we cannot accept mandatory parameters anymore
87 736 metFirstOptionalParam = true;
88 }
89
90 // Add substantiation with the optional parameter
91
1/2
✓ Branch 0 (19→20) taken 742 times.
✗ Branch 1 (19→33) not taken.
742 currentFunctionParamTypes.push_back({qualType, /*optional=*/false});
92
1/2
✓ Branch 0 (20→21) taken 742 times.
✗ Branch 1 (20→34) not taken.
742 manifestation.paramList = currentFunctionParamTypes;
93
1/2
✓ Branch 0 (21→22) taken 742 times.
✗ Branch 1 (21→34) not taken.
742 manifestations.push_back(manifestation);
94 }
95
96 // Ensure at least once manifestation
97
2/2
✓ Branch 0 (26→27) taken 6914 times.
✓ Branch 1 (26→28) taken 736 times.
7650 if (manifestations.empty())
98
1/2
✓ Branch 0 (27→28) taken 6914 times.
✗ Branch 1 (27→35) not taken.
6914 manifestations.push_back(baseFunction);
99 7650 }
100
101 2 Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const QualTypeList &paramTypes, ASTNode *declNode) {
102 2 ParamList paramList;
103
2/2
✓ Branch 0 (8→4) taken 2 times.
✓ Branch 1 (8→9) taken 2 times.
4 for (const QualType &paramType : paramTypes)
104
1/2
✓ Branch 0 (5→6) taken 2 times.
✗ Branch 1 (5→24) not taken.
2 paramList.push_back({paramType, false});
105
4/8
✓ Branch 0 (11→12) taken 2 times.
✗ Branch 1 (11→31) not taken.
✓ Branch 2 (12→13) taken 2 times.
✗ Branch 3 (12→28) not taken.
✓ Branch 4 (13→14) taken 2 times.
✗ Branch 5 (13→27) not taken.
✓ Branch 6 (14→15) taken 2 times.
✗ Branch 7 (14→26) not taken.
4 return {MAIN_FUNCTION_NAME, entry, QualType(TY_DYN), QualType(TY_INT), paramList, {}, declNode};
106 2 }
107
108 12712 Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) {
109
2/4
✓ Branch 0 (2→3) taken 12712 times.
✗ Branch 1 (2→79) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 12712 times.
12712 assert(newManifestation.hasSubstantiatedParams());
110
111
1/2
✓ Branch 0 (5→6) taken 12712 times.
✗ Branch 1 (5→79) not taken.
12712 const std::string signature = newManifestation.getSignature();
112
113 // Check if the function exists already
114
5/8
✓ Branch 0 (6→7) taken 12712 times.
✗ Branch 1 (6→68) not taken.
✓ Branch 2 (7→8) taken 12712 times.
✗ Branch 3 (7→68) not taken.
✓ Branch 4 (8→9) taken 12712 times.
✗ Branch 5 (8→68) not taken.
✓ Branch 6 (33→10) taken 154692 times.
✓ Branch 7 (33→34) taken 12710 times.
167402 for (const auto &manifestations : insertScope->functions | std::views::values) {
115
3/4
✓ Branch 0 (11→12) taken 154692 times.
✗ Branch 1 (11→68) not taken.
✓ Branch 2 (12→13) taken 2 times.
✓ Branch 3 (12→31) taken 154690 times.
154692 if (manifestations.contains(signature)) {
116
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→24) taken 1 times.
2 if (newManifestation.isFunction())
117
3/6
✓ Branch 0 (18→19) taken 1 times.
✗ Branch 1 (18→55) not taken.
✓ Branch 2 (19→20) taken 1 times.
✗ Branch 3 (19→53) not taken.
✓ Branch 4 (20→21) taken 1 times.
✗ Branch 5 (20→50) not taken.
1 throw SemanticError(declNode, FUNCTION_DECLARED_TWICE, "'" + signature + "' is declared twice");
118 else
119
3/6
✓ Branch 0 (25→26) taken 1 times.
✗ Branch 1 (25→64) not taken.
✓ Branch 2 (26→27) taken 1 times.
✗ Branch 3 (26→62) not taken.
✓ Branch 4 (27→28) taken 1 times.
✗ Branch 5 (27→59) 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 0 (34→35) taken 12710 times.
✗ Branch 1 (34→74) not taken.
✓ Branch 2 (35→36) taken 12710 times.
✗ Branch 3 (35→71) not taken.
✓ Branch 4 (36→37) taken 12710 times.
✗ Branch 5 (36→69) not taken.
12710 const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn();
125
2/4
✓ Branch 0 (39→40) taken 12710 times.
✗ Branch 1 (39→75) not taken.
✗ Branch 2 (40→41) not taken.
✓ Branch 3 (40→42) taken 12710 times.
12710 assert(insertScope->functions.contains(fctId));
126
1/2
✓ Branch 0 (42→43) taken 12710 times.
✗ Branch 1 (42→75) not taken.
12710 FunctionManifestationList &manifestationList = insertScope->functions.at(fctId);
127
128 // Add substantiated function
129
1/2
✓ Branch 0 (43→44) taken 12710 times.
✗ Branch 1 (43→75) not taken.
12710 manifestationList.emplace(signature, newManifestation);
130
1/2
✓ Branch 0 (44→45) taken 12710 times.
✗ Branch 1 (44→75) not taken.
25420 return &manifestationList.at(signature);
131 12712 }
132
133 /**
134 * Checks if a function exists by matching it, but not setting it to used
135 *
136 * @param matchScope Scope to match against
137 * @param reqName Function name requirement
138 * @param reqThisType This type requirement
139 * @param reqArgs Argument requirement
140 * @param strictQualifierMatching Match argument and this type qualifiers strictly
141 * @return Found function or nullptr
142 */
143 6719 const Function *FunctionManager::lookup(Scope *matchScope, const std::string &reqName, const QualType &reqThisType,
144 const ArgList &reqArgs, bool strictQualifierMatching) {
145
2/4
✓ Branch 0 (2→3) taken 6719 times.
✗ Branch 1 (2→66) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 6719 times.
6719 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT}));
146
147 // Do cache lookup
148
1/2
✓ Branch 0 (6→7) taken 6719 times.
✗ Branch 1 (6→67) not taken.
6719 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, {});
149
3/4
✓ Branch 0 (8→9) taken 6719 times.
✗ Branch 1 (8→83) not taken.
✓ Branch 2 (9→10) taken 1867 times.
✓ Branch 3 (9→12) taken 4852 times.
6719 if (lookupCache.contains(cacheKey))
150
1/2
✓ Branch 0 (10→11) taken 1867 times.
✗ Branch 1 (10→83) not taken.
1867 return lookupCache.at(cacheKey);
151
152 2145 const auto pred = [&](const Arg &arg) { return arg.first.hasAnyGenericParts(); };
153
5/8
✓ Branch 0 (12→13) taken 4852 times.
✗ Branch 1 (12→83) not taken.
✓ Branch 2 (13→14) taken 4673 times.
✓ Branch 3 (13→17) taken 179 times.
✓ Branch 4 (14→15) taken 4673 times.
✗ Branch 5 (14→83) not taken.
✓ Branch 6 (15→16) taken 4673 times.
✗ Branch 7 (15→17) not taken.
4852 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 0 (18→19) taken 4852 times.
✗ Branch 1 (18→83) not taken.
4852 FunctionRegistry functionRegistry = matchScope->functions;
157 // Loop over function registry to find functions, that match the requirements of the call
158 4852 std::vector<const Function *> matches;
159
2/2
✓ Branch 0 (55→21) taken 38531 times.
✓ Branch 1 (55→56) taken 4852 times.
43383 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 0 (24→25) taken 38531 times.
✗ Branch 1 (24→77) not taken.
38531 const FunctionManifestationList manifestations = m;
162
2/2
✓ Branch 0 (51→27) taken 40995 times.
✓ Branch 1 (51→52) taken 12870 times.
53865 for (const auto &[signature, presetFunction] : manifestations) {
163
2/4
✓ Branch 0 (30→31) taken 40995 times.
✗ Branch 1 (30→73) not taken.
✗ Branch 2 (31→32) not taken.
✓ Branch 3 (31→33) taken 40995 times.
40995 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 0 (33→34) taken 40995 times.
✗ Branch 1 (33→73) not taken.
✓ Branch 2 (34→35) taken 10102 times.
✓ Branch 3 (34→36) taken 30893 times.
40995 if (presetFunction.isFullySubstantiated() != requestedFullySubstantiated)
168 15334 continue;
169
170 // Copy the function to be able to substantiate types
171
1/2
✓ Branch 0 (36→37) taken 30893 times.
✗ Branch 1 (36→73) not taken.
30893 Function candidate = presetFunction;
172
173 // Create empty type mapping
174 30893 TypeMapping &typeMapping = candidate.typeMapping;
175
176 30893 bool forceSubstantiation = false;
177
1/2
✓ Branch 0 (37→38) taken 30893 times.
✗ Branch 1 (37→71) not taken.
30893 const MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
178 strictQualifierMatching, forceSubstantiation, nullptr);
179
2/2
✓ Branch 0 (38→39) taken 24773 times.
✓ Branch 1 (38→40) taken 6120 times.
30893 if (matchResult == MatchResult::SKIP_FUNCTION)
180 24773 break; // Leave the whole function
181
2/2
✓ Branch 0 (40→41) taken 5232 times.
✓ Branch 1 (40→42) taken 888 times.
6120 if (matchResult == MatchResult::SKIP_MANIFESTATION)
182 5232 continue; // Leave this manifestation and try the next one
183
184 // Add to matches
185
3/6
✓ Branch 0 (42→43) taken 888 times.
✗ Branch 1 (42→70) not taken.
✓ Branch 2 (43→44) taken 888 times.
✗ Branch 3 (43→70) not taken.
✓ Branch 4 (44→45) taken 888 times.
✗ Branch 5 (44→70) not taken.
888 matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature));
186
187 888 break; // Leave the whole manifestation list to not double-match the manifestation
188
2/2
✓ Branch 0 (47→48) taken 5232 times.
✓ Branch 1 (47→49) taken 25661 times.
30893 }
189 38531 }
190
191 // Return the very match or a nullptr
192
2/2
✓ Branch 0 (57→58) taken 888 times.
✓ Branch 1 (57→60) taken 3964 times.
4852 return !matches.empty() ? matches.front() : nullptr;
193 4852 }
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 strictQualifierMatching Match argument and this type qualifiers strictly
206 * @param callNode Call AST node for printing error messages
207 * @return Matched function or nullptr
208 */
209 61196 Function *FunctionManager::match(TypeChecker *typeChecker, Scope *matchScope, const std::string &reqName,
210 const QualType &reqThisType, const ArgList &reqArgs, const QualTypeList &templateTypeHints,
211 bool strictQualifierMatching, const ASTNode *callNode) {
212
2/4
✓ Branch 0 (2→3) taken 61196 times.
✗ Branch 1 (2→174) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 61196 times.
61196 assert(reqThisType.isOneOf({TY_DYN, TY_STRUCT, TY_INTERFACE}));
213
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 61196 times.
61196 assert(typeChecker != nullptr && "The match() function must be called from the TypeChecker");
214
215 // Do cache lookup
216
1/2
✓ Branch 0 (7→8) taken 61196 times.
✗ Branch 1 (7→222) not taken.
61196 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqThisType, reqArgs, templateTypeHints);
217
3/4
✓ Branch 0 (8→9) taken 61196 times.
✗ Branch 1 (8→222) not taken.
✓ Branch 2 (9→10) taken 10132 times.
✓ Branch 3 (9→12) taken 51064 times.
61196 if (lookupCache.contains(cacheKey))
218
1/2
✓ Branch 0 (10→11) taken 10132 times.
✗ Branch 1 (10→222) not taken.
10132 return lookupCache.at(cacheKey);
219
220 // Copy the registry to prevent iterating over items, that are created within the loop
221
1/2
✓ Branch 0 (12→13) taken 51064 times.
✗ Branch 1 (12→222) not taken.
51064 FunctionRegistry functionRegistry = matchScope->functions;
222 // Loop over function registry to find functions, that match the requirements of the call
223 51064 std::vector<Function *> matches;
224
2/2
✓ Branch 0 (139→15) taken 514089 times.
✓ Branch 1 (139→140) taken 51063 times.
565152 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 0 (18→19) taken 514089 times.
✗ Branch 1 (18→203) not taken.
514089 const FunctionManifestationList manifestations = m;
227
2/2
✓ Branch 0 (135→21) taken 544765 times.
✓ Branch 1 (135→136) taken 38955 times.
583720 for (const auto &[signature, presetFunction] : manifestations) {
228
2/4
✓ Branch 0 (24→25) taken 544765 times.
✗ Branch 1 (24→199) not taken.
✗ Branch 2 (25→26) not taken.
✓ Branch 3 (25→27) taken 544765 times.
544765 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 0 (27→28) taken 544765 times.
✗ Branch 1 (27→199) not taken.
✓ Branch 2 (28→29) taken 28938 times.
✓ Branch 3 (28→30) taken 515827 times.
544765 if (presetFunction.isGenericSubstantiation())
232 69631 continue;
233
234 // Copy the function to be able to substantiate types
235
1/2
✓ Branch 0 (30→31) taken 515827 times.
✗ Branch 1 (30→199) not taken.
515827 Function candidate = presetFunction;
236
237 // Prepare type mapping, based on the given initial type mapping
238 515827 TypeMapping &typeMapping = candidate.typeMapping;
239 515827 typeMapping.clear();
240
2/2
✓ Branch 0 (43→33) taken 3046 times.
✓ Branch 1 (43→44) taken 515827 times.
518873 for (size_t i = 0; i < std::min(templateTypeHints.size(), candidate.templateTypes.size()); i++) {
241
2/4
✓ Branch 0 (33→34) taken 3046 times.
✗ Branch 1 (33→197) not taken.
✓ Branch 2 (34→35) taken 3046 times.
✗ Branch 3 (34→197) not taken.
3046 const std::string &typeName = candidate.templateTypes.at(i).getSubType();
242
1/2
✓ Branch 0 (35→36) taken 3046 times.
✗ Branch 1 (35→197) not taken.
3046 const QualType &templateType = templateTypeHints.at(i);
243
2/4
✓ Branch 0 (36→37) taken 3046 times.
✗ Branch 1 (36→177) not taken.
✓ Branch 2 (37→38) taken 3046 times.
✗ Branch 3 (37→175) not taken.
3046 typeMapping.insert({typeName, templateType});
244 }
245
246 515827 bool forceSubstantiation = false;
247
2/2
✓ Branch 0 (44→45) taken 515826 times.
✓ Branch 1 (44→197) taken 1 times.
515827 MatchResult matchResult = matchManifestation(candidate, matchScope, reqName, reqThisType, reqArgs, typeMapping,
248 strictQualifierMatching, forceSubstantiation, callNode);
249
2/2
✓ Branch 0 (45→46) taken 473285 times.
✓ Branch 1 (45→47) taken 42541 times.
515826 if (matchResult == MatchResult::SKIP_FUNCTION)
250 473285 break; // Leave the whole function
251
2/2
✓ Branch 0 (47→48) taken 35801 times.
✓ Branch 1 (47→49) taken 6740 times.
42541 if (matchResult == MatchResult::SKIP_MANIFESTATION)
252 35801 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 6740 candidate.used = true;
256 6740 candidate.entry->used = true;
257
258 // Check if the function is generic needs to be substantiated
259
6/6
✓ Branch 0 (50→51) taken 4895 times.
✓ Branch 1 (50→53) taken 1845 times.
✓ Branch 2 (51→52) taken 4892 times.
✓ Branch 3 (51→53) taken 3 times.
✓ Branch 4 (54→55) taken 4892 times.
✓ Branch 5 (54→67) taken 1848 times.
6740 if (presetFunction.templateTypes.empty() && !forceSubstantiation) {
260
5/10
✓ Branch 0 (55→56) taken 4892 times.
✗ Branch 1 (55→197) not taken.
✓ Branch 2 (56→57) taken 4892 times.
✗ Branch 3 (56→61) not taken.
✓ Branch 4 (57→58) taken 4892 times.
✗ Branch 5 (57→197) not taken.
✓ Branch 6 (58→59) taken 4892 times.
✗ Branch 7 (58→197) not taken.
✓ Branch 8 (59→60) taken 4892 times.
✗ Branch 9 (59→61) not taken.
4892 assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature));
261
3/6
✓ Branch 0 (62→63) taken 4892 times.
✗ Branch 1 (62→178) not taken.
✓ Branch 2 (63→64) taken 4892 times.
✗ Branch 3 (63→178) not taken.
✓ Branch 4 (64→65) taken 4892 times.
✗ Branch 5 (64→178) not taken.
4892 matches.push_back(&matchScope->functions.at(fctId).at(signature));
262 4892 matches.back()->used = true;
263 4892 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 0 (67→68) taken 1848 times.
✗ Branch 1 (67→197) not taken.
1848 const std::string nonGenericSignature = candidate.getSignature();
268
4/6
✓ Branch 0 (68→69) taken 1848 times.
✗ Branch 1 (68→195) not taken.
✓ Branch 2 (69→70) taken 1848 times.
✗ Branch 3 (69→195) not taken.
✓ Branch 4 (70→71) taken 138 times.
✓ Branch 5 (70→75) taken 1710 times.
1848 if (matchScope->functions.at(fctId).contains(nonGenericSignature)) {
269
3/6
✓ Branch 0 (71→72) taken 138 times.
✗ Branch 1 (71→179) not taken.
✓ Branch 2 (72→73) taken 138 times.
✗ Branch 3 (72→179) not taken.
✓ Branch 4 (73→74) taken 138 times.
✗ Branch 5 (73→179) not taken.
138 matches.push_back(&matchScope->functions.at(fctId).at(nonGenericSignature));
270 138 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 0 (75→76) taken 1710 times.
✗ Branch 1 (75→195) not taken.
1710 Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode);
275
2/4
✓ Branch 0 (76→77) taken 1710 times.
✗ Branch 1 (76→195) not taken.
✓ Branch 2 (77→78) taken 1710 times.
✗ Branch 3 (77→195) not taken.
1710 substantiatedFunction->genericPreset = &matchScope->functions.at(fctId).at(signature);
276 1710 substantiatedFunction->alreadyTypeChecked = false;
277
2/4
✓ Branch 0 (78→79) taken 1710 times.
✗ Branch 1 (78→195) not taken.
✓ Branch 2 (79→80) taken 1710 times.
✗ Branch 3 (79→195) not taken.
1710 substantiatedFunction->declNode->getFctManifestations(reqName)->push_back(substantiatedFunction);
278
279 // Copy function entry
280
1/2
✓ Branch 0 (80→81) taken 1710 times.
✗ Branch 1 (80→195) not taken.
1710 const std::string newSignature = substantiatedFunction->getSignature(false);
281
1/2
✓ Branch 0 (81→82) taken 1710 times.
✗ Branch 1 (81→193) not taken.
1710 matchScope->lookupStrict(presetFunction.entry->name)->used = true;
282
1/2
✓ Branch 0 (84→85) taken 1710 times.
✗ Branch 1 (84→193) not taken.
1710 substantiatedFunction->entry = matchScope->symbolTable.copySymbol(presetFunction.entry->name, newSignature);
283
1/2
✗ Branch 0 (85→86) not taken.
✓ Branch 1 (85→87) taken 1710 times.
1710 assert(substantiatedFunction->entry != nullptr);
284
285 // Copy function scope
286
1/2
✓ Branch 0 (87→88) taken 1710 times.
✗ Branch 1 (87→193) not taken.
1710 const std::string oldSignature = presetFunction.getSignature(false);
287
1/2
✓ Branch 0 (88→89) taken 1710 times.
✗ Branch 1 (88→191) not taken.
1710 Scope *childScope = matchScope->copyChildScope(oldSignature, newSignature);
288
1/2
✗ Branch 0 (89→90) not taken.
✓ Branch 1 (89→91) taken 1710 times.
1710 assert(childScope != nullptr);
289 1710 childScope->isGenericScope = false;
290 1710 substantiatedFunction->bodyScope = childScope;
291
292 // Insert symbols for generic type names with concrete types into the child block
293
2/2
✓ Branch 0 (101→93) taken 2006 times.
✓ Branch 1 (101→102) taken 1710 times.
3716 for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
294
2/4
✓ Branch 0 (96→97) taken 2006 times.
✗ Branch 1 (96→182) not taken.
✓ Branch 2 (97→98) taken 2006 times.
✗ Branch 3 (97→180) not taken.
2006 childScope->insertGenericType(typeName, GenericType(concreteType));
295
296 // Substantiate the 'this' entry in the new function scope
297
6/6
✓ Branch 0 (105→106) taken 1443 times.
✓ Branch 1 (105→109) taken 267 times.
✓ Branch 2 (107→108) taken 1442 times.
✓ Branch 3 (107→109) taken 1 times.
✓ Branch 4 (110→111) taken 1442 times.
✓ Branch 5 (110→124) taken 268 times.
1710 if (presetFunction.isMethod() && !presetFunction.templateTypes.empty()) {
298
1/2
✓ Branch 0 (113→114) taken 1442 times.
✗ Branch 1 (113→186) not taken.
4326 SymbolTableEntry *thisEntry = childScope->lookupStrict(THIS_VARIABLE_NAME);
299
1/2
✗ Branch 0 (119→120) not taken.
✓ Branch 1 (119→121) taken 1442 times.
1442 assert(thisEntry != nullptr);
300
2/4
✓ Branch 0 (121→122) taken 1442 times.
✗ Branch 1 (121→190) not taken.
✓ Branch 2 (122→123) taken 1442 times.
✗ Branch 3 (122→190) not taken.
1442 thisEntry->updateType(candidate.thisType.toPtr(callNode), /*overwriteExistingType=*/true);
301 }
302
303 // Add to matched functions
304
1/2
✓ Branch 0 (124→125) taken 1710 times.
✗ Branch 1 (124→191) not taken.
1710 matches.push_back(substantiatedFunction);
305
306 1710 break; // Leave the whole manifestation list to not double-match the manifestation
307
2/2
✓ Branch 0 (131→132) taken 40693 times.
✓ Branch 1 (131→133) taken 475133 times.
517675 }
308 514089 }
309
310 // If no matches were found, return a nullptr
311
2/2
✓ Branch 0 (141→142) taken 44324 times.
✓ Branch 1 (141→143) taken 6739 times.
51063 if (matches.empty())
312 44324 return nullptr;
313
314 // Check if more than one function matches the requirements
315
2/2
✓ Branch 0 (144→145) taken 1 times.
✓ Branch 1 (144→164) taken 6738 times.
6739 if (matches.size() > 1) {
316
1/2
✓ Branch 0 (145→146) taken 1 times.
✗ Branch 1 (145→217) not taken.
1 std::stringstream errorMessage;
317
3/6
✓ Branch 0 (146→147) taken 1 times.
✗ Branch 1 (146→215) not taken.
✓ Branch 2 (147→148) taken 1 times.
✗ Branch 3 (147→215) not taken.
✓ Branch 4 (148→149) taken 1 times.
✗ Branch 5 (148→215) not taken.
1 errorMessage << "The function/procedure '" << reqName << "' is ambiguous. All of the following match the requested criteria:";
318
2/2
✓ Branch 0 (158→151) taken 2 times.
✓ Branch 1 (158→159) taken 1 times.
3 for (const Function *match : matches)
319
3/6
✓ Branch 0 (152→153) taken 2 times.
✗ Branch 1 (152→208) not taken.
✓ Branch 2 (153→154) taken 2 times.
✗ Branch 3 (153→207) not taken.
✓ Branch 4 (154→155) taken 2 times.
✗ Branch 5 (154→205) not taken.
2 errorMessage << "\n " << match->getSignature();
320
2/4
✓ Branch 0 (160→161) taken 1 times.
✗ Branch 1 (160→212) not taken.
✓ Branch 2 (161→162) taken 1 times.
✗ Branch 3 (161→209) not taken.
1 throw SemanticError(callNode, FUNCTION_AMBIGUITY, errorMessage.str());
321 1 }
322
323 // Insert into cache
324
1/2
✓ Branch 0 (165→166) taken 6738 times.
✗ Branch 1 (165→218) not taken.
6738 lookupCache[cacheKey] = matches.front();
325
326 // Trigger revisit in type checker if required
327
1/2
✓ Branch 0 (167→168) taken 6738 times.
✗ Branch 1 (167→218) not taken.
6738 typeChecker->requestRevisitIfRequired(matches.front());
328
329 // Return the very match
330 6738 return matches.front();
331 51066 }
332
333 546720 MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &reqName,
334 const QualType &reqThisType, const ArgList &reqArgs, TypeMapping &typeMapping,
335 bool strictQualifierMatching, bool &forceSubstantiation,
336 const ASTNode *callNode) {
337 // Check name requirement
338
2/2
✓ Branch 0 (3→4) taken 498058 times.
✓ Branch 1 (3→5) taken 48662 times.
546720 if (!matchName(candidate, reqName))
339 498058 return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name
340
341 // Check 'this' type requirement
342
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→8) taken 48662 times.
48662 if (!matchThisType(candidate, reqThisType, typeMapping, strictQualifierMatching, callNode))
343 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
344
345 // Check arg types requirement
346
2/2
✓ Branch 0 (9→10) taken 41032 times.
✓ Branch 1 (9→11) taken 7629 times.
48662 if (!matchArgTypes(candidate, reqArgs, typeMapping, strictQualifierMatching, forceSubstantiation, callNode))
347 41032 return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one
348
349 // Check if there are unresolved generic types
350
2/2
✓ Branch 0 (13→14) taken 1 times.
✓ Branch 1 (13→15) taken 7628 times.
7629 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 7628 substantiateReturnType(candidate, typeMapping, callNode);
355
356 7628 const QualType &thisType = candidate.thisType;
357
2/2
✓ Branch 0 (17→18) taken 4644 times.
✓ Branch 1 (17→28) taken 2984 times.
7628 if (!thisType.is(TY_DYN)) {
358 // If we only have the generic struct scope, lookup the concrete manifestation scope
359
2/2
✓ Branch 0 (18→19) taken 45 times.
✓ Branch 1 (18→26) taken 4599 times.
4644 if (matchScope->isGenericScope) {
360 45 const std::string &structName = thisType.getSubType();
361 45 Scope *scope = thisType.getBodyScope()->parent;
362 45 Struct *spiceStruct = StructManager::match(scope, structName, thisType.getTemplateTypes(), candidate.declNode);
363
1/2
✗ Branch 0 (23→24) not taken.
✓ Branch 1 (23→25) taken 45 times.
45 assert(spiceStruct != nullptr);
364 45 matchScope = spiceStruct->scope;
365 }
366
1/2
✓ Branch 0 (26→27) taken 4644 times.
✗ Branch 1 (26→30) not taken.
4644 candidate.thisType = candidate.thisType.getWithBodyScope(matchScope);
367 }
368
369 7628 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 546720 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 strictQualifierMatching Match qualifiers strictly
388 * @param callNode Call AST node for printing error messages
389 * @return Fulfilled or not
390 */
391 48662 bool FunctionManager::matchThisType(Function &candidate, const QualType &reqThisType, TypeMapping &typeMapping,
392 bool strictQualifierMatching, const ASTNode *callNode) {
393 48662 QualType &candidateThisType = candidate.thisType;
394
395 // Shortcut for procedures
396
7/10
✓ Branch 0 (2→3) taken 48662 times.
✗ Branch 1 (2→23) not taken.
✓ Branch 2 (3→4) taken 33217 times.
✓ Branch 3 (3→7) taken 15445 times.
✓ Branch 4 (4→5) taken 33217 times.
✗ Branch 5 (4→23) not taken.
✓ Branch 6 (5→6) taken 33217 times.
✗ Branch 7 (5→7) not taken.
✓ Branch 8 (8→9) taken 33217 times.
✓ Branch 9 (8→10) taken 15445 times.
48662 if (candidateThisType.is(TY_DYN) && reqThisType.is(TY_DYN))
397 33217 return true;
398
399 // Give the type matcher a way to retrieve instances of GenericType by their name
400 33003 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
401 2113 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
402 15445 };
403
404 // Check if the requested 'this' type matches the candidate 'this' type. The type mapping may be extended
405
2/4
✓ Branch 0 (11→12) taken 15445 times.
✗ Branch 1 (11→21) not taken.
✗ Branch 2 (12→13) not taken.
✓ Branch 3 (12→14) taken 15445 times.
15445 if (!TypeMatcher::matchRequestedToCandidateType(candidateThisType, reqThisType, typeMapping, genericTypeResolver,
406 strictQualifierMatching))
407 return false;
408
409 // Substantiate the candidate param type, based on the type mapping
410
3/4
✓ Branch 0 (14→15) taken 15445 times.
✗ Branch 1 (14→21) not taken.
✓ Branch 2 (15→16) taken 2424 times.
✓ Branch 3 (15→17) taken 13021 times.
15445 if (candidateThisType.hasAnyGenericParts())
411
1/2
✓ Branch 0 (16→17) taken 2424 times.
✗ Branch 1 (16→21) not taken.
2424 TypeMatcher::substantiateTypeWithTypeMapping(candidateThisType, typeMapping, callNode);
412
413 15445 return true;
414 15445 }
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 strictQualifierMatching Match qualifiers 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 48662 bool FunctionManager::matchArgTypes(Function &candidate, const ArgList &reqArgs, TypeMapping &typeMapping,
428 bool strictQualifierMatching, bool &needsSubstantiation, const ASTNode *callNode) {
429 48662 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 0 (4→5) taken 6459 times.
✓ Branch 1 (4→6) taken 42203 times.
48662 if (reqArgs.size() != candidateParamList.size())
433 6459 return false;
434
435 // Give the type matcher a way to retrieve instances of GenericType by their name
436 88061 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
437 3655 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
438 42203 };
439
440 // Loop over all parameters
441
2/2
✓ Branch 0 (45→8) taken 45086 times.
✓ Branch 1 (45→46) taken 7629 times.
52715 for (size_t i = 0; i < reqArgs.size(); i++) {
442 // Retrieve actual and requested types
443
2/4
✓ Branch 0 (8→9) taken 45086 times.
✗ Branch 1 (8→62) not taken.
✗ Branch 2 (9→10) not taken.
✓ Branch 3 (9→11) taken 45086 times.
45086 assert(!candidateParamList.at(i).isOptional);
444
1/2
✓ Branch 0 (11→12) taken 45086 times.
✗ Branch 1 (11→62) not taken.
45086 QualType &candidateParamType = candidateParamList.at(i).qualType;
445
1/2
✓ Branch 0 (12→13) taken 45086 times.
✗ Branch 1 (12→62) not taken.
45086 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 0 (15→16) taken 45086 times.
✗ Branch 1 (15→62) not taken.
✓ Branch 2 (16→17) taken 34573 times.
✓ Branch 3 (16→18) taken 10513 times.
45086 if (!TypeMatcher::matchRequestedToCandidateType(candidateParamType, requestedType, typeMapping, genericTypeResolver,
449 strictQualifierMatching))
450 34573 return false;
451
452 // Substantiate the candidate param type, based on the type mapping
453
3/4
✓ Branch 0 (18→19) taken 10513 times.
✗ Branch 1 (18→62) not taken.
✓ Branch 2 (19→20) taken 1611 times.
✓ Branch 3 (19→21) taken 8902 times.
10513 if (candidateParamType.hasAnyGenericParts())
454
1/2
✓ Branch 0 (20→21) taken 1611 times.
✗ Branch 1 (20→62) not taken.
1611 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 0 (21→22) taken 10513 times.
✗ Branch 1 (21→62) not taken.
✓ Branch 2 (22→23) taken 1 times.
✓ Branch 3 (22→33) taken 10512 times.
10513 if (!candidateParamType.canBind(requestedType, isArgTemporary)) {
458
1/2
✓ Branch 0 (23→24) taken 1 times.
✗ Branch 1 (23→32) not taken.
1 if (callNode)
459
2/4
✓ Branch 0 (27→28) taken 1 times.
✗ Branch 1 (27→53) not taken.
✓ Branch 2 (28→29) taken 1 times.
✗ Branch 3 (28→50) 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 0 (33→34) taken 10512 times.
✗ Branch 1 (33→59) not taken.
✓ Branch 2 (34→35) taken 10512 times.
✗ Branch 3 (34→59) not taken.
✓ Branch 4 (35→36) taken 41 times.
✓ Branch 5 (35→39) taken 10471 times.
✓ Branch 6 (36→37) taken 41 times.
✗ Branch 7 (36→59) not taken.
✓ Branch 8 (37→38) taken 4 times.
✓ Branch 9 (37→39) taken 37 times.
✓ Branch 10 (40→41) taken 4 times.
✓ Branch 11 (40→43) taken 10508 times.
10512 if (requestedType.getBase().isOneOf({TY_FUNCTION, TY_PROCEDURE}) && requestedType.hasLambdaCaptures()) {
465
1/2
✓ Branch 0 (41→42) taken 4 times.
✗ Branch 1 (41→61) not taken.
4 candidateParamType = candidateParamType.getWithLambdaCaptures();
466 4 needsSubstantiation = true;
467 }
468 }
469
470 7629 return true;
471 42203 }
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 7628 void FunctionManager::substantiateReturnType(Function &candidate, const TypeMapping &typeMapping, const ASTNode *callNode) {
481
2/2
✓ Branch 0 (3→4) taken 532 times.
✓ Branch 1 (3→5) taken 7096 times.
7628 if (candidate.returnType.hasAnyGenericParts())
482 532 TypeMatcher::substantiateTypeWithTypeMapping(candidate.returnType, typeMapping, callNode);
483 7628 }
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 5768 const GenericType *FunctionManager::getGenericTypeOfCandidateByName(const Function &candidate,
493 const std::string &templateTypeName) {
494
1/2
✓ Branch 0 (11→4) taken 6143 times.
✗ Branch 1 (11→12) not taken.
6143 for (const GenericType &templateType : candidate.templateTypes) {
495
3/4
✓ Branch 0 (5→6) taken 6143 times.
✗ Branch 1 (5→14) not taken.
✓ Branch 2 (7→8) taken 5768 times.
✓ Branch 3 (7→9) taken 375 times.
6143 if (templateType.getSubType() == templateTypeName)
496 5768 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 67915 uint64_t FunctionManager::getCacheKey(Scope *scope, const std::string &name, const QualType &thisType, const ArgList &args,
512 const QualTypeList &templateTypes) {
513 104805 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 104805 const uint64_t typeHash = std::hash<QualType>{}(val.first);
516 104805 const uint64_t temporaryHash = std::hash<bool>{}(val.second);
517 104805 const uint64_t newHash = typeHash ^ (temporaryHash << 1);
518 104805 return acc * 31 + newHash;
519 };
520 602 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 602 return acc * 31 + std::hash<QualType>{}(val);
523 };
524 // Calculate the cache key
525 67915 const uint64_t scopeHash = std::hash<Scope *>{}(scope);
526 67915 const uint64_t hashName = std::hash<std::string>{}(name);
527 67915 const uint64_t hashThisType = std::hash<QualType>{}(thisType);
528 67915 const uint64_t hashArgs = std::accumulate(args.begin(), args.end(), 0u, pred1);
529 67915 const uint64_t hashTemplateTypes = std::accumulate(templateTypes.begin(), templateTypes.end(), 0u, pred2);
530 67915 return scopeHash ^ (hashName << 1) ^ (hashThisType << 2) ^ (hashArgs << 3) ^ (hashTemplateTypes << 4);
531 }
532
533 /**
534 * Clear all statics
535 */
536 401 void FunctionManager::clear() { lookupCache.clear(); }
537
538 } // namespace spice::compiler
539