GCC Code Coverage Report


Directory: ../
File: src/symboltablebuilder/SymbolTable.cpp
Date: 2024-11-22 23:10:59
Exec Total Coverage
Lines: 107 109 98.2%
Functions: 14 14 100.0%
Branches: 124 190 65.3%

Line Branch Exec Source
1 // Copyright (c) 2021-2024 ChilliBits. All rights reserved.
2
3 #include "SymbolTable.h"
4
5 #include "SourceFile.h"
6 #include <ast/ASTNodes.h>
7 #include <symboltablebuilder/SymbolTableBuilder.h>
8 #include <util/CodeLoc.h>
9 #include <util/CompilerWarning.h>
10
11 namespace spice::compiler {
12
13 /**
14 * Insert a new symbol into the current symbol table. If it is a parameter, append its name to the paramNames vector
15 *
16 * @param name Name of the symbol
17 * @param declNode AST node where the symbol is declared
18 * @param isAnonymousSymbol If this symbol should be anonymous
19 * @return Inserted entry
20 */
21 37174 SymbolTableEntry *SymbolTable::insert(const std::string &name, ASTNode *declNode, bool isAnonymousSymbol) {
22 37174 const bool isGlobal = parent == nullptr;
23 37174 size_t orderIndex = SIZE_MAX;
24
2/2
✓ Branch 0 taken 34890 times.
✓ Branch 1 taken 2284 times.
37174 if (!isAnonymousSymbol)
25 214000 orderIndex = std::ranges::count_if(symbols, [](const auto &entry) { return !entry.second.anonymous; });
26 // Insert into symbols map. The type is 'dyn', because concrete types are determined by the type checker later on
27
5/10
✓ Branch 1 taken 37174 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 37174 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 37174 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 37174 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 37174 times.
✗ Branch 14 not taken.
37174 symbols.insert({name, SymbolTableEntry(name, QualType(TY_INVALID), scope, declNode, orderIndex, isGlobal)});
28 // Set entry to declared
29 37174 SymbolTableEntry *entry = &symbols.at(name);
30
1/2
✓ Branch 1 taken 37174 times.
✗ Branch 2 not taken.
37174 entry->updateState(DECLARED, declNode);
31
32 // Check if shadowed
33
8/8
✓ Branch 0 taken 31247 times.
✓ Branch 1 taken 5927 times.
✓ Branch 3 taken 670 times.
✓ Branch 4 taken 30577 times.
✓ Branch 6 taken 11 times.
✓ Branch 7 taken 659 times.
✓ Branch 8 taken 11 times.
✓ Branch 9 taken 37163 times.
37174 if (parent != nullptr && parent->lookup(name) != nullptr && !declNode->isParamNode()) {
34
3/6
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 11 times.
✗ Branch 8 not taken.
11 CompilerWarning warning(declNode->codeLoc, SHADOWED_VARIABLE, "Variable '" + name + "' shadows a variable in a parent scope");
35
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
11 scope->sourceFile->compilerOutput.warnings.push_back(warning);
36 11 }
37
38 37174 return entry;
39 }
40
41 /**
42 * Insert a new anonymous symbol into the current symbol table.
43 * The anonymous symbol will be identified via the definition code location
44 *
45 * @param qualType Type of the symbol
46 * @param declNode AST node where the anonymous symbol is declared
47 * @param numericSuffix Custom numeric suffix
48 * @return Inserted entry
49 */
50 2295 SymbolTableEntry *SymbolTable::insertAnonymous(const QualType &qualType, ASTNode *declNode, size_t numericSuffix) {
51 // Check if the anonymous entry already exists
52
3/4
✓ Branch 1 taken 2295 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 2284 times.
2295 if (SymbolTableEntry *anonSymbol = lookupAnonymous(declNode->codeLoc, numericSuffix))
53 11 return anonSymbol;
54 // Otherwise, create an anonymous entry
55
1/2
✓ Branch 1 taken 2284 times.
✗ Branch 2 not taken.
2284 std::stringstream name;
56
3/6
✓ Branch 1 taken 2284 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2284 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2284 times.
✗ Branch 8 not taken.
2284 name << "anon." << declNode->codeLoc.toString();
57
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 2265 times.
2284 if (numericSuffix > 0)
58
3/6
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 19 times.
✗ Branch 8 not taken.
19 name << "." << std::to_string(numericSuffix);
59
2/4
✓ Branch 1 taken 2284 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2284 times.
✗ Branch 5 not taken.
2284 SymbolTableEntry *anonSymbol = insert(name.str(), declNode, true);
60
1/2
✓ Branch 1 taken 2284 times.
✗ Branch 2 not taken.
2284 anonSymbol->updateType(qualType, false);
61
1/2
✓ Branch 1 taken 2284 times.
✗ Branch 2 not taken.
2284 anonSymbol->updateState(DECLARED, declNode);
62
1/2
✓ Branch 1 taken 2284 times.
✗ Branch 2 not taken.
2284 anonSymbol->updateState(INITIALIZED, declNode);
63 2284 anonSymbol->anonymous = true;
64 2284 anonSymbol->used = true;
65 2284 return anonSymbol;
66 2284 }
67
68 /**
69 * Copy a symbol by its name
70 *
71 * @param originalName Original symbol name
72 * @param newName New symbol name
73 * @return Copied entry
74 */
75 1896 SymbolTableEntry *SymbolTable::copySymbol(const std::string &originalName, const std::string &newName) {
76
1/2
✓ Branch 1 taken 1896 times.
✗ Branch 2 not taken.
1896 SymbolTableEntry *entryToCopy = lookupStrict(originalName);
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1896 times.
1896 assert(entryToCopy != nullptr);
78
2/4
✓ Branch 1 taken 1896 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1896 times.
✗ Branch 5 not taken.
1896 auto [it, success] = symbols.insert({newName, *entryToCopy});
79
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1896 times.
1896 assert(success);
80 3792 return &it->second;
81 }
82
83 /**
84 * Check if a symbol exists in the current or any parent scope and return it if possible
85 *
86 * @param name Name of the desired symbol
87 * @return Desired symbol / nullptr if the symbol was not found
88 */
89 175540 SymbolTableEntry *SymbolTable::lookup(const std::string &name) { // NOLINT(misc-no-recursion)
90 // Check if the symbol exists in the current scope. If yes, take it
91 175540 SymbolTableEntry *entry = lookupStrict(name);
92
2/2
✓ Branch 0 taken 118962 times.
✓ Branch 1 taken 56578 times.
175540 if (!entry) { // Symbol was not found in the current scope
93 // We reached the root scope, the symbol does not exist at all
94
2/2
✓ Branch 0 taken 40002 times.
✓ Branch 1 taken 78960 times.
118962 if (parent == nullptr)
95 40002 return nullptr;
96 // If we search for the result variable, we want to stop the search when exiting a lambda body
97
6/6
✓ Branch 1 taken 5189 times.
✓ Branch 2 taken 73771 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 5188 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 78959 times.
78960 if (name == RETURN_VARIABLE_NAME && scope->type == ScopeType::LAMBDA_BODY)
98 1 return nullptr;
99 // If there is a parent scope, continue the search there
100 78959 entry = parent->lookup(name);
101 // Symbol was also not found in all the parent scopes, return nullptr
102
2/2
✓ Branch 0 taken 36389 times.
✓ Branch 1 taken 42570 times.
78959 if (!entry)
103 36389 return nullptr;
104
105 // Check if this scope requires capturing and capture the variable if appropriate
106
9/14
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 42548 times.
✓ Branch 3 taken 22 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 22 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 22 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 22 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 22 times.
✓ Branch 16 taken 42548 times.
42570 if (capturingRequired && !captures.contains(name) && !entry->getQualType().isOneOf({TY_IMPORT, TY_FUNCTION, TY_PROCEDURE})) {
107 // We need to make the symbol volatile if we are in an async scope and try to access a symbol that is not in an async scope
108
3/4
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 14 times.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
22 entry->isVolatile = scope->isInAsyncScope() && !entry->scope->isInAsyncScope();
109 // Add the capture to the current scope
110
3/6
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 22 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 22 times.
✗ Branch 8 not taken.
22 captures.insert({name, Capture(entry)});
111 }
112 }
113 99148 return entry;
114 }
115
116 /**
117 * Check if a symbol exists in the current scope and return it if possible
118 *
119 * @param name Name of the desired symbol
120 * @return Desired symbol / nullptr if the symbol was not found
121 */
122 296226 SymbolTableEntry *SymbolTable::lookupStrict(const std::string &name) {
123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 296226 times.
296226 if (name.empty())
124 return nullptr;
125 // Check if a symbol with this name exists in this scope
126
2/2
✓ Branch 1 taken 150973 times.
✓ Branch 2 taken 145253 times.
296226 if (symbols.contains(name))
127 150973 return &symbols.at(name);
128 // Check if a capture with this name exists in this scope
129
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 145236 times.
145253 if (captures.contains(name))
130 17 return captures.at(name).capturedSymbol;
131 // Otherwise, return a nullptr
132 145236 return nullptr;
133 }
134
135 /**
136 * Check if a symbol exists in one of the composed field scopes of the current scope and return it if possible.
137 * This only works if the current scope is a struct scope.
138 *
139 * @param name Name of the desired symbol
140 * @param indexPath How to index the found symbol using order indices (e.g. for GEP)
141 * @return Desired symbol / nullptr if the symbol was not found
142 */
143 23260 SymbolTableEntry *SymbolTable::lookupInComposedFields(const std::string &name, // NOLINT(misc-no-recursion)
144 std::vector<size_t> &indexPath) {
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23260 times.
23260 assert(scope->type == ScopeType::STRUCT);
146
147 // Check if we have a symbol with this name in the current scope
148
2/2
✓ Branch 1 taken 23231 times.
✓ Branch 2 taken 29 times.
23260 if (SymbolTableEntry *result = lookupStrict(name)) {
149 23231 indexPath.push_back(result->orderIndex);
150 23231 return result;
151 }
152
153 // If it was not found in the current scope, loop through all composed fields in this scope
154
2/2
✓ Branch 1 taken 41 times.
✓ Branch 2 taken 13 times.
54 for (size_t i = 0; i < scope->getFieldCount(); i++) {
155 41 const SymbolTableEntry *fieldEntry = lookupStrictByIndex(i);
156
157 // Skip all fields that are not composition fields
158
2/2
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 27 times.
41 if (!fieldEntry->getQualType().isComposition())
159 14 continue;
160
161 // Add the current field's order index to the index path
162 27 indexPath.push_back(fieldEntry->orderIndex);
163
164 // Search in the composed field's body scope
165 27 Scope *searchScope = fieldEntry->getQualType().getBodyScope();
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 assert(searchScope != nullptr);
167
2/2
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 11 times.
27 if (SymbolTableEntry *result = searchScope->symbolTable.lookupInComposedFields(name, indexPath))
168 16 return result;
169
170 // Remove the current field's order index from the index path
171 11 indexPath.pop_back();
172 }
173
174 // Symbol was not found in current scope, return nullptr
175 13 return nullptr;
176 }
177
178 /**
179 * Check if an order index exists in the current or any parent scope and returns it if possible.
180 * Warning: Unlike the `lookup` method, this one doesn't consider the parent scopes
181 *
182 * @param orderIndex Order index of the desired symbol
183 * @return Desired symbol / nullptr if the symbol was not found
184 */
185 28525 SymbolTableEntry *SymbolTable::lookupStrictByIndex(unsigned int orderIndex) {
186
4/8
✓ Branch 1 taken 28525 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 28525 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 28525 times.
✗ Branch 8 not taken.
✓ Branch 12 taken 307903 times.
✗ Branch 13 not taken.
307903 for (auto &val : symbols | std::views::values) {
187
2/2
✓ Branch 0 taken 28525 times.
✓ Branch 1 taken 279378 times.
307903 if (val.orderIndex == orderIndex)
188 28525 return &val;
189 }
190 return nullptr;
191 }
192
193 /**
194 * Check if an anonymous symbol exists in the current scope and return it if possible
195 *
196 * @param codeLoc Definition code loc
197 * @param numericSuffix Numeric suffix of the anonymous symbol
198 * @return Anonymous symbol
199 */
200 4286 SymbolTableEntry *SymbolTable::lookupAnonymous(const CodeLoc &codeLoc, size_t numericSuffix) {
201
2/4
✓ Branch 1 taken 4286 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4286 times.
✗ Branch 5 not taken.
4286 std::string name = "anon." + codeLoc.toString();
202
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 4248 times.
4286 if (numericSuffix > 0)
203
3/6
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 38 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 38 times.
✗ Branch 8 not taken.
38 name += "." + std::to_string(numericSuffix);
204
1/2
✓ Branch 1 taken 4286 times.
✗ Branch 2 not taken.
8572 return lookup(name);
205 4286 }
206
207 /**
208 * Check if a capture exists in the current or any parent scope scope and return it if possible
209 *
210 * @param name Name of the desired captured symbol
211 * @return Capture / nullptr if the capture was not found
212 */
213 157557 Capture *SymbolTable::lookupCapture(const std::string &name) { // NOLINT(misc-no-recursion)
214 // Check if the capture exists in the current scope. If yes, take it
215
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 157523 times.
157557 if (Capture *capture = lookupCaptureStrict(name))
216 34 return capture;
217
218 // We reached the root scope, the symbol does not exist at all
219
2/2
✓ Branch 0 taken 42394 times.
✓ Branch 1 taken 115129 times.
157523 if (parent == nullptr)
220 42394 return nullptr;
221
222 115129 return parent->lookupCapture(name);
223 }
224
225 /**
226 * Check if a capture exists in the current scope and return it if possible
227 *
228 * @param name Name of the desired captured symbol
229 * @return Capture / nullptr if the capture was not found
230 */
231 157557 Capture *SymbolTable::lookupCaptureStrict(const std::string &name) {
232 // If available in the current scope, return it
233
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 157523 times.
157557 if (captures.contains(name))
234 34 return &captures.at(name);
235 // Otherwise, return nullptr
236 157523 return nullptr;
237 }
238
239 /**
240 * Set capturing for this scope to required.
241 */
242 37 void SymbolTable::setCapturingRequired() { capturingRequired = true; }
243
244 /**
245 * Deletes an existing anonymous symbol
246 *
247 * @param name Anonymous symbol name
248 */
249 1551 void SymbolTable::deleteAnonymous(const std::string &name) { symbols.erase(name); }
250
251 /**
252 * Stringify a symbol table to a human-readable form. This is used to realize dumps of symbol tables
253 *
254 * Example:
255 * {
256 * "name": "<SymbolTableName>"
257 * "symbols": [
258 * ... (SymbolTableEntry)
259 * ],
260 * "children": [
261 * ... (SymbolTable)
262 * ]
263 * }
264 *
265 * @return Symbol table if form of a string
266 */
267 147115 nlohmann::json SymbolTable::toJSON() const {
268 // Collect all symbols
269 147115 std::vector<nlohmann::json> jsonSymbols;
270
1/2
✓ Branch 2 taken 147115 times.
✗ Branch 3 not taken.
147115 jsonSymbols.reserve(symbols.size());
271
5/8
✓ Branch 1 taken 147115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147115 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 147115 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 323095 times.
✓ Branch 12 taken 147115 times.
470210 for (const auto &symbol : symbols | std::views::values)
272
2/4
✓ Branch 1 taken 323095 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 323095 times.
✗ Branch 5 not taken.
323095 jsonSymbols.emplace_back(symbol.toJSON());
273
274 // Collect all captures
275 147115 std::vector<nlohmann::json> jsonCaptures;
276
1/2
✓ Branch 2 taken 147115 times.
✗ Branch 3 not taken.
147115 jsonCaptures.reserve(captures.size());
277
5/8
✓ Branch 1 taken 147115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147115 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 147115 times.
✗ Branch 8 not taken.
✓ Branch 11 taken 24 times.
✓ Branch 12 taken 147115 times.
147139 for (const auto &capture : captures | std::views::values)
278
2/4
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
24 jsonCaptures.emplace_back(capture.toJSON());
279
280 // Generate json
281 147115 nlohmann::json result;
282
2/4
✓ Branch 1 taken 147115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147115 times.
✗ Branch 5 not taken.
147115 result["symbols"] = jsonSymbols;
283
2/4
✓ Branch 1 taken 147115 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 147115 times.
✗ Branch 5 not taken.
147115 result["captures"] = jsonCaptures;
284 294230 return result;
285 147115 }
286
287 } // namespace spice::compiler
288