GCC Code Coverage Report


Directory: ../
File: src/typechecker/InterfaceManager.cpp
Date: 2025-02-05 01:09:36
Exec Total Coverage
Lines: 104 113 92.0%
Functions: 11 11 100.0%
Branches: 104 189 55.0%

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