Line | Branch | Exec | Source |
---|---|---|---|
1 | // Copyright (c) 2021-2025 ChilliBits. All rights reserved. | ||
2 | |||
3 | #include "StructManager.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/CodeLoc.h> | ||
11 | #include <util/CustomHashFunctions.h> | ||
12 | |||
13 | namespace spice::compiler { | ||
14 | |||
15 | // Static member initialization | ||
16 | std::unordered_map<uint64_t, Struct *> StructManager::lookupCache = {}; | ||
17 | size_t StructManager::lookupCacheHits = 0; | ||
18 | size_t StructManager::lookupCacheMisses = 0; | ||
19 | |||
20 | 601 | Struct *StructManager::insert(Scope *insertScope, Struct &spiceStruct, std::vector<Struct *> *nodeStructList) { | |
21 | // Open a new manifestation list. Which gets filled by the substantiated manifestations of the struct | ||
22 |
3/6✓ Branch 0 (2→3) taken 601 times.
✗ Branch 1 (2→22) not taken.
✓ Branch 2 (3→4) taken 601 times.
✗ Branch 3 (3→19) not taken.
✓ Branch 4 (4→5) taken 601 times.
✗ Branch 5 (4→17) not taken.
|
601 | const std::string structId = spiceStruct.name + ":" + spiceStruct.declNode->codeLoc.toPrettyLineAndColumn(); |
23 |
2/4✓ Branch 0 (8→9) taken 601 times.
✗ Branch 1 (8→25) not taken.
✓ Branch 2 (9→10) taken 601 times.
✗ Branch 3 (9→23) not taken.
|
601 | insertScope->structs.insert({structId, StructManifestationList()}); |
24 | |||
25 | // Save substantiation in declaration node | ||
26 |
1/2✓ Branch 0 (12→13) taken 601 times.
✗ Branch 1 (12→29) not taken.
|
601 | Struct *substantiation = insertSubstantiation(insertScope, spiceStruct, spiceStruct.declNode); |
27 |
1/2✓ Branch 0 (13→14) taken 601 times.
✗ Branch 1 (13→29) not taken.
|
601 | nodeStructList->push_back(substantiation); |
28 | |||
29 | 601 | return substantiation; | |
30 | 601 | } | |
31 | |||
32 | 952 | Struct *StructManager::insertSubstantiation(Scope *insertScope, Struct &newManifestation, const ASTNode *declNode) { | |
33 |
1/2✓ Branch 0 (2→3) taken 952 times.
✗ Branch 1 (2→42) not taken.
|
952 | const std::string signature = newManifestation.getSignature(); |
34 | |||
35 | #ifndef NDEBUG | ||
36 | // Make sure that the manifestation does not exist already | ||
37 |
5/8✓ Branch 0 (3→4) taken 952 times.
✗ Branch 1 (3→31) not taken.
✓ Branch 2 (4→5) taken 952 times.
✗ Branch 3 (4→31) not taken.
✓ Branch 4 (5→6) taken 952 times.
✗ Branch 5 (5→31) not taken.
✓ Branch 6 (13→7) taken 1444 times.
✓ Branch 7 (13→14) taken 952 times.
|
2396 | for (const auto &val : insertScope->structs | std::views::values) |
38 |
2/4✓ Branch 0 (8→9) taken 1444 times.
✗ Branch 1 (8→31) not taken.
✗ Branch 2 (9→10) not taken.
✓ Branch 3 (9→11) taken 1444 times.
|
1444 | assert(!val.contains(signature)); |
39 | #endif | ||
40 | |||
41 | // Retrieve the matching manifestation list of the scope | ||
42 |
3/6✓ Branch 0 (14→15) taken 952 times.
✗ Branch 1 (14→37) not taken.
✓ Branch 2 (15→16) taken 952 times.
✗ Branch 3 (15→34) not taken.
✓ Branch 4 (16→17) taken 952 times.
✗ Branch 5 (16→32) not taken.
|
952 | const std::string structId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn(); |
43 |
2/4✓ Branch 0 (19→20) taken 952 times.
✗ Branch 1 (19→38) not taken.
✗ Branch 2 (20→21) not taken.
✓ Branch 3 (20→22) taken 952 times.
|
952 | assert(insertScope->structs.contains(structId)); |
44 |
1/2✓ Branch 0 (22→23) taken 952 times.
✗ Branch 1 (22→38) not taken.
|
952 | StructManifestationList &manifestationList = insertScope->structs.at(structId); |
45 | |||
46 | // Add substantiated struct | ||
47 | 952 | newManifestation.manifestationIndex = manifestationList.size(); | |
48 |
1/2✓ Branch 0 (24→25) taken 952 times.
✗ Branch 1 (24→38) not taken.
|
952 | manifestationList.emplace(signature, newManifestation); |
49 |
1/2✓ Branch 0 (25→26) taken 952 times.
✗ Branch 1 (25→38) not taken.
|
1904 | return &manifestationList.at(signature); |
50 | 952 | } | |
51 | |||
52 | /** | ||
53 | * Check if there is a struct in this scope, fulfilling all given requirements and if found, return it. | ||
54 | * If more than one struct matches the requirement, an error gets thrown | ||
55 | * | ||
56 | * @param matchScope Scope to match against | ||
57 | * @param qt Struct name requirement | ||
58 | * @param reqTemplateTypes Template types to substantiate generic types | ||
59 | * @param node Instantiation AST node for printing error messages | ||
60 | * @return Matched struct or nullptr | ||
61 | */ | ||
62 | 20971 | Struct *StructManager::match(Scope *matchScope, const std::string &qt, const QualTypeList &reqTemplateTypes, | |
63 | const ASTNode *node) { | ||
64 | // Do cache lookup | ||
65 |
1/2✓ Branch 0 (2→3) taken 20971 times.
✗ Branch 1 (2→212) not taken.
|
20971 | const uint64_t cacheKey = getCacheKey(matchScope, qt, reqTemplateTypes); |
66 |
3/4✓ Branch 0 (3→4) taken 20971 times.
✗ Branch 1 (3→212) not taken.
✓ Branch 2 (4→5) taken 19936 times.
✓ Branch 3 (4→7) taken 1035 times.
|
20971 | if (lookupCache.contains(cacheKey)) { |
67 | 19936 | lookupCacheHits++; | |
68 |
1/2✓ Branch 0 (5→6) taken 19936 times.
✗ Branch 1 (5→212) not taken.
|
19936 | return lookupCache.at(cacheKey); |
69 | } | ||
70 | 1035 | lookupCacheMisses++; | |
71 | |||
72 | // Copy the registry to prevent iterating over items, that are created within the loop | ||
73 |
1/2✓ Branch 0 (7→8) taken 1035 times.
✗ Branch 1 (7→212) not taken.
|
1035 | StructRegistry structRegistry = matchScope->structs; |
74 | // Loop over struct registry to find structs, that match the requirements of the instantiation | ||
75 | 1035 | std::vector<Struct *> matches; | |
76 |
2/2✓ Branch 0 (144→10) taken 1618 times.
✓ Branch 1 (144→145) taken 1035 times.
|
2653 | for (const auto &[structId, m] : structRegistry) { |
77 | // Copy the manifestation list to prevent iterating over items, that are created within the loop | ||
78 |
1/2✓ Branch 0 (13→14) taken 1618 times.
✗ Branch 1 (13→197) not taken.
|
1618 | const StructManifestationList manifestations = m; |
79 |
2/2✓ Branch 0 (140→16) taken 2255 times.
✓ Branch 1 (140→141) taken 756 times.
|
3011 | for (const auto &[mangledName, presetStruct] : manifestations) { |
80 | // Skip generic substantiations to prevent double matching of a struct | ||
81 |
3/4✓ Branch 0 (19→20) taken 2255 times.
✗ Branch 1 (19→193) not taken.
✓ Branch 2 (20→21) taken 637 times.
✓ Branch 3 (20→22) taken 1618 times.
|
2255 | if (presetStruct.isGenericSubstantiation()) |
82 | 1042 | continue; | |
83 | |||
84 | // Copy the struct to be able to substantiate types | ||
85 |
1/2✓ Branch 0 (22→23) taken 1618 times.
✗ Branch 1 (22→193) not taken.
|
1618 | Struct candidate = presetStruct; |
86 | |||
87 | // Check name requirement | ||
88 |
2/2✓ Branch 0 (24→25) taken 621 times.
✓ Branch 1 (24→26) taken 997 times.
|
1618 | if (!matchName(candidate, qt)) |
89 | 621 | break; // Leave the whole manifestation list, because all manifestations in this list have the same name | |
90 | |||
91 | // Prepare mapping table from generic type name to concrete type | ||
92 | 997 | TypeMapping &typeMapping = candidate.typeMapping; | |
93 | 997 | typeMapping.clear(); | |
94 |
1/2✓ Branch 0 (28→29) taken 997 times.
✗ Branch 1 (28→191) not taken.
|
997 | typeMapping.reserve(candidate.templateTypes.size()); |
95 | |||
96 | // Check template types requirement | ||
97 |
3/4✓ Branch 0 (29→30) taken 997 times.
✗ Branch 1 (29→191) not taken.
✓ Branch 2 (30→31) taken 1 times.
✓ Branch 3 (30→32) taken 996 times.
|
997 | if (!matchTemplateTypes(candidate, reqTemplateTypes, typeMapping, node)) |
98 | 1 | continue; // Leave this manifestation and continue with the next one | |
99 | |||
100 | // Map field types from generic to concrete | ||
101 |
1/2✓ Branch 0 (32→33) taken 996 times.
✗ Branch 1 (32→191) not taken.
|
996 | substantiateFieldTypes(candidate, typeMapping, node); |
102 | |||
103 | // We found a match! -> Set the actual candidate and its entry to used | ||
104 | 996 | candidate.used = true; | |
105 | 996 | candidate.entry->used = true; | |
106 | |||
107 | // Check if it needs to be substantiated | ||
108 |
2/2✓ Branch 0 (34→35) taken 404 times.
✓ Branch 1 (34→47) taken 592 times.
|
996 | if (presetStruct.templateTypes.empty()) { |
109 |
5/10✓ Branch 0 (35→36) taken 404 times.
✗ Branch 1 (35→191) not taken.
✓ Branch 2 (36→37) taken 404 times.
✗ Branch 3 (36→41) not taken.
✓ Branch 4 (37→38) taken 404 times.
✗ Branch 5 (37→191) not taken.
✓ Branch 6 (38→39) taken 404 times.
✗ Branch 7 (38→191) not taken.
✓ Branch 8 (39→40) taken 404 times.
✗ Branch 9 (39→41) not taken.
|
404 | assert(matchScope->structs.contains(structId) && matchScope->structs.at(structId).contains(mangledName)); |
110 |
3/6✓ Branch 0 (42→43) taken 404 times.
✗ Branch 1 (42→166) not taken.
✓ Branch 2 (43→44) taken 404 times.
✗ Branch 3 (43→166) not taken.
✓ Branch 4 (44→45) taken 404 times.
✗ Branch 5 (44→166) not taken.
|
404 | matches.push_back(&matchScope->structs.at(structId).at(mangledName)); |
111 | 404 | matches.back()->used = true; | |
112 | 404 | continue; // Match was successful -> match the next struct | |
113 | } | ||
114 | |||
115 | // Check if we already have this manifestation and can simply re-use it | ||
116 |
4/6✓ Branch 0 (47→48) taken 592 times.
✗ Branch 1 (47→169) not taken.
✓ Branch 2 (48→49) taken 592 times.
✗ Branch 3 (48→167) not taken.
✓ Branch 4 (50→51) taken 241 times.
✓ Branch 5 (50→57) taken 351 times.
|
592 | if (manifestations.contains(candidate.getSignature())) { |
117 |
4/8✓ Branch 0 (51→52) taken 241 times.
✗ Branch 1 (51→173) not taken.
✓ Branch 2 (52→53) taken 241 times.
✗ Branch 3 (52→172) not taken.
✓ Branch 4 (53→54) taken 241 times.
✗ Branch 5 (53→170) not taken.
✓ Branch 6 (54→55) taken 241 times.
✗ Branch 7 (54→170) not taken.
|
241 | matches.push_back(&matchScope->structs.at(structId).at(candidate.getSignature())); |
118 | 241 | break; // Leave the whole manifestation list to not double-match the manifestation | |
119 | } | ||
120 | |||
121 | // Insert the substantiated version if required | ||
122 |
1/2✓ Branch 0 (57→58) taken 351 times.
✗ Branch 1 (57→191) not taken.
|
351 | Struct *substantiatedStruct = insertSubstantiation(matchScope, candidate, presetStruct.declNode); |
123 |
2/4✓ Branch 0 (58→59) taken 351 times.
✗ Branch 1 (58→191) not taken.
✓ Branch 2 (59→60) taken 351 times.
✗ Branch 3 (59→191) not taken.
|
351 | substantiatedStruct->genericPreset = &matchScope->structs.at(structId).at(mangledName); |
124 |
2/4✓ Branch 0 (60→61) taken 351 times.
✗ Branch 1 (60→191) not taken.
✓ Branch 2 (61→62) taken 351 times.
✗ Branch 3 (61→191) not taken.
|
351 | substantiatedStruct->declNode->getStructManifestations()->push_back(substantiatedStruct); |
125 | |||
126 | // Copy struct entry | ||
127 |
1/2✓ Branch 0 (62→63) taken 351 times.
✗ Branch 1 (62→191) not taken.
|
351 | const std::string newSignature = substantiatedStruct->getSignature(); |
128 |
1/2✓ Branch 0 (63→64) taken 351 times.
✗ Branch 1 (63→189) not taken.
|
351 | matchScope->lookupStrict(substantiatedStruct->name)->used = true; |
129 |
1/2✓ Branch 0 (66→67) taken 351 times.
✗ Branch 1 (66→189) not taken.
|
351 | substantiatedStruct->entry = matchScope->symbolTable.copySymbol(substantiatedStruct->name, newSignature); |
130 |
1/2✗ Branch 0 (67→68) not taken.
✓ Branch 1 (67→69) taken 351 times.
|
351 | assert(substantiatedStruct->entry != nullptr); |
131 | |||
132 | // Copy struct scope | ||
133 |
1/2✓ Branch 0 (69→70) taken 351 times.
✗ Branch 1 (69→189) not taken.
|
351 | const std::string oldScopeName = STRUCT_SCOPE_PREFIX + presetStruct.name; |
134 |
1/2✓ Branch 0 (70→71) taken 351 times.
✗ Branch 1 (70→187) not taken.
|
351 | const std::string newScopeName = STRUCT_SCOPE_PREFIX + newSignature; |
135 |
1/2✓ Branch 0 (71→72) taken 351 times.
✗ Branch 1 (71→185) not taken.
|
351 | substantiatedStruct->scope = matchScope->copyChildScope(oldScopeName, newScopeName); |
136 |
1/2✗ Branch 0 (72→73) not taken.
✓ Branch 1 (72→74) taken 351 times.
|
351 | assert(substantiatedStruct->scope != nullptr); |
137 | 351 | substantiatedStruct->scope->isGenericScope = false; | |
138 | |||
139 | // Attach the template types to the new struct entry | ||
140 |
1/2✓ Branch 0 (74→75) taken 351 times.
✗ Branch 1 (74→177) not taken.
|
351 | QualType entryType = substantiatedStruct->entry->getQualType() |
141 |
2/4✓ Branch 0 (75→76) taken 351 times.
✗ Branch 1 (75→176) not taken.
✓ Branch 2 (76→77) taken 351 times.
✗ Branch 3 (76→174) not taken.
|
702 | .getWithTemplateTypes(substantiatedStruct->getTemplateTypes()) |
142 |
1/2✓ Branch 0 (77→78) taken 351 times.
✗ Branch 1 (77→174) not taken.
|
351 | .getWithBodyScope(substantiatedStruct->scope); |
143 |
1/2✓ Branch 0 (79→80) taken 351 times.
✗ Branch 1 (79→185) not taken.
|
351 | substantiatedStruct->entry->updateType(entryType, true); |
144 | |||
145 | // Replace symbol types of field entries with concrete types | ||
146 |
1/2✗ Branch 0 (80→81) not taken.
✓ Branch 1 (80→82) taken 351 times.
|
351 | assert(substantiatedStruct->scope != nullptr); |
147 | 351 | const size_t fieldCount = substantiatedStruct->fieldTypes.size(); | |
148 |
1/2✓ Branch 0 (83→84) taken 351 times.
✗ Branch 1 (83→185) not taken.
|
351 | const size_t explicitFieldsStartIdx = substantiatedStruct->scope->getFieldCount() - fieldCount; |
149 |
2/2✓ Branch 0 (108→85) taken 819 times.
✓ Branch 1 (108→109) taken 351 times.
|
1170 | for (size_t i = 0; i < fieldCount; i++) { |
150 | // Replace field type with concrete template type | ||
151 |
1/2✗ Branch 0 (85→86) not taken.
✓ Branch 1 (85→87) taken 819 times.
|
819 | SymbolTableEntry *fieldEntry = substantiatedStruct->scope->lookupField(explicitFieldsStartIdx + i); |
152 |
3/6✓ Branch 0 (90→91) taken 819 times.
✗ Branch 1 (90→94) not taken.
✓ Branch 2 (91→92) taken 819 times.
✗ Branch 3 (91→180) not taken.
✓ Branch 4 (92→93) taken 819 times.
✗ Branch 5 (92→94) not taken.
|
819 | assert(fieldEntry != nullptr && fieldEntry->isField()); |
153 |
1/2✓ Branch 0 (95→96) taken 819 times.
✗ Branch 1 (95→180) not taken.
|
819 | QualType &fieldType = substantiatedStruct->fieldTypes.at(i); |
154 |
1/2✓ Branch 0 (96→97) taken 819 times.
✗ Branch 1 (96→180) not taken.
|
819 | QualType baseType = fieldType.getBase(); |
155 | |||
156 | // Set the body scope of fields that are of type <candidate-struct>* | ||
157 |
4/6✓ Branch 0 (97→98) taken 819 times.
✗ Branch 1 (97→180) not taken.
✓ Branch 2 (98→99) taken 819 times.
✗ Branch 3 (98→180) not taken.
✓ Branch 4 (99→100) taken 19 times.
✓ Branch 5 (99→103) taken 800 times.
|
819 | if (baseType.matches(substantiatedStruct->entry->getQualType(), false, true, true)) |
158 |
2/4✓ Branch 0 (100→101) taken 19 times.
✗ Branch 1 (100→178) not taken.
✓ Branch 2 (101→102) taken 19 times.
✗ Branch 3 (101→178) not taken.
|
19 | fieldType = fieldType.replaceBaseType(baseType.getWithBodyScope(substantiatedStruct->scope)); |
159 | |||
160 |
1/2✓ Branch 0 (103→104) taken 819 times.
✗ Branch 1 (103→180) not taken.
|
819 | fieldEntry->updateType(fieldType, /*overwriteExistingType=*/true); |
161 | |||
162 | // Instantiate structs | ||
163 |
3/4✓ Branch 0 (104→105) taken 819 times.
✗ Branch 1 (104→180) not taken.
✓ Branch 2 (105→106) taken 313 times.
✓ Branch 3 (105→107) taken 506 times.
|
819 | if (baseType.is(TY_STRUCT)) |
164 |
1/2✓ Branch 0 (106→107) taken 313 times.
✗ Branch 1 (106→180) not taken.
|
313 | (void)baseType.getStruct(node); |
165 | } | ||
166 | |||
167 | // Instantiate implemented interfaces if required | ||
168 |
2/2✓ Branch 0 (126→111) taken 120 times.
✓ Branch 1 (126→127) taken 351 times.
|
471 | for (QualType &interfaceType : substantiatedStruct->interfaceTypes) { |
169 | // Skip non-generic interfaces | ||
170 |
2/4✓ Branch 0 (112→113) taken 120 times.
✗ Branch 1 (112→183) not taken.
✗ Branch 2 (113→114) not taken.
✓ Branch 3 (113→115) taken 120 times.
|
120 | if (!interfaceType.hasAnyGenericParts()) |
171 | ✗ | continue; | |
172 | |||
173 | // Build template types | ||
174 |
2/4✓ Branch 0 (115→116) taken 120 times.
✗ Branch 1 (115→183) not taken.
✓ Branch 2 (116→117) taken 120 times.
✗ Branch 3 (116→183) not taken.
|
120 | QualTypeList templateTypes = interfaceType.getTemplateTypes(); |
175 |
1/2✓ Branch 0 (117→118) taken 120 times.
✗ Branch 1 (117→181) not taken.
|
120 | TypeMatcher::substantiateTypesWithTypeMapping(templateTypes, typeMapping, node); |
176 | |||
177 | // Instantiate interface | ||
178 |
1/2✓ Branch 0 (118→119) taken 120 times.
✗ Branch 1 (118→181) not taken.
|
120 | Interface *spiceInterface = interfaceType.getInterface(node, templateTypes); |
179 |
1/2✗ Branch 0 (119→120) not taken.
✓ Branch 1 (119→121) taken 120 times.
|
120 | assert(spiceInterface != nullptr); |
180 |
1/2✓ Branch 0 (121→122) taken 120 times.
✗ Branch 1 (121→181) not taken.
|
120 | interfaceType = spiceInterface->entry->getQualType(); |
181 | 120 | } | |
182 | |||
183 | // Add to matched structs | ||
184 |
1/2✓ Branch 0 (127→128) taken 351 times.
✗ Branch 1 (127→185) not taken.
|
351 | matches.push_back(substantiatedStruct); |
185 |
3/3✓ Branch 0 (133→134) taken 351 times.
✓ Branch 1 (133→136) taken 405 times.
✓ Branch 2 (133→137) taken 862 times.
|
1618 | } |
186 | 1618 | } | |
187 | |||
188 | // If no matches were found, return a nullptr | ||
189 |
2/2✓ Branch 0 (146→147) taken 39 times.
✓ Branch 1 (146→148) taken 996 times.
|
1035 | if (matches.empty()) |
190 | 39 | return nullptr; | |
191 | |||
192 | // Check if more than one struct matches the requirements | ||
193 |
1/2✗ Branch 0 (149→150) not taken.
✓ Branch 1 (149→158) taken 996 times.
|
996 | if (matches.size() > 1) |
194 | ✗ | throw SemanticError(node, STRUCT_AMBIGUITY, "Multiple structs match the requested signature"); | |
195 | |||
196 | // Insert into cache | ||
197 |
1/2✓ Branch 0 (159→160) taken 996 times.
✗ Branch 1 (159→208) not taken.
|
996 | lookupCache[cacheKey] = matches.front(); |
198 | |||
199 | 996 | return matches.front(); | |
200 | 1035 | } | |
201 | |||
202 | /** | ||
203 | * Checks if the matching candidate fulfills the name requirement | ||
204 | * | ||
205 | * @param candidate Matching candidate struct | ||
206 | * @param reqName Requested struct name | ||
207 | * @return Fulfilled or not | ||
208 | */ | ||
209 | 1618 | bool StructManager::matchName(const Struct &candidate, const std::string &reqName) { return candidate.name == reqName; } | |
210 | |||
211 | /** | ||
212 | * Checks if the matching candidate fulfills the template types requirement | ||
213 | * | ||
214 | * @param candidate Matching candidate struct | ||
215 | * @param reqTemplateTypes Requested struct template types | ||
216 | * @param typeMapping Generic type mapping | ||
217 | * @param node Instantiation AST node for printing error messages | ||
218 | * @return Fulfilled or not | ||
219 | */ | ||
220 | 997 | bool StructManager::matchTemplateTypes(Struct &candidate, const QualTypeList &reqTemplateTypes, TypeMapping &typeMapping, | |
221 | const ASTNode *node) { | ||
222 | // Check if the number of types match | ||
223 | 997 | const size_t typeCount = reqTemplateTypes.size(); | |
224 |
2/2✓ Branch 0 (4→5) taken 1 times.
✓ Branch 1 (4→6) taken 996 times.
|
997 | if (typeCount != candidate.templateTypes.size()) |
225 | 1 | return false; | |
226 | |||
227 | // Give the type matcher a way to retrieve instances of GenericType by their name | ||
228 | 2841 | TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) { | |
229 | 849 | return getGenericTypeOfCandidateByName(candidate, genericTypeName); | |
230 | 996 | }; | |
231 | |||
232 | // Loop over all template types | ||
233 |
2/2✓ Branch 0 (17→8) taken 849 times.
✓ Branch 1 (17→18) taken 996 times.
|
1845 | for (size_t i = 0; i < typeCount; i++) { |
234 |
1/2✓ Branch 0 (8→9) taken 849 times.
✗ Branch 1 (8→22) not taken.
|
849 | const QualType &reqType = reqTemplateTypes.at(i); |
235 |
1/2✓ Branch 0 (9→10) taken 849 times.
✗ Branch 1 (9→22) not taken.
|
849 | QualType &candidateType = candidate.templateTypes.at(i); |
236 | |||
237 | // Check if the requested template type matches the candidate template type. The type mapping may be extended | ||
238 |
2/4✓ Branch 0 (10→11) taken 849 times.
✗ Branch 1 (10→22) not taken.
✗ Branch 2 (11→12) not taken.
✓ Branch 3 (11→13) taken 849 times.
|
849 | if (!TypeMatcher::matchRequestedToCandidateType(candidateType, reqType, typeMapping, genericTypeResolver, false)) |
239 | ✗ | return false; | |
240 | |||
241 | // Substantiate the candidate param type, based on the type mapping | ||
242 |
2/4✓ Branch 0 (13→14) taken 849 times.
✗ Branch 1 (13→22) not taken.
✓ Branch 2 (14→15) taken 849 times.
✗ Branch 3 (14→16) not taken.
|
849 | if (candidateType.hasAnyGenericParts()) |
243 |
1/2✓ Branch 0 (15→16) taken 849 times.
✗ Branch 1 (15→22) not taken.
|
849 | TypeMatcher::substantiateTypeWithTypeMapping(candidateType, typeMapping, node); |
244 | } | ||
245 | |||
246 | 996 | return true; | |
247 | 996 | } | |
248 | |||
249 | /** | ||
250 | * Come up with the concrete field types, by applying the type mapping onto the generic field types | ||
251 | * | ||
252 | * @param candidate Candidate struct | ||
253 | * @param typeMapping Generic type mapping | ||
254 | * @param node Instantiation AST node for printing error messages | ||
255 | */ | ||
256 | 996 | void StructManager::substantiateFieldTypes(Struct &candidate, const TypeMapping &typeMapping, const ASTNode *node) { | |
257 | // Loop over all implicit fields and substantiate the generic ones | ||
258 | 996 | const size_t fieldCount = candidate.scope->getFieldCount() - candidate.fieldTypes.size(); | |
259 |
2/2✓ Branch 0 (16→5) taken 229 times.
✓ Branch 1 (16→17) taken 996 times.
|
1225 | for (size_t i = 0; i < fieldCount; i++) { |
260 |
1/2✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 229 times.
|
229 | SymbolTableEntry *fieldEntry = candidate.scope->lookupField(i); |
261 |
1/2✓ Branch 0 (10→11) taken 229 times.
✗ Branch 1 (10→27) not taken.
|
229 | QualType fieldType = fieldEntry->getQualType(); |
262 |
3/4✓ Branch 0 (11→12) taken 229 times.
✗ Branch 1 (11→27) not taken.
✓ Branch 2 (12→13) taken 174 times.
✓ Branch 3 (12→15) taken 55 times.
|
229 | if (fieldType.hasAnyGenericParts()) { |
263 |
1/2✓ Branch 0 (13→14) taken 174 times.
✗ Branch 1 (13→27) not taken.
|
174 | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, node); |
264 |
1/2✓ Branch 0 (14→15) taken 174 times.
✗ Branch 1 (14→27) not taken.
|
174 | fieldEntry->updateType(fieldType, true); |
265 | } | ||
266 | } | ||
267 | |||
268 | // Loop over all explicit field types and substantiate the generic ones | ||
269 |
2/2✓ Branch 0 (25→19) taken 2230 times.
✓ Branch 1 (25→26) taken 996 times.
|
3226 | for (QualType &fieldType : candidate.fieldTypes) |
270 |
3/4✓ Branch 0 (20→21) taken 2230 times.
✗ Branch 1 (20→28) not taken.
✓ Branch 2 (21→22) taken 929 times.
✓ Branch 3 (21→23) taken 1301 times.
|
2230 | if (fieldType.hasAnyGenericParts()) |
271 |
1/2✓ Branch 0 (22→23) taken 929 times.
✗ Branch 1 (22→28) not taken.
|
929 | TypeMatcher::substantiateTypeWithTypeMapping(fieldType, typeMapping, node); |
272 | 996 | } | |
273 | |||
274 | /** | ||
275 | * Searches the candidate template types for a generic type object with a certain name and return it | ||
276 | * | ||
277 | * @param candidate Matching candidate struct | ||
278 | * @param templateTypeName Template type name | ||
279 | * @return Generic type object | ||
280 | */ | ||
281 | 849 | const GenericType *StructManager::getGenericTypeOfCandidateByName(const Struct &candidate, const std::string &templateTypeName) { | |
282 |
1/2✓ Branch 0 (14→4) taken 1108 times.
✗ Branch 1 (14→15) not taken.
|
1108 | for (const GenericType &templateType : candidate.templateTypes) { |
283 |
3/4✓ Branch 0 (5→6) taken 1108 times.
✗ Branch 1 (5→17) not taken.
✓ Branch 2 (6→7) taken 175 times.
✓ Branch 3 (6→8) taken 933 times.
|
1108 | if (!templateType.is(TY_GENERIC)) |
284 | 175 | continue; | |
285 |
3/4✓ Branch 0 (8→9) taken 933 times.
✗ Branch 1 (8→17) not taken.
✓ Branch 2 (10→11) taken 849 times.
✓ Branch 3 (10→12) taken 84 times.
|
933 | if (templateType.getSubType() == templateTypeName) |
286 | 849 | return &templateType; | |
287 | } | ||
288 | ✗ | return nullptr; | |
289 | } | ||
290 | |||
291 | /** | ||
292 | * Calculate the cache key for the struct lookup cache | ||
293 | * | ||
294 | * @param scope Scope to match against | ||
295 | * @param name Struct name requirement | ||
296 | * @param templateTypes Template types to substantiate generic types | ||
297 | * @return Cache key | ||
298 | */ | ||
299 | 20971 | uint64_t StructManager::getCacheKey(Scope *scope, const std::string &name, const QualTypeList &templateTypes) { | |
300 | 10583 | const auto pred = [](size_t acc, const QualType &val) { | |
301 | // Combine the previous hash value with the current element's hash, adjusted by a prime number to reduce collisions | ||
302 | 10583 | return acc * 31 + std::hash<QualType>{}(val); | |
303 | }; | ||
304 | // Calculate the cache key | ||
305 | 20971 | const uint64_t scopeHash = std::hash<Scope *>{}(scope); | |
306 | 20971 | const uint64_t hashName = std::hash<std::string>{}(name); | |
307 | 20971 | const uint64_t hashTemplateTypes = std::accumulate(templateTypes.begin(), templateTypes.end(), 0u, pred); | |
308 | 20971 | return scopeHash ^ (hashName << 1) ^ (hashTemplateTypes << 2); | |
309 | } | ||
310 | |||
311 | /** | ||
312 | * Clear the lookup cache | ||
313 | */ | ||
314 | 407 | void StructManager::cleanup() { | |
315 | 407 | lookupCache.clear(); | |
316 | 407 | lookupCacheHits = 0; | |
317 | 407 | lookupCacheMisses = 0; | |
318 | 407 | } | |
319 | |||
320 | /** | ||
321 | * Dump usage statistics for the lookup cache | ||
322 | */ | ||
323 | 191 | std::string StructManager::dumpLookupCacheStatistics() { | |
324 |
1/2✓ Branch 0 (2→3) taken 191 times.
✗ Branch 1 (2→22) not taken.
|
191 | std::stringstream stats; |
325 |
2/4✓ Branch 0 (3→4) taken 191 times.
✗ Branch 1 (3→20) not taken.
✓ Branch 2 (4→5) taken 191 times.
✗ Branch 3 (4→20) not taken.
|
191 | stats << "StructManager lookup cache statistics:" << std::endl; |
326 |
3/6✓ Branch 0 (5→6) taken 191 times.
✗ Branch 1 (5→20) not taken.
✓ Branch 2 (7→8) taken 191 times.
✗ Branch 3 (7→20) not taken.
✓ Branch 4 (8→9) taken 191 times.
✗ Branch 5 (8→20) not taken.
|
191 | stats << " lookup cache entries: " << lookupCache.size() << std::endl; |
327 |
3/6✓ Branch 0 (9→10) taken 191 times.
✗ Branch 1 (9→20) not taken.
✓ Branch 2 (10→11) taken 191 times.
✗ Branch 3 (10→20) not taken.
✓ Branch 4 (11→12) taken 191 times.
✗ Branch 5 (11→20) not taken.
|
191 | stats << " lookup cache hits: " << lookupCacheHits << std::endl; |
328 |
3/6✓ Branch 0 (12→13) taken 191 times.
✗ Branch 1 (12→20) not taken.
✓ Branch 2 (13→14) taken 191 times.
✗ Branch 3 (13→20) not taken.
✓ Branch 4 (14→15) taken 191 times.
✗ Branch 5 (14→20) not taken.
|
191 | stats << " lookup cache misses: " << lookupCacheMisses << std::endl; |
329 |
1/2✓ Branch 0 (15→16) taken 191 times.
✗ Branch 1 (15→20) not taken.
|
382 | return stats.str(); |
330 | 191 | } | |
331 | |||
332 | } // namespace spice::compiler | ||
333 |