GCC Code Coverage Report


Directory: ../
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 93.1% 121 / 0 / 130
Functions: 100.0% 11 / 0 / 11
Branches: 55.1% 113 / 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 104 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 104 times.
✗ Branch 3 → 9 not taken.
104 insertScope->interfaces.emplace(spiceInterface.declNode->codeLoc, InterfaceManifestationList());
24
25 // Save substantiation in declaration node
26
1/2
✓ Branch 5 → 6 taken 104 times.
✗ Branch 5 → 12 not taken.
104 Interface *substantiation = insertSubstantiation(insertScope, spiceInterface, spiceInterface.declNode);
27
1/2
✓ Branch 6 → 7 taken 104 times.
✗ Branch 6 → 12 not taken.
104 nodeInterfaceList->push_back(substantiation);
28
29 104 return substantiation;
30 }
31
32 269 Interface *InterfaceManager::insertSubstantiation(Scope *insertScope, Interface &newManifestation, const ASTNode *declNode) {
33
1/2
✓ Branch 2 → 3 taken 269 times.
✗ Branch 2 → 31 not taken.
269 const std::string signature = newManifestation.getSignature();
34
35 // Make sure that the manifestation does not exist already
36
2/2
✓ Branch 13 → 5 taken 269 times.
✓ Branch 13 → 14 taken 269 times.
538 for ([[maybe_unused]] const auto &manifestations : insertScope->interfaces)
37
3/6
✓ Branch 6 → 7 taken 269 times.
✗ Branch 6 → 27 not taken.
✓ Branch 7 → 8 taken 269 times.
✗ Branch 7 → 25 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 269 times.
269 assert(!manifestations.second.contains(newManifestation.getSignature()));
38
39 // Retrieve the matching manifestation list of the scope
40
2/4
✓ Branch 14 → 15 taken 269 times.
✗ Branch 14 → 29 not taken.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 269 times.
269 assert(insertScope->interfaces.contains(declNode->codeLoc));
41
1/2
✓ Branch 17 → 18 taken 269 times.
✗ Branch 17 → 29 not taken.
269 InterfaceManifestationList &manifestationList = insertScope->interfaces.at(declNode->codeLoc);
42
43 // Add substantiated interface
44 269 newManifestation.manifestationIndex = manifestationList.size();
45
1/2
✓ Branch 19 → 20 taken 269 times.
✗ Branch 19 → 29 not taken.
269 manifestationList.emplace(signature, newManifestation);
46
1/2
✓ Branch 20 → 21 taken 269 times.
✗ Branch 20 → 29 not taken.
538 return &manifestationList.at(signature);
47 269 }
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 1617 Interface *InterfaceManager::match(Scope *matchScope, const std::string &reqName, const QualTypeList &reqTemplateTypes,
60 const ASTNode *node) {
61 // Do cache lookup
62 1617 const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqTemplateTypes);
63
3/4
✓ Branch 3 → 4 taken 1617 times.
✗ Branch 3 → 122 not taken.
✓ Branch 6 → 7 taken 1320 times.
✓ Branch 6 → 9 taken 297 times.
1617 if (const auto it = lookupCache.find(cacheKey); it != lookupCache.end()) {
64 1320 lookupCacheHits++;
65 1320 return it->second;
66 }
67 297 lookupCacheMisses++;
68
69 // Loop over interface registry to find interfaces, that match the requirements of the instantiation
70 297 std::vector<Interface *> matches;
71
2/2
✓ Branch 102 → 12 taken 297 times.
✓ Branch 102 → 103 taken 297 times.
594 for (auto &[defCodeLocStr, manifestations] : matchScope->interfaces) {
72
2/2
✓ Branch 99 → 17 taken 567 times.
✓ Branch 99 → 100 taken 188 times.
755 for (const auto &[mangledName, presetInterface] : manifestations) {
73 // Skip generic and newly inserted substantiations to prevent double matching of an interface
74
6/8
✓ Branch 20 → 21 taken 567 times.
✗ Branch 20 → 141 not taken.
✓ Branch 21 → 22 taken 297 times.
✓ Branch 21 → 23 taken 270 times.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 297 times.
✓ Branch 25 → 26 taken 270 times.
✓ Branch 25 → 27 taken 297 times.
567 if (presetInterface.isGenericSubstantiation() || presetInterface.isNewlyInserted)
75 293 continue;
76
77 // Copy the interface to be able to substantiate types
78
1/2
✓ Branch 27 → 28 taken 297 times.
✗ Branch 27 → 141 not taken.
297 Interface candidate = presetInterface;
79
80 // Check name requirement
81
1/2
✗ Branch 29 → 30 not taken.
✓ Branch 29 → 31 taken 297 times.
297 if (!matchName(candidate, reqName))
82 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 297 TypeMapping &typeMapping = candidate.typeMapping;
86 297 typeMapping.clear();
87
1/2
✓ Branch 33 → 34 taken 297 times.
✗ Branch 33 → 139 not taken.
297 typeMapping.reserve(candidate.templateTypes.size());
88
89 // Check template types requirement
90
2/4
✓ Branch 34 → 35 taken 297 times.
✗ Branch 34 → 139 not taken.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 297 times.
297 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 37 → 38 taken 297 times.
✗ Branch 37 → 139 not taken.
297 substantiateSignatures(candidate, typeMapping, node);
95
96 // We found a match! -> Set the actual candidate and its entry to used
97 297 candidate.used = true;
98 297 candidate.entry->used = true;
99
100 // Check if it needs to be substantiated
101
2/2
✓ Branch 39 → 40 taken 23 times.
✓ Branch 39 → 52 taken 274 times.
297 if (presetInterface.templateTypes.empty()) {
102
5/10
✓ Branch 40 → 41 taken 23 times.
✗ Branch 40 → 123 not taken.
✓ Branch 41 → 42 taken 23 times.
✗ Branch 41 → 46 not taken.
✓ Branch 42 → 43 taken 23 times.
✗ Branch 42 → 123 not taken.
✓ Branch 43 → 44 taken 23 times.
✗ Branch 43 → 123 not taken.
✓ Branch 44 → 45 taken 23 times.
✗ Branch 44 → 46 not taken.
23 assert(matchScope->interfaces.contains(defCodeLocStr) && matchScope->interfaces.at(defCodeLocStr).contains(mangledName));
103
2/4
✓ Branch 47 → 48 taken 23 times.
✗ Branch 47 → 123 not taken.
✓ Branch 48 → 49 taken 23 times.
✗ Branch 48 → 123 not taken.
23 Interface *match = &matchScope->interfaces.at(defCodeLocStr).at(mangledName);
104 23 match->used = true;
105
1/2
✓ Branch 49 → 50 taken 23 times.
✗ Branch 49 → 123 not taken.
23 matches.push_back(match);
106 23 continue; // Match was successful -> match the next interface
107 23 }
108
109 // Check if we already have this manifestation and can simply re-use it
110
4/6
✓ Branch 52 → 53 taken 274 times.
✗ Branch 52 → 126 not taken.
✓ Branch 53 → 54 taken 274 times.
✗ Branch 53 → 124 not taken.
✓ Branch 57 → 58 taken 109 times.
✓ Branch 57 → 62 taken 165 times.
274 if (auto it = manifestations.find(candidate.getSignature()); it != manifestations.end()) {
111 109 it->second.used = true;
112
1/2
✓ Branch 60 → 61 taken 109 times.
✗ Branch 60 → 127 not taken.
109 matches.push_back(&it->second);
113 109 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 62 → 63 taken 165 times.
✗ Branch 62 → 139 not taken.
165 Interface *substantiatedInterface = insertSubstantiation(matchScope, candidate, presetInterface.declNode);
118
2/4
✓ Branch 63 → 64 taken 165 times.
✗ Branch 63 → 139 not taken.
✓ Branch 64 → 65 taken 165 times.
✗ Branch 64 → 139 not taken.
165 substantiatedInterface->genericPreset = &matchScope->interfaces.at(defCodeLocStr).at(mangledName);
119
2/4
✓ Branch 65 → 66 taken 165 times.
✗ Branch 65 → 139 not taken.
✓ Branch 66 → 67 taken 165 times.
✗ Branch 66 → 139 not taken.
165 substantiatedInterface->declNode->getInterfaceManifestations()->push_back(substantiatedInterface);
120 165 substantiatedInterface->isNewlyInserted = true; // To not iterate over it in the same matching
121
122 // Copy interface entry
123
1/2
✓ Branch 67 → 68 taken 165 times.
✗ Branch 67 → 139 not taken.
165 const std::string &newSignature = substantiatedInterface->getSignature();
124
1/2
✓ Branch 68 → 69 taken 165 times.
✗ Branch 68 → 137 not taken.
165 matchScope->lookupStrict(substantiatedInterface->name)->used = true;
125
1/2
✓ Branch 71 → 72 taken 165 times.
✗ Branch 71 → 137 not taken.
165 substantiatedInterface->entry = matchScope->symbolTable.copySymbol(substantiatedInterface->name, newSignature);
126
1/2
✗ Branch 72 → 73 not taken.
✓ Branch 72 → 74 taken 165 times.
165 assert(substantiatedInterface->entry != nullptr);
127
128 // Copy interface scope
129
1/2
✓ Branch 74 → 75 taken 165 times.
✗ Branch 74 → 137 not taken.
165 const std::string &oldScopeName = presetInterface.getScopeName();
130
1/2
✓ Branch 75 → 76 taken 165 times.
✗ Branch 75 → 135 not taken.
165 const std::string &newScopeName = substantiatedInterface->getScopeName();
131
1/2
✓ Branch 76 → 77 taken 165 times.
✗ Branch 76 → 133 not taken.
165 matchScope->copyChildScope(oldScopeName, newScopeName);
132
1/2
✓ Branch 77 → 78 taken 165 times.
✗ Branch 77 → 133 not taken.
165 substantiatedInterface->scope = matchScope->getChildScope(newScopeName);
133
1/2
✗ Branch 78 → 79 not taken.
✓ Branch 78 → 80 taken 165 times.
165 assert(substantiatedInterface->scope != nullptr);
134 165 substantiatedInterface->scope->isGenericScope = false;
135
136 // Attach the template types to the new interface entry
137
1/2
✓ Branch 80 → 81 taken 165 times.
✗ Branch 80 → 132 not taken.
165 QualType entryType = substantiatedInterface->entry->getQualType()
138
2/4
✓ Branch 81 → 82 taken 165 times.
✗ Branch 81 → 131 not taken.
✓ Branch 82 → 83 taken 165 times.
✗ Branch 82 → 129 not taken.
330 .getWithTemplateTypes(substantiatedInterface->getTemplateTypes())
139
1/2
✓ Branch 83 → 84 taken 165 times.
✗ Branch 83 → 129 not taken.
165 .getWithBodyScope(substantiatedInterface->scope);
140
1/2
✓ Branch 85 → 86 taken 165 times.
✗ Branch 85 → 133 not taken.
165 substantiatedInterface->entry->updateType(entryType, true);
141
142 // Add to matched interfaces
143
1/2
✓ Branch 86 → 87 taken 165 times.
✗ Branch 86 → 133 not taken.
165 matches.push_back(substantiatedInterface);
144
3/3
✓ Branch 92 → 93 taken 165 times.
✓ Branch 92 → 95 taken 23 times.
✓ Branch 92 → 96 taken 109 times.
297 }
145 }
146
147 // If no matches were found, return a nullptr
148
1/2
✗ Branch 104 → 105 not taken.
✓ Branch 104 → 106 taken 297 times.
297 if (matches.empty())
149 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 297 times.
297 if (matches.size() > 1)
153 throw SemanticError(node, INTERFACE_AMBIGUITY, "Multiple interfaces match the requested signature");
154 297 Interface *matchedInterface = matches.front();
155 297 matchedInterface->isNewlyInserted = false;
156
157 // Insert into cache
158
1/2
✓ Branch 117 → 118 taken 297 times.
✗ Branch 117 → 153 not taken.
297 lookupCache[cacheKey] = matchedInterface;
159
160 297 return matchedInterface;
161 297 }
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 297 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 297 bool InterfaceManager::matchTemplateTypes(Interface &candidate, const QualTypeList &reqTemplateTypes, TypeMapping &typeMapping,
182 const ASTNode *node) {
183 // Check if the number of types match
184 297 const size_t typeCount = reqTemplateTypes.size();
185
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 297 times.
297 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 868 TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) {
190 274 return getGenericTypeOfCandidateByName(candidate, genericTypeName);
191 297 };
192
193 // Loop over all template types
194
2/2
✓ Branch 17 → 8 taken 274 times.
✓ Branch 17 → 18 taken 297 times.
571 for (size_t i = 0; i < typeCount; i++) {
195
1/2
✓ Branch 8 → 9 taken 274 times.
✗ Branch 8 → 22 not taken.
274 const QualType &reqType = reqTemplateTypes.at(i);
196
1/2
✓ Branch 9 → 10 taken 274 times.
✗ Branch 9 → 22 not taken.
274 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 274 times.
✗ Branch 10 → 22 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 274 times.
274 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 274 times.
✗ Branch 13 → 22 not taken.
✓ Branch 14 → 15 taken 274 times.
✗ Branch 14 → 16 not taken.
274 if (candidateType.hasAnyGenericParts())
204
1/2
✓ Branch 15 → 16 taken 274 times.
✗ Branch 15 → 22 not taken.
274 TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, node);
205 }
206
207 297 return true;
208 297 }
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 297 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 34 → 4 taken 775 times.
✓ Branch 34 → 35 taken 297 times.
1072 for (Function *method : candidate.methods) {
220 // Skip methods, that are already fully substantiated
221
3/4
✓ Branch 5 → 6 taken 775 times.
✗ Branch 5 → 37 not taken.
✓ Branch 6 → 7 taken 343 times.
✓ Branch 6 → 8 taken 432 times.
775 if (method->isFullySubstantiated())
222 343 continue;
223
224 // Substantiate return type
225
1/2
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 432 times.
432 if (method->isNormalFunction())
226 FunctionManager::substantiateReturnType(*method, typeMapping, node);
227
228 // Substantiate param types
229
2/2
✓ Branch 30 → 24 taken 2 times.
✓ Branch 30 → 31 taken 432 times.
434 for (auto &[qualType, isOptional] : method->paramList)
230
2/4
✓ Branch 25 → 26 taken 2 times.
✗ Branch 25 → 36 not taken.
✓ Branch 26 → 27 taken 2 times.
✗ Branch 26 → 28 not taken.
2 if (qualType.isBase(TY_GENERIC))
231
1/2
✓ Branch 27 → 28 taken 2 times.
✗ Branch 27 → 36 not taken.
2 TypeMatcher::substantiateTypeWithTypeMapping(qualType, typeMapping, node);
232 }
233 297 }
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 274 const GenericType *InterfaceManager::getGenericTypeOfCandidateByName(const Interface &candidate,
243 const std::string &templateTypeName) {
244
1/2
✓ Branch 14 → 4 taken 274 times.
✗ Branch 14 → 15 not taken.
274 for (const GenericType &templateType : candidate.templateTypes) {
245
2/4
✓ Branch 5 → 6 taken 274 times.
✗ Branch 5 → 17 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 274 times.
274 if (!templateType.is(TY_GENERIC))
246 continue;
247
2/4
✓ Branch 8 → 9 taken 274 times.
✗ Branch 8 → 17 not taken.
✓ Branch 10 → 11 taken 274 times.
✗ Branch 10 → 12 not taken.
274 if (templateType.getSubType() == templateTypeName)
248 274 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 1617 uint64_t InterfaceManager::getCacheKey(const Scope *scope, const std::string &name, const QualTypeList &templateTypes) {
262 1617 uint64_t hash = 0;
263 1617 hashCombine64(hash, hashPointer(scope));
264 1617 hashCombine64(hash, std::hash<std::string>{}(name));
265 1617 hashCombine64(hash, hashVector(templateTypes));
266 1617 return hash;
267 }
268
269 /**
270 * Clear the lookup cache
271 */
272 458 void InterfaceManager::cleanup() {
273 458 lookupCache.clear();
274 458 lookupCacheHits = 0;
275 458 lookupCacheMisses = 0;
276 458 }
277
278 /**
279 * Dump usage statistics for the lookup cache
280 */
281 223 std::string InterfaceManager::dumpLookupCacheStatistics() {
282
1/2
✓ Branch 2 → 3 taken 223 times.
✗ Branch 2 → 22 not taken.
223 std::stringstream stats;
283
2/4
✓ Branch 3 → 4 taken 223 times.
✗ Branch 3 → 20 not taken.
✓ Branch 4 → 5 taken 223 times.
✗ Branch 4 → 20 not taken.
223 stats << "InterfaceManager lookup cache statistics:" << std::endl;
284
3/6
✓ Branch 5 → 6 taken 223 times.
✗ Branch 5 → 20 not taken.
✓ Branch 7 → 8 taken 223 times.
✗ Branch 7 → 20 not taken.
✓ Branch 8 → 9 taken 223 times.
✗ Branch 8 → 20 not taken.
223 stats << " lookup cache entries: " << lookupCache.size() << std::endl;
285
3/6
✓ Branch 9 → 10 taken 223 times.
✗ Branch 9 → 20 not taken.
✓ Branch 10 → 11 taken 223 times.
✗ Branch 10 → 20 not taken.
✓ Branch 11 → 12 taken 223 times.
✗ Branch 11 → 20 not taken.
223 stats << " lookup cache hits: " << lookupCacheHits << std::endl;
286
3/6
✓ Branch 12 → 13 taken 223 times.
✗ Branch 12 → 20 not taken.
✓ Branch 13 → 14 taken 223 times.
✗ Branch 13 → 20 not taken.
✓ Branch 14 → 15 taken 223 times.
✗ Branch 14 → 20 not taken.
223 stats << " lookup cache misses: " << lookupCacheMisses << std::endl;
287
1/2
✓ Branch 15 → 16 taken 223 times.
✗ Branch 15 → 20 not taken.
446 return stats.str();
288 223 }
289
290 } // namespace spice::compiler
291