From ea3275303c0d8bd613be0014aebdb22c9d072a05 Mon Sep 17 00:00:00 2001 From: Marc Auberer Date: Wed, 6 Sep 2023 01:44:33 +0200 Subject: [PATCH] Fix skipped function typecheck (#311) --- .github/workflows/ci-cpp.yml | 4 +- .github/workflows/codeql-analysis.yml | 4 +- .github/workflows/publish.yml | 8 +- .run/spice.run.xml | 2 +- dev-setup.bat | 2 +- dev-setup.sh | 2 +- docs/docs/how-to/cli-parser.md | 43 ++- docs/docs/how-to/web-assembly.md | 4 +- media/test-project/test.spice | 64 +++- src-bootstrap/SourceFile.spice | 2 +- src/SourceFile.cpp | 9 +- src/SourceFile.h | 2 +- src/global/RuntimeModuleManager.cpp | 46 ++- src/global/RuntimeModuleManager.h | 4 +- src/irgenerator/DebugInfoGenerator.cpp | 39 ++- src/irgenerator/DebugInfoGenerator.h | 4 +- src/irgenerator/GenControlStructures.cpp | 45 ++- src/irgenerator/GenTopLevelDefinitions.cpp | 33 +- src/irgenerator/GenValues.cpp | 19 +- src/irgenerator/IRGenerator.cpp | 19 ++ src/irgenerator/IRGenerator.h | 2 + src/irgenerator/StdFunctionManager.cpp | 6 +- src/model/Function.cpp | 2 +- src/model/Interface.cpp | 3 +- src/model/Struct.cpp | 3 +- src/typechecker/OpRuleManager.cpp | 5 +- src/typechecker/TypeChecker.cpp | 81 +++-- src/typechecker/TypeChecker.h | 2 + src/typechecker/TypeCheckerCheck.cpp | 21 +- std/io/cli-option.spice | 8 +- std/io/cli-parser.spice | 9 +- std/io/cli-subcommand.spice | 115 +++++-- std/os/cpu.spice | 2 +- std/os/thread-pool.spice | 49 ++- std/os/thread.spice | 6 +- std/time/delay.spice | 2 +- std/type/string.spice | 57 +--- .../success-dbg-info-simple/ir-code.ll | 2 +- .../success-dgb-info-complex/ir-code.ll | 313 +++++++++--------- .../cli-flags.txt | 0 .../{cli-parser => cli-parser-flags}/cout.out | 0 .../ir-code-O2.ll | 6 +- .../source.spice | 0 .../io/cli-parser-subcommands/cli-flags.txt | 1 + .../std/io/cli-parser-subcommands/cout.out | 1 + .../io/cli-parser-subcommands/ir-code-O2.ll | 68 ++++ .../io/cli-parser-subcommands/source.spice | 26 ++ 47 files changed, 717 insertions(+), 428 deletions(-) rename test/test-files/std/io/{cli-parser => cli-parser-flags}/cli-flags.txt (100%) rename test/test-files/std/io/{cli-parser => cli-parser-flags}/cout.out (100%) rename test/test-files/std/io/{cli-parser => cli-parser-flags}/ir-code-O2.ll (93%) rename test/test-files/std/io/{cli-parser => cli-parser-flags}/source.spice (100%) create mode 100644 test/test-files/std/io/cli-parser-subcommands/cli-flags.txt create mode 100644 test/test-files/std/io/cli-parser-subcommands/cout.out create mode 100644 test/test-files/std/io/cli-parser-subcommands/ir-code-O2.ll create mode 100644 test/test-files/std/io/cli-parser-subcommands/source.spice diff --git a/.github/workflows/ci-cpp.yml b/.github/workflows/ci-cpp.yml index 4d9ed7a7a..39181ea89 100644 --- a/.github/workflows/ci-cpp.yml +++ b/.github/workflows/ci-cpp.yml @@ -52,14 +52,14 @@ jobs: uses: actions/cache@v3 with: path: /home/runner/work/spice/llvm - key: llvm-17.0.0-rc3 + key: llvm-17.0.0-rc4 - name: Setup LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | cd .. rm -rf llvm - git clone --depth 1 --branch llvmorg-17.0.0-rc3 https://github.com/llvm/llvm-project llvm + git clone --depth 1 --branch llvmorg-17.0.0-rc4 https://github.com/llvm/llvm-project llvm mkdir ./llvm/build cd ./llvm/build cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS_RELEASE="-O2" -DLLVM_ENABLE_RTTI=ON -GNinja ../llvm diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 079d3a34d..48be37de8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,14 +46,14 @@ jobs: uses: actions/cache@v3 with: path: /home/runner/work/spice/llvm - key: llvm-17.0.0-rc3 + key: llvm-17.0.0-rc4 - name: Setup LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH cd .. - git clone --depth 1 --branch llvmorg-17.0.0-rc3 https://github.com/llvm/llvm-project llvm + git clone --depth 1 --branch llvmorg-17.0.0-rc4 https://github.com/llvm/llvm-project llvm mkdir ./llvm/build cd ./llvm/build cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS_RELEASE="-O2" -DLLVM_ENABLE_RTTI=ON -GNinja ../llvm diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f89a94a72..9e9781965 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -42,12 +42,12 @@ jobs: uses: actions/cache@v3 with: path: /home/runner/work/spice/spice/llvm - key: llvm-17.0.0-rc3-linux-x64 + key: llvm-17.0.0-rc4-linux-x64 - name: Setup LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | - git clone --depth 1 --branch llvmorg-17.0.0-rc3 https://github.com/llvm/llvm-project.git llvm + git clone --depth 1 --branch llvmorg-17.0.0-rc4 https://github.com/llvm/llvm-project.git llvm mkdir ./llvm/build cd ./llvm/build cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_CXX_FLAGS_RELEASE="-O2" -DLLVM_ENABLE_RTTI=ON -GNinja -Wno-dev -Wattributes ../llvm @@ -116,12 +116,12 @@ jobs: uses: actions/cache@v3 with: path: D:/a/spice/spice/llvm - key: llvm-17.0.0-rc3-win-x64 + key: llvm-17.0.0-rc4-win-x64 - name: Setup LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | - git clone --depth 1 --branch llvmorg-17.0.0-rc3 https://github.com/llvm/llvm-project.git llvm + git clone --depth 1 --branch llvmorg-17.0.0-rc4 https://github.com/llvm/llvm-project.git llvm setx /M PATH "%PATH%;C:\mingw64\mingw64\bin" $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") echo "Adding MinGW to path done." diff --git a/.run/spice.run.xml b/.run/spice.run.xml index ede9e3acd..13bbfd476 100644 --- a/.run/spice.run.xml +++ b/.run/spice.run.xml @@ -1,5 +1,5 @@ - + diff --git a/dev-setup.bat b/dev-setup.bat index d666cd107..fefb46b44 100644 --- a/dev-setup.bat +++ b/dev-setup.bat @@ -27,7 +27,7 @@ echo done. REM - Clone LLVM echo [Step 2] Cloning LLVM (Could take a while) ... -git clone --depth 1 --branch llvmorg-17.0.0-rc3 https://github.com/llvm/llvm-project llvm +git clone --depth 1 --branch llvmorg-17.0.0-rc4 https://github.com/llvm/llvm-project llvm echo done. REM - Build LLVM diff --git a/dev-setup.sh b/dev-setup.sh index e2ee09f8a..161e6afb8 100644 --- a/dev-setup.sh +++ b/dev-setup.sh @@ -15,7 +15,7 @@ colored_echo "done." # Clone LLVM colored_echo "[Step 2] Cloning LLVM (Could take a while) ... " -git clone --depth 1 --branch llvmorg-17.0.0-rc3 https://github.com/llvm/llvm-project llvm +git clone --depth 1 --branch llvmorg-17.0.0-rc4 https://github.com/llvm/llvm-project llvm colored_echo "done." # Build LLVM diff --git a/docs/docs/how-to/cli-parser.md b/docs/docs/how-to/cli-parser.md index 4d4b11eff..9ef195f5d 100644 --- a/docs/docs/how-to/cli-parser.md +++ b/docs/docs/how-to/cli-parser.md @@ -87,6 +87,45 @@ $ ./app-name --hi Hi! ``` -## Add sub-commands +## Add subcommands -ToDo \ No newline at end of file +To build more advanced CLI interfaces, you can add subcommands. Let's add a `greet` subcommand to our CLI interface: + +```spice +// app-name.spice + +import "std/io/cli-parser"; + +f main(int argc, string[] argv) { + CliParser cli = CliParser("app-name", "Short description of the app"); + cli.setVersion("v1.0.0"); + cli.setFooter("(c) 2023 by John Doe"); + + CliSubcommand& greet = cli.addSubcommand("greet", "Greet someone"); + CliSubcommand& walk = cli.addSubcommand("walk", "Walk somewhere"); + + cli.parse(argc, argv); +} +``` + +Each subcommand comes with its own `--help` flag out of the box, that prints the help text for that specific subcommand. +Subcommands can also have their own sub-commands. In other words, they can be nested: + +```spice +// app-name.spice + +import "std/io/cli-parser"; + +f main(int argc, string[] argv) { + CliParser cli = CliParser("app-name", "Short description of the app"); + cli.setVersion("v1.0.0"); + cli.setFooter("(c) 2023 by John Doe"); + + CliSubcommand& greet = cli.addSubcommand("greet", "Greet someone"); + CliSubcommand& greetFriendly = greet.addSubcommand("friendly", "Greet someone in a friendly way"); + CliSubcommand& greetFormal = greet.addSubcommand("formal", "Greet someone in a formal way"); + CliSubcommand& walk = cli.addSubcommand("walk", "Walk somewhere"); + + cli.parse(argc, argv); +} +``` \ No newline at end of file diff --git a/docs/docs/how-to/web-assembly.md b/docs/docs/how-to/web-assembly.md index c24ab1463..ec8111f67 100644 --- a/docs/docs/how-to/web-assembly.md +++ b/docs/docs/how-to/web-assembly.md @@ -1,5 +1,5 @@ --- -title: Leveraging Spice WebAssembly capabilities +title: Compile to WebAssembly --- Due to the fact, that Spice uses LLVM as compiler backbone, it is capable of cross-compiling to many different target @@ -77,7 +77,7 @@ WebAssembly.instantiateStreaming(fetch('main.wasm')) const stopWasm = window.performance.now(); console.log("Fibonacci Spice wasm: " + result); console.log("Duration (millis): " + (stopWasm - startWasm)); - // JS + // JavaScript const startJS = window.performance.now(); result = fibo(fiboBase); const stopJS = window.performance.now(); diff --git a/media/test-project/test.spice b/media/test-project/test.spice index 076b6d18d..c8b4ae010 100644 --- a/media/test-project/test.spice +++ b/media/test-project/test.spice @@ -1,21 +1,81 @@ -import "std/os/thread-pool"; +import "std/io/cli-parser"; + +type CliOptions struct { + bool sayHi = false +} + +p callback(bool& value) { + printf("Callback called with value %d\n", value); +} + +f main(int argc, string[] argv) { + CliParser parser = CliParser("Test Program", "This is a simple test program"); + parser.setVersion("v0.1.0"); + parser.setFooter("Copyright (c) Marc Auberer 2021-2023"); + + CliOptions options; + parser.addFlag("--hi", options.sayHi, "Say hi to the user"); + parser.addFlag("--callback", callback, "Call a callback function"); + parser.addFlag("-cb", p(bool& value) { + printf("CB called with value %d\n", value); + }, "Call a callback function"); + + parser.parse(argc, argv); + + // Print hi if requested + if options.sayHi { + printf("Hi!\n"); + } +} + +/*import "std/os/thread-pool"; +import "std/time/delay"; f main() { ThreadPool tp = ThreadPool(3s); tp.enqueue(p() { + delay(100); printf("Hello from task 1\n"); }); tp.enqueue(p() { + delay(200); printf("Hello from task 2\n"); }); tp.enqueue(p() { + delay(300); printf("Hello from task 3\n"); }); tp.enqueue(p() { + delay(400); printf("Hello from task 4\n"); }); + tp.enqueue(p() { + delay(500); + printf("Hello from task 5\n"); + }); + tp.enqueue(p() { + delay(600); + printf("Hello from task 6\n"); + }); + tp.enqueue(p() { + delay(700); + printf("Hello from task 7\n"); + }); + tp.enqueue(p() { + delay(800); + printf("Hello from task 8\n"); + }); + tp.enqueue(p() { + delay(900); + printf("Hello from task 9\n"); + }); + tp.enqueue(p() { + delay(1000); + printf("Hello from task 10\n"); + }); tp.start(); -} + tp.join(); +}*/ /*import "std/os/thread-pool"; diff --git a/src-bootstrap/SourceFile.spice b/src-bootstrap/SourceFile.spice index 6d3b9a39a..bff85ca4f 100644 --- a/src-bootstrap/SourceFile.spice +++ b/src-bootstrap/SourceFile.spice @@ -96,7 +96,7 @@ public type SourceFile struct { GlobalResourceManager& resourceManager unsigned short importedRuntimeModules = 0 - unsigned short typeCheckerRuns = 0 + unsigned short totalTypeCheckerRuns = 0 } public p SourceFile.ctor(GlobalResourceManager &resourceManager, SourceFile* parent, string name, string filePath, bool stdFile) { diff --git a/src/SourceFile.cpp b/src/SourceFile.cpp index 90bf0da29..4afcc1d72 100644 --- a/src/SourceFile.cpp +++ b/src/SourceFile.cpp @@ -267,7 +267,7 @@ void SourceFile::runTypeCheckerPre() { // NOLINT(misc-no-recursion) void SourceFile::runTypeCheckerPost() { // NOLINT(misc-no-recursion) // Skip if restored from cache, this stage has already been done or not all dependants finished type checking - if (restoredFromCache || previousStage >= TYPE_CHECKER_POST || !haveAllDependantsBeenTypeChecked()) + if (restoredFromCache || !haveAllDependantsBeenTypeChecked()) return; Timer timer(&compilerOutput.times.typeCheckerPost); @@ -275,8 +275,10 @@ void SourceFile::runTypeCheckerPost() { // NOLINT(misc-no-recursion) // Start type-checking loop. The type-checker can request a re-execution. The max number of type-checker runs is limited TypeChecker typeChecker(resourceManager, this, TC_MODE_CHECK); + unsigned short typeCheckerRuns = 0; do { typeCheckerRuns++; + totalTypeCheckerRuns++; // Type-check the current file first. Multiple times, if requested timer.resume(); @@ -288,7 +290,7 @@ void SourceFile::runTypeCheckerPost() { // NOLINT(misc-no-recursion) sourceFile.first->runTypeCheckerPost(); // GCOV_EXCL_START - if (typeCheckerRuns >= 10) + if (typeCheckerRuns >= 10 || totalTypeCheckerRuns >= 50) throw CompilerError(TYPE_CHECKER_RUNS_EXCEEDED, "Number of type checker runs for one source file exceeded. Please report " "this as a bug on GitHub."); // GCOV_EXCL_STOP @@ -680,8 +682,7 @@ void SourceFile::collectAndPrintWarnings() { // NOLINT(misc-no-recursion) } bool SourceFile::haveAllDependantsBeenTypeChecked() const { - return std::all_of(dependants.begin(), dependants.end(), - [=](const SourceFile *dependant) { return dependant->typeCheckerRuns >= 1; }); + return std::ranges::all_of(dependants, [](const SourceFile *dependant) { return dependant->totalTypeCheckerRuns >= 1; }); } /** diff --git a/src/SourceFile.h b/src/SourceFile.h index 6f752f7f1..5ea15fde4 100644 --- a/src/SourceFile.h +++ b/src/SourceFile.h @@ -173,7 +173,7 @@ class SourceFile { GlobalResourceManager &resourceManager; BS::synced_stream &tout; uint8_t importedRuntimeModules = 0; - unsigned short typeCheckerRuns = 0; + unsigned short totalTypeCheckerRuns = 0; // Private methods bool haveAllDependantsBeenTypeChecked() const; diff --git a/src/global/RuntimeModuleManager.cpp b/src/global/RuntimeModuleManager.cpp index 7b8c7ec95..d1c371620 100644 --- a/src/global/RuntimeModuleManager.cpp +++ b/src/global/RuntimeModuleManager.cpp @@ -9,48 +9,42 @@ namespace spice::compiler { -SourceFile *RuntimeModuleManager::requestModule(SourceFile *sourceFile, RuntimeModule requestedModule) { - // Check if the requested module is available already - bool available = isModuleAvailable(requestedModule); +SourceFile *RuntimeModuleManager::requestModule(SourceFile *parentSourceFile, RuntimeModule requestedModule) { + const std::string importName = resolveNamePair(requestedModule).importName; - // If not, try to make it available - if (!available) - available = addModule(sourceFile, requestedModule); + // Check if the requested module is available already, if not load it + auto rtFile = isModuleAvailable(requestedModule) ? modules.at(requestedModule) : loadModule(parentSourceFile, requestedModule); - // If the module is still not available, cancel here - if (!available) - return nullptr; + // Add the dependency to the parent source file + parentSourceFile->addDependency(rtFile, parentSourceFile->ast, importName, rtFile->filePath.string()); + SourceFile *runtimeFile = parentSourceFile->dependencies.at(importName).first; + modules.emplace(requestedModule, runtimeFile); // Merge the module name registry with the one of the source file - const ModuleNamePair names = resolveNamePair(requestedModule); - SourceFile *runtimeModuleFile = modules.at(requestedModule); - sourceFile->mergeNameRegistries(*runtimeModuleFile, names.importName); + parentSourceFile->mergeNameRegistries(*rtFile, importName); // Tell the source file, that the requested runtime has been imported - sourceFile->importedRuntimeModules |= requestedModule; + parentSourceFile->importedRuntimeModules |= requestedModule; - return runtimeModuleFile; + return rtFile; } bool RuntimeModuleManager::isModuleAvailable(RuntimeModule requestedModule) const { return modules.contains(requestedModule); } -bool RuntimeModuleManager::addModule(SourceFile *parentSourceFile, RuntimeModule requestedModule) { +SourceFile *RuntimeModuleManager::loadModule(SourceFile *parentSourceFile, RuntimeModule requestedModule) { const auto [importName, fileName] = resolveNamePair(requestedModule); - const std::filesystem::path filePath = FileUtil::getStdDir() / "runtime" / (std::string(fileName) + ".spice"); - if (filePath == parentSourceFile->filePath) - return false; + assert(filePath != parentSourceFile->filePath); - const auto moduleSourceFile = resourceManager.createSourceFile(parentSourceFile, importName, filePath, true); - parentSourceFile->addDependency(moduleSourceFile, parentSourceFile->ast, importName, filePath.string()); + // Instruct the global resource manager to create a new source file + SourceFile *moduleSourceFile = resourceManager.createSourceFile(parentSourceFile, importName, filePath, true); + moduleSourceFile->mainFile = false; - // Run frontend and type checker for runtime module source file - const auto runtimeFile = parentSourceFile->dependencies.at(importName).first; - runtimeFile->runFrontEnd(); - runtimeFile->runTypeCheckerPre(); - modules.emplace(requestedModule, runtimeFile); + // Run frontend and first type checker run for the loaded source file + moduleSourceFile->runFrontEnd(); + moduleSourceFile->runTypeCheckerPre(); - return true; + return moduleSourceFile; } ModuleNamePair RuntimeModuleManager::resolveNamePair(spice::compiler::RuntimeModule runtimeModule) { diff --git a/src/global/RuntimeModuleManager.h b/src/global/RuntimeModuleManager.h index 0167c41dc..fc41bfe72 100644 --- a/src/global/RuntimeModuleManager.h +++ b/src/global/RuntimeModuleManager.h @@ -34,12 +34,12 @@ class RuntimeModuleManager { RuntimeModuleManager(const RuntimeModuleManager &) = delete; // Public methods - SourceFile *requestModule(SourceFile *sourceFile, RuntimeModule requestedModule); + SourceFile *requestModule(SourceFile *parentSourceFile, RuntimeModule requestedModule); [[nodiscard]] bool isModuleAvailable(RuntimeModule requestedModule) const; private: // Private methods - bool addModule(SourceFile *parentSourceFile, RuntimeModule requestedModule); + SourceFile *loadModule(SourceFile *parentSourceFile, RuntimeModule requestedModule); static ModuleNamePair resolveNamePair(RuntimeModule requestedModule); // Private members diff --git a/src/irgenerator/DebugInfoGenerator.cpp b/src/irgenerator/DebugInfoGenerator.cpp index 639aae3b5..eae80da22 100644 --- a/src/irgenerator/DebugInfoGenerator.cpp +++ b/src/irgenerator/DebugInfoGenerator.cpp @@ -26,7 +26,7 @@ void DebugInfoGenerator::initialize(const std::string &sourceFileName, std::file absolutePath.make_preferred(); sourceFileDir.make_preferred(); llvm::DIFile *cuDiFile = diBuilder->createFile(absolutePath.string(), sourceFileDir.string()); - compileUnit = diBuilder->createCompileUnit(llvm::dwarf::DW_LANG_lo_user, cuDiFile, producerString, + compileUnit = diBuilder->createCompileUnit(llvm::dwarf::DW_LANG_C17, cuDiFile, producerString, irGenerator->cliOptions.optLevel > O0, "", 0, "", llvm::DICompileUnit::FullDebug, 0, false, false, llvm::DICompileUnit::DebugNameTableKind::None); @@ -47,7 +47,6 @@ void DebugInfoGenerator::initialize(const std::string &sourceFileName, std::file pointerWidth = irGenerator->module->getDataLayout().getPointerSizeInBits(); // Initialize primitive debug types - const unsigned int pointerWidth = irGenerator->module->getDataLayout().getPointerSizeInBits(); doubleTy = diBuilder->createBasicType("double", 64, llvm::dwarf::DW_ATE_float); intTy = diBuilder->createBasicType("int", 32, llvm::dwarf::DW_ATE_signed); uIntTy = diBuilder->createBasicType("unsigned int", 32, llvm::dwarf::DW_ATE_unsigned); @@ -58,7 +57,7 @@ void DebugInfoGenerator::initialize(const std::string &sourceFileName, std::file byteTy = diBuilder->createBasicType("byte", 8, llvm::dwarf::DW_ATE_unsigned); charTy = diBuilder->createBasicType("char", 8, llvm::dwarf::DW_ATE_unsigned_char); stringTy = diBuilder->createPointerType(charTy, pointerWidth); - boolTy = diBuilder->createBasicType("bool", 1, llvm::dwarf::DW_ATE_boolean); + boolTy = diBuilder->createBasicType("bool", 8, llvm::dwarf::DW_ATE_boolean); voidTy = diBuilder->createBasicType("void", 0, llvm::dwarf::DW_ATE_unsigned); // Initialize fat ptr type @@ -133,6 +132,32 @@ void DebugInfoGenerator::generateFunctionDebugInfo(llvm::Function *llvmFunction, lexicalBlocks.push(subprogram); } +void DebugInfoGenerator::concludeFunctionDebugInfo() { + if (!irGenerator->cliOptions.generateDebugInfo) + return; + + assert(!lexicalBlocks.empty()); + lexicalBlocks.pop(); +} + +void DebugInfoGenerator::pushLexicalBlock(const ASTNode *node) { + if (!irGenerator->cliOptions.generateDebugInfo) + return; + + const size_t line = node->codeLoc.line; + const size_t col = node->codeLoc.col; + llvm::DILexicalBlock* lexicalBlock = diBuilder->createLexicalBlock(lexicalBlocks.top(), diFile, line, col); + lexicalBlocks.push(lexicalBlock); +} + +void DebugInfoGenerator::popLexicalBlock() { + if (!irGenerator->cliOptions.generateDebugInfo) + return; + + assert(!lexicalBlocks.empty()); + lexicalBlocks.pop(); +} + llvm::DICompositeType *DebugInfoGenerator::generateCaptureStructDebugInfo(const Function *spiceFunc) { const std::unordered_map &captures = spiceFunc->bodyScope->symbolTable.captures; const CaptureMode captureMode = spiceFunc->bodyScope->symbolTable.capturingMode; @@ -236,14 +261,6 @@ void DebugInfoGenerator::setSourceLocation(const ASTNode *node) { irGenerator->builder.SetCurrentDebugLocation(codeLoc); } -void DebugInfoGenerator::concludeFunctionDebugInfo() { - if (!irGenerator->cliOptions.generateDebugInfo) - return; - - assert(!lexicalBlocks.empty()); - lexicalBlocks.pop(); -} - void DebugInfoGenerator::finalize() { if (irGenerator->cliOptions.generateDebugInfo) diBuilder->finalize(); diff --git a/src/irgenerator/DebugInfoGenerator.h b/src/irgenerator/DebugInfoGenerator.h index 7f44c4dac..25206f759 100644 --- a/src/irgenerator/DebugInfoGenerator.h +++ b/src/irgenerator/DebugInfoGenerator.h @@ -26,6 +26,9 @@ class DebugInfoGenerator { // Public methods void initialize(const std::string &sourceFileName, std::filesystem::path sourceFileDir); void generateFunctionDebugInfo(llvm::Function *llvmFunction, const Function *spiceFunc, bool isLambda = false); + void concludeFunctionDebugInfo(); + void pushLexicalBlock(const ASTNode *node); + void popLexicalBlock(); llvm::DICompositeType *generateCaptureStructDebugInfo(const Function *spiceFunc); void generateGlobalVarDebugInfo(llvm::GlobalVariable *global, const SymbolTableEntry *globalEntry); void generateGlobalStringDebugInfo(llvm::GlobalVariable *global, const std::string &name, size_t length, @@ -33,7 +36,6 @@ class DebugInfoGenerator { void generateLocalVarDebugInfo(const std::string &varName, llvm::Value *address, const size_t argNumber = SIZE_MAX, bool moveToPrev = false); void setSourceLocation(const ASTNode *node); - void concludeFunctionDebugInfo(); void finalize(); private: diff --git a/src/irgenerator/GenControlStructures.cpp b/src/irgenerator/GenControlStructures.cpp index f7a94590b..c2cb6ac6f 100644 --- a/src/irgenerator/GenControlStructures.cpp +++ b/src/irgenerator/GenControlStructures.cpp @@ -12,14 +12,16 @@ std::any IRGenerator::visitUnsafeBlockDef(const UnsafeBlockDefNode *node) { diGenerator.setSourceLocation(node); // Change scope - changeToScope(node->bodyScope, SCOPE_UNSAFE_BODY); + changeToScope(node->getScopeId(), SCOPE_UNSAFE_BODY); + diGenerator.pushLexicalBlock(node); // Visit instructions in the block visit(node->body()); // Change scope back - currentScope = node->bodyScope->parent; + changeToParentScope(); assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); return nullptr; } @@ -35,7 +37,8 @@ std::any IRGenerator::visitForLoop(const ForLoopNode *node) { llvm::BasicBlock *bExit = createBlock("for.exit." + codeLine); // Change scope - changeToScope(node->bodyScope, SCOPE_FOR_BODY); + changeToScope(node->getScopeId(), SCOPE_FOR_BODY); + diGenerator.pushLexicalBlock(node); // Save the blocks for break and continue breakBlocks.push_back(bExit); @@ -77,8 +80,9 @@ std::any IRGenerator::visitForLoop(const ForLoopNode *node) { continueBlocks.pop_back(); // Change scope back - currentScope = node->bodyScope->parent; + changeToParentScope(); assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); return nullptr; } @@ -94,7 +98,8 @@ std::any IRGenerator::visitForeachLoop(const ForeachLoopNode *node) { llvm::BasicBlock *bExit = createBlock("foreach.exit." + codeLine); // Change scope - changeToScope(node->bodyScope, SCOPE_FOREACH_BODY); + changeToScope(node->getScopeId(), SCOPE_FOREACH_BODY); + diGenerator.pushLexicalBlock(node); // Save the blocks for break and continue breakBlocks.push_back(bExit); @@ -195,8 +200,9 @@ std::any IRGenerator::visitForeachLoop(const ForeachLoopNode *node) { continueBlocks.pop_back(); // Change scope back - currentScope = node->bodyScope->parent; + changeToParentScope(); assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); return nullptr; } @@ -211,7 +217,8 @@ std::any IRGenerator::visitWhileLoop(const WhileLoopNode *node) { llvm::BasicBlock *bExit = createBlock("while.exit." + codeLine); // Change scope - changeToScope(node->bodyScope, SCOPE_WHILE_BODY); + changeToScope(node->getScopeId(), SCOPE_WHILE_BODY); + diGenerator.pushLexicalBlock(node); // Save the blocks for break and continue breakBlocks.push_back(bExit); @@ -246,6 +253,7 @@ std::any IRGenerator::visitWhileLoop(const WhileLoopNode *node) { // Change scope back currentScope = node->bodyScope->parent; assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); return nullptr; } @@ -260,7 +268,8 @@ std::any IRGenerator::visitDoWhileLoop(const DoWhileLoopNode *node) { llvm::BasicBlock *bExit = createBlock("dowhile.exit." + codeLine); // Change scope - changeToScope(node->bodyScope, SCOPE_WHILE_BODY); + changeToScope(node->getScopeId(), SCOPE_WHILE_BODY); + diGenerator.pushLexicalBlock(node); // Save the blocks for break and continue breakBlocks.push_back(bExit); @@ -293,8 +302,9 @@ std::any IRGenerator::visitDoWhileLoop(const DoWhileLoopNode *node) { continueBlocks.pop_back(); // Change scope back - currentScope = node->bodyScope->parent; + changeToParentScope(); assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); return nullptr; } @@ -309,7 +319,8 @@ std::any IRGenerator::visitIfStmt(const IfStmtNode *node) { llvm::BasicBlock *bExit = createBlock("if.exit." + codeLine); // Change scope - changeToScope(node->thenBodyScope, SCOPE_IF_ELSE_BODY); + changeToScope(node->getScopeId(), SCOPE_IF_ELSE_BODY); + diGenerator.pushLexicalBlock(node); // Retrieve condition value llvm::Value *condValue = resolveValue(node->condition()); @@ -324,8 +335,9 @@ std::any IRGenerator::visitIfStmt(const IfStmtNode *node) { insertJump(bExit); // Change scope back - currentScope = node->thenBodyScope->parent; + changeToParentScope(); assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); if (node->elseStmt()) { // Switch to else block @@ -350,14 +362,16 @@ std::any IRGenerator::visitElseStmt(const ElseStmtNode *node) { visit(node->ifStmt()); } else { // It is an else branch // Change scope - changeToScope(node->elseBodyScope, SCOPE_IF_ELSE_BODY); + changeToScope(node->getScopeId(), SCOPE_IF_ELSE_BODY); + diGenerator.pushLexicalBlock(node); // Generate IR for nested statements visit(node->body()); // Change scope back - currentScope = node->elseBodyScope->parent; + changeToParentScope(); assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); } return nullptr; @@ -369,15 +383,16 @@ std::any IRGenerator::visitAnonymousBlockStmt(const AnonymousBlockStmtNode *node // Change scope node->bodyScope->parent = currentScope; // Needed for nested scopes in generic functions node->bodyScope->symbolTable.parent = ¤tScope->symbolTable; // Needed for nested scopes in generic functions - currentScope = node->bodyScope; + changeToScope(node->getScopeId(), SCOPE_ANONYMOUS_BLOCK_BODY); assert(currentScope != nullptr); // Visit instructions in the block visit(node->body()); // Change scope back - currentScope = node->bodyScope->parent; + changeToParentScope(); assert(currentScope != nullptr); + diGenerator.popLexicalBlock(); return nullptr; } diff --git a/src/irgenerator/GenTopLevelDefinitions.cpp b/src/irgenerator/GenTopLevelDefinitions.cpp index 667004ef5..404b4f6cd 100644 --- a/src/irgenerator/GenTopLevelDefinitions.cpp +++ b/src/irgenerator/GenTopLevelDefinitions.cpp @@ -131,15 +131,9 @@ std::any IRGenerator::visitFctDef(const FctDefNode *node) { for (const Function *manifestation : node->fctManifestations) { assert(manifestation->entry != nullptr); - // Check if the manifestation is substantiated - if (!manifestation->isFullySubstantiated()) { - manIdx++; // Increment symbolTypeIndex - continue; - } - - // Do not generate this manifestation if it is private and used by nobody + // Check if the manifestation is substantiated or not public and not used by anybody const bool isPublic = manifestation->entry->getType().isPublic(); - if (!isPublic && !manifestation->used) { + if (!manifestation->isFullySubstantiated() || (!isPublic && !manifestation->used)) { manIdx++; // Increment symbolTypeIndex continue; } @@ -180,9 +174,9 @@ std::any IRGenerator::visitFctDef(const FctDefNode *node) { const SymbolType paramSymbolType = manifestation->getParamTypes().at(argIdx); // Pass the information if captures are taken for function/procedure types if (paramSymbolType.isOneOf({TY_FUNCTION, TY_PROCEDURE}) && paramSymbolType.hasLambdaCaptures()) { - SymbolType paramSymbolType = paramSymbol->getType(); - paramSymbolType.setHasLambdaCaptures(true); - paramSymbol->updateType(paramSymbolType, true); + SymbolType paramSymbolSymbolType = paramSymbol->getType(); + paramSymbolSymbolType.setHasLambdaCaptures(true); + paramSymbol->updateType(paramSymbolSymbolType, true); } // Retrieve type of param llvm::Type *paramType = paramSymbolType.toLLVMType(context, currentScope); @@ -297,18 +291,13 @@ std::any IRGenerator::visitProcDef(const ProcDefNode *node) { for (const Function *manifestation : node->procManifestations) { assert(manifestation->entry != nullptr); - // Check if the manifestation is substantiated - if (!manifestation->isFullySubstantiated()) { - manIdx++; // Increment symbolTypeIndex - continue; - } - - // Do not generate this manifestation if it is private and used by nobody + // Check if the manifestation is substantiated or not public and not used by anybody const bool isPublic = manifestation->entry->getType().isPublic(); - if (!isPublic && !manifestation->used) { + if (!manifestation->isFullySubstantiated() || (!isPublic && !manifestation->used)) { manIdx++; // Increment symbolTypeIndex continue; } + assert(manifestation->alreadyTypeChecked); // Change to struct scope if (manifestation->isMethod()) { @@ -347,9 +336,9 @@ std::any IRGenerator::visitProcDef(const ProcDefNode *node) { const SymbolType paramSymbolType = manifestation->getParamTypes().at(argIdx); // Pass the information if captures are taken for function/procedure types if (paramSymbolType.isOneOf({TY_FUNCTION, TY_PROCEDURE}) && paramSymbolType.hasLambdaCaptures()) { - SymbolType paramSymbolType = paramSymbol->getType(); - paramSymbolType.setHasLambdaCaptures(true); - paramSymbol->updateType(paramSymbolType, true); + SymbolType paramSymbolSymbolType = paramSymbol->getType(); + paramSymbolSymbolType.setHasLambdaCaptures(true); + paramSymbol->updateType(paramSymbolSymbolType, true); } // Retrieve type of param llvm::Type *paramType = paramSymbolType.toLLVMType(context, currentScope); diff --git a/src/irgenerator/GenValues.cpp b/src/irgenerator/GenValues.cpp index 225ad1d95..bfed54d3c 100644 --- a/src/irgenerator/GenValues.cpp +++ b/src/irgenerator/GenValues.cpp @@ -197,7 +197,6 @@ std::any IRGenerator::visitFctCall(const FctCallNode *node) { llvm::Value *result; if (data.isFctPtrCall()) { // Get entry to load the function pointer - SymbolTableEntry *firstFragEntry = currentScope->lookup(node->functionNameFragments.front()); assert(firstFragEntry != nullptr); SymbolType firstFragType = firstFragEntry->getType(); if (!fctPtr) @@ -382,8 +381,8 @@ std::any IRGenerator::visitLambdaFunc(const LambdaFuncNode *node) { std::vector paramTypes; // Change scope - Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); - assert(bodyScope != nullptr && bodyScope->type == SCOPE_LAMBDA_BODY); + Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); + changeToScope(bodyScope, SCOPE_LAMBDA_BODY); // If there are captures, we pass them in a struct as the first function argument const std::unordered_map &captures = bodyScope->symbolTable.captures; @@ -540,7 +539,7 @@ std::any IRGenerator::visitLambdaFunc(const LambdaFuncNode *node) { verifyFunction(lambda, node->codeLoc); // Change back to original scope - currentScope = bodyScope->parent; + changeToParentScope(); // Captures, create a struct { , } llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda); @@ -554,8 +553,8 @@ std::any IRGenerator::visitLambdaProc(const LambdaProcNode *node) { std::vector paramTypes; // Change scope - Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); - assert(bodyScope != nullptr && bodyScope->type == SCOPE_LAMBDA_BODY); + Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); + changeToScope(bodyScope, SCOPE_LAMBDA_BODY); // If there are captures, we pass them in a struct as the first function argument const std::unordered_map &captures = bodyScope->symbolTable.captures; @@ -699,7 +698,7 @@ std::any IRGenerator::visitLambdaProc(const LambdaProcNode *node) { verifyFunction(lambda, node->codeLoc); // Change back to original scope - currentScope = bodyScope->parent; + changeToParentScope(); // Create a struct { , } llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda); @@ -713,8 +712,8 @@ std::any IRGenerator::visitLambdaExpr(const LambdaExprNode *node) { std::vector paramTypes; // Change scope - Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); - assert(bodyScope != nullptr && bodyScope->type == SCOPE_LAMBDA_BODY); + Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); + changeToScope(bodyScope, SCOPE_LAMBDA_BODY); // If there are captures, we pass them in a struct as the first function argument const std::unordered_map &captures = bodyScope->symbolTable.captures; @@ -859,7 +858,7 @@ std::any IRGenerator::visitLambdaExpr(const LambdaExprNode *node) { verifyFunction(lambda, node->codeLoc); // Change back to original scope - currentScope = bodyScope->parent; + changeToParentScope(); // Create a struct { , } llvm::Value *result = buildFatFctPtr(bodyScope, capturesStructType, lambda); diff --git a/src/irgenerator/IRGenerator.cpp b/src/irgenerator/IRGenerator.cpp index 62c80bada..3f527fcd3 100644 --- a/src/irgenerator/IRGenerator.cpp +++ b/src/irgenerator/IRGenerator.cpp @@ -601,6 +601,25 @@ void IRGenerator::changeToScope(Scope *scope, const ScopeType scopeType) { currentScope = scope; } +/** + * Change to the scope with the given name. + * + * @param scopeName Name of the scope to change to + * @param scopeType Expected type of the given scope + */ +void IRGenerator::changeToScope(const std::string &scopeName, const ScopeType scopeType) { + assert(!scopeName.empty()); + changeToScope(currentScope->getChildScope(scopeName), scopeType); +} + +/** + * Change to the parent scope of the current. + */ +void IRGenerator::changeToParentScope() { + assert(currentScope->parent != nullptr); + currentScope = currentScope->parent; +} + /** * Returns the operator function list for the current manifestation and the given node * diff --git a/src/irgenerator/IRGenerator.h b/src/irgenerator/IRGenerator.h index c058dff8a..a1049804f 100644 --- a/src/irgenerator/IRGenerator.h +++ b/src/irgenerator/IRGenerator.h @@ -135,6 +135,8 @@ class IRGenerator : private CompilerPass, public ParallelizableASTVisitor { void materializeConstant(LLVMExprResult &exprResult); llvm::Value *doImplicitCast(llvm::Value *src, SymbolType dstSTy, SymbolType srcSTy); void changeToScope(Scope *scope, ScopeType scopeType); + void changeToScope(const std::string &scopeName, ScopeType scopeType); + void changeToParentScope(); const std::vector &getOpFctPointers(const ASTNode *node) const; llvm::Value *buildFatFctPtr(Scope *bodyScope, llvm::StructType *capturesStructType, llvm::Value *lambda); diff --git a/src/irgenerator/StdFunctionManager.cpp b/src/irgenerator/StdFunctionManager.cpp index 9f9ff0796..f05cf12c0 100644 --- a/src/irgenerator/StdFunctionManager.cpp +++ b/src/irgenerator/StdFunctionManager.cpp @@ -30,8 +30,7 @@ llvm::Function *StdFunctionManager::getExitFct() const { llvm::Function *StdFunctionManager::getMemcpyIntrinsic() const { llvm::Type *ptrTy = builder.getPtrTy(); - llvm::ArrayRef paramList = {ptrTy, ptrTy, builder.getInt64Ty()}; - llvm::Function *memcpyFct = llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::memcpy, paramList); + llvm::Function *memcpyFct = getProcedure("llvm.memcpy.p0.p0.i64", {ptrTy, ptrTy, builder.getInt64Ty(), builder.getInt1Ty()}); // Set attributes memcpyFct->addFnAttr(llvm::Attribute::NoCallback); memcpyFct->addFnAttr(llvm::Attribute::NoFree); @@ -42,8 +41,7 @@ llvm::Function *StdFunctionManager::getMemcpyIntrinsic() const { llvm::Function *StdFunctionManager::getMemcmpIntrinsic() const { llvm::Type *ptrTy = builder.getPtrTy(); - llvm::ArrayRef paramList = {ptrTy, ptrTy, builder.getInt64Ty()}; - llvm::Function *memcmpFct = getFunction("memcmp", builder.getInt32Ty(), paramList); + llvm::Function *memcmpFct = getFunction("memcmp", builder.getInt32Ty(), {ptrTy, ptrTy, builder.getInt64Ty()}); // Set attributes memcmpFct->addFnAttr(llvm::Attribute::NoUnwind); return memcmpFct; diff --git a/src/model/Function.cpp b/src/model/Function.cpp index c7dc8a1ca..927104b94 100644 --- a/src/model/Function.cpp +++ b/src/model/Function.cpp @@ -105,7 +105,7 @@ std::string Function::getSignature(const std::string &name, const SymbolType &th * @return Substantiated params or not */ bool Function::hasSubstantiatedParams() const { - return std::none_of(paramList.begin(), paramList.end(), [](auto t) { return t.isOptional; }); + return std::ranges::none_of(paramList, [](const Param ¶m) { return param.isOptional; }); } /** diff --git a/src/model/Interface.cpp b/src/model/Interface.cpp index a4544916f..130ee1be8 100644 --- a/src/model/Interface.cpp +++ b/src/model/Interface.cpp @@ -49,8 +49,7 @@ std::string Interface::getSignature(const std::string &name, const std::vector &op, size_t opIdx) { Scope *calleeParentScope = nullptr; Function *callee = nullptr; - for (auto &[_, sourceFile] : typeChecker->resourceManager.sourceFiles) { + for (const auto &[_, sourceFile] : typeChecker->resourceManager.sourceFiles) { // Check if there is a registered operator function if (!sourceFile->getNameRegistryEntry(fctName)) continue; @@ -592,7 +592,8 @@ ExprResult OpRuleManager::isOperatorOverloadingFctAvailable(ASTNode *node, const opFctPointers.at(opIdx) = callee; // Check if we need to request a re-visit, because the function body was not type-checked yet - if (!callee->alreadyTypeChecked) + const bool isCallToImportedSourceFile = callee->entry->scope->isImportedBy(typeChecker->rootScope); + if (!callee->alreadyTypeChecked && !isCallToImportedSourceFile) typeChecker->reVisitRequested = true; // Check if the called function has sufficient visibility diff --git a/src/typechecker/TypeChecker.cpp b/src/typechecker/TypeChecker.cpp index fb79ab8a9..65bd13117 100644 --- a/src/typechecker/TypeChecker.cpp +++ b/src/typechecker/TypeChecker.cpp @@ -93,20 +93,20 @@ std::any TypeChecker::visitExtDecl(ExtDeclNode *node) { std::any TypeChecker::visitUnsafeBlockDef(UnsafeBlockDefNode *node) { // Change to unsafe block body scope - changeToScope(node->bodyScope, SCOPE_UNSAFE_BODY); + changeToScope(node->getScopeId(), SCOPE_UNSAFE_BODY); // Visit body visit(node->body()); // Leave unsafe block body scope - currentScope = node->bodyScope->parent; + changeToParentScope(); return nullptr; } std::any TypeChecker::visitForLoop(ForLoopNode *node) { // Change to for body scope - changeToScope(node->bodyScope, SCOPE_FOR_BODY); + changeToScope(node->getScopeId(), SCOPE_FOR_BODY); // Visit loop variable declaration visit(node->initDecl()); @@ -125,14 +125,14 @@ std::any TypeChecker::visitForLoop(ForLoopNode *node) { visit(node->body()); // Leave body scope - currentScope = node->bodyScope->parent; + changeToParentScope(); return nullptr; } std::any TypeChecker::visitForeachLoop(ForeachLoopNode *node) { // Change to foreach body scope - changeToScope(node->bodyScope, SCOPE_FOREACH_BODY); + changeToScope(node->getScopeId(), SCOPE_FOREACH_BODY); // Check iterator type SymbolType iteratorType = std::any_cast(visit(node->iteratorAssign())).type; @@ -197,14 +197,14 @@ std::any TypeChecker::visitForeachLoop(ForeachLoopNode *node) { visit(node->body()); // Leave foreach body scope - currentScope = node->bodyScope->parent; + changeToParentScope(); return nullptr; } std::any TypeChecker::visitWhileLoop(WhileLoopNode *node) { // Change to while body scope - changeToScope(node->bodyScope, SCOPE_WHILE_BODY); + changeToScope(node->getScopeId(), SCOPE_WHILE_BODY); // Visit condition SymbolType conditionType = std::any_cast(visit(node->condition())).type; @@ -217,14 +217,14 @@ std::any TypeChecker::visitWhileLoop(WhileLoopNode *node) { visit(node->body()); // Leave while body scope - currentScope = node->bodyScope->parent; + changeToParentScope(); return nullptr; } std::any TypeChecker::visitDoWhileLoop(DoWhileLoopNode *node) { // Change to while body scope - changeToScope(node->bodyScope, SCOPE_WHILE_BODY); + changeToScope(node->getScopeId(), SCOPE_WHILE_BODY); // Visit body visit(node->body()); @@ -237,14 +237,14 @@ std::any TypeChecker::visitDoWhileLoop(DoWhileLoopNode *node) { SOFT_ERROR_ER(node->condition(), CONDITION_MUST_BE_BOOL, "Do-While loop condition must be of type bool") // Leave while body scope - currentScope = node->bodyScope->parent; + changeToParentScope(); return nullptr; } std::any TypeChecker::visitIfStmt(IfStmtNode *node) { // Change to then body scope - changeToScope(node->thenBodyScope, SCOPE_IF_ELSE_BODY); + changeToScope(node->getScopeId(), SCOPE_IF_ELSE_BODY); // Visit condition AssignExprNode *condition = node->condition(); @@ -263,7 +263,7 @@ std::any TypeChecker::visitIfStmt(IfStmtNode *node) { visit(node->thenBody()); // Leave then body scope - currentScope = node->thenBodyScope->parent; + changeToParentScope(); // Visit else statement if existing if (node->elseStmt()) @@ -280,29 +280,26 @@ std::any TypeChecker::visitElseStmt(ElseStmtNode *node) { } // Change to else body scope - changeToScope(node->elseBodyScope, SCOPE_IF_ELSE_BODY); + changeToScope(node->getScopeId(), SCOPE_IF_ELSE_BODY); // Visit body visit(node->body()); // Leave else body scope - currentScope = node->elseBodyScope->parent; + changeToParentScope(); return nullptr; } std::any TypeChecker::visitAnonymousBlockStmt(AnonymousBlockStmtNode *node) { // Change to anonymous scope body scope - node->bodyScope->parent = currentScope; // Needed for nested scopes in generic functions - node->bodyScope->symbolTable.parent = ¤tScope->symbolTable; // Needed for nested scopes in generic functions - currentScope = node->bodyScope; - assert(currentScope->type == SCOPE_ANONYMOUS_BLOCK_BODY); + changeToScope(node->getScopeId(), SCOPE_ANONYMOUS_BLOCK_BODY); // Visit body visit(node->body()); // Leave anonymous scope body scope - currentScope = node->bodyScope->parent; + changeToParentScope(); return nullptr; } @@ -1326,7 +1323,7 @@ std::any TypeChecker::visitAtomicExpr(AtomicExprNode *node) { SOFT_ERROR_ER(node, INVALID_SYMBOL_ACCESS, "A symbol of type " + varType.getName() + " cannot be accessed here") // Check if is an imported variable - if (accessScope->isImportedBy(currentScope)) { + if (accessScope->isImportedBy(rootScope)) { // Check if the entry is public if (varEntry->scope->type != SCOPE_ENUM && !varEntry->getType().isPublic()) SOFT_ERROR_ER(node, INSUFFICIENT_VISIBILITY, "Cannot access '" + varEntry->name + "' due to its private visibility") @@ -1515,7 +1512,8 @@ std::any TypeChecker::visitFctCall(FctCallNode *node) { } // Check if we need to request a re-visit, because the function body was not type-checked yet - if (!data.callee->alreadyTypeChecked && !data.callee->external) + const bool isCallToImportedSourceFile = data.callee->entry->scope->isImportedBy(rootScope); + if (!data.callee->alreadyTypeChecked && !isCallToImportedSourceFile) reVisitRequested = true; // Get function entry from function object @@ -1892,14 +1890,14 @@ std::any TypeChecker::visitLambdaFunc(LambdaFuncNode *node) { SOFT_ERROR_ER(node, MISSING_RETURN_STMT, "Not all control paths of this lambda function have a return statement") // Change to function scope - Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); - assert(bodyScope != nullptr && bodyScope->type == SCOPE_LAMBDA_BODY); + Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); + changeToScope(bodyScope, SCOPE_LAMBDA_BODY); // Visit return type auto returnType = std::any_cast(visit(node->returnType())); HANDLE_UNRESOLVED_TYPE_ST(returnType) if (returnType.is(TY_DYN)) { - currentScope = bodyScope->parent; + changeToParentScope(); SOFT_ERROR_ER(node, UNEXPECTED_DYN_TYPE, "Dyn return types are not allowed") } @@ -1928,7 +1926,7 @@ std::any TypeChecker::visitLambdaFunc(LambdaFuncNode *node) { visit(node->body()); // Leave function body scope - currentScope = bodyScope->parent; + changeToParentScope(); // Prepare type of function SymbolType functionType(TY_FUNCTION); @@ -1947,8 +1945,8 @@ std::any TypeChecker::visitLambdaFunc(LambdaFuncNode *node) { std::any TypeChecker::visitLambdaProc(LambdaProcNode *node) { // Change to function scope - Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); - assert(bodyScope != nullptr && bodyScope->type == SCOPE_LAMBDA_BODY); + Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); + changeToScope(bodyScope, SCOPE_LAMBDA_BODY); // Visit parameters std::vector paramTypes; @@ -1969,7 +1967,7 @@ std::any TypeChecker::visitLambdaProc(LambdaProcNode *node) { visit(node->body()); // Leave function body scope - currentScope = bodyScope->parent; + changeToParentScope(); // Prepare type of function SymbolType functionType(TY_PROCEDURE); @@ -1988,8 +1986,8 @@ std::any TypeChecker::visitLambdaProc(LambdaProcNode *node) { std::any TypeChecker::visitLambdaExpr(LambdaExprNode *node) { // Change to function scope - Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); - assert(bodyScope != nullptr && bodyScope->type == SCOPE_LAMBDA_BODY); + Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); + changeToScope(bodyScope, SCOPE_LAMBDA_BODY); // Visit parameters std::vector paramTypes; @@ -2010,12 +2008,12 @@ std::any TypeChecker::visitLambdaExpr(LambdaExprNode *node) { SymbolType returnType = std::any_cast(visit(node->lambdaExpr())).type; HANDLE_UNRESOLVED_TYPE_ER(returnType) if (returnType.is(TY_DYN)) { - currentScope = bodyScope->parent; + changeToParentScope(); SOFT_ERROR_ER(node, UNEXPECTED_DYN_TYPE, "Dyn return types are not allowed") } // Leave function body scope - currentScope = bodyScope->parent; + changeToParentScope(); // Prepare type of function const bool isFunction = !returnType.is(TY_DYN); @@ -2363,6 +2361,25 @@ void TypeChecker::changeToScope(Scope *scope, const ScopeType scopeType) { currentScope = scope; } +/** + * Change to the scope with the given name. + * + * @param scopeName Name of the scope to change to + * @param scopeType Expected type of the given scope + */ +void TypeChecker::changeToScope(const std::string &scopeName, const ScopeType scopeType) { + assert(!scopeName.empty()); + changeToScope(currentScope->getChildScope(scopeName), scopeType); +} + +/** + * Change to the parent scope of the current. + */ +void TypeChecker::changeToParentScope() { + assert(currentScope->parent != nullptr); + currentScope = currentScope->parent; +} + /** * Auto-dereference the given symbol type. * This process is NOT equivalent with getBaseType() because getBaseType() also removes e.g. array wrappers diff --git a/src/typechecker/TypeChecker.h b/src/typechecker/TypeChecker.h index 0ed0d3b77..dbe2fd64c 100644 --- a/src/typechecker/TypeChecker.h +++ b/src/typechecker/TypeChecker.h @@ -133,6 +133,8 @@ class TypeChecker : private CompilerPass, public ASTVisitor { [[nodiscard]] SymbolType mapLocalTypeToImportedScopeType(const Scope *targetScope, const SymbolType &symbolType) const; [[nodiscard]] SymbolType mapImportedScopeTypeToLocalType(const Scope *sourceScope, const SymbolType &symbolType) const; void changeToScope(Scope *scope, ScopeType scopeType); + void changeToScope(const std::string &scopeName, ScopeType scopeType); + void changeToParentScope(); static void autoDeReference(SymbolType &symbolType); void doScopeCleanup(StmtLstNode *node); void callStructDtor(SymbolTableEntry *entry, StmtLstNode *node); diff --git a/src/typechecker/TypeCheckerCheck.cpp b/src/typechecker/TypeCheckerCheck.cpp index 31e9d0194..dc02769ad 100644 --- a/src/typechecker/TypeCheckerCheck.cpp +++ b/src/typechecker/TypeCheckerCheck.cpp @@ -17,16 +17,13 @@ std::any TypeChecker::visitMainFctDefCheck(MainFctDefNode *node) { // Change to function body scope currentScope = node->fctScope; - // Visit statements in new scope visit(node->body()); - // Leave main function body scope currentScope = rootScope; // Set to type-checked typeCheckedMainFct = true; - return nullptr; } @@ -44,15 +41,12 @@ std::any TypeChecker::visitFctDefCheck(FctDefNode *node) { // Change scope to concrete struct specialization scope if (node->isMethod) { - const std::string structSignature = - Struct::getSignature(node->fctName->structName, manifestation->thisType.getTemplateTypes()); - currentScope = rootScope->getChildScope(STRUCT_SCOPE_PREFIX + structSignature); - assert(currentScope != nullptr && currentScope->type == SCOPE_STRUCT); + const auto structSignature = Struct::getSignature(node->fctName->structName, manifestation->thisType.getTemplateTypes()); + changeToScope(STRUCT_SCOPE_PREFIX + structSignature, SCOPE_STRUCT); } // Change to function scope - currentScope = currentScope->getChildScope(manifestation->getSignature(false)); - assert(currentScope != nullptr && currentScope->type == SCOPE_FUNC_PROC_BODY); + changeToScope(manifestation->getSignature(false), SCOPE_FUNC_PROC_BODY); // Mount type mapping for this manifestation assert(typeMapping.empty()); @@ -103,15 +97,12 @@ std::any TypeChecker::visitProcDefCheck(ProcDefNode *node) { // Change scope to concrete struct specialization scope if (node->isMethod) { - const std::string structSignature = - Struct::getSignature(node->procName->structName, manifestation->thisType.getTemplateTypes()); - currentScope = rootScope->getChildScope(STRUCT_SCOPE_PREFIX + structSignature); - assert(currentScope != nullptr && currentScope->type == SCOPE_STRUCT); + const auto structSignature = Struct::getSignature(node->procName->structName, manifestation->thisType.getTemplateTypes()); + changeToScope(STRUCT_SCOPE_PREFIX + structSignature, SCOPE_STRUCT); } // Change to procedure scope - currentScope = currentScope->getChildScope(manifestation->getSignature(false)); - assert(currentScope != nullptr && currentScope->type == SCOPE_FUNC_PROC_BODY); + changeToScope(manifestation->getSignature(false), SCOPE_FUNC_PROC_BODY); // Mount type mapping for this manifestation assert(typeMapping.empty()); diff --git a/std/io/cli-option.spice b/std/io/cli-option.spice index 4f67cd46d..04d7c45af 100644 --- a/std/io/cli-option.spice +++ b/std/io/cli-option.spice @@ -36,18 +36,12 @@ public f CliOption.getDescription() { return this.description; } -public p CliOption.setTargetValue(T& value) { +public p CliOption.setTargetValue(T value) { if this.mode == CliOptionMode::SET_VALUE { this.targetVariable = value; } } -public p CliOption.setToTrue() { - if this.mode == CliOptionMode::SET_VALUE { - this.targetVariable = true; - } -} - public p CliOption.callCallback(T& value) { if this.mode == CliOptionMode::CALL_CALLBACK { p(T&) cb = this.callback; diff --git a/std/io/cli-parser.spice b/std/io/cli-parser.spice index dfddb8bce..491851cb5 100644 --- a/std/io/cli-parser.spice +++ b/std/io/cli-parser.spice @@ -4,20 +4,15 @@ import "std/io/cli-subcommand"; type T bool|string|int|long|short; public type CliParser struct { - string versionString = "v0.0.1" CliSubcommand rootSubcommand } public p CliParser.ctor(string appName, string appDescription = "") { - this.rootSubcommand = CliSubcommand(nil, appName, appDescription); - // Add version flag - this.rootSubcommand.addFlag("--version", p(bool& _) { - printf("%s\n", this.versionString); - }, "Prints the version of the application"); + this.rootSubcommand = CliSubcommand(nil, "v0.1.0", appName, appDescription); } public p CliParser.setVersion(string versionString) { - this.versionString = versionString; + this.rootSubcommand.setVersion(versionString); } public p CliParser.setFooter(string footer) { diff --git a/std/io/cli-subcommand.spice b/std/io/cli-subcommand.spice index a6be7cd5c..300ed3ed5 100644 --- a/std/io/cli-subcommand.spice +++ b/std/io/cli-subcommand.spice @@ -1,35 +1,34 @@ import "std/io/cli-option"; import "std/text/print"; import "std/runtime/iterator_rt"; +import "std/type/string"; // Generic types -type T bool|string|int|long|short; +type T bool|string|int; public type CliSubcommand struct { string name string description CliSubcommand* parent + string versionString string footer = "" p() callback Vector subcommands Vector> boolOptions Vector> stringOptions Vector> intOptions - Vector> longOptions - Vector> shortOptions bool allowUnknownOptions = false } -public p CliSubcommand.ctor(CliSubcommand* parent, string name, string description = "") { +public p CliSubcommand.ctor(CliSubcommand* parent, string versionString, string name, string description = "") { this.name = name; this.description = description; + this.versionString = versionString; this.parent = parent; this.subcommands = Vector(); this.boolOptions = Vector>(); this.stringOptions = Vector>(); this.intOptions = Vector>(); - this.longOptions = Vector>(); - this.shortOptions = Vector>(); } public f CliSubcommand.parse(unsigned int argc, string[] argv, int layer = 1) { @@ -45,7 +44,7 @@ public f CliSubcommand.parse(unsigned int argc, string[] argv, int layer = } for unsigned int argNo = layer; argNo < argc; argNo++ { - const string arg = argv[argNo]; + string arg = argv[argNo]; // Check for subcommands foreach const CliSubcommand& subcommand : iterate(this.subcommands) { @@ -59,12 +58,40 @@ public f CliSubcommand.parse(unsigned int argc, string[] argv, int layer = this.printHelp(argv, layer); return EXIT_CODE_SUCCESS; } + if (arg == "-v" || arg == "--version") { // Version + printf("%s\n", this.versionString); + return EXIT_CODE_SUCCESS; + } // Check for flags - foreach const CliOption& flag : iterate(this.boolOptions) { - if arg == flag.getName() { - flag.setToTrue(); - flag.callCallback(true); + foreach const CliOption& boolOption : iterate(this.boolOptions) { + if arg == boolOption.getName() { + bool value = true; + boolOption.setTargetValue(value); + boolOption.callCallback(value); + continue 2; // Continue with next argument + } + } + + // Check for other options + foreach const CliOption& stringOption : iterate(this.stringOptions) { + if arg == stringOption.getName() { + // get the argument value + arg = argv[++argNo]; + + stringOption.setTargetValue(arg); + stringOption.callCallback(arg); + continue 2; // Continue with next argument + } + } + foreach const CliOption& intOption : iterate(this.intOptions) { + if arg == intOption.getName() { + // get the argument value + arg = argv[++argNo]; + int parsedArg = toInt(arg); + + intOption.setTargetValue(parsedArg); + intOption.callCallback(parsedArg); continue 2; // Continue with next argument } } @@ -79,11 +106,6 @@ public f CliSubcommand.parse(unsigned int argc, string[] argv, int layer = return EXIT_CODE_SUCCESS; // Parsing was successful, return success exit code } -public f CliSubcommand.addSubcommand(string name, string description) { - this.subcommands.pushBack(CliSubcommand(this, name, description)); - return this.subcommands.back(); -} - public f CliSubcommand.getName() { return this.name; } @@ -92,6 +114,10 @@ public f CliSubcommand.getDescription() { return this.description; } +public p CliSubcommand.setVersion(string versionString) { + this.versionString = versionString; +} + public p CliSubcommand.setFooter(string footer) { this.footer = footer; } @@ -107,8 +133,25 @@ public p CliSubcommand.allowUnknownOptions() { } } -public p CliSubcommand.addOption(string name, T& targetVariable, string description) { - this.options.pushBack(CliOption(name, targetVariable, description)); +public f CliSubcommand.addSubcommand(string name, string description) { + this.subcommands.pushBack(CliSubcommand(this, this.versionString, name, description)); + return this.subcommands.back(); +} + +public p CliSubcommand.addOption(string name, string& targetVariable, string description) { + this.stringOptions.pushBack(CliOption(name, targetVariable, description)); +} + +public p CliSubcommand.addOption(string name, p(string&) callback, string description) { + this.stringOptions.pushBack(CliOption(name, callback, description)); +} + +public p CliSubcommand.addOption(string name, int& targetVariable, string description) { + this.intOptions.pushBack(CliOption(name, targetVariable, description)); +} + +public p CliSubcommand.addOption(string name, p(int&) callback, string description) { + this.intOptions.pushBack(CliOption(name, callback, description)); } public p CliSubcommand.addFlag(string name, bool& targetVariable, string description) { @@ -119,33 +162,51 @@ public p CliSubcommand.addFlag(string name, p(bool&) callback, string descriptio this.boolOptions.pushBack(CliOption(name, callback, description)); } +p printHelpItem(string name, string description) { + printFixedWidth(name, 25, true); + printFixedWidth(description, 85, true); + lineBreak(); +} + +p printHelpItemWithValue(string name, string description) { + String str = String(name) + " "; + printHelpItem(str.getRaw(), description); +} + p CliSubcommand.printHelp(string[] argv, int layer) { // Build subcommand string String subcommand = String(argv[0]); for int i = 1; i < layer; i++ { subcommand += " " + argv[i]; } + // Print usage printf("%s\n\nUsage: %s [options]\n", this.description, subcommand); // Print subcommands if !this.subcommands.isEmpty() { printf("\nSubcommands:\n"); foreach const CliSubcommand& subCommand : iterate(this.subcommands) { - printFixedWidth(subCommand.getName(), 25, true); - printFixedWidth(subCommand.getDescription(), 85, true); - lineBreak(); + printHelpItem(subCommand.getName(), subCommand.getDescription()); } } + + // Print options + printf("\nOptions:\n"); + foreach const CliOption& option : iterate(this.stringOptions) { + printHelpItemWithValue(option.getName(), option.getDescription()); + } + foreach const CliOption& option : iterate(this.intOptions) { + printHelpItemWithValue(option.getName(), option.getDescription()); + } + // Print flags printf("\nFlags:\n"); foreach const CliOption& flag : iterate(this.boolOptions) { - printFixedWidth(flag.getName(), 25, true); - printFixedWidth(flag.getDescription(), 85, true); - lineBreak(); + printHelpItem(flag.getName(), flag.getDescription()); } - printFixedWidth("--help,-h", 25, true); - printFixedWidth("Print this help message", 85, true); - printf("\n"); + printHelpItem("--help,-h", "Print this help message"); + printHelpItem("--version,-v", "Print the version of the application"); + // Print footer if this.footer != "" { printf("\n%s\n", this.footer); diff --git a/std/os/cpu.spice b/std/os/cpu.spice index 1cb76fb93..27271364c 100644 --- a/std/os/cpu.spice +++ b/std/os/cpu.spice @@ -4,6 +4,6 @@ ext f sched_yield(); * Causes the calling thread to relinquish the CPU. * The thread is moved to the end of the scheduling queue and the next thread gets to run. */ -public p yield() { +public inline p yield() { sched_yield(); } \ No newline at end of file diff --git a/std/os/thread-pool.spice b/std/os/thread-pool.spice index da9e6a5db..54d2922be 100644 --- a/std/os/thread-pool.spice +++ b/std/os/thread-pool.spice @@ -10,10 +10,11 @@ import "std/runtime/iterator_rt"; public type ThreadPool struct { Vector workerThreads Queue queuedJobs - unsigned short runningJobs + unsigned short runningJobs = 0s unsigned short workerThreadCount bool stopRequested = false - bool paused = false + bool stopOnEmptyQueueRequested = false + bool pauseRequested = false } /** @@ -25,14 +26,13 @@ public p ThreadPool.ctor(unsigned short workerThreadCount = 0s) { this.workerThreads = Vector(); this.queuedJobs = Queue(); this.workerThreadCount = workerThreadCount > 0s ? workerThreadCount : (unsigned short) getCPUCoreCount(); - this.runningJobs = 0s; } /** * Destroy the thread pool. */ public p ThreadPool.dtor() { - this.join(); + this.stop(); } /** @@ -41,7 +41,11 @@ public p ThreadPool.dtor() { public p ThreadPool.start() { p() workerRoutine = p() { do { - if !this.paused & !this.queuedJobs.isEmpty() { + bool hasQueuedJobs = !this.queuedJobs.isEmpty(); + if (this.stopOnEmptyQueueRequested & !hasQueuedJobs) { + break; + } + if !this.pauseRequested & hasQueuedJobs { p() task = this.queuedJobs.pop(); printf("Worker %d took job\n", getThreadId()); this.runningJobs++; @@ -50,6 +54,7 @@ public p ThreadPool.start() { } yield(); } while (!this.stopRequested); + printf("Worker %d terminated\n", getThreadId()); }; // Create worker threads @@ -61,15 +66,27 @@ public p ThreadPool.start() { } /** - * Stop the thread pool. This will wait for all running jobs to finish. + * Finish the running jobs and stop the thread pool. */ -public p ThreadPool.join() { +public p ThreadPool.stop() { // Stop worker threads this.stopRequested = true; // Wait for all worker threads to terminate - foreach (const Thread& workerThread : iterate(this.workerThreads)) { - workerThread.join(); - } + this.joinWorkerThreads(); + // Reset the stop flag + this.stopRequested = false; +} + +/** + * Wait for all queued jobs to finish and stop the thread pool. + */ +public p ThreadPool.join() { + // Stop worker threads + this.stopOnEmptyQueueRequested = true; + // Wait for all worker threads to terminate + this.joinWorkerThreads(); + // Reset the stop flag + this.stopOnEmptyQueueRequested = false; } /** @@ -85,21 +102,21 @@ public p ThreadPool.enqueue(p() job) { * Pause the thread pool. The worker threads will finish their current job and then wait for the pool to be resumed. */ public p ThreadPool.pause() { - this.paused = true; + this.pauseRequested = true; } /** * Resume the thread pool. */ public p ThreadPool.resume() { - this.paused = false; + this.pauseRequested = false; } /** * Check if the thread pool is paused. */ public f ThreadPool.isPaused() { - return this.paused; + return this.pauseRequested; } /** @@ -121,4 +138,10 @@ public f ThreadPool.getQueuedJobCount() { */ public f ThreadPool.getWorkerThreadCount() { return this.workerThreadCount; +} + +p ThreadPool.joinWorkerThreads() { + foreach (const Thread& workerThread : iterate(this.workerThreads)) { + workerThread.join(); + } } \ No newline at end of file diff --git a/std/os/thread.spice b/std/os/thread.spice index 516a71036..5042a69fa 100644 --- a/std/os/thread.spice +++ b/std/os/thread.spice @@ -9,7 +9,9 @@ type Pthread_attr_t struct { } // Link external functions -ext f pthread_create(Pthread_t* /*thread*/, Pthread_attr_t* /*attr*/, p() /*start_routine*/, byte* /*arg*/); +// Here we use a little trick to pass the arguments: +// Spice represents the procedure type as { ptr, ptr } struct, but the pthread_create accepts ptr, ptr. +ext f pthread_create(Pthread_t* /*thread*/, Pthread_attr_t* /*attr*/, p() /*start_routine + arg*/); ext f pthread_join(Pthread_t /*thread*/, byte** /*retval*/); ext f pthread_self(); @@ -29,7 +31,7 @@ public p Thread.ctor(p() threadRoutine) { * Start the thread */ public p Thread.run() { - pthread_create(&this.threadId, nil, this.threadRoutine, nil); + pthread_create(&this.threadId, nil, this.threadRoutine); } /** diff --git a/std/time/delay.spice b/std/time/delay.spice index f0fc02067..d03cdb9c7 100644 --- a/std/time/delay.spice +++ b/std/time/delay.spice @@ -3,6 +3,6 @@ ext f usleep(int); /** * Suspends the execution for the given number of milliseconds */ -public p delay(int millis) { +public inline p delay(int millis) { usleep(millis * 1000); } \ No newline at end of file diff --git a/std/type/string.spice b/std/type/string.spice index 0cacc4a97..73079f97a 100644 --- a/std/type/string.spice +++ b/std/type/string.spice @@ -1,3 +1,6 @@ +// External declarations +ext f strtol(string, char**, int); + // Converts a string to a double public f toDouble(string input) { // ToDo: implement @@ -5,61 +8,31 @@ public f toDouble(string input) { } // Converts a string to an int -public f toInt(string input) { - if input == "" { - return 0; - } - - bool neg = false; - - - // ToDo: implement the rest - return 0; +public f toInt(string input, int base = 10) { + return (int) toLong(input, base); } // Converts a string to a short -public f toShort(string input) { - if input == "" { - return (short) 0; - } - - bool neg = false; - - - // ToDo: implement the rest - return (short) 0; +public f toShort(string input, int base = 10) { + return (short) toLong(input, base); } // Converts a string to a long -public f toLong(string input) { - if input == "" { - return (long) 0; - } - - bool neg = false; - - - // ToDo: implement the rest - return (long) 0; +public f toLong(string input, int base = 10) { + char* endPtr = nil; + result = strtol(input, &endPtr, base); + // Check if the conversion was successful + if (((string) endPtr) == input || *endPtr != '\0') { return 0l; } } // Converts a string to a byte -public f toByte(string input) { - if input == "" { - return (byte) 0; - } - - bool neg = false; - - - // ToDo: implement the rest - return (byte) 0; +public f toByte(string input, int base = 10) { + return (byte) ((int) toLong(input, base)); } // Converts a string to a char public f toChar(string input) { - // ToDo: Implement - return '0'; + return input[0]; } // Converts a string to a bool diff --git a/test/test-files/irgenerator/debug-info/success-dbg-info-simple/ir-code.ll b/test/test-files/irgenerator/debug-info/success-dbg-info-simple/ir-code.ll index e58a38a64..d33b40634 100644 --- a/test/test-files/irgenerator/debug-info/success-dbg-info-simple/ir-code.ll +++ b/test/test-files/irgenerator/debug-info/success-dbg-info-simple/ir-code.ll @@ -80,7 +80,7 @@ attributes #2 = { nofree nounwind } !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "anon.string.0", linkageName: "anon.string.0", scope: !2, file: !7, line: 8, type: !15, isLocal: true, isDefinition: true) -!2 = distinct !DICompileUnit(language: 32768, file: !3, producer: "spice version dev (https://github.com/spicelang/spice)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!2 = distinct !DICompileUnit(language: DW_LANG_C17, file: !3, producer: "spice version dev (https://github.com/spicelang/spice)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) !3 = !DIFile(filename: "C:\\Users\\I516467\\Documents\\JustForFunGitHubClones\\spice\\cmake-build-debug\\test\\test-files\\irgenerator\\debug-info\\success-dbg-info-simple\\source.spice", directory: ".\\test-files\\irgenerator\\debug-info\\success-dbg-info-simple") !4 = !{!0, !5, !9, !12} !5 = !DIGlobalVariableExpression(var: !6, expr: !DIExpression()) diff --git a/test/test-files/irgenerator/debug-info/success-dgb-info-complex/ir-code.ll b/test/test-files/irgenerator/debug-info/success-dgb-info-complex/ir-code.ll index 0bd795961..4766f79cb 100644 --- a/test/test-files/irgenerator/debug-info/success-dgb-info-complex/ir-code.ll +++ b/test/test-files/irgenerator/debug-info/success-dgb-info-complex/ir-code.ll @@ -263,187 +263,187 @@ assert.then.L42: ; preds = %assert.exit.L40 assert.exit.L42: ; preds = %assert.exit.L40 %72 = call %struct.VectorIterator @"_Z7iterateR35std/iterator/vector-iterator.VectorIiE"(ptr %vi), !dbg !95 - call void @llvm.dbg.declare(metadata ptr %item, metadata !96, metadata !DIExpression()), !dbg !97 + call void @llvm.dbg.declare(metadata ptr %item, metadata !97, metadata !DIExpression()), !dbg !98 store %struct.VectorIterator %72, ptr %10, align 8, !dbg !95 - br label %foreach.head.L45, !dbg !97 + br label %foreach.head.L45, !dbg !98 foreach.head.L45: ; preds = %foreach.tail.L45, %assert.exit.L42 - %73 = call i1 @_ZN14VectorIteratorIiE7isValidEv(ptr %10), !dbg !97 - br i1 %73, label %foreach.body.L45, label %foreach.exit.L45, !dbg !97 + %73 = call i1 @_ZN14VectorIteratorIiE7isValidEv(ptr %10), !dbg !98 + br i1 %73, label %foreach.body.L45, label %foreach.exit.L45, !dbg !98 foreach.body.L45: ; preds = %foreach.head.L45 - %74 = call ptr @_ZN14VectorIteratorIiE3getEv(ptr %10), !dbg !97 - %75 = load i32, ptr %74, align 4, !dbg !97 - store i32 %75, ptr %item, align 4, !dbg !97 - %76 = load i32, ptr %item, align 4, !dbg !98 - %77 = add i32 %76, 1, !dbg !98 - store i32 %77, ptr %item, align 4, !dbg !98 - br label %foreach.tail.L45, !dbg !98 + %74 = call ptr @_ZN14VectorIteratorIiE3getEv(ptr %10), !dbg !98 + %75 = load i32, ptr %74, align 4, !dbg !98 + store i32 %75, ptr %item, align 4, !dbg !98 + %76 = load i32, ptr %item, align 4, !dbg !99 + %77 = add i32 %76, 1, !dbg !99 + store i32 %77, ptr %item, align 4, !dbg !99 + br label %foreach.tail.L45, !dbg !99 foreach.tail.L45: ; preds = %foreach.body.L45 - call void @_ZN14VectorIteratorIiE4nextEv(ptr %10), !dbg !98 - br label %foreach.head.L45, !dbg !98 + call void @_ZN14VectorIteratorIiE4nextEv(ptr %10), !dbg !99 + br label %foreach.head.L45, !dbg !99 foreach.exit.L45: ; preds = %foreach.head.L45 - %78 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 0), !dbg !99 - %79 = load i32, ptr %78, align 4, !dbg !100 - %80 = icmp eq i32 %79, 123, !dbg !100 - br i1 %80, label %assert.exit.L48, label %assert.then.L48, !dbg !100, !prof !43 + %78 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 0), !dbg !100 + %79 = load i32, ptr %78, align 4, !dbg !101 + %80 = icmp eq i32 %79, 123, !dbg !101 + br i1 %80, label %assert.exit.L48, label %assert.then.L48, !dbg !101, !prof !43 assert.then.L48: ; preds = %foreach.exit.L45 - %81 = call i32 (ptr, ...) @printf(ptr @anon.string.16), !dbg !100 - call void @exit(i32 1), !dbg !100 - unreachable, !dbg !100 + %81 = call i32 (ptr, ...) @printf(ptr @anon.string.16), !dbg !101 + call void @exit(i32 1), !dbg !101 + unreachable, !dbg !101 assert.exit.L48: ; preds = %foreach.exit.L45 - %82 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 1), !dbg !101 - %83 = load i32, ptr %82, align 4, !dbg !102 - %84 = icmp eq i32 %83, 4321, !dbg !102 - br i1 %84, label %assert.exit.L49, label %assert.then.L49, !dbg !102, !prof !43 + %82 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 1), !dbg !102 + %83 = load i32, ptr %82, align 4, !dbg !103 + %84 = icmp eq i32 %83, 4321, !dbg !103 + br i1 %84, label %assert.exit.L49, label %assert.then.L49, !dbg !103, !prof !43 assert.then.L49: ; preds = %assert.exit.L48 - %85 = call i32 (ptr, ...) @printf(ptr @anon.string.17), !dbg !102 - call void @exit(i32 1), !dbg !102 - unreachable, !dbg !102 + %85 = call i32 (ptr, ...) @printf(ptr @anon.string.17), !dbg !103 + call void @exit(i32 1), !dbg !103 + unreachable, !dbg !103 assert.exit.L49: ; preds = %assert.exit.L48 - %86 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 2), !dbg !103 - %87 = load i32, ptr %86, align 4, !dbg !104 - %88 = icmp eq i32 %87, 9876, !dbg !104 - br i1 %88, label %assert.exit.L50, label %assert.then.L50, !dbg !104, !prof !43 + %86 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 2), !dbg !104 + %87 = load i32, ptr %86, align 4, !dbg !105 + %88 = icmp eq i32 %87, 9876, !dbg !105 + br i1 %88, label %assert.exit.L50, label %assert.then.L50, !dbg !105, !prof !43 assert.then.L50: ; preds = %assert.exit.L49 - %89 = call i32 (ptr, ...) @printf(ptr @anon.string.18), !dbg !104 - call void @exit(i32 1), !dbg !104 - unreachable, !dbg !104 + %89 = call i32 (ptr, ...) @printf(ptr @anon.string.18), !dbg !105 + call void @exit(i32 1), !dbg !105 + unreachable, !dbg !105 assert.exit.L50: ; preds = %assert.exit.L49 - %90 = call %struct.VectorIterator @"_Z7iterateR35std/iterator/vector-iterator.VectorIiE"(ptr %vi), !dbg !105 - call void @llvm.dbg.declare(metadata ptr %item1, metadata !106, metadata !DIExpression()), !dbg !107 - store %struct.VectorIterator %90, ptr %11, align 8, !dbg !105 - br label %foreach.head.L53, !dbg !107 + %90 = call %struct.VectorIterator @"_Z7iterateR35std/iterator/vector-iterator.VectorIiE"(ptr %vi), !dbg !106 + call void @llvm.dbg.declare(metadata ptr %item1, metadata !108, metadata !DIExpression()), !dbg !109 + store %struct.VectorIterator %90, ptr %11, align 8, !dbg !106 + br label %foreach.head.L53, !dbg !109 foreach.head.L53: ; preds = %foreach.tail.L53, %assert.exit.L50 - %91 = call i1 @_ZN14VectorIteratorIiE7isValidEv(ptr %11), !dbg !107 - br i1 %91, label %foreach.body.L53, label %foreach.exit.L53, !dbg !107 + %91 = call i1 @_ZN14VectorIteratorIiE7isValidEv(ptr %11), !dbg !109 + br i1 %91, label %foreach.body.L53, label %foreach.exit.L53, !dbg !109 foreach.body.L53: ; preds = %foreach.head.L53 - %92 = call ptr @_ZN14VectorIteratorIiE3getEv(ptr %11), !dbg !107 - store ptr %92, ptr %12, align 8, !dbg !107 - %93 = load ptr, ptr %12, align 8, !dbg !108 - %94 = load i32, ptr %93, align 4, !dbg !108 - %95 = add i32 %94, 1, !dbg !108 - store i32 %95, ptr %93, align 4, !dbg !108 - br label %foreach.tail.L53, !dbg !108 + %92 = call ptr @_ZN14VectorIteratorIiE3getEv(ptr %11), !dbg !109 + store ptr %92, ptr %12, align 8, !dbg !109 + %93 = load ptr, ptr %12, align 8, !dbg !110 + %94 = load i32, ptr %93, align 4, !dbg !110 + %95 = add i32 %94, 1, !dbg !110 + store i32 %95, ptr %93, align 4, !dbg !110 + br label %foreach.tail.L53, !dbg !110 foreach.tail.L53: ; preds = %foreach.body.L53 - call void @_ZN14VectorIteratorIiE4nextEv(ptr %11), !dbg !108 - br label %foreach.head.L53, !dbg !108 + call void @_ZN14VectorIteratorIiE4nextEv(ptr %11), !dbg !110 + br label %foreach.head.L53, !dbg !110 foreach.exit.L53: ; preds = %foreach.head.L53 - %96 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 0), !dbg !109 - %97 = load i32, ptr %96, align 4, !dbg !110 - %98 = icmp eq i32 %97, 124, !dbg !110 - br i1 %98, label %assert.exit.L56, label %assert.then.L56, !dbg !110, !prof !43 + %96 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 0), !dbg !111 + %97 = load i32, ptr %96, align 4, !dbg !112 + %98 = icmp eq i32 %97, 124, !dbg !112 + br i1 %98, label %assert.exit.L56, label %assert.then.L56, !dbg !112, !prof !43 assert.then.L56: ; preds = %foreach.exit.L53 - %99 = call i32 (ptr, ...) @printf(ptr @anon.string.19), !dbg !110 - call void @exit(i32 1), !dbg !110 - unreachable, !dbg !110 + %99 = call i32 (ptr, ...) @printf(ptr @anon.string.19), !dbg !112 + call void @exit(i32 1), !dbg !112 + unreachable, !dbg !112 assert.exit.L56: ; preds = %foreach.exit.L53 - %100 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 1), !dbg !111 - %101 = load i32, ptr %100, align 4, !dbg !112 - %102 = icmp eq i32 %101, 4322, !dbg !112 - br i1 %102, label %assert.exit.L57, label %assert.then.L57, !dbg !112, !prof !43 + %100 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 1), !dbg !113 + %101 = load i32, ptr %100, align 4, !dbg !114 + %102 = icmp eq i32 %101, 4322, !dbg !114 + br i1 %102, label %assert.exit.L57, label %assert.then.L57, !dbg !114, !prof !43 assert.then.L57: ; preds = %assert.exit.L56 - %103 = call i32 (ptr, ...) @printf(ptr @anon.string.20), !dbg !112 - call void @exit(i32 1), !dbg !112 - unreachable, !dbg !112 + %103 = call i32 (ptr, ...) @printf(ptr @anon.string.20), !dbg !114 + call void @exit(i32 1), !dbg !114 + unreachable, !dbg !114 assert.exit.L57: ; preds = %assert.exit.L56 - %104 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 2), !dbg !113 - %105 = load i32, ptr %104, align 4, !dbg !114 - %106 = icmp eq i32 %105, 9877, !dbg !114 - br i1 %106, label %assert.exit.L58, label %assert.then.L58, !dbg !114, !prof !43 + %104 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 2), !dbg !115 + %105 = load i32, ptr %104, align 4, !dbg !116 + %106 = icmp eq i32 %105, 9877, !dbg !116 + br i1 %106, label %assert.exit.L58, label %assert.then.L58, !dbg !116, !prof !43 assert.then.L58: ; preds = %assert.exit.L57 - %107 = call i32 (ptr, ...) @printf(ptr @anon.string.21), !dbg !114 - call void @exit(i32 1), !dbg !114 - unreachable, !dbg !114 + %107 = call i32 (ptr, ...) @printf(ptr @anon.string.21), !dbg !116 + call void @exit(i32 1), !dbg !116 + unreachable, !dbg !116 assert.exit.L58: ; preds = %assert.exit.L57 - %108 = call %struct.VectorIterator @"_Z7iterateR35std/iterator/vector-iterator.VectorIiE"(ptr %vi), !dbg !115 - store %struct.VectorIterator %108, ptr %13, align 8, !dbg !115 - call void @llvm.dbg.declare(metadata ptr %idx, metadata !116, metadata !DIExpression()), !dbg !118 - call void @llvm.dbg.declare(metadata ptr %item2, metadata !119, metadata !DIExpression()), !dbg !120 - store i64 0, ptr %idx, align 8, !dbg !118 - br label %foreach.head.L60, !dbg !120 + %108 = call %struct.VectorIterator @"_Z7iterateR35std/iterator/vector-iterator.VectorIiE"(ptr %vi), !dbg !117 + store %struct.VectorIterator %108, ptr %13, align 8, !dbg !117 + call void @llvm.dbg.declare(metadata ptr %idx, metadata !119, metadata !DIExpression()), !dbg !121 + call void @llvm.dbg.declare(metadata ptr %item2, metadata !122, metadata !DIExpression()), !dbg !123 + store i64 0, ptr %idx, align 8, !dbg !121 + br label %foreach.head.L60, !dbg !123 foreach.head.L60: ; preds = %foreach.tail.L60, %assert.exit.L58 - %109 = call i1 @_ZN14VectorIteratorIiE7isValidEv(ptr %13), !dbg !120 - br i1 %109, label %foreach.body.L60, label %foreach.exit.L60, !dbg !120 + %109 = call i1 @_ZN14VectorIteratorIiE7isValidEv(ptr %13), !dbg !123 + br i1 %109, label %foreach.body.L60, label %foreach.exit.L60, !dbg !123 foreach.body.L60: ; preds = %foreach.head.L60 - %pair3 = call %struct.Pair @_ZN14VectorIteratorIiE6getIdxEv(ptr %13), !dbg !120 - store %struct.Pair %pair3, ptr %pair_addr, align 8, !dbg !120 - %idx_addr = getelementptr inbounds %struct.Pair, ptr %pair_addr, i32 0, i32 0, !dbg !120 - %110 = load i64, ptr %idx_addr, align 8, !dbg !120 - store i64 %110, ptr %idx, align 8, !dbg !120 - %item_addr = getelementptr inbounds %struct.Pair, ptr %pair_addr, i32 0, i32 1, !dbg !120 - %111 = load ptr, ptr %item_addr, align 8, !dbg !120 - store ptr %111, ptr %14, align 8, !dbg !120 - %112 = load i64, ptr %idx, align 8, !dbg !121 - %113 = trunc i64 %112 to i32, !dbg !121 - %114 = load ptr, ptr %14, align 8, !dbg !121 - %115 = load i32, ptr %114, align 4, !dbg !121 - %116 = add i32 %115, %113, !dbg !121 - store i32 %116, ptr %114, align 4, !dbg !121 - br label %foreach.tail.L60, !dbg !121 + %pair3 = call %struct.Pair @_ZN14VectorIteratorIiE6getIdxEv(ptr %13), !dbg !123 + store %struct.Pair %pair3, ptr %pair_addr, align 8, !dbg !123 + %idx_addr = getelementptr inbounds %struct.Pair, ptr %pair_addr, i32 0, i32 0, !dbg !123 + %110 = load i64, ptr %idx_addr, align 8, !dbg !123 + store i64 %110, ptr %idx, align 8, !dbg !123 + %item_addr = getelementptr inbounds %struct.Pair, ptr %pair_addr, i32 0, i32 1, !dbg !123 + %111 = load ptr, ptr %item_addr, align 8, !dbg !123 + store ptr %111, ptr %14, align 8, !dbg !123 + %112 = load i64, ptr %idx, align 8, !dbg !124 + %113 = trunc i64 %112 to i32, !dbg !124 + %114 = load ptr, ptr %14, align 8, !dbg !124 + %115 = load i32, ptr %114, align 4, !dbg !124 + %116 = add i32 %115, %113, !dbg !124 + store i32 %116, ptr %114, align 4, !dbg !124 + br label %foreach.tail.L60, !dbg !124 foreach.tail.L60: ; preds = %foreach.body.L60 - call void @_ZN14VectorIteratorIiE4nextEv(ptr %13), !dbg !121 - br label %foreach.head.L60, !dbg !121 + call void @_ZN14VectorIteratorIiE4nextEv(ptr %13), !dbg !124 + br label %foreach.head.L60, !dbg !124 foreach.exit.L60: ; preds = %foreach.head.L60 - %117 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 0), !dbg !122 - %118 = load i32, ptr %117, align 4, !dbg !123 - %119 = icmp eq i32 %118, 124, !dbg !123 - br i1 %119, label %assert.exit.L63, label %assert.then.L63, !dbg !123, !prof !43 + %117 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 0), !dbg !125 + %118 = load i32, ptr %117, align 4, !dbg !126 + %119 = icmp eq i32 %118, 124, !dbg !126 + br i1 %119, label %assert.exit.L63, label %assert.then.L63, !dbg !126, !prof !43 assert.then.L63: ; preds = %foreach.exit.L60 - %120 = call i32 (ptr, ...) @printf(ptr @anon.string.22), !dbg !123 - call void @exit(i32 1), !dbg !123 - unreachable, !dbg !123 + %120 = call i32 (ptr, ...) @printf(ptr @anon.string.22), !dbg !126 + call void @exit(i32 1), !dbg !126 + unreachable, !dbg !126 assert.exit.L63: ; preds = %foreach.exit.L60 - %121 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 1), !dbg !124 - %122 = load i32, ptr %121, align 4, !dbg !125 - %123 = icmp eq i32 %122, 4323, !dbg !125 - br i1 %123, label %assert.exit.L64, label %assert.then.L64, !dbg !125, !prof !43 + %121 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 1), !dbg !127 + %122 = load i32, ptr %121, align 4, !dbg !128 + %123 = icmp eq i32 %122, 4323, !dbg !128 + br i1 %123, label %assert.exit.L64, label %assert.then.L64, !dbg !128, !prof !43 assert.then.L64: ; preds = %assert.exit.L63 - %124 = call i32 (ptr, ...) @printf(ptr @anon.string.23), !dbg !125 - call void @exit(i32 1), !dbg !125 - unreachable, !dbg !125 + %124 = call i32 (ptr, ...) @printf(ptr @anon.string.23), !dbg !128 + call void @exit(i32 1), !dbg !128 + unreachable, !dbg !128 assert.exit.L64: ; preds = %assert.exit.L63 - %125 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 2), !dbg !126 - %126 = load i32, ptr %125, align 4, !dbg !127 - %127 = icmp eq i32 %126, 9879, !dbg !127 - br i1 %127, label %assert.exit.L65, label %assert.then.L65, !dbg !127, !prof !43 + %125 = call ptr @_ZN6VectorIiE3getEj(ptr %vi, i32 2), !dbg !129 + %126 = load i32, ptr %125, align 4, !dbg !130 + %127 = icmp eq i32 %126, 9879, !dbg !130 + br i1 %127, label %assert.exit.L65, label %assert.then.L65, !dbg !130, !prof !43 assert.then.L65: ; preds = %assert.exit.L64 - %128 = call i32 (ptr, ...) @printf(ptr @anon.string.24), !dbg !127 - call void @exit(i32 1), !dbg !127 - unreachable, !dbg !127 + %128 = call i32 (ptr, ...) @printf(ptr @anon.string.24), !dbg !130 + call void @exit(i32 1), !dbg !130 + unreachable, !dbg !130 assert.exit.L65: ; preds = %assert.exit.L64 - %129 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0), !dbg !128 - call void @_ZN6VectorIiE4dtorEv(ptr %vi), !dbg !128 - %130 = load i32, ptr %result, align 4, !dbg !128 - ret i32 %130, !dbg !128 + %129 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0), !dbg !131 + call void @_ZN6VectorIiE4dtorEv(ptr %vi), !dbg !131 + %130 = load i32, ptr %result, align 4, !dbg !131 + ret i32 %130, !dbg !131 } ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none) @@ -498,7 +498,7 @@ attributes #3 = { cold noreturn nounwind } !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "printf.str.0", linkageName: "printf.str.0", scope: !2, file: !5, line: 67, type: !6, isLocal: true, isDefinition: true) -!2 = distinct !DICompileUnit(language: 32768, file: !3, producer: "spice version dev (https://github.com/spicelang/spice)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) +!2 = distinct !DICompileUnit(language: DW_LANG_C17, file: !3, producer: "spice version dev (https://github.com/spicelang/spice)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) !3 = !DIFile(filename: "C:\\Users\\I516467\\Documents\\JustForFunGitHubClones\\spice\\cmake-build-debug\\test\\test-files\\irgenerator\\debug-info\\success-dgb-info-complex\\source.spice", directory: ".\\test-files\\irgenerator\\debug-info\\success-dgb-info-complex") !4 = !{!0} !5 = !DIFile(filename: "source.spice", directory: ".\\test-files\\irgenerator\\debug-info\\success-dgb-info-complex") @@ -591,37 +591,40 @@ attributes #3 = { cold noreturn nounwind } !92 = !DILocation(line: 40, column: 24, scope: !15) !93 = !DILocation(line: 41, column: 5, scope: !15) !94 = !DILocation(line: 42, column: 13, scope: !15) -!95 = !DILocation(line: 45, column: 32, scope: !15) -!96 = !DILocalVariable(name: "item", scope: !15, file: !5, line: 45, type: !18) -!97 = !DILocation(line: 45, column: 13, scope: !15) -!98 = !DILocation(line: 46, column: 9, scope: !15) -!99 = !DILocation(line: 48, column: 19, scope: !15) -!100 = !DILocation(line: 48, column: 25, scope: !15) -!101 = !DILocation(line: 49, column: 19, scope: !15) -!102 = !DILocation(line: 49, column: 25, scope: !15) -!103 = !DILocation(line: 50, column: 19, scope: !15) -!104 = !DILocation(line: 50, column: 25, scope: !15) -!105 = !DILocation(line: 53, column: 33, scope: !15) -!106 = !DILocalVariable(name: "item", scope: !15, file: !5, line: 53, type: !32) -!107 = !DILocation(line: 53, column: 13, scope: !15) -!108 = !DILocation(line: 54, column: 9, scope: !15) -!109 = !DILocation(line: 56, column: 19, scope: !15) -!110 = !DILocation(line: 56, column: 25, scope: !15) -!111 = !DILocation(line: 57, column: 19, scope: !15) -!112 = !DILocation(line: 57, column: 25, scope: !15) -!113 = !DILocation(line: 58, column: 19, scope: !15) -!114 = !DILocation(line: 58, column: 25, scope: !15) -!115 = !DILocation(line: 60, column: 43, scope: !15) -!116 = !DILocalVariable(name: "idx", scope: !15, file: !5, line: 60, type: !117) -!117 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) -!118 = !DILocation(line: 60, column: 13, scope: !15) -!119 = !DILocalVariable(name: "item", scope: !15, file: !5, line: 60, type: !32) -!120 = !DILocation(line: 60, column: 23, scope: !15) -!121 = !DILocation(line: 61, column: 9, scope: !15) -!122 = !DILocation(line: 63, column: 19, scope: !15) -!123 = !DILocation(line: 63, column: 25, scope: !15) -!124 = !DILocation(line: 64, column: 19, scope: !15) -!125 = !DILocation(line: 64, column: 25, scope: !15) -!126 = !DILocation(line: 65, column: 19, scope: !15) -!127 = !DILocation(line: 65, column: 25, scope: !15) -!128 = !DILocation(line: 67, column: 5, scope: !15) +!95 = !DILocation(line: 45, column: 32, scope: !96) +!96 = distinct !DILexicalBlock(scope: !15, file: !5, line: 45, column: 5) +!97 = !DILocalVariable(name: "item", scope: !96, file: !5, line: 45, type: !18) +!98 = !DILocation(line: 45, column: 13, scope: !96) +!99 = !DILocation(line: 46, column: 9, scope: !96) +!100 = !DILocation(line: 48, column: 19, scope: !15) +!101 = !DILocation(line: 48, column: 25, scope: !15) +!102 = !DILocation(line: 49, column: 19, scope: !15) +!103 = !DILocation(line: 49, column: 25, scope: !15) +!104 = !DILocation(line: 50, column: 19, scope: !15) +!105 = !DILocation(line: 50, column: 25, scope: !15) +!106 = !DILocation(line: 53, column: 33, scope: !107) +!107 = distinct !DILexicalBlock(scope: !15, file: !5, line: 53, column: 5) +!108 = !DILocalVariable(name: "item", scope: !107, file: !5, line: 53, type: !32) +!109 = !DILocation(line: 53, column: 13, scope: !107) +!110 = !DILocation(line: 54, column: 9, scope: !107) +!111 = !DILocation(line: 56, column: 19, scope: !15) +!112 = !DILocation(line: 56, column: 25, scope: !15) +!113 = !DILocation(line: 57, column: 19, scope: !15) +!114 = !DILocation(line: 57, column: 25, scope: !15) +!115 = !DILocation(line: 58, column: 19, scope: !15) +!116 = !DILocation(line: 58, column: 25, scope: !15) +!117 = !DILocation(line: 60, column: 43, scope: !118) +!118 = distinct !DILexicalBlock(scope: !15, file: !5, line: 60, column: 5) +!119 = !DILocalVariable(name: "idx", scope: !118, file: !5, line: 60, type: !120) +!120 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) +!121 = !DILocation(line: 60, column: 13, scope: !118) +!122 = !DILocalVariable(name: "item", scope: !118, file: !5, line: 60, type: !32) +!123 = !DILocation(line: 60, column: 23, scope: !118) +!124 = !DILocation(line: 61, column: 9, scope: !118) +!125 = !DILocation(line: 63, column: 19, scope: !15) +!126 = !DILocation(line: 63, column: 25, scope: !15) +!127 = !DILocation(line: 64, column: 19, scope: !15) +!128 = !DILocation(line: 64, column: 25, scope: !15) +!129 = !DILocation(line: 65, column: 19, scope: !15) +!130 = !DILocation(line: 65, column: 25, scope: !15) +!131 = !DILocation(line: 67, column: 5, scope: !15) diff --git a/test/test-files/std/io/cli-parser/cli-flags.txt b/test/test-files/std/io/cli-parser-flags/cli-flags.txt similarity index 100% rename from test/test-files/std/io/cli-parser/cli-flags.txt rename to test/test-files/std/io/cli-parser-flags/cli-flags.txt diff --git a/test/test-files/std/io/cli-parser/cout.out b/test/test-files/std/io/cli-parser-flags/cout.out similarity index 100% rename from test/test-files/std/io/cli-parser/cout.out rename to test/test-files/std/io/cli-parser-flags/cout.out diff --git a/test/test-files/std/io/cli-parser/ir-code-O2.ll b/test/test-files/std/io/cli-parser-flags/ir-code-O2.ll similarity index 93% rename from test/test-files/std/io/cli-parser/ir-code-O2.ll rename to test/test-files/std/io/cli-parser-flags/ir-code-O2.ll index f801f6b70..c7d4563d2 100644 --- a/test/test-files/std/io/cli-parser/ir-code-O2.ll +++ b/test/test-files/std/io/cli-parser-flags/ir-code-O2.ll @@ -3,14 +3,12 @@ source_filename = "source.spice" target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-w64-windows-gnu" -%struct.CliParser = type { ptr, %struct.CliSubcommand.15 } -%struct.CliSubcommand.15 = type { ptr, ptr, ptr, ptr, { ptr, ptr }, %struct.Vector, %struct.Vector.0, %struct.Vector.1, %struct.Vector.2, %struct.Vector.3, %struct.Vector.4, i1 } +%struct.CliParser = type { %struct.CliSubcommand.11 } +%struct.CliSubcommand.11 = type { ptr, ptr, ptr, ptr, ptr, { ptr, ptr }, %struct.Vector, %struct.Vector.0, %struct.Vector.1, %struct.Vector.2, i1 } %struct.Vector = type { ptr, i64, i64 } %struct.Vector.0 = type { ptr, i64, i64 } %struct.Vector.1 = type { ptr, i64, i64 } %struct.Vector.2 = type { ptr, i64, i64 } -%struct.Vector.3 = type { ptr, i64, i64 } -%struct.Vector.4 = type { ptr, i64, i64 } %struct.CliOptions = type { i1 } @printf.str.0 = private unnamed_addr constant [31 x i8] c"Callback called with value %d\0A\00", align 1 diff --git a/test/test-files/std/io/cli-parser/source.spice b/test/test-files/std/io/cli-parser-flags/source.spice similarity index 100% rename from test/test-files/std/io/cli-parser/source.spice rename to test/test-files/std/io/cli-parser-flags/source.spice diff --git a/test/test-files/std/io/cli-parser-subcommands/cli-flags.txt b/test/test-files/std/io/cli-parser-subcommands/cli-flags.txt new file mode 100644 index 000000000..d7bae2a6a --- /dev/null +++ b/test/test-files/std/io/cli-parser-subcommands/cli-flags.txt @@ -0,0 +1 @@ +greet --name World \ No newline at end of file diff --git a/test/test-files/std/io/cli-parser-subcommands/cout.out b/test/test-files/std/io/cli-parser-subcommands/cout.out new file mode 100644 index 000000000..980a0d5f1 --- /dev/null +++ b/test/test-files/std/io/cli-parser-subcommands/cout.out @@ -0,0 +1 @@ +Hello World! diff --git a/test/test-files/std/io/cli-parser-subcommands/ir-code-O2.ll b/test/test-files/std/io/cli-parser-subcommands/ir-code-O2.ll new file mode 100644 index 000000000..7e9fbe4c7 --- /dev/null +++ b/test/test-files/std/io/cli-parser-subcommands/ir-code-O2.ll @@ -0,0 +1,68 @@ +; ModuleID = 'source.spice' +source_filename = "source.spice" +target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-w64-windows-gnu" + +%struct.CliParser = type { %struct.CliSubcommand.11 } +%struct.CliSubcommand.11 = type { ptr, ptr, ptr, ptr, ptr, { ptr, ptr }, %struct.Vector, %struct.Vector.0, %struct.Vector.1, %struct.Vector.2, i1 } +%struct.Vector = type { ptr, i64, i64 } +%struct.Vector.0 = type { ptr, i64, i64 } +%struct.Vector.1 = type { ptr, i64, i64 } +%struct.Vector.2 = type { ptr, i64, i64 } +%struct.CliOptions = type { ptr } + +@anon.string.0 = private unnamed_addr constant [13 x i8] c"Test Program\00", align 1 +@anon.string.1 = private unnamed_addr constant [30 x i8] c"This is a simple test program\00", align 1 +@anon.string.2 = private unnamed_addr constant [7 x i8] c"v0.1.0\00", align 1 +@anon.string.3 = private unnamed_addr constant [37 x i8] c"Copyright (c) Marc Auberer 2021-2023\00", align 1 +@anon.string.5 = private unnamed_addr constant [6 x i8] c"greet\00", align 1 +@anon.string.6 = private unnamed_addr constant [14 x i8] c"Greet someone\00", align 1 +@anon.string.7 = private unnamed_addr constant [7 x i8] c"--name\00", align 1 +@anon.string.8 = private unnamed_addr constant [28 x i8] c"Name of the person to greet\00", align 1 +@anon.string.9 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 +@printf.str.0 = private unnamed_addr constant [11 x i8] c"Hello %s!\0A\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main(i32 %0, ptr %1) local_unnamed_addr #0 { + %parser = alloca %struct.CliParser, align 8 + %options = alloca %struct.CliOptions, align 8 + call void @_ZN9CliParser4ctorEPcPc(ptr nonnull %parser, ptr nonnull @anon.string.0, ptr nonnull @anon.string.1) #2 + call void @_ZN9CliParser10setVersionEPc(ptr nonnull %parser, ptr nonnull @anon.string.2) #2 + call void @_ZN9CliParser9setFooterEPc(ptr nonnull %parser, ptr nonnull @anon.string.3) #2 + store ptr @anon.string.9, ptr %options, align 8 + %3 = call ptr @_ZN9CliParser13addSubcommandEPcPc(ptr nonnull %parser, ptr nonnull @anon.string.5, ptr nonnull @anon.string.6) #2 + call void @_ZN13CliSubcommand9addOptionEPcRPcPc(ptr %3, ptr nonnull @anon.string.7, ptr nonnull %options, ptr nonnull @anon.string.8) #2 + %4 = call i32 @_ZN9CliParser5parseEjPPc(ptr nonnull %parser, i32 %0, ptr %1) #2 + %5 = load ptr, ptr %options, align 8 + %6 = call i1 @_Z10isRawEqualPcPc(ptr %5, ptr nonnull @anon.string.9) #2 + br i1 %6, label %if.exit.L23, label %if.then.L23 + +if.then.L23: ; preds = %2 + %7 = load ptr, ptr %options, align 8 + %8 = call i32 (ptr, ...) @printf(ptr noundef nonnull dereferenceable(1) @printf.str.0, ptr %7) + br label %if.exit.L23 + +if.exit.L23: ; preds = %if.then.L23, %2 + ret i32 0 +} + +declare void @_ZN9CliParser4ctorEPcPc(ptr, ptr, ptr) local_unnamed_addr + +declare void @_ZN9CliParser10setVersionEPc(ptr, ptr) local_unnamed_addr + +declare void @_ZN9CliParser9setFooterEPc(ptr, ptr) local_unnamed_addr + +declare ptr @_ZN9CliParser13addSubcommandEPcPc(ptr, ptr, ptr) local_unnamed_addr + +declare void @_ZN13CliSubcommand9addOptionEPcRPcPc(ptr, ptr, ptr, ptr) local_unnamed_addr + +declare i32 @_ZN9CliParser5parseEjPPc(ptr, i32, ptr) local_unnamed_addr + +declare i1 @_Z10isRawEqualPcPc(ptr, ptr) local_unnamed_addr + +; Function Attrs: nofree nounwind +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) local_unnamed_addr #1 + +attributes #0 = { noinline nounwind optnone uwtable } +attributes #1 = { nofree nounwind } +attributes #2 = { nounwind } diff --git a/test/test-files/std/io/cli-parser-subcommands/source.spice b/test/test-files/std/io/cli-parser-subcommands/source.spice new file mode 100644 index 000000000..b4e4cc0e2 --- /dev/null +++ b/test/test-files/std/io/cli-parser-subcommands/source.spice @@ -0,0 +1,26 @@ +import "std/io/cli-parser"; + +type CliOptions struct { + string greetName = "" +} + +p callback(bool& value) { + printf("Callback called with value %d\n", value); +} + +f main(int argc, string[] argv) { + CliParser parser = CliParser("Test Program", "This is a simple test program"); + parser.setVersion("v0.1.0"); + parser.setFooter("Copyright (c) Marc Auberer 2021-2023"); + + CliOptions options; + CliSubcommand& greet = parser.addSubcommand("greet", "Greet someone"); + greet.addOption("--name", options.greetName, "Name of the person to greet"); + + parser.parse(argc, argv); + + // Greet persion if requested + if options.greetName != "" { + printf("Hello %s!\n", options.greetName); + } +} \ No newline at end of file