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 | 50716 | if qualifierLst == nil<ASTQualifierLstNode*> { | |
| 70 | 6217 | return true; | |
| 71 | } | ||
| 72 | 101969 | foreach ASTQualifierNode* qualifier : qualifierLst.qualifiers { | |
| 73 | 57471 | if qualifier.qualifierType == QualifierType::PUBLIC { | |
| 74 | 44328 | qualifiers.isPublic = true; | |
| 75 | 57471 | } else if allowInlineAndConst && qualifier.qualifierType == QualifierType::INLINE { | |
| 76 | 13026 | qualifiers.isInline = true; | |
| 77 | 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 | 45762 | if node.attrs != nil<IVisitable*> { | |
| 113 | 1116 | this.visit(node.attrs); | |
| 114 | 1116 | } | |
| 115 | |||
| 116 | // Build function qualifiers | ||
| 117 | 45762 | const String illegalMessage = isFunction ? String("Cannot use this qualifier on a function definition") : String("Cannot use this qualifier on a procedure definition"); | |
| 118 | 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 | 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 | 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 | 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 | 45761 | fctScope.isGenericScope = node.hasTemplateTypes || (structScope != nil<Scope*> && structScope.isGenericScope); | |
| 145 | 45761 | if !isFunction { | |
| 146 | 17648 | fctScope.isCtorScope = node.isMethod && node.name.name == CTOR_FUNCTION_NAME; | |
| 147 | 17648 | fctScope.isDtorScope = node.isMethod && node.name.name == DTOR_FUNCTION_NAME; | |
| 148 | 17648 | } | |
| 149 | |||
| 150 | // Create symbol for 'this' variable | ||
| 151 | 45761 | if node.isMethod { | |
| 152 | 26619 | fctScope.insert(String(THIS_VARIABLE_NAME), baseNode); | |
| 153 | 26619 | } | |
| 154 | |||
| 155 | // Create symbol for 'result' variable | ||
| 156 | 45761 | if isFunction { | |
| 157 | 28113 | fctScope.insert(String(RETURN_VARIABLE_NAME), baseNode); | |
| 158 | 28113 | } | |
| 159 | |||
| 160 | // Create symbols for the parameters | ||
| 161 | 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 | 45761 | if !this.sourceFile.hasNameRegistryTarget(node.name.fqName, node.entry) { | |
| 177 | 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 | 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 | 5480 | foreach IVisitable* modAttr : node.modAttrs { | |
| 198 | 1538 | this.visit(modAttr); | |
| 199 | 1538 | } | |
| 200 | 6450 | foreach IVisitable* importDef : node.importDefs { | |
| 201 | 2508 | this.visit(importDef); | |
| 202 | 2508 | } | |
| 203 | 67518 | foreach IVisitable* topLevelDef : node.topLevelDefs { | |
| 204 | 63576 | this.visit(topLevelDef); | |
| 205 | 63576 | } | |
| 206 | |||
| 207 | // Check if the main function exists | ||
| 208 | 3942 | const bool mainFctRequired = this.cliOptions.outputContainer == OutputContainer::EXECUTABLE && !this.cliOptions.noEntryFct; | |
| 209 | 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 | 1019 | if node.attrs != nil<IVisitable*> { | |
| 221 | 1 | this.visit(node.attrs); | |
| 222 | 1 | } | |
| 223 | |||
| 224 | // Check if the function is already defined | ||
| 225 | 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 | 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 | 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 | 3232 | if node.attrs != nil<IVisitable*> { | |
| 280 | 202 | this.visit(node.attrs); | |
| 281 | 202 | } | |
| 282 | |||
| 283 | // Check if this name already exists | ||
| 284 | 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 | 3231 | if node.hasInterfaces { | |
| 298 | 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 | 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 | 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 | 327 | if node.attrs != nil<IVisitable*> { | |
| 333 | 254 | this.visit(node.attrs); | |
| 334 | 254 | } | |
| 335 | |||
| 336 | // Check if this name already exists | ||
| 337 | 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 | 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 | 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 | 26 | if node.attrs != nil<IVisitable*> { | |
| 374 | ✗ | this.visit(node.attrs); | |
| 375 | ✗ | } | |
| 376 | |||
| 377 | // Check if this name already exists | ||
| 378 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 5229 | if node.attrs != nil<IVisitable*> { | |
| 507 | 1 | this.visit(node.attrs); | |
| 508 | 1 | } | |
| 509 | |||
| 510 | // Check if this name already exists | ||
| 511 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 83807 | const bool isLoopBody = parentScope != nil<Scope*> && parentScope.scopeType == ScopeType::LOOP_HEAD; | |
| 789 | 83807 | const bool declaredInLoopHead = isLoopBody && parentScope.lookupStrict(node.varName) != nil<SymbolTableEntry*>; | |
| 790 | 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 | 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 | 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 | 1538 | if wrapInGroup && !coreValues.isEmpty() { | |
| 835 | 30 | resourceManager.addLinkerFlag(String("-Wl,--start-group")); | |
| 836 | 30 | } | |
| 837 | 1949 | foreach CompileTimeValue value : coreValues { | |
| 838 | 411 | const String& flag = resourceManager.getCompileTimeStringValue(value.getStringValueOffset()); | |
| 839 | 411 | resourceManager.addLinkerFlag(flag); | |
| 840 | 411 | } | |
| 841 | 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 | 1538 | if isLinux { | |
| 848 | 1514 | platformValues = attrs.getAttrValuesByName(String(ATTR_CORE_LINUX_LINKER_FLAG)); | |
| 849 | 1538 | } else if isDarwin { | |
| 850 | 12 | platformValues = attrs.getAttrValuesByName(String(ATTR_CORE_DARWIN_LINKER_FLAG)); | |
| 851 | 24 | } else if isWindows { | |
| 852 | 12 | platformValues = attrs.getAttrValuesByName(String(ATTR_CORE_WINDOWS_LINKER_FLAG)); | |
| 853 | 24 | } | |
| 854 | 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 | 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 | 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 | 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 | 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 | 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 | 144 | if hasResultVar { | |
| 927 | 67 | bodyScope.insert(String(RETURN_VARIABLE_NAME), baseNode); | |
| 928 | 67 | } | |
| 929 | |||
| 930 | // Create symbols for the parameters | ||
| 931 | 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 | 2598 | if node.trueExpr != nil<IVisitable*> && !node.isShortened { | |
| 963 | 2596 | this.visitInExprScope(node.trueExpr); | |
| 964 | 2596 | } | |
| 965 | 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 | 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 | 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 | 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 | 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 | 2211 | foreach ASTEnumItemNode* item : node.items { | |
| 1014 | 1746 | this.visitEnumItem(item); | |
| 1015 | 1746 | } | |
| 1016 | 465 | return Any(); | |
| 1017 | } | ||
| 1018 |