GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 94.6% 123 / 0 / 130
Functions: 100.0% 11 / 0 / 11
Branches: 56.1% 115 / 0 / 205

src/typechecker/InterfaceManager.cpp
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 #include "InterfaceManager.h"
4
5 #include <ast/ASTNodes.h>
6 #include <exception/SemanticError.h>
7 #include <model/GenericType.h>
8 #include <model/Interface.h>
9 #include <symboltablebuilder/Scope.h>
10 #include <typechecker/FunctionManager.h>
11 #include <typechecker/TypeMatcher.h>
12 #include <util/CustomHashFunctions.h>
13
14 namespace spice::compiler {
15
16 // Static member initialization
17 std::unordered_map<uint64_t, Interface *> InterfaceManager::lookupCache = {};
18 size_t InterfaceManager::lookupCacheHits = 0;
19 size_t InterfaceManager::lookupCacheMisses = 0;
20
21 363 Interface *InterfaceManager::insert(Scope *insertScope, Interface &spiceInterface, std::vector<Interface *> *nodeInterfaceList) {
22 // Open a new manifestation list. Which gets filled by the substantiated manifestations of the interface
23
1/2
✓ Branch 3 → 4 taken 363 times.
✗ Branch 3 → 9 not taken.
363 insertScope->interfaces.emplace(spiceInterface.declNode->codeLoc, InterfaceManifestationList());
24
25 // Save substantiation in declaration node
26
1/2
✓ Branch 5 → 6 taken 363 times.
✗ Branch 5 → 12 not taken.
363 Interface *substantiation = insertSubstantiation(insertScope, spiceInterface, spiceInterface.declNode);
27
1/2
✓ Branch 6 → 7 taken 363 times.
✗ Branch 6 → 12 not taken.
363 nodeInterfaceList->push_back(substantiation);
28
29 363 return substantiation;
30 }
31
32 1305 Interface *InterfaceManager::insertSubstantiation(Scope *insertScope, Interface &newManifestation, const ASTNode *declNode) {
33
1/2
✓ Branch 2 → 3 taken 1305 times.
✗ Branch 2 → 31 not taken.
1305 const std::string signature = newManifestation.getSignature();
34
35 // Make sure that the manifestation does not exist already
36
2/2
✓ Branch 13 → 5 taken 1320 times.
✓ Branch 13 → 14 taken 1305 times.
2625 for ([[maybe_unused]] const auto &manifestations : insertScope->interfaces)
37
3/6
✓ Branch 6 → 7 taken 1320 times.
✗ Branch 6 → 27 not taken.
✓ Branch 7 → 8 taken 1320 times.
✗ Branch 7 → 25 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 1320 times.
1320 assert(!manifestations.second.contains(newManifestation.getSignature()));
38
39 // Retrieve the matching manifestation list of the scope
40
2/4
✓ Branch 14 → 15 taken 1305 times.
✗ Branch 14 → 29 not taken.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 1305 times.
1305 assert(insertScope->interfaces.contains(declNode->codeLoc));
41
1/2
✓ Branch 17 → 18 taken 1305 times.
✗ Branch 17 → 29 not taken.
1305 InterfaceManifestationList &manifestationList = insertScope->interfaces.at(declNode->codeLoc);
42
43 // Add substantiated interface
44 1305 newManifestation.manifestationIndex = manifestationList.size();
45
1/2
✓ Branch 19 → 20 taken 1305 times.
✗ Branch 19 → 29 not taken.
1305 manifestationList.emplace(signature, newManifestation);
46
1/2
✓ Branch 20 → 21 taken 1305 times.
✗ Branch 20 → 29 not taken.
2610 return &manifestationList.at(signature);
47 1305 }
48
49 /**
50 * Check if there is an interface in this scope, fulfilling all given requirements and if found, return it.
51 * If more than one interface matches the requirement, an error gets thrown
52 *
53 * @param matchScope Scope to match against
54 * @param reqName Interface name requirement
55 * @param reqTemplateTypes Template types to substantiate generic types
56 * @param node Instantiation AST node for printing error messages
57 * @return Matched interface or nullptr
58 */
59 12267 Interface *InterfaceManager::match(Scope *matchScope, const std::string &reqName, const QualTypeList &reqTemplateTypes,
60 const ASTNode *node) {
61 // Do cache lookup
62 12267 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqTemplateTypes);
63
3/4
✓ Branch 3 → 4 taken 12267 times.
✗ Branch 3 → 122 not taken.
✓ Branch 6 → 7 taken 10589 times.
✓ Branch 6 → 9 taken 1678 times.
12267 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
64 10589 lookupCacheHits++;
65 10589 return it->second;
66 }
67 1678 lookupCacheMisses++;
68
69 // Loop over interface registry to find interfaces, that match the requirements of the instantiation
70 1678 std::vector<Interface *> matches;
71
2/2
✓ Branch 102 → 11 taken 1373 times.
✓ Branch 102 → 103 taken 1678 times.
3051 for (auto &[defCodeLocStr, manifestations] : matchScope->interfaces) {
72
2/2
✓ Branch 99 → 16 taken 11045 times.
✓ Branch 99 → 100 taken 1128 times.
12173 for (const auto &[mangledName, presetInterface] : manifestations) {
73 // Skip generic and newly inserted substantiations to prevent double matching of an interface
74
6/8
✓ Branch 19 → 20 taken 11045 times.
✗ Branch 19 → 141 not taken.
✓ Branch 20 → 21 taken 1373 times.
✓ Branch 20 → 22 taken 9672 times.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 1373 times.
✓ Branch 24 → 25 taken 9672 times.
✓ Branch 24 → 26 taken 1373 times.
11045 if (presetInterface.isGenericSubstantiation() || presetInterface.isNewlyInserted)
75 9858 continue;
76
77 // Copy the interface to be able to substantiate types
78
1/2
✓ Branch 26 → 27 taken 1373 times.
✗ Branch 26 → 141 not taken.
1373 Interface candidate = presetInterface;
79
80 // Check name requirement
81
2/2
✓ Branch 28 → 29 taken 10 times.
✓ Branch 28 → 30 taken 1363 times.
1373 if (!matchName(candidate, reqName))
82 10 break; // Leave the whole manifestation list, because all manifestations in this list have the same name
83
84 // Prepare mapping table from generic type name to concrete type
85 1363 TypeMapping &typeMapping = candidate.typeMapping;
86 1363 typeMapping.clear();
87
1/2
✓ Branch 32 → 33 taken 1363 times.
✗ Branch 32 → 139 not taken.
1363 typeMapping.reserve(candidate.templateTypes.size());
88
89 // Check template types requirement
90
2/4
✓ Branch 33 → 34 taken 1363 times.
✗ Branch 33 → 139 not taken.
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 1363 times.
1363 if (!matchTemplateTypes(candidate, reqTemplateTypes, typeMapping, node))
91 continue; // Leave this manifestation and continue with the next one
92
93 // Map signatures from generic to concrete
94
1/2
✓ Branch 36 → 37 taken 1363 times.
✗ Branch 36 → 139 not taken.
1363 substantiateSignatures(candidate, typeMapping, node);
95
96 // We found a match! -> Set the actual candidate and its entry to used
97 1363 candidate.used = true;
98 1363 candidate.entry->used = true;
99
100 // Check if it needs to be substantiated
101
2/2
✓ Branch 38 → 39 taken 186 times.
✓ Branch 38 → 51 taken 1177 times.
1363 if (presetInterface.templateTypes.empty()) {
102
5/10
✓ Branch 39 → 40 taken 186 times.
✗ Branch 39 → 123 not taken.
✓ Branch 40 → 41 taken 186 times.
✗ Branch 40 → 45 not taken.
✓ Branch 41 → 42 taken 186 times.
✗ Branch 41 → 123 not taken.
✓ Branch 42 → 43 taken 186 times.
✗ Branch 42 → 123 not taken.
✓ Branch 43 → 44 taken 186 times.
✗ Branch 43 → 45 not taken.
186 assert(matchScope->interfaces.contains(defCodeLocStr) && matchScope->interfaces.at(defCodeLocStr).contains(mangledName));
103
2/4
✓ Branch 46 → 47 taken 186 times.
✗ Branch 46 → 123 not taken.
✓ Branch 47 → 48 taken 186 times.
✗ Branch 47 → 123 not taken.
186 Interface *match = &matchScope->interfaces.at(defCodeLocStr).at(mangledName);
104 186 match->used = true;
105
1/2
✓ Branch 48 → 49 taken 186 times.
✗ Branch 48 → 123 not taken.
186 matches.push_back(match);
106 186 continue; // Match was successful -> match the next interface
107 186 }
108
109 // Check if we already have this manifestation and can simply re-use it
110
4/6
✓ Branch 51 → 52 taken 1177 times.
✗ Branch 51 → 126 not taken.
✓ Branch 52 → 53 taken 1177 times.
✗ Branch 52 → 124 not taken.
✓ Branch 56 → 57 taken 235 times.
✓ Branch 56 → 61 taken 942 times.
1177 if (auto it = manifestations.find(candidate.getSignature()); it != manifestations.end()) {
111 235 it->second.used = true;
112
1/2
✓ Branch 59 → 60 taken 235 times.
✗ Branch 59 → 127 not taken.
235 matches.push_back(&it->second);
113 235 break; // Leave the whole manifestation list to not double-match the manifestation
114 }
115
116 // Insert the substantiated version if required
117
1/2
✓ Branch 61 → 63 taken 942 times.
✗ Branch 61 → 139 not taken.
942 Interface *substantiatedInterface = insertSubstantiation(matchScope, candidate, presetInterface.declNode);
118
2/4
✓ Branch 63 → 64 taken 942 times.
✗ Branch 63 → 139 not taken.
✓ Branch 64 → 65 taken 942 times.
✗ Branch 64 → 139 not taken.
942 substantiatedInterface->genericPreset = &matchScope->interfaces.at(defCodeLocStr).at(mangledName);
119
2/4
✓ Branch 65 → 66 taken 942 times.
✗ Branch 65 → 139 not taken.
✓ Branch 66 → 67 taken 942 times.
✗ Branch 66 → 139 not taken.
942 substantiatedInterface->declNode->getInterfaceManifestations()->push_back(substantiatedInterface);
120 942 substantiatedInterface->isNewlyInserted = true; // To not iterate over it in the same matching
121
122 // Copy interface entry
123
1/2
✓ Branch 67 → 68 taken 942 times.
✗ Branch 67 → 139 not taken.
942 const std::string &newSignature = substantiatedInterface->getSignature();
124
1/2
✓ Branch 68 → 69 taken 942 times.
✗ Branch 68 → 137 not taken.
942 matchScope->lookupStrict(substantiatedInterface->name)->used = true;
125
1/2
✓ Branch 71 → 72 taken 942 times.
✗ Branch 71 → 137 not taken.
942 substantiatedInterface->entry = matchScope->symbolTable.copySymbol(substantiatedInterface->name, newSignature);
126
1/2
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 942 times.
942 assert(substantiatedInterface->entry != nullptr);
127
128 // Copy interface scope
129
1/2
✓ Branch 74 → 75 taken 942 times.
✗ Branch 74 → 137 not taken.
942 const std::string &oldScopeName = presetInterface.getScopeName();
130
1/2
✓ Branch 75 → 76 taken 942 times.
✗ Branch 75 → 135 not taken.
942 const std::string &newScopeName = substantiatedInterface->getScopeName();
131
1/2
✓ Branch 76 → 77 taken 942 times.
✗ Branch 76 → 133 not taken.
942 matchScope->copyChildScope(oldScopeName, newScopeName);
132
1/2
✓ Branch 77 → 78 taken 942 times.
✗ Branch 77 → 133 not taken.
942 substantiatedInterface->scope = matchScope->getChildScope(newScopeName);
133
1/2
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 80 taken 942 times.
942 assert(substantiatedInterface->scope != nullptr);
134 942 substantiatedInterface->scope->isGenericScope = false;
135
136 // Attach the template types to the new interface entry
137
1/2
✓ Branch 80 → 81 taken 942 times.
✗ Branch 80 → 132 not taken.
942 QualType entryType = substantiatedInterface->entry->getQualType()
138
2/4
✓ Branch 81 → 82 taken 942 times.
✗ Branch 81 → 131 not taken.
✓ Branch 82 → 83 taken 942 times.
✗ Branch 82 → 129 not taken.
1884 .getWithTemplateTypes(substantiatedInterface->getTemplateTypes())
139
1/2
✓ Branch 83 → 84 taken 942 times.
✗ Branch 83 → 129 not taken.
942 .getWithBodyScope(substantiatedInterface->scope);
140
1/2
✓ Branch 85 → 86 taken 942 times.
✗ Branch 85 → 133 not taken.
942 substantiatedInterface->entry->updateType(entryType, true);
141
142 // Add to matched interfaces
143
1/2
✓ Branch 86 → 87 taken 942 times.
✗ Branch 86 → 133 not taken.
942 matches.push_back(substantiatedInterface);
144
3/3
✓ Branch 92 → 93 taken 942 times.
✓ Branch 92 → 95 taken 186 times.
✓ Branch 92 → 96 taken 245 times.
1373 }
145 }
146
147 // If no matches were found, return a nullptr
148
2/2
✓ Branch 104 → 105 taken 315 times.
✓ Branch 104 → 106 taken 1363 times.
1678 if (matches.empty())
149 315 return nullptr;
150
151 // Check if more than one interface matches the requirements
152
1/2
✗ Branch 107 → 108 not taken.
✓ Branch 107 → 116 taken 1363 times.
1363 if (matches.size() > 1)
153 throw SemanticError(node, INTERFACE_AMBIGUITY, "Multiple interfaces match the requested signature");
154 1363 Interface *matchedInterface = matches.front();
155 1363 matchedInterface->isNewlyInserted = false;
156
157 // Insert into cache
158
1/2
✓ Branch 117 → 118 taken 1363 times.
✗ Branch 117 → 153 not taken.
1363 lookupCache[cacheKey] = matchedInterface;
159
160 1363 return matchedInterface;
161 1678 }
162
163 /**
164 * Checks if the matching candidate fulfills the name requirement
165 *
166 * @param candidate Matching candidate interface
167 * @param reqName Requested interface name
168 * @return Fulfilled or not
169 */
170 1373 bool InterfaceManager::matchName(const Interface &candidate, const std::string &reqName) { return candidate.name == reqName; }
171
172 /**
173 * Checks if the matching candidate fulfills the template types requirement
174 *
175 * @param candidate Matching candidate interface
176 * @param reqTemplateTypes Requested interface template types
177 * @param typeMapping Generic type mapping
178 * @param node Instantiation AST node for printing error messages
179 * @return Fulfilled or not
180 */
181 1363 bool InterfaceManager::matchTemplateTypes(Interface &candidate, const QualTypeList &reqTemplateTypes, TypeMapping &typeMapping,
182 const ASTNode *node) {
183 // Check if the number of types match
184 1363 const size_t typeCount = reqTemplateTypes.size();
185
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1363 times.
1363 if (typeCount != candidate.templateTypes.size())
186 return false;
187
188 // Give the type matcher a way to retrieve instances of GenericType by their name
189 3903 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
190 1177 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
191 1363 };
192
193 // Loop over all template types
194
2/2
✓ Branch 17 → 8 taken 1177 times.
✓ Branch 17 → 18 taken 1363 times.
2540 for (size_t i = 0; i < typeCount; i++) {
195
1/2
✓ Branch 8 → 9 taken 1177 times.
✗ Branch 8 → 22 not taken.
1177 const QualType &reqType = reqTemplateTypes.at(i);
196
1/2
✓ Branch 9 → 10 taken 1177 times.
✗ Branch 9 → 22 not taken.
1177 QualType &candidateType = candidate.templateTypes.at(i);
197
198 // Check if the requested template type matches the candidate template type. The type mapping may be extended
199
2/4
✓ Branch 10 → 11 taken 1177 times.
✗ Branch 10 → 22 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 1177 times.
1177 if (!TypeMatcher::matchRequestedToCandidateType(candidateType, reqType, typeMapping, genericTypeResolver, false))
200 return false;
201
202 // Substantiate the candidate param type, based on the type mapping
203
2/4
✓ Branch 13 → 14 taken 1177 times.
✗ Branch 13 → 22 not taken.
✓ Branch 14 → 15 taken 1177 times.
✗ Branch 14 → 16 not taken.
1177 if (candidateType.hasAnyGenericParts())
204
1/2
✓ Branch 15 → 16 taken 1177 times.
✗ Branch 15 → 22 not taken.
1177 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, node);
205 }
206
207 1363 return true;
208 1363 }
209
210 /**
211 * Come up with the concrete signatures, by applying the type mapping onto the generic signatures
212 *
213 * @param candidate Candidate interface
214 * @param typeMapping Generic type mapping
215 * @param node Instantiation AST node for printing error messages
216 */
217 1363 void InterfaceManager::substantiateSignatures(Interface &candidate, const TypeMapping &typeMapping, const ASTNode *node) {
218 // Loop over all signatures and substantiate the generic ones
219
2/2
✓ Branch 50 → 4 taken 4910 times.
✓ Branch 50 → 51 taken 1363 times.
7636 for (Function *method : candidate.methods) {
220 // Skip methods, that are already fully substantiated
221
3/4
✓ Branch 6 → 7 taken 4910 times.
✗ Branch 6 → 53 not taken.
✓ Branch 7 → 8 taken 3103 times.
✓ Branch 7 → 9 taken 1807 times.
4910 if (method->isFullySubstantiated())
222 3103 continue;
223
224 // Substantiate return type
225
1/2
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 23 taken 1807 times.
1807 if (method->isNormalFunction())
226 FunctionManager::substantiateReturnType(*method, typeMapping, node);
227
228 // Substantiate param types
229
2/2
✓ Branch 39 → 25 taken 2 times.
✓ Branch 39 → 40 taken 1807 times.
3616 for (auto &[qualType, isOptional] : method->paramList)
230
2/4
✓ Branch 27 → 28 taken 2 times.
✗ Branch 27 → 52 not taken.
✓ Branch 28 → 29 taken 2 times.
✗ Branch 28 → 30 not taken.
2 if (qualType.isBase(TY_GENERIC))
231
1/2
✓ Branch 29 → 30 taken 2 times.
✗ Branch 29 → 52 not taken.
2 TypeMatcher::substantiateTypeWithTypeMapping(qualType, typeMapping, node);
232 }
233 1363 }
234
235 /**
236 * Searches the candidate template types for a generic type object with a certain name and return it
237 *
238 * @param candidate Matching candidate interface
239 * @param templateTypeName Template type name
240 * @return Generic type object
241 */
242 1177 const GenericType *InterfaceManager::getGenericTypeOfCandidateByName(const Interface &candidate,
243 const std::string &templateTypeName) {
244
1/2
✓ Branch 22 → 4 taken 1177 times.
✗ Branch 22 → 23 not taken.
2354 for (const GenericType &templateType : candidate.templateTypes) {
245
2/4
✓ Branch 6 → 7 taken 1177 times.
✗ Branch 6 → 25 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1177 times.
1177 if (!templateType.is(TY_GENERIC))
246 continue;
247
2/4
✓ Branch 9 → 10 taken 1177 times.
✗ Branch 9 → 25 not taken.
✓ Branch 11 → 12 taken 1177 times.
✗ Branch 11 → 13 not taken.
1177 if (templateType.getSubType() == templateTypeName)
248 1177 return &templateType;
249 }
250 return nullptr;
251 }
252
253 /**
254 * Calculate the cache key for the interface lookup cache
255 *
256 * @param scope Scope to match against
257 * @param name Interface name requirement
258 * @param templateTypes Template types to substantiate generic types
259 * @return Cache key
260 */
261 12267 uint64_t InterfaceManager::getCacheKey(const Scope *scope, const std::string &name, const QualTypeList &templateTypes) {
262 12267 uint64_t hash = 0;
263 12267 hashCombine64(hash, hashPointer(scope));
264 12267 hashCombine64(hash, std::hash<std::string>{}(name));
265 12267 hashCombine64(hash, hashVector(templateTypes));
266 12267 return hash;
267 }
268
269 /**
270 * Clear the lookup cache
271 */
272 566 void InterfaceManager::cleanup() {
273 566 lookupCache.clear();
274 566 lookupCacheHits = 0;
275 566 lookupCacheMisses = 0;
276 566 }
277
278 /**
279 * Dump usage statistics for the lookup cache
280 */
281 321 std::string InterfaceManager::dumpLookupCacheStatistics() {
282
1/2
✓ Branch 2 → 3 taken 321 times.
✗ Branch 2 → 22 not taken.
321 std::stringstream stats;
283
2/4
✓ Branch 3 → 4 taken 321 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 321 times.
✗ Branch 4 → 20 not taken.
321 stats << "InterfaceManager lookup cache statistics:" << std::endl;
284
3/6
✓ Branch 5 → 6 taken 321 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 321 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 321 times.
✗ Branch 8 → 20 not taken.
321 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
285
3/6
✓ Branch 9 → 10 taken 321 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 321 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 321 times.
✗ Branch 11 → 20 not taken.
321 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
286
3/6
✓ Branch 12 → 13 taken 321 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 321 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 321 times.
✗ Branch 14 → 20 not taken.
321 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
287
1/2
✓ Branch 15 → 16 taken 321 times.
✗ Branch 15 → 20 not taken.
642 return stats.str();
288 321 }
289
290 } // namespace spice::compiler
291