GCC Code Coverage Report


src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
548 / 560
97.9%
Branches:
178 / 192, 2 excl
92.7%
Functions:
47 / 47, 1 excl
100.0%

symboltablebuilder/symbol-table-builder.spice
Line Branch Exec Source
1 // Copyright (c) 2021-2026 ChilliBits. All rights reserved.
2
3 // Std imports
4 import "std/type/any";
5 import "std/data/vector";
6 import "std/io/filepath";
7 import "std/bindings/llvm/llvm" as llvm;
8
9 // Own imports
10 import "@/compiler-pass";
11 import "@/driver";
12 import "@/ast/ast-nodes";
13 import "@/ast/attributes";
14 import "@/ast/abstract-ast-visitor-intf";
15 import "@/exception/error-manager";
16 import "@/exception/semantic-error";
17 import "@/global/global-resource-manager";
18 import "@/model/struct";
19 import "@/model/interface";
20 import "@/model/union";
21 import "@/reader/code-loc";
22 import "@/source-file";
23 import "@/symboltablebuilder/qual-type";
24 import "@/symboltablebuilder/scope";
25 import "@/symboltablebuilder/super-type";
26 import "@/symboltablebuilder/symbol-table-entry";
27 import "@/symboltablebuilder/type-qualifiers";
28
29 // Reserved names (THIS_VARIABLE_NAME, RETURN_VARIABLE_NAME, CTOR_FUNCTION_NAME and DTOR_FUNCTION_NAME live in symbol-table-entry.spice)
30 const string MAIN_FUNCTION_NAME = "main";
31 public const string UNUSED_VARIABLE_NAME = "_";
32 const string ENUM_SCOPE_PREFIX = "enum:";
33
34 /**
35 * Jobs:
36 * - Build symbol table
37 *
38 * Error handling: Spice has no exceptions, so this pass records a soft error at the first problem and returns from the visit method of the offending node instead (SourceFile.checkForSoftErrors() aborts the
39 * compilation afterwards).
40 */
41 public type SymbolTableBuilder struct : IAbstractAstVisitor {
42 compose public CompilerPass compilerPass
43 3943 bool hasMainFunction = false
44 }
45
46 3943 public p SymbolTableBuilder.ctor(GlobalResourceManager& resourceManager, SourceFile* sourceFile) {
47 3943 this.compilerPass = CompilerPass(resourceManager, sourceFile);
48 3943 }
49
50 // ─── helpers ──────────────────────────────────────────────────────────────────
51
52 // All errors of the symbol table builder are hard errors
53 22 p SymbolTableBuilder.reportError(const ASTNode* node, SemanticErrorType errorType, const String& message, bool printErrorMessage = true) {
54 22 ErrorManager& errorManager = this.resourceManager.getErrorManager();
55 22 errorManager.addHardError(node, errorType, message, printErrorMessage);
56 22 }
57
58 // Entry name of a function/procedure in its symbol table
59 46509 f<String> getFctSymbolTableEntryName(const String& name, const CodeLoc& codeLoc) {
60 93018 return name + ":" + codeLoc.toString();
61 46509 }
62
63 /**
64 * Build the qualifiers of a definition. 'public' is always allowed, 'inline' and 'const' only if requested.
65 *
66 * @return True if all qualifiers were allowed. Otherwise, an error was reported
67 */
68 50716 f<bool> SymbolTableBuilder.buildQualifiers(ASTQualifierLstNode* qualifierLst, TypeQualifiers& qualifiers, bool allowInlineAndConst, const String& illegalMessage) {
69
2/2
✓ Branch 0 taken 44499 times.
✓ Branch 1 taken 6217 times.
50716 if qualifierLst == nil<ASTQualifierLstNode*> {
70 6217 return true;
71 }
72
2/2
✓ Branch 0 taken 57471 times.
✓ Branch 1 taken 44498 times.
101969 foreach ASTQualifierNode* qualifier : qualifierLst.qualifiers {
73
2/2
✓ Branch 0 taken 44328 times.
✓ Branch 1 taken 13143 times.
57471 if qualifier.qualifierType == QualifierType::PUBLIC {
74 44328 qualifiers.isPublic = true;
75
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 13142 times.
✓ Branch 2 taken 13026 times.
✓ Branch 3 taken 117 times.
57471 } else if allowInlineAndConst && qualifier.qualifierType == QualifierType::INLINE {
76 13026 qualifiers.isInline = true;
77
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 116 times.
✓ Branch 2 taken 116 times.
✓ Branch 3 taken 1 time.
13143 } else if allowInlineAndConst && qualifier.qualifierType == QualifierType::CONST {
78 116 qualifiers.isConst = true;
79 116 } else {
80 1 this.reportError(cast<ASTNode*>(qualifier), SemanticErrorType::QUALIFIER_AT_ILLEGAL_CONTEXT, illegalMessage);
81 1 return false;
82 13142 }
83 57470 }
84 44498 return true;
85 }
86
87 /**
88 * Visit an expression in a scope of its own. That scope holds the temporaries of the expression, which allows destructing
89 * them right after the expression was evaluated. This is required for conditions and for operands that are only evaluated
90 * conditionally, since a temporary of those must not be destructed if it never was constructed.
91 *
92 * @param expr Expression to visit
93 */
94 48512 p SymbolTableBuilder.visitInExprScope(IVisitable* expr) {
95 // Create scope for the expression
96 48512 const CodeLoc& exprCodeLoc = expr.getCodeLoc();
97 48512 Scope* exprScope = this.currentScope.createChildScope(getExprScopeId(expr), ScopeType::EXPR_BODY, &exprCodeLoc);
98 48512 this.currentScope = exprScope;
99
100 // Visit the expression
101 48512 this.visit(expr);
102
103 // Leave expression scope
104 48512 this.currentScope = exprScope.parent;
105 48512 }
106
107 // Shared implementation of visitFctDef and visitProcDef
108 45762 f<Any> SymbolTableBuilder.visitFctOrProcDef(ASTFctDefBaseNode* node, bool isFunction, const String& scopeId) {
109 45762 ASTNode* baseNode = cast<ASTNode*>(node);
110
111 // Visit attributes
112
2/2
✓ Branch 0 taken 44646 times.
✓ Branch 1 taken 1116 times.
45762 if node.attrs != nil<IVisitable*> {
113 1116 this.visit(node.attrs);
114 1116 }
115
116 // Build function qualifiers
117
2/2
✓ Branch 0 taken 28113 times.
✓ Branch 1 taken 17649 times.
45762 const String illegalMessage = isFunction ? String("Cannot use this qualifier on a function definition") : String("Cannot use this qualifier on a procedure definition");
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45762 times.
45762 if !this.buildQualifiers(node.qualifierLst, node.qualifiers, true, illegalMessage) {
119 ✗ return Any();
120 }
121
122 // Change to struct scope if this function is a method. Methods of interfaces are default methods
123 45762 Scope* structScope = nil<Scope*>;
124
2/2
✓ Branch 0 taken 19142 times.
✓ Branch 1 taken 26620 times.
45762 if node.isMethod {
125 26620 const QualTypeList noTemplateTypes;
126 26620 const String structScopeName = getStructScopeName(node.name.structName, noTemplateTypes);
127 26620 structScope = this.currentScope.getChildScope(structScopeName);
128
2/2
✓ Branch 0 taken 26606 times.
✓ Branch 1 taken 14 times.
26620 if structScope == nil<Scope*> {
129 14 const String interfaceScopeName = getInterfaceScopeName(node.name.structName, noTemplateTypes);
130 14 structScope = this.currentScope.getChildScope(interfaceScopeName);
131 14 }
132
2/2
✓ Branch 0 taken 26619 times.
✓ Branch 1 taken 1 time.
26620 if structScope == nil<Scope*> {
133 1 this.reportError(baseNode, SemanticErrorType::REFERENCED_UNDEFINED_STRUCT, "Struct '" + node.name.structName + "' could not be found");
134 2 return Any();
135 1 }
136 26619 node.structScope = structScope;
137 26619 this.currentScope = structScope;
138 26620 }
139
140 // Create scope for the function
141 45761 Scope* fctScope = this.currentScope.createChildScope(scopeId, ScopeType::FUNC_PROC_BODY, &node.codeLoc);
142 45761 node.scope = fctScope;
143 45761 this.currentScope = fctScope;
144
4/4
✓ Branch 0 taken 6897 times.
✓ Branch 1 taken 38864 times.
✓ Branch 2 taken 14493 times.
✓ Branch 3 taken 24371 times.
45761 fctScope.isGenericScope = node.hasTemplateTypes || (structScope != nil<Scope*> && structScope.isGenericScope);
145
2/2
✓ Branch 0 taken 28113 times.
✓ Branch 1 taken 17648 times.
45761 if !isFunction {
146
2/2
✓ Branch 0 taken 3656 times.
✓ Branch 1 taken 13992 times.
17648 fctScope.isCtorScope = node.isMethod && node.name.name == CTOR_FUNCTION_NAME;
147
2/2
✓ Branch 0 taken 3656 times.
✓ Branch 1 taken 13992 times.
17648 fctScope.isDtorScope = node.isMethod && node.name.name == DTOR_FUNCTION_NAME;
148 17648 }
149
150 // Create symbol for 'this' variable
151
2/2
✓ Branch 0 taken 19142 times.
✓ Branch 1 taken 26619 times.
45761 if node.isMethod {
152 26619 fctScope.insert(String(THIS_VARIABLE_NAME), baseNode);
153 26619 }
154
155 // Create symbol for 'result' variable
156
2/2
✓ Branch 0 taken 17648 times.
✓ Branch 1 taken 28113 times.
45761 if isFunction {
157 28113 fctScope.insert(String(RETURN_VARIABLE_NAME), baseNode);
158 28113 }
159
160 // Create symbols for the parameters
161
2/2
✓ Branch 0 taken 12691 times.
✓ Branch 1 taken 33070 times.
45761 if node.hasParams {
162 33070 this.visit(node.paramLst);
163 33070 }
164
165 // Visit the function body
166 45761 this.visit(node.body);
167
168 // Leave function body scope
169 45761 this.currentScope = fctScope.parent;
170
171 // Insert symbol for function into the symbol table
172 45761 node.entry = this.currentScope.insert(getFctSymbolTableEntryName(node.name.name, node.codeLoc), baseNode);
173
174 // Add to external name registry
175 // if a function has overloads, they both refer to the same entry in the registry. So we only register the name once
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45761 times.
45761 if !this.sourceFile.hasNameRegistryTarget(node.name.fqName, node.entry) {
177
2/2
✓ Branch 0 taken 28113 times.
✓ Branch 1 taken 17648 times.
45761 const unsigned long typeId = isFunction ? cast<unsigned long>(SuperType::TY_FUNCTION) : cast<unsigned long>(SuperType::TY_PROCEDURE);
178 45761 this.sourceFile.addNameRegistryEntry(node.name.fqName, typeId, node.entry, this.currentScope, true, nil<SymbolTableEntry*>);
179 45761 }
180
181 // Leave the struct scope
182
2/2
✓ Branch 0 taken 19142 times.
✓ Branch 1 taken 26619 times.
45761 if node.isMethod {
183 26619 this.currentScope = structScope.parent;
184 26619 }
185
186 91522 return Any();
187 45762 }
188
189 // ─── real logic ───────────────────────────────────────────────────────────────
190
191 3942 public f<Any> SymbolTableBuilder.visitEntry(ASTEntryNode* node) {
192
193 // Initialize
194 3942 this.currentScope = this.rootScope;
195
196 // Visit children: module attributes, imports (already handled by the import collector) and top level definitions
197
2/2
✓ Branch 0 taken 1538 times.
✓ Branch 1 taken 3942 times.
5480 foreach IVisitable* modAttr : node.modAttrs {
198 1538 this.visit(modAttr);
199 1538 }
200
2/2
✓ Branch 0 taken 2508 times.
✓ Branch 1 taken 3942 times.
6450 foreach IVisitable* importDef : node.importDefs {
201 2508 this.visit(importDef);
202 2508 }
203
2/2
✓ Branch 0 taken 3942 times.
✓ Branch 1 taken 63576 times.
67518 foreach IVisitable* topLevelDef : node.topLevelDefs {
204 63576 this.visit(topLevelDef);
205 63576 }
206
207 // Check if the main function exists
208
2/2
✓ Branch 0 taken 729 times.
✓ Branch 1 taken 3213 times.
3942 const bool mainFctRequired = this.cliOptions.outputContainer == OutputContainer::EXECUTABLE && !this.cliOptions.noEntryFct;
209
6/6
✓ Branch 0 taken 1030 times.
✓ Branch 1 taken 2912 times.
✓ Branch 2 taken 305 times.
✓ Branch 3 taken 725 times.
✓ Branch 4 taken 3939 times.
✓ Branch 5 taken 3 times.
3942 if this.sourceFile.isMainSourceFile() && mainFctRequired && !this.hasMainFunction {
210 3 this.reportError(cast<ASTNode*>(node), SemanticErrorType::MISSING_MAIN_FUNCTION, String("No main function found"), false);
211 3 }
212
213 3942 return Any();
214 }
215
216 1019 public f<Any> SymbolTableBuilder.visitMainFctDef(ASTMainFctDefNode* node) {
217 1019 ASTNode* baseNode = cast<ASTNode*>(node);
218
219 // Visit attributes
220
2/2
✓ Branch 0 taken 1018 times.
✓ Branch 1 taken 1 time.
1019 if node.attrs != nil<IVisitable*> {
221 1 this.visit(node.attrs);
222 1 }
223
224 // Check if the function is already defined
225
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1018 times.
1019 if this.rootScope.lookupStrict(String(MAIN_FUNCTION_NAME)) != nil<SymbolTableEntry*> {
226 1 this.reportError(baseNode, SemanticErrorType::FUNCTION_DECLARED_TWICE, String("Main function is declared twice"));
227 2 return Any();
228 1 }
229
230 // Insert symbol for main function
231 1018 SymbolTableEntry* mainFctEntry = this.currentScope.insert(String(MAIN_FUNCTION_NAME), baseNode);
232 1018 mainFctEntry.used = true;
233
234 // Create scope for main function body
235 1018 Scope* bodyScope = this.rootScope.createChildScope(node.getScopeId(), ScopeType::FUNC_PROC_BODY, &node.codeLoc);
236 1018 node.bodyScope = bodyScope;
237 1018 this.currentScope = bodyScope;
238 1018 bodyScope.isGenericScope = false;
239
240 // Declare variable for the return value in the function scope
241 1018 SymbolTableEntry* resultVarEntry = bodyScope.insert(String(RETURN_VARIABLE_NAME), baseNode);
242 1018 resultVarEntry.used = true;
243
244 // Visit arguments in new scope
245
2/2
✓ Branch 0 taken 1011 times.
✓ Branch 1 taken 7 times.
1018 if node.takesArgs {
246 7 this.visit(node.paramLst);
247 7 }
248
249 // Visit function body in new scope
250 1018 this.visit(node.body);
251
252 // Return to root scope
253 1018 this.currentScope = this.rootScope;
254
255 1018 this.hasMainFunction = true;
256 2036 return Any();
257 1018 }
258
259 28113 public f<Any> SymbolTableBuilder.visitFctDef(ASTFctDefNode* node) {
260 56226 return this.visitFctOrProcDef(cast<ASTFctDefBaseNode*>(node), true, node.getScopeId());
261 28113 }
262
263 17649 public f<Any> SymbolTableBuilder.visitProcDef(ASTProcDefNode* node) {
264 17649 dyn outcome = this.visitFctOrProcDef(cast<ASTFctDefBaseNode*>(node), false, node.getScopeId());
265
266 // Check if this is a constructor (the name is empty if visiting the definition failed early)
267 17649 const Vector<String>& fragments = node.name.nameFragments;
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17649 times.
17649 if !fragments.isEmpty() {
269 17649 node.isCtor = fragments.get(fragments.getSize() - 1l) == CTOR_FUNCTION_NAME;
270 17649 }
271
272 35298 return outcome;
273 17649 }
274
275 3232 public f<Any> SymbolTableBuilder.visitStructDef(ASTStructDefNode* node) {
276 3232 ASTNode* baseNode = cast<ASTNode*>(node);
277
278 // Visit attributes
279
2/2
✓ Branch 0 taken 3030 times.
✓ Branch 1 taken 202 times.
3232 if node.attrs != nil<IVisitable*> {
280 202 this.visit(node.attrs);
281 202 }
282
283 // Check if this name already exists
284
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3231 times.
3232 if this.rootScope.lookupStrict(node.structName) != nil<SymbolTableEntry*> {
285 1 this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.structName + "'");
286 2 return Any();
287 1 }
288
289 // Create scope for the struct
290 3231 const QualTypeList noTemplateTypes;
291 3231 Scope* structScope = this.rootScope.createChildScope(getStructScopeName(node.structName, noTemplateTypes), ScopeType::STRUCT, &node.codeLoc);
292 3231 node.structScope = structScope;
293 3231 this.currentScope = structScope;
294 3231 structScope.isGenericScope = node.hasTemplateTypes;
295
296 // Insert implicit field for each interface type
297
2/2
✓ Branch 0 taken 2806 times.
✓ Branch 1 taken 425 times.
3231 if node.hasInterfaces {
298
2/2
✓ Branch 0 taken 426 times.
✓ Branch 1 taken 425 times.
851 foreach ASTDataTypeNode* interfaceNode : node.interfaceTypeLst.dataTypes {
299 426 const Vector<String>& fragments = interfaceNode.baseDataType.customDataType.typeNameFragments;
300 426 const String& interfaceName = fragments.get(fragments.getSize() - 1l);
301 426 SymbolTableEntry* interfaceFieldEntry = structScope.insert(String("this.") + interfaceName, cast<ASTNode*>(interfaceNode));
302 426 interfaceFieldEntry.used = true;
303 426 interfaceFieldEntry.isImplicitField = true;
304 426 }
305 425 }
306
307 // Visit fields
308
2/2
✓ Branch 0 taken 7801 times.
✓ Branch 1 taken 3231 times.
11032 foreach IVisitable* field : node.fields {
309 7801 this.visit(field);
310 7801 }
311
312 // Leave the struct scope
313 3231 this.currentScope = structScope.parent;
314
315 // Build struct qualifiers
316
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3231 times.
3231 if !this.buildQualifiers(node.qualifierLst, node.qualifiers, false, String("Cannot use this qualifier on a struct definition")) {
317 ✗ return Any();
318 }
319
320 // Add the struct to the symbol table
321 3231 node.entry = this.rootScope.insert(node.structName, baseNode);
322 // Register the name in the exported name registry
323 3231 this.sourceFile.addNameRegistryEntry(node.structName, node.typeId, node.entry, structScope, true, nil<SymbolTableEntry*>);
324
325 6462 return Any();
326 3231 }
327
328 327 public f<Any> SymbolTableBuilder.visitInterfaceDef(ASTInterfaceDefNode* node) {
329 327 ASTNode* baseNode = cast<ASTNode*>(node);
330
331 // Visit attributes
332
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 254 times.
327 if node.attrs != nil<IVisitable*> {
333 254 this.visit(node.attrs);
334 254 }
335
336 // Check if this name already exists
337
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 326 times.
327 if this.rootScope.lookupStrict(node.interfaceName) != nil<SymbolTableEntry*> {
338 1 this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.interfaceName + "'");
339 2 return Any();
340 1 }
341
342 // Create scope for the interface
343 326 const QualTypeList noTemplateTypes;
344 326 Scope* interfaceScope = this.rootScope.createChildScope(getInterfaceScopeName(node.interfaceName, noTemplateTypes), ScopeType::INTERFACE, &node.codeLoc);
345 326 node.interfaceScope = interfaceScope;
346 326 this.currentScope = interfaceScope;
347
348 // Visit signatures
349
2/2
✓ Branch 0 taken 748 times.
✓ Branch 1 taken 326 times.
1074 foreach IVisitable* signature : node.signatures {
350 748 this.visit(signature);
351 748 }
352
353 // Leave the interface scope
354 326 this.currentScope = interfaceScope.parent;
355
356 // Build interface qualifiers
357
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 326 times.
326 if !this.buildQualifiers(node.qualifierLst, node.qualifiers, false, String("Cannot use this qualifier on an interface definition")) {
358 ✗ return Any();
359 }
360
361 // Add the interface to the symbol table
362 326 node.entry = this.rootScope.insert(node.interfaceName, baseNode);
363 // Register the name in the exported name registry
364 326 this.sourceFile.addNameRegistryEntry(node.interfaceName, node.typeId, node.entry, interfaceScope, true, nil<SymbolTableEntry*>);
365
366 652 return Any();
367 326 }
368
369 26 public f<Any> SymbolTableBuilder.visitUnionDef(ASTUnionDefNode* node) {
370 26 ASTNode* baseNode = cast<ASTNode*>(node);
371
372 // Visit attributes
373
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 if node.attrs != nil<IVisitable*> {
374 ✗ this.visit(node.attrs);
375 ✗ }
376
377 // Check if this name already exists
378
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 if this.rootScope.lookupStrict(node.unionName) != nil<SymbolTableEntry*> {
379 ✗ this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.unionName + "'");
380 ✗ return Any();
381 ✗ }
382
383 // Create scope for the union
384 26 const QualTypeList noTemplateTypes;
385 26 Scope* unionScope = this.rootScope.createChildScope(getUnionScopeName(node.unionName, noTemplateTypes), ScopeType::UNION, &node.codeLoc);
386 26 node.unionScope = unionScope;
387 26 this.currentScope = unionScope;
388 26 unionScope.isGenericScope = node.hasTemplateTypes;
389
390 // Visit fields
391
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 26 times.
83 foreach IVisitable* field : node.fields {
392 57 this.visit(field);
393 57 }
394
395 // Leave the union scope
396 26 this.currentScope = unionScope.parent;
397
398 // Build union qualifiers
399
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 25 times.
26 if !this.buildQualifiers(node.qualifierLst, node.qualifiers, false, String("Cannot use this qualifier on a union definition")) {
400 2 return Any();
401 }
402
403 // Add the union to the symbol table
404 25 node.entry = this.rootScope.insert(node.unionName, baseNode);
405 // Register the name in the exported name registry
406 25 this.sourceFile.addNameRegistryEntry(node.unionName, node.typeId, node.entry, unionScope, true, nil<SymbolTableEntry*>);
407
408 50 return Any();
409 26 }
410
411 466 public f<Any> SymbolTableBuilder.visitEnumDef(ASTEnumDefNode* node) {
412 466 ASTNode* baseNode = cast<ASTNode*>(node);
413
414 // Check if this name already exists
415
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 465 times.
466 if this.rootScope.lookupStrict(node.enumName) != nil<SymbolTableEntry*> {
416 1 this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.enumName + "'");
417 2 return Any();
418 1 }
419
420 // Create scope for the enum
421 465 Scope* enumScope = this.rootScope.createChildScope(String(ENUM_SCOPE_PREFIX) + node.enumName, ScopeType::ENUM, &node.codeLoc);
422 465 node.enumScope = enumScope;
423 465 this.currentScope = enumScope;
424
425 // Visit items
426 465 this.visit(node.itemLst);
427
428 // Leave the enum scope
429 465 this.currentScope = enumScope.parent;
430
431 // Build enum qualifiers
432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 465 times.
465 if !this.buildQualifiers(node.qualifierLst, node.qualifiers, false, String("Cannot use this qualifier on an enum definition")) {
433 ✗ return Any();
434 }
435
436 // Add the enum to the symbol table
437 465 node.entry = this.rootScope.insert(node.enumName, baseNode);
438 // Register the name in the exported name registry
439 465 this.sourceFile.addNameRegistryEntry(node.enumName, node.typeId, node.entry, enumScope, true, nil<SymbolTableEntry*>);
440
441 930 return Any();
442 465 }
443
444 3071 public f<Any> SymbolTableBuilder.visitGenericTypeDef(ASTGenericTypeDefNode* node) {
445 3071 ASTNode* baseNode = cast<ASTNode*>(node);
446
447 // Check if this name already exists
448
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3070 times.
3071 if this.rootScope.lookupStrict(node.typeName) != nil<SymbolTableEntry*> {
449 1 this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.typeName + "'");
450 2 return Any();
451 1 }
452
453 // Create the generic type to the symbol table
454 3070 node.entry = this.rootScope.insert(node.typeName, baseNode);
455 3070 node.entry.used = true; // Generic types are always used
456
457 3070 return Any();
458 }
459
460 159 public f<Any> SymbolTableBuilder.visitAliasDef(ASTAliasDefNode* node) {
461 159 ASTNode* baseNode = cast<ASTNode*>(node);
462
463 // Check if this name already exists
464
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 158 times.
159 if this.rootScope.lookupStrict(node.aliasName) != nil<SymbolTableEntry*> {
465 1 this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.aliasName + "'");
466 2 return Any();
467 1 }
468
469 // Build alias qualifiers
470
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 158 times.
158 if !this.buildQualifiers(node.qualifierLst, node.qualifiers, false, String("Cannot use this qualifier on an alias definition")) {
471 ✗ return Any();
472 }
473
474 // Add the alias to the symbol table
475 158 node.entry = this.rootScope.insert(node.aliasName, baseNode);
476 // Register the name in the exported name registry
477 158 this.sourceFile.addNameRegistryEntry(node.aliasName, node.typeId, node.entry, this.rootScope, true, nil<SymbolTableEntry*>);
478
479 // Add another symbol for the aliased type container
480 158 node.aliasedTypeContainerEntry = this.rootScope.insert(node.aliasName + ALIAS_CONTAINER_SUFFIX, baseNode);
481
482 316 return Any();
483 158 }
484
485 4285 public f<Any> SymbolTableBuilder.visitGlobalVarDef(ASTGlobalVarDefNode* node) {
486 4285 ASTNode* baseNode = cast<ASTNode*>(node);
487
488 // Check if this name already exists
489
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4284 times.
4285 if this.rootScope.lookupStrict(node.varName) != nil<SymbolTableEntry*> {
490 1 this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.varName + "'");
491 2 return Any();
492 1 }
493
494 // Add the global to the symbol table
495 4284 node.entry = this.rootScope.insert(node.varName, baseNode);
496 // Register the name in the exported name registry
497 4284 this.sourceFile.addNameRegistryEntry(node.varName, cast<unsigned long>(SuperType::TY_INVALID), node.entry, this.currentScope, true, nil<SymbolTableEntry*>);
498
499 4284 return Any();
500 }
501
502 5229 public f<Any> SymbolTableBuilder.visitExtDecl(ASTExtDeclNode* node) {
503 5229 ASTNode* baseNode = cast<ASTNode*>(node);
504
505 // Visit attributes
506
2/2
✓ Branch 0 taken 5228 times.
✓ Branch 1 taken 1 time.
5229 if node.attrs != nil<IVisitable*> {
507 1 this.visit(node.attrs);
508 1 }
509
510 // Check if this name already exists
511
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5228 times.
5229 if this.rootScope.lookupStrict(node.extFunctionName) != nil<SymbolTableEntry*> {
512 1 this.reportError(baseNode, SemanticErrorType::DUPLICATE_SYMBOL, "Duplicate symbol '" + node.extFunctionName + "'");
513 2 return Any();
514 1 }
515
516 // Create scope for the external function (this is required in case of forceSubstantiation in FunctionManager::matchFunction)
517 5228 this.rootScope.createChildScope(node.getScopeId(), ScopeType::FUNC_PROC_BODY, &node.codeLoc);
518
519 // Add the external declaration to the symbol table
520 5228 node.entry = this.rootScope.insert(node.extFunctionName, baseNode);
521 // Register the name in the exported name registry
522
2/2
✓ Branch 0 taken 4549 times.
✓ Branch 1 taken 679 times.
5228 const unsigned long typeId = node.returnType != nil<ASTDataTypeNode*> ? cast<unsigned long>(SuperType::TY_FUNCTION) : cast<unsigned long>(SuperType::TY_PROCEDURE);
523 5228 this.sourceFile.addNameRegistryEntry(node.extFunctionName, typeId, node.entry, this.rootScope, true, nil<SymbolTableEntry*>);
524
525 10456 return Any();
526 5228 }
527
528 12758 public f<Any> SymbolTableBuilder.visitUnsafeBlock(ASTUnsafeBlockNode* node) {
529 // Create scope for the unsafe block body
530 12758 Scope* bodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::UNSAFE_BODY, &node.codeLoc);
531 12758 node.bodyScope = bodyScope;
532 12758 this.currentScope = bodyScope;
533
534 // Visit body
535 12758 this.visit(node.body);
536
537 // Leave unsafe body scope
538 12758 this.currentScope = bodyScope.parent;
539
540 25516 return Any();
541 12758 }
542
543 4405 public f<Any> SymbolTableBuilder.visitForLoop(ASTForLoopNode* node) {
544
545 // Create scope for the loop head. The loop variable lives there, since it has to survive from one round to the next
546 4405 Scope* headScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::LOOP_HEAD, &node.codeLoc);
547 4405 node.headScope = headScope;
548 4405 this.currentScope = headScope;
549
550 // Visit loop variable declaration
551 4405 this.visit(node.initDecl);
552
553 // Visit condition
554 4405 this.visitInExprScope(node.condAssign);
555
556 // Visit incrementer
557 4405 this.visitInExprScope(node.incAssign);
558
559 // Create scope for the loop body
560 4405 const CodeLoc& bodyCodeLoc = node.body.getCodeLoc();
561 4405 Scope* bodyScope = this.currentScope.createChildScope(node.getBodyScopeId(), ScopeType::FOR_BODY, &bodyCodeLoc);
562 4405 node.bodyScope = bodyScope;
563 4405 this.currentScope = bodyScope;
564
565 // Visit body
566 4405 this.visit(node.body);
567
568 // Leave for body and head scope
569 4405 this.currentScope = headScope.parent;
570
571 8810 return Any();
572 4405 }
573
574 435 public f<Any> SymbolTableBuilder.visitForeachLoop(ASTForeachLoopNode* node) {
575
576 // Create scope for the loop head. The index variable lives there, since it has to survive from one round to the next
577 435 Scope* headScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::LOOP_HEAD, &node.codeLoc);
578 435 node.headScope = headScope;
579 435 this.currentScope = headScope;
580
581 // Visit index variable declaration
582
2/2
✓ Branch 0 taken 412 times.
✓ Branch 1 taken 23 times.
435 if node.idxVarDecl != nil<IVisitable*> {
583 23 this.visit(node.idxVarDecl);
584 23 }
585
586 // Create scope for the loop body
587 435 const CodeLoc& bodyCodeLoc = node.body.getCodeLoc();
588 435 Scope* bodyScope = this.currentScope.createChildScope(node.getBodyScopeId(), ScopeType::FOREACH_BODY, &bodyCodeLoc);
589 435 node.bodyScope = bodyScope;
590 435 this.currentScope = bodyScope;
591
592 // Visit item variable declaration. The item lives in the body scope, since it is copied anew in each round
593 435 this.visit(node.itemVarDecl);
594
595 // Visit body
596 435 this.visit(node.body);
597
598 // Leave foreach body and head scope
599 435 this.currentScope = headScope.parent;
600
601 870 return Any();
602 435 }
603
604 1865 public f<Any> SymbolTableBuilder.visitWhileLoop(ASTWhileLoopNode* node) {
605
606 // Create scope for the loop body
607 1865 Scope* bodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::WHILE_BODY, &node.codeLoc);
608 1865 node.bodyScope = bodyScope;
609 1865 this.currentScope = bodyScope;
610
611 // Visit condition
612 1865 this.visitInExprScope(node.condition);
613
614 // Visit body
615 1865 this.visit(node.body);
616
617 // Leave while body scope
618 1865 this.currentScope = bodyScope.parent;
619
620 3730 return Any();
621 1865 }
622
623 50 public f<Any> SymbolTableBuilder.visitDoWhileLoop(ASTDoWhileLoopNode* node) {
624
625 // Create scope for the loop body
626 50 Scope* bodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::WHILE_BODY, &node.codeLoc);
627 50 node.bodyScope = bodyScope;
628 50 this.currentScope = bodyScope;
629
630 // Visit condition
631 50 this.visitInExprScope(node.condition);
632
633 // Visit body
634 50 this.visit(node.body);
635
636 // Leave do-while body scope
637 50 this.currentScope = bodyScope.parent;
638
639 100 return Any();
640 50 }
641
642 25175 public f<Any> SymbolTableBuilder.visitIfStmt(ASTIfStmtNode* node) {
643
644 // Create scope for the then body
645 25175 Scope* thenBodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::IF_ELSE_BODY, &node.codeLoc);
646 25175 node.thenBodyScope = thenBodyScope;
647 25175 this.currentScope = thenBodyScope;
648
649 // Visit condition
650 25175 this.visitInExprScope(node.condition);
651
652 // Visit then body (manifestations do not exist yet, so both branches are always visited here)
653 25175 this.visit(node.thenBody);
654
655 // Leave then body scope
656 25175 this.currentScope = thenBodyScope.parent;
657
658 // Visit else stmt
659
2/2
✓ Branch 0 taken 23778 times.
✓ Branch 1 taken 1397 times.
25175 if node.elseStmt != nil<IVisitable*> {
660 1397 this.visit(node.elseStmt);
661 1397 }
662
663 50350 return Any();
664 25175 }
665
666 1397 public f<Any> SymbolTableBuilder.visitElseStmt(ASTElseStmtNode* node) {
667
668 // Visit if statement in the case of an else if branch
669
2/2
✓ Branch 0 taken 563 times.
✓ Branch 1 taken 834 times.
1397 if node.isElseIf {
670 563 this.visit(node.ifStmt);
671 1126 return Any();
672 563 }
673
674 // Create scope for the else body
675 834 Scope* elseBodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::IF_ELSE_BODY, &node.codeLoc);
676 834 node.elseBodyScope = elseBodyScope;
677 834 this.currentScope = elseBodyScope;
678
679 // Visit else body
680 834 this.visit(node.body);
681
682 // Leave else body scope
683 834 this.currentScope = elseBodyScope.parent;
684
685 1668 return Any();
686 834 }
687
688 195 public f<Any> SymbolTableBuilder.visitCaseBranch(ASTCaseBranchNode* node) {
689 // Create scope for the case branch
690 195 Scope* bodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::CASE_BODY, &node.codeLoc);
691 195 node.bodyScope = bodyScope;
692 195 this.currentScope = bodyScope;
693
694 // Visit case body
695 195 this.visit(node.body);
696
697 // Leave case body scope
698 195 this.currentScope = bodyScope.parent;
699
700 390 return Any();
701 195 }
702
703 20 public f<Any> SymbolTableBuilder.visitDefaultBranch(ASTDefaultBranchNode* node) {
704 // Create scope for the default branch
705 20 Scope* bodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::DEFAULT_BODY, &node.codeLoc);
706 20 node.bodyScope = bodyScope;
707 20 this.currentScope = bodyScope;
708
709 // Visit default body
710 20 this.visit(node.body);
711
712 // Leave default body scope
713 20 this.currentScope = bodyScope.parent;
714
715 40 return Any();
716 20 }
717
718 126 public f<Any> SymbolTableBuilder.visitAnonymousBlockStmt(ASTAnonymousBlockStmtNode* node) {
719 // Create scope for the anonymous block body
720 126 Scope* bodyScope = this.currentScope.createChildScope(node.getScopeId(), ScopeType::ANONYMOUS_BLOCK_BODY, &node.codeLoc);
721 126 node.bodyScope = bodyScope;
722 126 this.currentScope = bodyScope;
723
724 // Visit body
725 126 this.visit(node.body);
726
727 // Leave anonymous block body scope
728 126 this.currentScope = bodyScope.parent;
729
730 252 return Any();
731 126 }
732
733 1746 #[ignoreUnusedReturnValue]
734 1746 public f<Any> SymbolTableBuilder.visitEnumItem(ASTEnumItemNode* node) {
735 1746 ASTNode* baseNode = cast<ASTNode*>(node);
736
737 // Check if enum item already exists in the same scope.
738
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1745 times.
1746 if this.currentScope.lookupStrict(node.itemName) != nil<SymbolTableEntry*> {
739 1 this.reportError(baseNode, SemanticErrorType::VARIABLE_DECLARED_TWICE, "The enum item '" + node.itemName + "' was declared more than once");
740 2 return Any();
741 1 }
742
743 // Add enum item entry to symbol table
744 1745 SymbolTableEntry* enumItemEntry = this.currentScope.insert(node.itemName, baseNode);
745
746 // Add external registry entry
747 1745 assert node.enumDef != nil<ASTEnumDefNode*>;
748 1745 const String name = node.enumDef.enumName + SCOPE_ACCESS_TOKEN + node.itemName;
749 1745 this.sourceFile.addNameRegistryEntry(name, cast<unsigned long>(SuperType::TY_INT), enumItemEntry, this.currentScope, true, nil<SymbolTableEntry*>);
750
751 3490 return Any();
752 1745 }
753
754 7858 public f<Any> SymbolTableBuilder.visitField(ASTFieldNode* node) {
755 7858 ASTNode* baseNode = cast<ASTNode*>(node);
756
757 // Check if field already exists in the same scope.
758
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 7856 times.
7858 if this.currentScope.lookupStrict(node.fieldName) != nil<SymbolTableEntry*> {
759 2 this.reportError(baseNode, SemanticErrorType::VARIABLE_DECLARED_TWICE, "The field '" + node.fieldName + "' was declared more than once");
760 4 return Any();
761 2 }
762
763 // Add field entry to symbol table
764 7856 this.currentScope.insert(node.fieldName, baseNode);
765
766 7856 return Any();
767 }
768
769 748 public f<Any> SymbolTableBuilder.visitSignature(ASTSignatureNode* node) {
770 // Build signature qualifiers
771
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 748 times.
748 if !this.buildQualifiers(node.qualifierLst, node.signatureQualifiers, true, String("Cannot use this qualifier on a signature definition")) {
772 ✗ return Any();
773 }
774
775 // Add signature entry to symbol table. We append the code location to disambiguate overloaded signatures
776 // (e.g. an interface declaring `getName()` and `getName(bool)`).
777 748 node.entry = this.currentScope.insert(getFctSymbolTableEntryName(node.methodName, node.codeLoc), cast<ASTNode*>(node));
778
779 1496 return Any();
780 748 }
781
782 83807 #[ignoreUnusedReturnValue]
783 83807 public f<Any> SymbolTableBuilder.visitDeclStmt(ASTDeclStmtNode* node) {
784 83807 ASTNode* baseNode = cast<ASTNode*>(node);
785
786 // Check if variable already exists in the same scope. The head of a loop counts as part of the scope of its body here
787 83807 Scope* parentScope = this.currentScope.parent;
788
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 83807 times.
83807 const bool isLoopBody = parentScope != nil<Scope*> && parentScope.scopeType == ScopeType::LOOP_HEAD;
789
2/2
✓ Branch 0 taken 82104 times.
✓ Branch 1 taken 1703 times.
83807 const bool declaredInLoopHead = isLoopBody && parentScope.lookupStrict(node.varName) != nil<SymbolTableEntry*>;
790
4/4
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 83806 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 83804 times.
83807 if this.currentScope.lookupStrict(node.varName) != nil<SymbolTableEntry*> || declaredInLoopHead {
791 3 this.reportError(baseNode, SemanticErrorType::VARIABLE_DECLARED_TWICE, "The variable '" + node.varName + "' was declared more than once");
792 6 return Any();
793 3 }
794
795 // Visit the right side
796
2/2
✓ Branch 0 taken 53271 times.
✓ Branch 1 taken 30533 times.
83804 if node.hasAssignment {
797 30533 this.visit(node.assignExpr);
798 30533 }
799
800 // Add variable entry to symbol table
801 83804 SymbolTableEntry* varEntry = this.currentScope.insert(node.varName, baseNode);
802 83804 varEntry.isParam = node.isFctParam;
803
804 83804 return Any();
805 }
806
807 1539 public f<Any> SymbolTableBuilder.visitModAttr(ASTModAttrNode* node) {
808 // Visit attributes
809 1539 this.visitChildren(cast<ASTNode*>(node));
810 // Stop at an invalid attribute, the compilation is aborted after this stage
811 1539 const ErrorManager& errorManager = this.resourceManager.getErrorManager();
812
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1538 times.
1539 if errorManager.hasHardError {
813 2 return Any();
814 }
815
816 // Retrieve attributes
817 1538 ASTAttrLstNode* attrs = node.attrLst;
818 1538 GlobalResourceManager& resourceManager = this.resourceManager;
819
820 // Determine the target OS from the triple of the compilation
821 1538 const llvm::Triple& targetTriple = this.cliOptions.targetTriple;
822 1538 const bool isLinux = targetTriple.isOSLinux();
823 1538 const bool isDarwin = targetTriple.isOSDarwin();
824 1538 const bool isWindows = targetTriple.isOSWindows();
825
826 // Collect and register linker flags.
827 // On Linux, platform-independent flags (core.linker.flag) are wrapped in --start-group/--end-group
828 // so the linker rescans the archive group until all circular symbol references are resolved (e.g.
829 // LLVM's core static archives have mutual dependencies that alphabetical ordering cannot satisfy).
830 1538 const bool wrapInGroup = isLinux;
831
832 // core.linker.flag - platform-independent; wrapped in a group on Linux
833 1538 Vector<CompileTimeValue> coreValues = attrs.getAttrValuesByName(String(ATTR_CORE_LINKER_FLAG));
834
4/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1514 times.
✓ Branch 2 taken 1508 times.
✓ Branch 3 taken 30 times.
1538 if wrapInGroup && !coreValues.isEmpty() {
835 30 resourceManager.addLinkerFlag(String("-Wl,--start-group"));
836 30 }
837
2/2
✓ Branch 0 taken 411 times.
✓ Branch 1 taken 1538 times.
1949 foreach CompileTimeValue value : coreValues {
838 411 const String& flag = resourceManager.getCompileTimeStringValue(value.getStringValueOffset());
839 411 resourceManager.addLinkerFlag(flag);
840 411 }
841
4/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1514 times.
✓ Branch 2 taken 1508 times.
✓ Branch 3 taken 30 times.
1538 if wrapInGroup && !coreValues.isEmpty() {
842 30 resourceManager.addLinkerFlag(String("-Wl,--end-group"));
843 30 }
844
845 // Platform-specific flags - always come after the (possibly grouped) core flags
846 1538 Vector<CompileTimeValue> platformValues;
847
2/2
✓ Branch 0 taken 1514 times.
✓ Branch 1 taken 24 times.
1538 if isLinux {
848 1514 platformValues = attrs.getAttrValuesByName(String(ATTR_CORE_LINUX_LINKER_FLAG));
849
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
1538 } else if isDarwin {
850 12 platformValues = attrs.getAttrValuesByName(String(ATTR_CORE_DARWIN_LINKER_FLAG));
851
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
24 } else if isWindows {
852 12 platformValues = attrs.getAttrValuesByName(String(ATTR_CORE_WINDOWS_LINKER_FLAG));
853 24 }
854
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 1538 times.
1560 foreach CompileTimeValue value : platformValues {
855 22 const String& flag = resourceManager.getCompileTimeStringValue(value.getStringValueOffset());
856 22 resourceManager.addLinkerFlag(flag);
857 22 }
858
859 // core.linker.additionalSource
860 1538 const Vector<CompileTimeValue> additionalSourceValues = attrs.getAttrValuesByName(String(ATTR_CORE_LINKER_ADDITIONAL_SOURCE));
861 1538 const FilePath& ownFilePath = this.sourceFile.getFilePath();
862
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1538 times.
1544 foreach CompileTimeValue additionalSourceValue : additionalSourceValues {
863 6 const String& stringValue = resourceManager.getCompileTimeStringValue(additionalSourceValue.getStringValueOffset());
864 6 const FilePath additionalSourcePath = FilePath(ownFilePath.getParentDir()) / stringValue;
865 6 resourceManager.addAdditionalSourcePath(additionalSourcePath);
866 6 }
867
868 3076 return Any();
869 1539 }
870
871 3579 public f<Any> SymbolTableBuilder.visitAttr(ASTAttrNode* node) {
872 3579 ASTNode* baseNode = cast<ASTNode*>(node);
873
874 // Check if this attribute exists
875 3579 AttrConfigValue config;
876
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3578 times.
3579 if !getAttrConfig(node.key, config) {
877 1 this.reportError(baseNode, SemanticErrorType::UNKNOWN_ATTR, "Unknown attribute '" + node.key + "'");
878 2 return Any();
879 1 }
880
881 // Check if the target is correct
882
2/2
✓ Branch 0 taken 3577 times.
✓ Branch 1 taken 1 time.
3578 if (cast<unsigned byte>(node.target) & config.target) == cast<unsigned byte>(0) {
883 1 this.reportError(baseNode, SemanticErrorType::INVALID_ATTR_TARGET, "Attribute '" + node.key + "' cannot be used on this target");
884 2 return Any();
885 1 }
886
887 // Check if a value is present
888
4/4
✓ Branch 0 taken 2462 times.
✓ Branch 1 taken 1115 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 3576 times.
3577 if node.value == nil<ASTConstantNode*> && config.attrType != AttrType::TYPE_BOOL {
889 1 this.reportError(baseNode, SemanticErrorType::MISSING_ATTR_VALUE, "Attribute '" + node.key + "' requires a value");
890 2 return Any();
891 1 }
892
893 3576 return Any();
894 }
895
896 // Shared by the lambda visit methods: an 'async' lambda attribute marks the lambda scope as async
897 144 p SymbolTableBuilder.applyLambdaAttr(ASTLambdaAttrNode* lambdaAttr, Scope* bodyScope) {
898
4/4
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 123 times.
✓ Branch 3 taken 21 times.
144 if lambdaAttr != nil<ASTLambdaAttrNode*> && lambdaAttr.attrLst.hasAttr(String(ATTR_ASYNC)) {
899 21 const CompileTimeValue isAsyncScopeAttrValue = lambdaAttr.attrLst.getAttrValueByName(String(ATTR_ASYNC));
900 21 bodyScope.isAsyncScope = isAsyncScopeAttrValue.getBoolValue();
901 21 }
902 144 }
903
904 /**
905 * Build the symbol table for a lambda: create its body scope, the 'result' variable of a lambda function and the param
906 * symbols, and visit its body or expression
907 *
908 * @param lambdaBase Lambda base node
909 * @param baseNode AST node of the lambda
910 * @param scopeId Scope id of the lambda
911 * @param lambdaAttr Lambda attributes, nil if the lambda has none
912 * @param hasResultVar Whether the lambda has a 'result' variable (lambda functions)
913 * @param body Body or expression of the lambda
914 */
915 144 f<Any> SymbolTableBuilder.visitLambda(ASTLambdaBaseNode* lambdaBase, ASTNode* baseNode, const String& scopeId, ASTLambdaAttrNode* lambdaAttr, bool hasResultVar, IVisitable* body) {
916 // Create scope for the lambda body
917 144 Scope* bodyScope = this.currentScope.createChildScope(scopeId, ScopeType::LAMBDA_BODY, &baseNode.codeLoc);
918 144 lambdaBase.bodyScope = bodyScope;
919 144 this.currentScope = bodyScope;
920 // Requires capturing because the LLVM IR will end up in a separate function
921 144 bodyScope.symbolTable.setCapturingRequired();
922 // Set to async scope if this is an async lambda
923 144 this.applyLambdaAttr(lambdaAttr, bodyScope);
924
925 // Create symbol for 'result' variable
926
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 67 times.
144 if hasResultVar {
927 67 bodyScope.insert(String(RETURN_VARIABLE_NAME), baseNode);
928 67 }
929
930 // Create symbols for the parameters
931
2/2
✓ Branch 0 taken 46 times.
✓ Branch 1 taken 98 times.
144 if lambdaBase.hasParams {
932 98 this.visit(lambdaBase.paramLst);
933 98 }
934
935 // Visit body or lambda expression
936 144 this.visit(body);
937
938 // Leave lambda body scope
939 144 this.currentScope = bodyScope.parent;
940
941 288 return Any();
942 144 }
943
944 67 public f<Any> SymbolTableBuilder.visitLambdaFunc(ASTLambdaFuncNode* node) {
945 134 return this.visitLambda(&node.node, cast<ASTNode*>(node), node.getScopeId(), node.lambdaAttr, /*hasResultVar=*/true, node.body);
946 67 }
947
948 73 public f<Any> SymbolTableBuilder.visitLambdaProc(ASTLambdaProcNode* node) {
949 146 return this.visitLambda(&node.node, cast<ASTNode*>(node), node.getScopeId(), node.lambdaAttr, /*hasResultVar=*/false, node.body);
950 73 }
951
952 4 public f<Any> SymbolTableBuilder.visitLambdaExpr(ASTLambdaExprNode* node) {
953 8 return this.visitLambda(&node.node, cast<ASTNode*>(node), node.getScopeId(), nil<ASTLambdaAttrNode*>, /*hasResultVar=*/false, node.lambdaExpr);
954 4 }
955
956 2598 public f<Any> SymbolTableBuilder.visitTernaryExpr(ASTTernaryExprNode* node) {
957 // Visit condition, which is evaluated unconditionally
958 2598 this.visit(node.condition);
959
960 // Visit the branches. Only one of them is evaluated, so each one gets its own scope for its temporaries.
961 // The true branch does not exist for shortened ternaries, as the condition takes its place.
962
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2596 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 2596 times.
2598 if node.trueExpr != nil<IVisitable*> && !node.isShortened {
963 2596 this.visitInExprScope(node.trueExpr);
964 2596 }
965
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2598 times.
2598 if node.falseExpr != nil<IVisitable*> {
966 2598 this.visitInExprScope(node.falseExpr);
967 2598 }
968
969 5196 return Any();
970 2598 }
971
972 // Shared by the logical operators
973 6178 f<Any> SymbolTableBuilder.visitOperands(const Vector<IVisitable*>& operands) {
974
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6178 times.
6178 if operands.isEmpty() {
975 ✗ return Any();
976 }
977
978 // Visit the first operand, which is evaluated unconditionally
979 6178 IVisitable* first = operands.get(0l);
980 6178 this.visit(first);
981
982 // All further operands are only evaluated if the ones before did not short-circuit
983
2/2
✓ Branch 0 taken 7418 times.
✓ Branch 1 taken 6178 times.
13596 for unsigned long i = 1l; i < operands.getSize(); i++ {
984 7418 this.visitInExprScope(operands.get(i));
985 7418 }
986
987 12356 return Any();
988 6178 }
989
990 4052 public f<Any> SymbolTableBuilder.visitLogicalOrExpr(ASTLogicalOrExprNode* node) {
991 4052 return this.visitOperands(node.operands);
992 }
993
994 2126 public f<Any> SymbolTableBuilder.visitLogicalAndExpr(ASTLogicalAndExprNode* node) {
995 2126 return this.visitOperands(node.operands);
996 }
997
998 92782 public f<Any> SymbolTableBuilder.visitStmtLst(ASTStmtLstNode* node) {
999
2/2
✓ Branch 0 taken 179056 times.
✓ Branch 1 taken 92782 times.
271838 foreach IVisitable* stmt : node.statements {
1000 179056 this.visit(stmt);
1001 179056 }
1002 92782 return Any();
1003 }
1004
1005 33175 public f<Any> SymbolTableBuilder.visitParamLst(ASTParamLstNode* node) {
1006
2/2
✓ Branch 0 taken 54130 times.
✓ Branch 1 taken 33175 times.
87305 foreach ASTDeclStmtNode* param : node.params {
1007 54130 this.visitDeclStmt(param);
1008 54130 }
1009 33175 return Any();
1010 }
1011
1012 465 public f<Any> SymbolTableBuilder.visitEnumItemLst(ASTEnumItemLstNode* node) {
1013
2/2
✓ Branch 0 taken 1746 times.
✓ Branch 1 taken 465 times.
2211 foreach ASTEnumItemNode* item : node.items {
1014 1746 this.visitEnumItem(item);
1015 1746 }
1016 465 return Any();
1017 }
1018