Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2024 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 | 73 | 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 3 taken 73 times.
✗ Branch 4 not taken.
|
73 | insertScope->interfaces.insert({spiceInterface.declNode->codeLoc, InterfaceManifestationList()}); |
20 | |||
21 | // Save substantiation in declaration node | ||
22 |
1/2✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
|
73 | Interface *substantiation = insertSubstantiation(insertScope, spiceInterface, spiceInterface.declNode); |
23 |
1/2✓ Branch 1 taken 73 times.
✗ Branch 2 not taken.
|
73 | nodeInterfaceList->push_back(substantiation); |
24 | |||
25 | 73 | return substantiation; | |
26 | } | ||
27 | |||
28 | 175 | Interface *InterfaceManager::insertSubstantiation(Scope *insertScope, Interface &newManifestation, const ASTNode *declNode) { | |
29 |
1/2✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
|
175 | const std::string signature = newManifestation.getSignature(); |
30 | |||
31 | // Make sure that the manifestation does not exist already | ||
32 |
2/2✓ Branch 4 taken 175 times.
✓ Branch 5 taken 175 times.
|
350 | for ([[maybe_unused]] const auto &manifestations : insertScope->interfaces) |
33 |
3/6✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 175 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 175 times.
|
175 | assert(!manifestations.second.contains(newManifestation.getSignature())); |
34 | |||
35 | // Retrieve the matching manifestation list of the scope | ||
36 |
2/4✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 175 times.
|
175 | assert(insertScope->interfaces.contains(declNode->codeLoc)); |
37 |
1/2✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
|
175 | InterfaceManifestationList &manifestationList = insertScope->interfaces.at(declNode->codeLoc); |
38 | |||
39 | // Add substantiated interface | ||
40 | 175 | newManifestation.manifestationIndex = manifestationList.size(); | |
41 |
1/2✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
|
175 | manifestationList.emplace(signature, newManifestation); |
42 |
1/2✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
|
350 | return &manifestationList.at(signature); |
43 | 175 | } | |
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 | 1351 | Interface *InterfaceManager::match(Scope *matchScope, const std::string &reqName, const QualTypeList &reqTemplateTypes, | |
56 | const ASTNode *node) { | ||
57 | // Do cache lookup | ||
58 |
1/2✓ Branch 1 taken 1351 times.
✗ Branch 2 not taken.
|
1351 | const uint64_t cacheKey = getCacheKey(matchScope, reqName, reqTemplateTypes); |
59 |
3/4✓ Branch 1 taken 1351 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1176 times.
✓ Branch 4 taken 175 times.
|
1351 | if (lookupCache.contains(cacheKey)) |
60 |
1/2✓ Branch 1 taken 1176 times.
✗ Branch 2 not taken.
|
1176 | return lookupCache.at(cacheKey); |
61 | |||
62 | // Copy the registry to prevent iterating over items, that are created within the loop | ||
63 |
1/2✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
|
175 | InterfaceRegistry interfaceRegistry = matchScope->interfaces; |
64 | // Loop over interface registry to find interfaces, that match the requirements of the instantiation | ||
65 | 175 | std::vector<Interface *> matches; | |
66 |
2/2✓ Branch 7 taken 175 times.
✓ Branch 8 taken 175 times.
|
350 | 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 1 taken 175 times.
✗ Branch 2 not taken.
|
175 | const InterfaceManifestationList manifestations = m; |
69 |
2/2✓ Branch 7 taken 298 times.
✓ Branch 8 taken 114 times.
|
412 | for (const auto &[mangledName, presetInterface] : manifestations) { |
70 | // Skip generic substantiations to prevent double matching of an interface | ||
71 |
3/4✓ Branch 1 taken 298 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 123 times.
✓ Branch 4 taken 175 times.
|
298 | if (presetInterface.isGenericSubstantiation()) |
72 | 135 | continue; | |
73 | |||
74 | // Copy the interface to be able to substantiate types | ||
75 |
1/2✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
|
175 | Interface candidate = presetInterface; |
76 | |||
77 | // Check name requirement | ||
78 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
|
175 | 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 | 175 | TypeMapping &typeMapping = candidate.typeMapping; | |
83 | 175 | typeMapping.clear(); | |
84 |
1/2✓ Branch 2 taken 175 times.
✗ Branch 3 not taken.
|
175 | typeMapping.reserve(candidate.templateTypes.size()); |
85 | |||
86 | // Check template types requirement | ||
87 |
2/4✓ Branch 1 taken 175 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 175 times.
|
175 | 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 1 taken 175 times.
✗ Branch 2 not taken.
|
175 | substantiateSignatures(candidate, typeMapping, node); |
92 | |||
93 | // We found a match! -> Set the actual candidate and its entry to used | ||
94 | 175 | candidate.used = true; | |
95 | 175 | candidate.entry->used = true; | |
96 | |||
97 | // Check if it needs to be substantiated | ||
98 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 163 times.
|
175 | if (presetInterface.templateTypes.empty()) { |
99 |
5/10✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 12 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 12 times.
✗ Branch 12 not taken.
|
12 | assert(matchScope->interfaces.contains(defCodeLocStr) && matchScope->interfaces.at(defCodeLocStr).contains(mangledName)); |
100 |
3/6✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 12 times.
✗ Branch 8 not taken.
|
12 | matches.push_back(&matchScope->interfaces.at(defCodeLocStr).at(mangledName)); |
101 | 12 | matches.back()->used = true; | |
102 | 12 | 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 1 taken 163 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 163 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 61 times.
✓ Branch 8 taken 102 times.
|
163 | if (manifestations.contains(candidate.getSignature())) { |
107 |
4/8✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 61 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 61 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 61 times.
✗ Branch 11 not taken.
|
61 | matches.push_back(&matchScope->interfaces.at(defCodeLocStr).at(candidate.getSignature())); |
108 | 61 | 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 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | Interface *substantiatedInterface = insertSubstantiation(matchScope, candidate, presetInterface.declNode); |
113 |
2/4✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
|
102 | substantiatedInterface->genericPreset = &matchScope->interfaces.at(defCodeLocStr).at(mangledName); |
114 |
2/4✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
|
102 | substantiatedInterface->declNode->getInterfaceManifestations()->push_back(substantiatedInterface); |
115 | |||
116 | // Copy interface entry | ||
117 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | const std::string newSignature = substantiatedInterface->getSignature(); |
118 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | matchScope->lookupStrict(substantiatedInterface->name)->used = true; |
119 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | substantiatedInterface->entry = matchScope->symbolTable.copySymbol(substantiatedInterface->name, newSignature); |
120 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
|
102 | assert(substantiatedInterface->entry != nullptr); |
121 | |||
122 | // Copy interface scope | ||
123 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | const std::string newScopeName = INTERFACE_SCOPE_PREFIX + newSignature; |
124 |
2/4✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
|
102 | matchScope->copyChildScope(INTERFACE_SCOPE_PREFIX + presetInterface.name, newScopeName); |
125 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | substantiatedInterface->scope = matchScope->getChildScope(newScopeName); |
126 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
|
102 | assert(substantiatedInterface->scope != nullptr); |
127 | 102 | substantiatedInterface->scope->isGenericScope = false; | |
128 | |||
129 | // Attach the template types to the new interface entry | ||
130 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | QualType entryType = substantiatedInterface->entry->getQualType() |
131 |
2/4✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 102 times.
✗ Branch 5 not taken.
|
204 | .getWithTemplateTypes(substantiatedInterface->getTemplateTypes()) |
132 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | .getWithBodyScope(substantiatedInterface->scope); |
133 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | substantiatedInterface->entry->updateType(entryType, true); |
134 | |||
135 | // Add to matched interfaces | ||
136 |
1/2✓ Branch 1 taken 102 times.
✗ Branch 2 not taken.
|
102 | matches.push_back(substantiatedInterface); |
137 |
3/3✓ Branch 3 taken 102 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 61 times.
|
175 | } |
138 | 175 | } | |
139 | |||
140 | // If no matches were found, return a nullptr | ||
141 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
|
175 | if (matches.empty()) |
142 | ✗ | return nullptr; | |
143 | |||
144 | // Check if more than one interface matches the requirements | ||
145 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
|
175 | 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 2 taken 175 times.
✗ Branch 3 not taken.
|
175 | lookupCache[cacheKey] = matches.front(); |
150 | |||
151 | 175 | return matches.front(); | |
152 | 175 | } | |
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 | 175 | 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 | 175 | bool InterfaceManager::matchTemplateTypes(Interface &candidate, const QualTypeList &reqTemplateTypes, TypeMapping &typeMapping, const ASTNode *node) { | |
173 | // Check if the number of types match | ||
174 | 175 | const size_t typeCount = reqTemplateTypes.size(); | |
175 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 175 times.
|
175 | 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 | 513 | TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) { | |
180 | 163 | return getGenericTypeOfCandidateByName(candidate, genericTypeName); | |
181 | 175 | }; | |
182 | |||
183 | // Loop over all template types | ||
184 |
2/2✓ Branch 0 taken 163 times.
✓ Branch 1 taken 175 times.
|
338 | for (size_t i = 0; i < typeCount; i++) { |
185 |
1/2✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
|
163 | const QualType &reqType = reqTemplateTypes.at(i); |
186 |
1/2✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
|
163 | 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 1 taken 163 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 163 times.
|
163 | 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 1 taken 163 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 163 times.
✗ Branch 4 not taken.
|
163 | if (candidateType.hasAnyGenericParts()) |
194 |
1/2✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
|
163 | TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, node); |
195 | } | ||
196 | |||
197 | 175 | return true; | |
198 | 175 | } | |
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 | 175 | void InterfaceManager::substantiateSignatures(Interface &candidate, TypeMapping &typeMapping, const ASTNode *node) { | |
208 | // Loop over all signatures and substantiate the generic ones | ||
209 |
2/2✓ Branch 5 taken 468 times.
✓ Branch 6 taken 175 times.
|
643 | for (Function *method : candidate.methods) { |
210 | // Skip methods, that are already fully substantiated | ||
211 |
3/4✓ Branch 1 taken 468 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 208 times.
✓ Branch 4 taken 260 times.
|
468 | if (method->isFullySubstantiated()) |
212 | 208 | continue; | |
213 | |||
214 | // Substantiate return type | ||
215 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 260 times.
|
260 | if (method->isNormalFunction()) |
216 | ✗ | FunctionManager::substantiateReturnType(*method, typeMapping, node); | |
217 | |||
218 | // Substantiate param types | ||
219 |
2/2✓ Branch 5 taken 2 times.
✓ Branch 6 taken 260 times.
|
262 | for (auto &[qualType, isOptional] : method->paramList) |
220 |
2/4✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
|
2 | if (qualType.isBase(TY_GENERIC)) |
221 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | TypeMatcher::substantiateTypeWithTypeMapping(qualType, typeMapping, node); |
222 | } | ||
223 | 175 | } | |
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 | 163 | const GenericType *InterfaceManager::getGenericTypeOfCandidateByName(const Interface &candidate, | |
233 | const std::string &templateTypeName) { | ||
234 |
1/2✓ Branch 5 taken 163 times.
✗ Branch 6 not taken.
|
163 | for (const GenericType &templateType : candidate.templateTypes) { |
235 |
2/4✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 163 times.
|
163 | if (!templateType.is(TY_GENERIC)) |
236 | ✗ | continue; | |
237 |
2/4✓ Branch 1 taken 163 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 163 times.
✗ Branch 5 not taken.
|
163 | if (templateType.getSubType() == templateTypeName) |
238 | 163 | 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 | 1351 | uint64_t InterfaceManager::getCacheKey(Scope *scope, const std::string &name, const QualTypeList &templateTypes) { | |
252 | 1295 | 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 | 1295 | return acc * 31 + std::hash<QualType>{}(val); | |
255 | }; | ||
256 | // Calculate the cache key | ||
257 | 1351 | const uint64_t scopeHash = std::hash<Scope *>{}(scope); | |
258 | 1351 | const uint64_t hashName = std::hash<std::string>{}(name); | |
259 | 1351 | const uint64_t hashTemplateTypes = std::accumulate(templateTypes.begin(), templateTypes.end(), 0u, pred); | |
260 | 1351 | return scopeHash ^ (hashName << 1) ^ (hashTemplateTypes << 2); | |
261 | } | ||
262 | |||
263 | /** | ||
264 | * Clear all statics | ||
265 | */ | ||
266 | 390 | void InterfaceManager::clear() { lookupCache.clear(); } | |
267 | |||
268 | } // namespace spice::compiler | ||
269 |