diff --git a/.github/workflows/ci-cpp.yml b/.github/workflows/ci-cpp.yml index ea4e7b0f4..a9d6b635b 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.1 + key: llvm-17.0.2 - name: Setup LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | cd .. rm -rf llvm - git clone --depth 1 --branch llvmorg-17.0.1 https://github.com/llvm/llvm-project llvm + git clone --depth 1 --branch llvmorg-17.0.2 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 4d6082f35..71ee9024d 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.1 + key: llvm-17.0.2 - 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.1 https://github.com/llvm/llvm-project llvm + git clone --depth 1 --branch llvmorg-17.0.2 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 5f9ef567b..da9839fa7 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.1-linux-x64 + key: llvm-17.0.2-linux-x64 - name: Setup LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | - git clone --depth 1 --branch llvmorg-17.0.1 https://github.com/llvm/llvm-project.git llvm + git clone --depth 1 --branch llvmorg-17.0.2 https://github.com/llvm/llvm-project.git llvm mkdir ./llvm/build cd ./llvm/build cmake -GNinja -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 -Wno-dev -Wattributes ../llvm @@ -107,12 +107,12 @@ jobs: uses: actions/cache@v3 with: path: /home/runner/work/spice/spice/llvm - key: llvm-17.0.1-linux-aarch64 + key: llvm-17.0.2-linux-aarch64 - name: Clone LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | - git clone --depth 1 --branch llvmorg-17.0.1 https://github.com/llvm/llvm-project.git llvm + git clone --depth 1 --branch llvmorg-17.0.2 https://github.com/llvm/llvm-project.git llvm mkdir ./llvm/build - name: Setup LLVM @@ -185,12 +185,12 @@ jobs: uses: actions/cache@v3 with: path: D:/a/spice/spice/llvm - key: llvm-17.0.1-win-x64 + key: llvm-17.0.2-win-x64 - name: Setup LLVM if: steps.cache-llvm.outputs.cache-hit != 'true' run: | - git clone --depth 1 --branch llvmorg-17.0.1 https://github.com/llvm/llvm-project.git llvm + git clone --depth 1 --branch llvmorg-17.0.2 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 fe1d933d0..5af7add85 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 7428355ef..cf4590a32 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.1 https://github.com/llvm/llvm-project llvm +git clone --depth 1 --branch llvmorg-17.0.2 https://github.com/llvm/llvm-project llvm echo done. REM - Build LLVM diff --git a/dev-setup.sh b/dev-setup.sh index d9cd9bb5e..0bc169343 100755 --- 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.1 https://github.com/llvm/llvm-project llvm +git clone --depth 1 --branch llvmorg-17.0.2 https://github.com/llvm/llvm-project llvm colored_echo "done." # Build LLVM diff --git a/docs/docs/language/attributes.md b/docs/docs/language/attributes.md index 4af4baa6f..086543435 100644 --- a/docs/docs/language/attributes.md +++ b/docs/docs/language/attributes.md @@ -16,6 +16,7 @@ Spice offers the option to annotate single function or whole modules via attribu ### Available attributes - `core.linker.flag: string`: Append linker flag +- `core.compiler.alwaysKeepOnNameCollision: bool`: Always keep the symbols of this source files when merging the name registries of multiple source files ## Function attributes diff --git a/media/test-project/test.spice b/media/test-project/test.spice index 17d4dbb48..a721a4128 100644 --- a/media/test-project/test.spice +++ b/media/test-project/test.spice @@ -1,42 +1,38 @@ +import "std/io/cli-parser"; +import "std/io/cli-subcommand"; + +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); + } +} + +/*import "std/data/hash-table"; + f main() { - String s = String("Hello "); - assert s.getRaw() == "Hello "; - assert s.getLength() == 6; - assert s.getCapacity() == 12; - s.append("World!"); - assert s.getRaw() == "Hello World!"; - assert s.getLength() == 12; - assert s.getCapacity() == 12; - s.append('?'); - assert s.getRaw() == "Hello World!?"; - assert s.getLength() == 13; - assert s.getCapacity() == 24; - s.append('!'); - assert s.getRaw() == "Hello World!?!"; - assert s.getLength() == 14; - assert s.getCapacity() == 24; - s.clear(); - assert s.getRaw() == ""; - assert s.getLength() == 0; - assert s.getCapacity() == 24; - s.reserve(100l); - assert s.getRaw() == ""; - assert s.getLength() == 0; - assert s.getCapacity() == 100; - s = String(""); - assert s.isEmpty(); - s.append('a'); - assert !s.isEmpty(); - s = String("This is a test. And because this is a test, it is a test."); - assert !s.replace("foo", "demo"); - assert s.replace("test", "demo"); - assert s.getRaw() == "This is a demo. And because this is a test, it is a test."; - assert s.replace("test", "demonstration", 45l); - assert s.getRaw() == "This is a demo. And because this is a test, it is a demonstration."; - assert s.replace("test", "d"); - assert s.getRaw() == "This is a demo. And because this is a d, it is a demonstration."; - assert s.replaceAll(" is ", " was ") == 3; - assert s.getRaw() == "This was a demo. And because this was a d, it was a demonstration."; - - printf("All assertions passed!"); -} \ No newline at end of file + HashTable ht; + ht.insert(1, 2); + ht.insert(2, 3); + ht.insert(3, 4); + ht.insert(4, 5); + ht.insert(5, 6); +}*/ \ No newline at end of file diff --git a/src-bootstrap/ast/AstNodes.spice b/src-bootstrap/ast/ASTNodes.spice similarity index 63% rename from src-bootstrap/ast/AstNodes.spice rename to src-bootstrap/ast/ASTNodes.spice index e527f40bd..e3ff36f55 100644 --- a/src-bootstrap/ast/AstNodes.spice +++ b/src-bootstrap/ast/ASTNodes.spice @@ -3,7 +3,7 @@ import "std/type/Any"; import "std/data/Vector"; // Own imports -import "AbstractAstVisitor"; +import "AbstractASTVisitor"; import "../util/CodeLoc"; import "../symbol/SymbolType"; @@ -21,16 +21,16 @@ public type CompileTimeValue struct { bool boolValue } -public type Visitable interface { +public type IVisitable interface { f accept(IAbstractAstVisitor*); } // =========================================================== ASTNode =========================================================== public type ASTNode struct : IVisitable { - AstNode* parent - Vector children - const CodeLoc codeLoc + ASTNode* parent + Vector children + CodeLoc codeLoc string errorMessage Vector symbolTypes CompileTimeValue compileTimeValue @@ -40,467 +40,535 @@ public type ASTNode struct : IVisitable { Vector> opFct // Operator overloading functions } -p AstNode.ctor(AstNode *parent, CodeLoc codeLoc) { +p ASTNode.ctor(ASTNode *parent, CodeLoc codeLoc) { this.parent = parent; this.codeLoc = codeLoc; } -public f AstNode.accept(AbstractAstVisitor* _) { +public f ASTNode.accept(AbstractAstVisitor* _) { assert false; // Please override at child level } // ========================================================== EntryNode ========================================================== -public type ASTEntryNode struct : ASTNode { +public type ASTEntryNode struct : IVisitable { + compose public ASTNode node } // ======================================================== MainFctDefNode ======================================================= -public type ASTMainFctDefNode struct : ASTNode { +public type ASTMainFctDefNode struct : IVisitable { + compose public ASTNode node } // ========================================================= FctNameNode ========================================================= -public type ASTFctNameNode struct : ASTNode { +public type ASTFctNameNode struct : IVisitable { + compose public ASTNode node } // ========================================================== FctDefNode ========================================================= -public type ASTFctDefNode struct : ASTNode { +public type ASTFctDefNode struct : IVisitable { + compose public ASTNode node } // ========================================================== ProcDefNode ======================================================== -public type ASTProcDefNode struct : ASTNode { +public type ASTProcDefNode struct : IVisitable { + compose public ASTNode node } // ========================================================= StructDefNode ======================================================= -public type ASTStructDefNode struct : ASTNode { +public type ASTStructDefNode struct : IVisitable { + compose public ASTNode node } // ======================================================== InterfaceDefNode ===================================================== -public type ASTInterfaceDefNode struct : ASTNode { +public type ASTInterfaceDefNode struct : IVisitable { + compose public ASTNode node } // ========================================================== EnumDefNode ======================================================== -public type ASTEnumDefNode struct : ASTNode { +public type ASTEnumDefNode struct : IVisitable { + compose public ASTNode node } // ======================================================= GenericTypeDefNode ==================================================== -public type ASTGenericTypeDefNode struct : ASTNode { +public type ASTGenericTypeDefNode struct : IVisitable { + compose public ASTNode node } // ========================================================== AliasDefNode ======================================================= -public type ASTAliasDefNode struct : ASTNode { +public type ASTAliasDefNode struct : IVisitable { + compose public ASTNode node } // ======================================================== GlobalVarDefNode ===================================================== -public type ASTGlobalVarDefNode struct : ASTNode { +public type ASTGlobalVarDefNode struct : IVisitable { + compose public ASTNode node } // ========================================================== ExtDeclNode ======================================================== -public type ASTExtDeclNode struct : ASTNode { +public type ASTExtDeclNode struct : IVisitable { + compose public ASTNode node } // ========================================================== UnsafeBlockNode ======================================================== -public type ASTUnsafeBlockNode struct : ASTNode { +public type ASTUnsafeBlockNode struct : IVisitable { + compose public ASTNode node } // ========================================================== ForLoopNode ======================================================== -public type ASTForLoopNode struct : ASTNode { +public type ASTForLoopNode struct : IVisitable { + compose public ASTNode node } // ======================================================== ForeachLoopNode ====================================================== -public type ASTForeachLoopNode struct : ASTNode { +public type ASTForeachLoopNode struct : IVisitable { + compose public ASTNode node } // ========================================================= WhileLoopNode ======================================================= -public type ASTWhileLoopNode struct : ASTNode { +public type ASTWhileLoopNode struct : IVisitable { + compose public ASTNode node } // ======================================================== DoWhileLoopNode ====================================================== -public type ASTDoWhileLoopNode struct : ASTNode { +public type ASTDoWhileLoopNode struct : IVisitable { + compose public ASTNode node } // ========================================================== IfStmtNode ======================================================== -public type ASTIfStmtNode struct : ASTNode { +public type ASTIfStmtNode struct : IVisitable { + compose public ASTNode node } // ========================================================== ElseStmtNode ======================================================= -public type ASTElseStmtNode struct : ASTNode { +public type ASTElseStmtNode struct : IVisitable { + compose public ASTNode node } // ===================================================== AnonymousBlockStmtNode ================================================== -public type ASTAnonymousBlockStmtNode struct : ASTNode { +public type ASTAnonymousBlockStmtNode struct : IVisitable { + compose public ASTNode node } // ========================================================== StmtLstNode ======================================================== -public type ASTStmtLstNode struct : ASTNode { +public type ASTStmtLstNode struct : IVisitable { + compose public ASTNode node } // ========================================================== TypeLstNode ======================================================== -public type ASTTypeLstNode struct : ASTNode { +public type ASTTypeLstNode struct : IVisitable { + compose public ASTNode node } // ======================================================== TypeAltsLstNode ====================================================== -public type ASTTypeAltsLstNode struct : ASTNode { +public type ASTTypeAltsLstNode struct : IVisitable { + compose public ASTNode node } // ========================================================== ParamLstNode ======================================================= -public type ASTParamLstNode struct : ASTNode { +public type ASTParamLstNode struct : IVisitable { + compose public ASTNode node } // =========================================================== ArgLstNode ======================================================== -public type ASTArgLstNode struct : ASTNode { +public type ASTArgLstNode struct : IVisitable { + compose public ASTNode node } // ======================================================== EnumItemLstNode ====================================================== -public type ASTEnumItemLstNode struct : ASTNode { +public type ASTEnumItemLstNode struct : IVisitable { + compose public ASTNode node } // ========================================================== EnumItemNode ======================================================= -public type ASTEnumItemNode struct : ASTNode { - -} - -// ========================================================== FieldLstNode ======================================================= - -public type ASTFieldLstNode struct : ASTNode { +public type ASTEnumItemNode struct : IVisitable { + compose public ASTNode node } // =========================================================== FieldNode ========================================================= -public type ASTFieldNode struct : ASTNode { +public type ASTFieldNode struct : IVisitable { + compose public ASTNode node } // ========================================================= SignatureNode ======================================================= -public type ASTSignatureNode struct : ASTNode { +public type ASTSignatureNode struct : IVisitable { + compose public ASTNode node } // ============================================================ StmtNode ========================================================= -public type ASTStmtNode struct : ASTNode { +public type ASTStmtNode struct : IVisitable { + compose public ASTNode node } // ========================================================== DeclStmtNode ======================================================= -public type ASTDeclStmtNode struct : ASTNode { +public type ASTDeclStmtNode struct : IVisitable { + compose public ASTNode node } // ======================================================== SpecifierLstNode ===================================================== -public type ASTSpecifierLstNode struct : ASTNode { +public type ASTSpecifierLstNode struct : IVisitable { + compose public ASTNode node } // ========================================================= SpecifierNode ======================================================= -public type ASTSpecifierNode struct : ASTNode { +public type ASTSpecifierNode struct : IVisitable { + compose public ASTNode node } // ========================================================== ModAttrNode ======================================================== -public type ASTModAttrNode struct : ASTNode { +public type ASTModAttrNode struct : IVisitable { + compose public ASTNode node } // ========================================================== FctAttrNode ======================================================== -public type ASTFctAttrNode struct : ASTNode { +public type ASTFctAttrNode struct : IVisitable { + compose public ASTNode node } // ========================================================== AttrLstNode ======================================================== -public type ASTAttrLstNode struct : ASTNode { +public type ASTAttrLstNode struct : IVisitable { + compose public ASTNode node } // ============================================================ AttrNode ========================================================= -public type ASTAttrNode struct : ASTNode { +public type ASTAttrNode struct : IVisitable { + compose public ASTNode node } // ========================================================= ImportStmtNode ====================================================== -public type ASTImportStmtNode struct : ASTNode { +public type ASTImportStmtNode struct : IVisitable { + compose public ASTNode node } // ========================================================= ReturnStmtNode ====================================================== -public type ASTReturnStmtNode struct : ASTNode { +public type ASTReturnStmtNode struct : IVisitable { + compose public ASTNode node } // ========================================================= BreakStmtNode ======================================================= -public type ASTBreakStmtNode struct : ASTNode { +public type ASTBreakStmtNode struct : IVisitable { + compose public ASTNode node } // ======================================================== ContinueStmtNode ===================================================== -public type ASTContinueStmtNode struct : ASTNode { +public type ASTContinueStmtNode struct : IVisitable { + compose public ASTNode node } // ========================================================= AssertStmtNode ====================================================== -public type ASTAssertStmtNode struct : ASTNode { +public type ASTAssertStmtNode struct : IVisitable { + compose public ASTNode node } // ========================================================= PrintfCallNode ======================================================= -public type ASTPrintfCallNode struct : ASTNode { +public type ASTPrintfCallNode struct : IVisitable { + compose public ASTNode node } // ========================================================= SizeofCallNode ====================================================== -public type ASTSizeofCallNode struct : ASTNode { +public type ASTSizeofCallNode struct : IVisitable { + compose public ASTNode node } // ======================================================== AlignofCallNode ====================================================== -public type ASTAlignofCallNode struct : ASTNode { +public type ASTAlignofCallNode struct : IVisitable { + compose public ASTNode node } // ========================================================== LenCallNode ======================================================== -public type ASTLenCallNode struct : ASTNode { +public type ASTLenCallNode struct : IVisitable { + compose public ASTNode node } // ========================================================= PanicCallNode ======================================================= -public type ASTPanicCallNode struct : ASTNode { +public type ASTPanicCallNode struct : IVisitable { + compose public ASTNode node } // ========================================================= AssignExprNode ======================================================= -public type ASTAssignExprNode struct : ASTNode { +public type ASTAssignExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================== TernaryExprNode ====================================================== -public type ASTTernaryExprNode struct : ASTNode { +public type ASTTernaryExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================= LogicalOrExprNode ===================================================== -public type ASTLogicalOrExprNode struct : ASTNode { +public type ASTLogicalOrExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================= LogicalAndExprNode ==================================================== -public type ASTLogicalAndExprNode struct : ASTNode { +public type ASTLogicalAndExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================= BitwiseOrExprNode ===================================================== -public type ASTBitwiseOrExprNode struct : ASTNode { +public type ASTBitwiseOrExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================= BitwiseXorExprNode ==================================================== -public type ASTBitwiseXorExprNode struct : ASTNode { +public type ASTBitwiseXorExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================= BitwiseAndExprNode ==================================================== -public type ASTBitwiseAndExprNode struct : ASTNode { +public type ASTBitwiseAndExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================== EqualityExprNode ===================================================== -public type ASTEqualityExprNode struct : ASTNode { +public type ASTEqualityExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================= RelationalExprNode ==================================================== -public type ASTRelationalExprNode struct : ASTNode { +public type ASTRelationalExprNode struct : IVisitable { + compose public ASTNode node } // ========================================================= ShiftExprNode ======================================================= -public type ASTShiftExprNode struct : ASTNode { +public type ASTShiftExprNode struct : IVisitable { + compose public ASTNode node } // ======================================================== AdditiveExprNode ===================================================== -public type ASTAdditiveExprNode struct : ASTNode { +public type ASTAdditiveExprNode struct : IVisitable { + compose public ASTNode node } // ===================================================== MultiplicativeExprNode ================================================== -public type ASTMultiplicativeExprNode struct : ASTNode { +public type ASTMultiplicativeExprNode struct : IVisitable { + compose public ASTNode node } // ========================================================== CastExprNode ======================================================= -public type ASTCastExprNode struct : ASTNode { +public type ASTCastExprNode struct : IVisitable { + compose public ASTNode node } // ====================================================== PrefixUnaryExprNode ==================================================== -public type ASTPrefixUnaryExprNode struct : ASTNode { +public type ASTPrefixUnaryExprNode struct : IVisitable { + compose public ASTNode node } // ====================================================== PostfixUnaryExprNode =================================================== -public type ASTPostfixUnaryExprNode struct : ASTNode { +public type ASTPostfixUnaryExprNode struct : IVisitable { + compose public ASTNode node } // ========================================================= AtomicExprNode ====================================================== -public type ASTAtomicExprNode struct : ASTNode { +public type ASTAtomicExprNode struct : IVisitable { + compose public ASTNode node } // =========================================================== ValueNode ========================================================= -public type ASTValueNode struct : ASTNode { +public type ASTValueNode struct : IVisitable { + compose public ASTNode node } // ======================================================= PrimitiveValueNode ==================================================== -public type ASTPrimitiveValueNode struct : ASTNode { +public type ASTPrimitiveValueNode struct : IVisitable { + compose public ASTNode node } // ========================================================== FctCallNode ======================================================== -public type ASTFctCallNode struct : ASTNode { +public type ASTFctCallNode struct : IVisitable { + compose public ASTNode node } // ==================================================== ArrayInitializationNode ================================================== -public type ASTArrayInitializationNode struct : ASTNode { +public type ASTArrayInitializationNode struct : IVisitable { + compose public ASTNode node } // ==================================================== StructInstantiationNode ================================================== -public type ASTStructInstantiationNode struct : ASTNode { +public type ASTStructInstantiationNode struct : IVisitable { + compose public ASTNode node } // ========================================================= LambdaFuncNode ====================================================== -public type ASTLambdaFuncNode struct : ASTNode { +public type ASTLambdaFuncNode struct : IVisitable { + compose public ASTNode node } // ========================================================= LambdaProcNode ====================================================== -public type ASTLambdaProcNode struct : ASTNode { +public type ASTLambdaProcNode struct : IVisitable { + compose public ASTNode node } // ========================================================= LambdaExprNode ======================================================= -public type ASTLambdaExprNode struct : ASTNode { +public type ASTLambdaExprNode struct : IVisitable { + compose public ASTNode node } // ========================================================== DataTypeNode ======================================================= -public type ASTDataTypeNode struct : ASTNode { +public type ASTDataTypeNode struct : IVisitable { + compose public ASTNode node } // ======================================================== BaseDataTypeNode ===================================================== -public type ASTBaseDataTypeNode struct : ASTNode { +public type ASTBaseDataTypeNode struct : IVisitable { + compose public ASTNode node } // ======================================================= CustomDataTypeNode ==================================================== -public type ASTCustomDataTypeNode struct : ASTNode { - +public type ASTCustomDataTypeNode struct : IVisitable { + compose public ASTNode node } // ====================================================== FunctionDataTypeNode =================================================== -public type ASTFunctionDataTypeNode struct : ASTNode { +public type ASTFunctionDataTypeNode struct : IVisitable { + compose public ASTNode node } \ No newline at end of file diff --git a/src-bootstrap/ast/AbstractAstVisitor.spice b/src-bootstrap/ast/AbstractASTVisitor.spice similarity index 100% rename from src-bootstrap/ast/AbstractAstVisitor.spice rename to src-bootstrap/ast/AbstractASTVisitor.spice diff --git a/src-bootstrap/symbol/Scope.spice b/src-bootstrap/symbol/Scope.spice new file mode 100644 index 000000000..08581027f --- /dev/null +++ b/src-bootstrap/symbol/Scope.spice @@ -0,0 +1,106 @@ +import "std/data/map"; + + +type ScopeType enum { + GLOBAL, + FUNC_PROC_BODY, + LAMBDA_BODY, + STRUCT, + INTERFACE, + ENUM, + IF_ELSE_BODY, + WHILE_BODY, + FOR_BODY, + FOREACH_BODY, + UNSAFE_BODY, + ANONYMOUS_BLOCK_BODY +} + +/** + * Represents an enclosed group of instructions, which are semantically separated from other scopes. + * In the source code, scopes usually are written as curly braces. + * + * Following language structures use scopes: + * - global scope + * - functions/procedures + * - structs + * - enums + * - interfaces + * - thread blocks + * - unsafe blocks + * - for loops + * - foreach loops + * - while loops + * - if statements + * - anonymous scopes + */ +type Scope struct { + // Public members + public Scope* parent + public SourceFile* sourceFile + public Map children + public ScopeType scopeType + public SymbolTable symbolTable + public CodeLoc* codeLoc = nil + public bool isGenericScope = false + public bool isDtorScope = false + // Private members + FunctionRegistry functions + StructRegistry structs + InterfaceRegistry interfaces + Map genericTypes +} + +p Scope.ctor(Scope* parent, SourceFile* sourceFile, ScopeType scopeType, const CodeLoc *codeLoc) { + this.parent = parent; + this.sourceFile = sourceFile; + this.scopeType = scopeType; + this.symbolTable = SymbolTable(parent == nil ? nil : &parent.symbolTable, this); + this.codeLoc = codeLoc; +} + +/** + * Create a child scope and return it + * + * @param scopeName Name of the child scope + * @param scopeType Type of the child scope + * @param codeLoc Code location of the scope + * @return Child scope (heap allocated) + */ +f Scope.createChildScope(const String& scopeName, ScopeType scopeType, const CodeLoc *codeLoc) { + children[scopeName] = Scope(this, sourceFile, scopeType, codeLoc); + return &children[scopeName]; +} + +/** + * Rename the child scope. This is useful for realizing function overloading by storing a function with not + * only its name, but also its signature + * + * @param oldName Old name of the child table + * @param newName New name of the child table + */ +p Scope.renameChildScope(const String& oldName, const String& newName) { + assert children.constains(oldName) && !children.contains(newName); + children[newName] = children[oldName]; + children.erase(oldName); +} + +/** + * Duplicates the child scope by copying it. The duplicated symbols point to the original ones. + * + * @param oldName Old name of the child block + * @param newName New block name + */ +p Scope.copyChildScope(const String& oldName, const String& newName) { + assert children.constains(oldName) && !children.contains(newName); + // Create copy + Scope* child = children.at(oldName); + const heap Scope* newScope = child.deepCopyScope(); + // Save copy under new name + children[newName] = newScope; +} + +f Scope.deepCopyScope() { + const heap Scope* newScope; // ToDo: Extend + return newScope; +} \ No newline at end of file diff --git a/src-bootstrap/symbol/SymbolType.spice b/src-bootstrap/symbol/SymbolType.spice index a9e9f7137..a61ad3f9e 100644 --- a/src-bootstrap/symbol/SymbolType.spice +++ b/src-bootstrap/symbol/SymbolType.spice @@ -1,45 +1,79 @@ // Std imports -import "std/data/vector" as vec; +import "std/data/vector"; +import "../util/CommonUtil"; +import "../symbol/Scope"; public type SymbolSuperType enum { TY_INVALID, + TY_UNRESOLVED, TY_DOUBLE, TY_INT, TY_SHORT, TY_LONG, TY_BYTE, TY_CHAR, - TY_STRING, + TY_STRING, // Alias for 'const char*' TY_BOOL, - TY_GENERIC, TY_STRUCT, TY_INTERFACE, TY_ENUM, + TY_GENERIC, + TY_ALIAS, TY_DYN, TY_PTR, + TY_REF, TY_ARRAY, - TYPE_FUNCTION, + TY_FUNCTION, TY_PROCEDURE, TY_IMPORT } +type TypeChainElementData struct { + unsigned long arraySize = 0l // TY_ARRAY + Scope* bodyScope = nil // TY_STRUCT, TY_INTERFACE, TY_ENUM + bool hasCaptures = false // TY_FUNCTION, TY_PROCEDURE (lambdas) +} + type TypeChainElement struct { - SuperSymbolType superType - string subType + SymbolSuperType superType = SymbolSuperType::TY_DYN + String subType TypeChainElementData data - vec::Vector templateTypes + Vector templateTypes + Vector paramTypes // First type is the return type } -p TypeChainElement.ctor() { - this.superType = SymbolSuperType::TY_DYN; -} +f operator==(const TypeChainElement& lhs, const TypeChainElement& rhs) { + // Check super type + if lhs.superType != rhs.superType { return false; } -f TypeChainElement.equalsIgnoreArraySize(const TypeChainElement* lhs, const TypeChainElement rhs) { - return lhs.superType == rhs.superType && lhs.subType == rhs.subType /*&& lhs.templateTypes == rhs.templateTypes*/; + // Check data + if lhs.superType == TY_ARRAY { + return lhs.data.arraySize == rhs.data.arraySize; + } else if lhs.superType == TY_STRUCT || lhs.superType == TY_INTERFACE || lhs.superType == TY_ENUM { + const String& lhsSubTypeSuffix = getLastFragment(lhs.subType, SCOPE_ACCESS_TOKEN); + const String& rhsSubTypeSuffix = getLastFragment(rhs.subType, SCOPE_ACCESS_TOKE); + if lhs.superType == TY_STRUCT { + assert lhs.data.bodyScope != nil && rhs.data.bodyScope != nil; + return lhsSubTypeSuffix == rhsSubTypeSuffix && lhs.templateTypes == rhs.templateTypes; + } else if lhs.superType == TY_INTERFACE { + return lhsSubTypeSuffix == rhsSubTypeSuffix; + } else { + assert lhs.data.bodyScope != nil && rhs.data.bodyScope != nil; + return lhsSubTypeSuffix == rhsSubTypeSuffix && lhs.data.bodyScope == rhs.data.bodyScope; + } + } else { + return true; + } } +/*f operator!=(const TypeChainElement& lhs, const TypeChainElement& rhs) { + return !(lhs == rhs); +}*/ + +type TypeChain alias Vector; + public type SymbolType struct { - vec::Vector typeChain + Vector typeChain bool isBaseTypeSigned } diff --git a/src-bootstrap/util/CommonUtil.spice b/src-bootstrap/util/CommonUtil.spice new file mode 100644 index 000000000..c1e61e4bc --- /dev/null +++ b/src-bootstrap/util/CommonUtil.spice @@ -0,0 +1,7 @@ +public f getLastFragment(String &haystack, const string needle) { + const unsigned long index = haystack.rfind(needle); + if index == -1l { + return haystack; + } + return haystack.getSubstring(index + getRawLength(needle)); +} \ No newline at end of file diff --git a/src/SourceFile.cpp b/src/SourceFile.cpp index c1cd4f5f1..62c2e507b 100644 --- a/src/SourceFile.cpp +++ b/src/SourceFile.cpp @@ -662,7 +662,8 @@ void SourceFile::mergeNameRegistries(const SourceFile &importedSourceFile, const const std::string newName = importName + SCOPE_ACCESS_TOKEN + originalName; exportedNameRegistry.insert({newName, {newName, entry.targetEntry, entry.targetScope, importEntry}}); // Add the shortened name, considering the name collision - addNameRegistryEntry(originalName, entry.targetEntry, entry.targetScope, /*keepNewOnCollision=*/false, importEntry, newName); + const bool keepOnCollision = importedSourceFile.alwaysKeepSymbolsOnNameCollision; + addNameRegistryEntry(originalName, entry.targetEntry, entry.targetScope, keepOnCollision, importEntry, newName); } } diff --git a/src/SourceFile.h b/src/SourceFile.h index e292b25c2..2cf149166 100644 --- a/src/SourceFile.h +++ b/src/SourceFile.h @@ -151,6 +151,7 @@ class SourceFile { std::filesystem::path objectFilePath; bool stdFile = false; bool mainFile = true; + bool alwaysKeepSymbolsOnNameCollision = false; CompileStageType previousStage = NONE; SourceFileAntlrCtx antlrCtx; CompilerOutput compilerOutput; diff --git a/src/Spice.g4 b/src/Spice.g4 index 3b336ea82..2148dc7da 100644 --- a/src/Spice.g4 +++ b/src/Spice.g4 @@ -8,7 +8,7 @@ mainFunctionDef: fctAttr? F LESS TYPE_INT GREATER MAIN LPAREN paramLst? RPAREN L functionDef: fctAttr? specifierLst? F LESS dataType GREATER fctName (LESS typeLst GREATER)? LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE; procedureDef: fctAttr? specifierLst? P fctName (LESS typeLst GREATER)? LPAREN paramLst? RPAREN LBRACE stmtLst RBRACE; fctName: (TYPE_IDENTIFIER DOT)? IDENTIFIER | OPERATOR overloadableOp; -structDef: specifierLst? TYPE TYPE_IDENTIFIER (LESS typeLst GREATER)? STRUCT (COLON typeLst)? LBRACE fieldLst RBRACE; +structDef: specifierLst? TYPE TYPE_IDENTIFIER (LESS typeLst GREATER)? STRUCT (COLON typeLst)? LBRACE field* RBRACE; interfaceDef: specifierLst? TYPE TYPE_IDENTIFIER (LESS typeLst GREATER)? INTERFACE LBRACE signature+ RBRACE; enumDef: specifierLst? TYPE TYPE_IDENTIFIER ENUM LBRACE enumItemLst RBRACE; genericTypeDef: TYPE TYPE_IDENTIFIER typeAltsLst SEMICOLON; @@ -36,13 +36,12 @@ paramLst: declStmt (COMMA declStmt)*; argLst: assignExpr (COMMA assignExpr)*; enumItemLst: enumItem (COMMA enumItem)*; enumItem: TYPE_IDENTIFIER (ASSIGN INT_LIT)?; -fieldLst: field*; field: dataType IDENTIFIER (ASSIGN ternaryExpr)?; signature: specifierLst? (F LESS dataType GREATER | P) IDENTIFIER (LESS typeLst GREATER)? LPAREN typeLst? RPAREN SEMICOLON; stmt: (declStmt | assignExpr | returnStmt | breakStmt | continueStmt) SEMICOLON; declStmt: dataType IDENTIFIER (ASSIGN assignExpr)?; specifierLst: specifier+; -specifier: CONST | SIGNED | UNSIGNED | INLINE | PUBLIC | HEAP; +specifier: CONST | SIGNED | UNSIGNED | INLINE | PUBLIC | HEAP | COMPOSE; modAttr: MOD_ATTR_PREAMBLE LBRACKET attrLst RBRACKET; fctAttr: FCT_ATTR_PREAMBLE LBRACKET attrLst RBRACKET; attrLst: attr (COMMA attr)*; @@ -115,6 +114,7 @@ UNSIGNED: 'unsigned'; INLINE: 'inline'; PUBLIC: 'public'; HEAP: 'heap'; +COMPOSE: 'compose'; F: 'f'; P: 'p'; IF: 'if'; diff --git a/src/ast/ASTBuilder.cpp b/src/ast/ASTBuilder.cpp index b31256a6f..6a5f0ff85 100644 --- a/src/ast/ASTBuilder.cpp +++ b/src/ast/ASTBuilder.cpp @@ -359,15 +359,6 @@ std::any ASTBuilder::visitEnumItem(SpiceParser::EnumItemContext *ctx) { return concludeNode(ctx, enumItemNode); } -std::any ASTBuilder::visitFieldLst(SpiceParser::FieldLstContext *ctx) { - auto fieldLstNode = createNode(ctx); - - // Visit children - visitChildren(ctx); - - return concludeNode(ctx, fieldLstNode); -} - std::any ASTBuilder::visitField(SpiceParser::FieldContext *ctx) { auto fieldNode = createNode(ctx); @@ -378,7 +369,7 @@ std::any ASTBuilder::visitField(SpiceParser::FieldContext *ctx) { visitChildren(ctx); // Tell the data type that it is a field type - fieldNode->dataType()->isFieldType = true; + fieldNode->dataType()->setFieldTypeRecursive(); return concludeNode(ctx, fieldNode); } @@ -460,6 +451,8 @@ std::any ASTBuilder::visitSpecifier(SpiceParser::SpecifierContext *ctx) { specifierNode->type = SpecifierNode::TY_PUBLIC; else if (symbolType == SpiceParser::HEAP) specifierNode->type = SpecifierNode::TY_HEAP; + else if (symbolType == SpiceParser::COMPOSE) + specifierNode->type = SpecifierNode::TY_COMPOSITION; else assert(false && "Unknown specifier type"); } diff --git a/src/ast/ASTBuilder.h b/src/ast/ASTBuilder.h index 7f86f6a44..92d6664e8 100644 --- a/src/ast/ASTBuilder.h +++ b/src/ast/ASTBuilder.h @@ -60,7 +60,6 @@ class ASTBuilder : private CompilerPass, public SpiceVisitor { std::any visitArgLst(SpiceParser::ArgLstContext *ctx) override; std::any visitEnumItemLst(SpiceParser::EnumItemLstContext *ctx) override; std::any visitEnumItem(SpiceParser::EnumItemContext *ctx) override; - std::any visitFieldLst(SpiceParser::FieldLstContext *ctx) override; std::any visitField(SpiceParser::FieldContext *ctx) override; std::any visitSignature(SpiceParser::SignatureContext *ctx) override; std::any visitStmt(SpiceParser::StmtContext *ctx) override; diff --git a/src/ast/ASTNodes.cpp b/src/ast/ASTNodes.cpp index 311c8a70c..6b383c0a5 100644 --- a/src/ast/ASTNodes.cpp +++ b/src/ast/ASTNodes.cpp @@ -421,4 +421,15 @@ bool LambdaProcNode::returnsOnAllControlPaths(bool *overrideUnreachable) const { return body()->returnsOnAllControlPaths(overrideUnreachable); } +void DataTypeNode::setFieldTypeRecursive() { + // Set the current node to field type + isFieldType = true; + // Do the same for all template nodes + BaseDataTypeNode *baseType = baseDataType(); + CustomDataTypeNode *customType = baseType->customDataType(); + if (customType != nullptr && customType->templateTypeLst()) + for (DataTypeNode *templateNode : customType->templateTypeLst()->dataTypes()) + templateNode->setFieldTypeRecursive(); +} + } // namespace spice::compiler \ No newline at end of file diff --git a/src/ast/ASTNodes.h b/src/ast/ASTNodes.h index 3362debdb..2db236d1f 100644 --- a/src/ast/ASTNodes.h +++ b/src/ast/ASTNodes.h @@ -339,7 +339,7 @@ class StructDefNode : public ASTNode { // Public get methods [[nodiscard]] SpecifierLstNode *specifierLst() const { return getChild(); } - [[nodiscard]] FieldLstNode *fieldLst() const { return getChild(); } + [[nodiscard]] std::vector fields() const { return getChildren(); } [[nodiscard]] TypeLstNode *templateTypeLst() const { return getChild(0); } [[nodiscard]] TypeLstNode *interfaceTypeLst() const { return getChild(hasTemplateTypes ? 1 : 0); } @@ -816,8 +816,6 @@ class EnumItemNode : public ASTNode { std::any accept(AbstractASTVisitor *visitor) override { return visitor->visitEnumItem(this); } std::any accept(ParallelizableASTVisitor *visitor) const override { return visitor->visitEnumItem(this); } - // Public get methods - // Public members std::string itemName; uint32_t itemValue; @@ -826,21 +824,6 @@ class EnumItemNode : public ASTNode { EnumDefNode *enumDef = nullptr; }; -// ========================================================= FieldLstNode ======================================================== - -class FieldLstNode : public ASTNode { -public: - // Constructors - using ASTNode::ASTNode; - - // Visitor methods - std::any accept(AbstractASTVisitor *visitor) override { return visitor->visitFieldLst(this); } - std::any accept(ParallelizableASTVisitor *visitor) const override { return visitor->visitFieldLst(this); } - - // Public get methods - [[nodiscard]] std::vector fields() const { return getChildren(); } -}; - // ========================================================== FieldNode ========================================================== class FieldNode : public ASTNode { @@ -962,7 +945,16 @@ class SpecifierLstNode : public ASTNode { class SpecifierNode : public ASTNode { public: // Enums - enum SpecifierType { TY_NONE, TY_CONST, TY_SIGNED, TY_UNSIGNED, TY_INLINE, TY_PUBLIC, TY_HEAP }; + enum SpecifierType { + TY_NONE, + TY_CONST, + TY_SIGNED, + TY_UNSIGNED, + TY_INLINE, + TY_PUBLIC, + TY_HEAP, + TY_COMPOSITION, + }; // Constructors using ASTNode::ASTNode; @@ -1033,6 +1025,7 @@ class AttrNode : public ASTNode { static constexpr const char *const ATTR_CORE_LINKER_DLL = "core.linker.dll"; static constexpr const char *const ATTR_CORE_COMPILER_MANGLED_NAME = "core.compiler.mangledName"; static constexpr const char *const ATTR_CORE_COMPILER_MANGLE = "core.compiler.mangle"; + static constexpr const char *const ATTR_CORE_COMPILER_KEEP_ON_NAME_COLLISION = "core.compiler.alwaysKeepOnNameCollision"; // Constructors using ASTNode::ASTNode; @@ -1677,6 +1670,9 @@ class ValueNode : public ASTNode { [[nodiscard]] LambdaExprNode *lambdaExpr() const { return getChild(); } [[nodiscard]] DataTypeNode *nilType() const { return getChild(); } + // Other methods + [[nodiscard]] bool hasCompileTimeValue() const override { return isNil; } + // Public members bool isNil = false; }; @@ -1896,6 +1892,9 @@ class DataTypeNode : public ASTNode { [[nodiscard]] SpecifierLstNode *specifierLst() const { return getChild(); } [[nodiscard]] BaseDataTypeNode *baseDataType() const { return getChild(); } + // Other methods + void setFieldTypeRecursive(); + // Public members std::queue tmQueue; bool isParamType = false; diff --git a/src/ast/ASTVisitor.cpp b/src/ast/ASTVisitor.cpp index 234187307..d29ab8eee 100644 --- a/src/ast/ASTVisitor.cpp +++ b/src/ast/ASTVisitor.cpp @@ -60,8 +60,6 @@ std::any ASTVisitor::visitEnumItemLst(EnumItemLstNode *node) { return visitChild std::any ASTVisitor::visitEnumItem(EnumItemNode *node) { return visitChildren(node); } -std::any ASTVisitor::visitFieldLst(FieldLstNode *node) { return visitChildren(node); } - std::any ASTVisitor::visitField(FieldNode *node) { return visitChildren(node); } std::any ASTVisitor::visitSignature(SignatureNode *node) { return visitChildren(node); } diff --git a/src/ast/ASTVisitor.h b/src/ast/ASTVisitor.h index e8e3e1ac5..9b7042685 100644 --- a/src/ast/ASTVisitor.h +++ b/src/ast/ASTVisitor.h @@ -36,7 +36,6 @@ class ASTVisitor : public AbstractASTVisitor { std::any visitArgLst(ArgLstNode *node) override; std::any visitEnumItemLst(EnumItemLstNode *node) override; std::any visitEnumItem(EnumItemNode *node) override; - std::any visitFieldLst(FieldLstNode *node) override; std::any visitField(FieldNode *node) override; std::any visitSignature(SignatureNode *node) override; std::any visitStmt(StmtNode *node) override; diff --git a/src/ast/AbstractASTVisitor.h b/src/ast/AbstractASTVisitor.h index f7945e252..11f0b5243 100644 --- a/src/ast/AbstractASTVisitor.h +++ b/src/ast/AbstractASTVisitor.h @@ -35,7 +35,6 @@ class ParamLstNode; class ArgLstNode; class EnumItemLstNode; class EnumItemNode; -class FieldLstNode; class FieldNode; class SignatureNode; class StmtNode; @@ -119,7 +118,6 @@ class AbstractASTVisitor { virtual std::any visitArgLst(ArgLstNode *node) = 0; virtual std::any visitEnumItemLst(EnumItemLstNode *node) = 0; virtual std::any visitEnumItem(EnumItemNode *node) = 0; - virtual std::any visitFieldLst(FieldLstNode *node) = 0; virtual std::any visitField(FieldNode *node) = 0; virtual std::any visitSignature(SignatureNode *node) = 0; virtual std::any visitStmt(StmtNode *node) = 0; diff --git a/src/ast/ParallelizableASTVisitor.cpp b/src/ast/ParallelizableASTVisitor.cpp index d8385fed4..32ea743d1 100644 --- a/src/ast/ParallelizableASTVisitor.cpp +++ b/src/ast/ParallelizableASTVisitor.cpp @@ -71,8 +71,6 @@ std::any ParallelizableASTVisitor::visitEnumItemLst(const EnumItemLstNode *node) std::any ParallelizableASTVisitor::visitEnumItem(const EnumItemNode *node) { return visitChildren(node); } -std::any ParallelizableASTVisitor::visitFieldLst(const FieldLstNode *node) { return visitChildren(node); } - std::any ParallelizableASTVisitor::visitField(const FieldNode *node) { return visitChildren(node); } std::any ParallelizableASTVisitor::visitSignature(const SignatureNode *node) { return visitChildren(node); } diff --git a/src/ast/ParallelizableASTVisitor.h b/src/ast/ParallelizableASTVisitor.h index 4a1d56c24..a3b8dab85 100644 --- a/src/ast/ParallelizableASTVisitor.h +++ b/src/ast/ParallelizableASTVisitor.h @@ -36,7 +36,6 @@ class ParamLstNode; class ArgLstNode; class EnumItemLstNode; class EnumItemNode; -class FieldLstNode; class FieldNode; class SignatureNode; class StmtNode; @@ -119,7 +118,6 @@ class ParallelizableASTVisitor { virtual std::any visitArgLst(const ArgLstNode *node); virtual std::any visitEnumItemLst(const EnumItemLstNode *node); virtual std::any visitEnumItem(const EnumItemNode *node); - virtual std::any visitFieldLst(const FieldLstNode *node); virtual std::any visitField(const FieldNode *node); virtual std::any visitSignature(const SignatureNode *node); virtual std::any visitStmt(const StmtNode *node); diff --git a/src/importcollector/ImportCollector.cpp b/src/importcollector/ImportCollector.cpp index 5d703fa19..a58129c26 100644 --- a/src/importcollector/ImportCollector.cpp +++ b/src/importcollector/ImportCollector.cpp @@ -85,6 +85,10 @@ std::any ImportCollector::visitModAttr(ModAttrNode *node) { resourceManager.linker.addLinkerFlag(value); } + // core.compiler.keep-on-name-collision + if (const AttrNode *attr = attrs->getAttrByName(AttrNode::ATTR_CORE_COMPILER_KEEP_ON_NAME_COLLISION)) + sourceFile->alwaysKeepSymbolsOnNameCollision = attr->getValue().boolValue; + return nullptr; } diff --git a/src/irgenerator/DebugInfoGenerator.cpp b/src/irgenerator/DebugInfoGenerator.cpp index 864bb79c7..640c7ac75 100644 --- a/src/irgenerator/DebugInfoGenerator.cpp +++ b/src/irgenerator/DebugInfoGenerator.cpp @@ -34,7 +34,7 @@ void DebugInfoGenerator::initialize(const std::string &sourceFileName, std::file module->addModuleFlag(llvm::Module::Warning, "Debug Info Version", llvm::DEBUG_METADATA_VERSION); module->addModuleFlag(llvm::Module::Error, "wchar_size", 4); module->addModuleFlag(llvm::Module::Min, "PIC Level", llvm::PICLevel::BigPIC); - module->addModuleFlag(llvm::Module::Max, "PIE Level", llvm::PIELevel::Default); + module->addModuleFlag(llvm::Module::Max, "PIE Level", llvm::PIELevel::Large); module->addModuleFlag(llvm::Module::Max, "uwtable", 2); module->addModuleFlag(llvm::Module::Max, "frame-pointer", 2); diff --git a/src/irgenerator/GenExpressions.cpp b/src/irgenerator/GenExpressions.cpp index 313327375..894aa65e1 100644 --- a/src/irgenerator/GenExpressions.cpp +++ b/src/irgenerator/GenExpressions.cpp @@ -707,12 +707,15 @@ std::any IRGenerator::visitPostfixUnaryExpr(const PostfixUnaryExprNode *node) { Scope *structScope = lhsSTy.getBodyScope(); // Retrieve field entry - lhs.entry = structScope->lookupStrict(fieldName); + std::vector indexPath; + lhs.entry = structScope->symbolTable.lookupInComposedFields(fieldName, indexPath); assert(lhs.entry != nullptr); SymbolType fieldSymbolType = lhs.entry->getType(); // Get address of the field in the struct instance - llvm::Value *indices[2] = {builder.getInt32(0), builder.getInt32(lhs.entry->orderIndex)}; + std::vector indices = {builder.getInt32(0)}; + for (size_t index : indexPath) + indices.push_back(builder.getInt32(index)); llvm::Value *memberAddress = builder.CreateInBoundsGEP(lhsSTy.toLLVMType(context, structScope->parent), lhs.ptr, indices); memberAddress->setName(fieldName + "_addr"); diff --git a/src/irgenerator/GenImplicit.cpp b/src/irgenerator/GenImplicit.cpp index 5fd84321f..d4441bc4f 100644 --- a/src/irgenerator/GenImplicit.cpp +++ b/src/irgenerator/GenImplicit.cpp @@ -230,16 +230,23 @@ void IRGenerator::generateDefaultDefaultCtor(const Function *ctorFunction) { } // Store default field values - if (cliOptions.buildMode == BuildMode::DEBUG && fieldNode->defaultValue() != nullptr) { - assert(fieldNode->defaultValue()->hasCompileTimeValue()); + if (fieldNode->defaultValue() != nullptr || cliOptions.buildMode == BuildMode::DEBUG) { // Retrieve field address if (!thisAddressLoaded) thisAddressLoaded = builder.CreateLoad(builder.getPtrTy(), thisAddress); llvm::Value *indices[2] = {builder.getInt32(0), builder.getInt32(i)}; llvm::Value *fieldAddress = builder.CreateInBoundsGEP(structType, thisAddressLoaded, indices); + // Retrieve default value + llvm::Value *value; + if (fieldNode->defaultValue() != nullptr) { + assert(fieldNode->defaultValue()->hasCompileTimeValue()); + const CompileTimeValue compileTimeValue = fieldNode->defaultValue()->getCompileTimeValue(); + value = getConst(compileTimeValue, fieldType, fieldNode->defaultValue()); + } else { + assert(cliOptions.buildMode == BuildMode::DEBUG); + value = getDefaultValueForSymbolType(fieldType); + } // Store default value - const CompileTimeValue compileTimeValue = fieldNode->defaultValue()->getCompileTimeValue(); - llvm::Value *value = getConst(compileTimeValue, fieldType, fieldNode->defaultValue()); builder.CreateStore(value, fieldAddress); } } diff --git a/src/irgenerator/GenTopLevelDefinitions.cpp b/src/irgenerator/GenTopLevelDefinitions.cpp index 106ba6351..dfb95c8fe 100644 --- a/src/irgenerator/GenTopLevelDefinitions.cpp +++ b/src/irgenerator/GenTopLevelDefinitions.cpp @@ -489,8 +489,8 @@ std::any IRGenerator::visitStructDef(const StructDefNode *node) { // Collect concrete field types std::vector fieldTypes; - fieldTypes.reserve(node->fieldLst()->fields().size()); - for (const FieldNode *field : node->fieldLst()->fields()) { + fieldTypes.reserve(node->fields().size()); + for (const FieldNode *field : node->fields()) { SymbolTableEntry *fieldEntry = currentScope->lookupStrict(field->fieldName); assert(fieldEntry && !fieldEntry->getType().hasAnyGenericParts()); fieldTypes.push_back(fieldEntry->getType().toLLVMType(context, currentScope)); diff --git a/src/irgenerator/GenValues.cpp b/src/irgenerator/GenValues.cpp index d37e24f03..7126d983f 100644 --- a/src/irgenerator/GenValues.cpp +++ b/src/irgenerator/GenValues.cpp @@ -376,8 +376,7 @@ std::any IRGenerator::visitLambdaFunc(const LambdaFuncNode *node) { std::vector paramTypes; // Change scope - Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); - ScopeHandle scopeHandle(this, bodyScope, ScopeType::LAMBDA_BODY, node); + Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); // If there are captures, we pass them in a struct as the first function argument const std::unordered_map &captures = bodyScope->symbolTable.captures; @@ -531,7 +530,7 @@ std::any IRGenerator::visitLambdaFunc(const LambdaFuncNode *node) { allocaInsertInst = allocaInsertInstOrig; // Change back to original scope - scopeHandle.leaveScopeEarly(); + currentScope = currentScope->parent; // Verify function verifyFunction(lambda, node->codeLoc); @@ -548,8 +547,7 @@ std::any IRGenerator::visitLambdaProc(const LambdaProcNode *node) { std::vector paramTypes; // Change scope - Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); - ScopeHandle scopeHandle(this, bodyScope, ScopeType::LAMBDA_BODY, node); + Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); // If there are captures, we pass them in a struct as the first function argument const std::unordered_map &captures = bodyScope->symbolTable.captures; @@ -690,7 +688,7 @@ std::any IRGenerator::visitLambdaProc(const LambdaProcNode *node) { allocaInsertInst = allocaInsertInstOrig; // Change back to original scope - scopeHandle.leaveScopeEarly(); + currentScope = currentScope->parent; // Verify function verifyFunction(lambda, node->codeLoc); @@ -707,8 +705,7 @@ std::any IRGenerator::visitLambdaExpr(const LambdaExprNode *node) { std::vector paramTypes; // Change scope - Scope *bodyScope = currentScope->getChildScope(node->getScopeId()); - ScopeHandle scopeHandle(this, bodyScope, ScopeType::LAMBDA_BODY, node); + Scope *bodyScope = currentScope = currentScope->getChildScope(node->getScopeId()); // If there are captures, we pass them in a struct as the first function argument const std::unordered_map &captures = bodyScope->symbolTable.captures; @@ -850,7 +847,7 @@ std::any IRGenerator::visitLambdaExpr(const LambdaExprNode *node) { allocaInsertInst = allocaInsertInstOrig; // Change back to original scope - scopeHandle.leaveScopeEarly(); + currentScope = currentScope->parent; // Verify function verifyFunction(lambda, node->codeLoc); diff --git a/src/symboltablebuilder/Scope.cpp b/src/symboltablebuilder/Scope.cpp index 71bafce76..08fbbabaf 100644 --- a/src/symboltablebuilder/Scope.cpp +++ b/src/symboltablebuilder/Scope.cpp @@ -17,7 +17,7 @@ namespace spice::compiler { * @param codeLoc Code location of the scope * @return Child scope (heap allocated) */ -Scope *Scope::createChildScope(const std::string &scopeName, const ScopeType &scopeType, const CodeLoc *codeLoc) { +Scope *Scope::createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *codeLoc) { children.insert({scopeName, std::make_shared(this, sourceFile, scopeType, codeLoc)}); return children.at(scopeName).get(); } diff --git a/src/symboltablebuilder/Scope.h b/src/symboltablebuilder/Scope.h index e0dc6cb97..484ccf08b 100644 --- a/src/symboltablebuilder/Scope.h +++ b/src/symboltablebuilder/Scope.h @@ -52,7 +52,7 @@ enum class ScopeType : uint8_t { class Scope { public: // Constructors - Scope(Scope *parent, SourceFile *sourceFile, const ScopeType &scopeType, const CodeLoc *codeLoc) + Scope(Scope *parent, SourceFile *sourceFile, ScopeType scopeType, const CodeLoc *codeLoc) : parent(parent), sourceFile(sourceFile), type(scopeType), codeLoc(codeLoc) {} // Friend classes @@ -62,7 +62,7 @@ class Scope { // Public methods // Scope management - Scope *createChildScope(const std::string &scopeName, const ScopeType &scopeType, const CodeLoc *codeLoc); + Scope *createChildScope(const std::string &scopeName, ScopeType scopeType, const CodeLoc *codeLoc); void renameChildScope(const std::string &oldName, const std::string &newName); void copyChildScope(const std::string &oldName, const std::string &newName); std::shared_ptr deepCopyScope(); diff --git a/src/symboltablebuilder/ScopeHandle.cpp b/src/symboltablebuilder/ScopeHandle.cpp index e66957996..8cce0a90f 100644 --- a/src/symboltablebuilder/ScopeHandle.cpp +++ b/src/symboltablebuilder/ScopeHandle.cpp @@ -22,6 +22,7 @@ ScopeHandle::ScopeHandle(IRGenerator *generator, Scope *childScope, const ScopeT generator->changeToParentScope(scopeType); generator->diGenerator.popLexicalBlock(); }) { + assert(scopeType != ScopeType::FUNC_PROC_BODY); // Functions/procedures manage scopes manually generator->changeToScope(childScope, scopeType); generator->diGenerator.pushLexicalBlock(node); } diff --git a/src/symboltablebuilder/SymbolTable.cpp b/src/symboltablebuilder/SymbolTable.cpp index 0ae9381b6..0fdf49588 100644 --- a/src/symboltablebuilder/SymbolTable.cpp +++ b/src/symboltablebuilder/SymbolTable.cpp @@ -122,6 +122,48 @@ SymbolTableEntry *SymbolTable::lookupStrict(const std::string &name) { return nullptr; } +/** + * Check if a symbol exists in one of the composed field scopes of the current scope and return it if possible. + * This only works if the current scope is a struct scope. + * + * @param name Name of the desired symbol + * @param indexPath How to index the found symbol using order indices (e.g. for GEP) + * @return Desired symbol / nullptr if the symbol was not found + */ +SymbolTableEntry *SymbolTable::lookupInComposedFields(const std::string &name, std::vector &indexPath) { + assert(scope->type == ScopeType::STRUCT); + + // Check if we have a symbol with this name in the current scope + if (SymbolTableEntry *result = lookupStrict(name)) { + indexPath.push_back(result->orderIndex); + return result; + } + + // If it was not found in the current scope, loop through all composed fields in this scope + for (size_t i = 0; i < scope->getFieldCount(); i++) { + const SymbolTableEntry *fieldEntry = lookupStrictByIndex(i); + + // Skip all fields that are not composition fields + if (!fieldEntry->getType().specifiers.isComposition) + continue; + + // Add the current field's order index to the index path + indexPath.push_back(fieldEntry->orderIndex); + + // Search in the composed field's body scope + Scope *searchScope = fieldEntry->getType().getBodyScope(); + assert(searchScope != nullptr); + if (SymbolTableEntry *result = searchScope->symbolTable.lookupInComposedFields(name, indexPath)) + return result; + + // Remove the current field's order index from the index path + indexPath.pop_back(); + } + + // Symbol was not found in current scope, return nullptr + return nullptr; +} + /** * Check if an order index exists in the current or any parent scope and returns it if possible. * Warning: Unlike the `lookup` method, this one doesn't consider the parent scopes diff --git a/src/symboltablebuilder/SymbolTable.h b/src/symboltablebuilder/SymbolTable.h index bcfcd5182..0e0d7fba9 100644 --- a/src/symboltablebuilder/SymbolTable.h +++ b/src/symboltablebuilder/SymbolTable.h @@ -40,6 +40,7 @@ class SymbolTable { void copySymbol(const std::string &originalName, const std::string &newName); SymbolTableEntry *lookup(const std::string &symbolName); SymbolTableEntry *lookupStrict(const std::string &symbolName); + SymbolTableEntry *lookupInComposedFields(const std::string &name, std::vector &indexPath); SymbolTableEntry *lookupStrictByIndex(unsigned int orderIndex); SymbolTableEntry *lookupAnonymous(const CodeLoc &codeLoc, size_t numericSuffix = 0); Capture *lookupCapture(const std::string &symbolName); diff --git a/src/symboltablebuilder/SymbolTableBuilder.cpp b/src/symboltablebuilder/SymbolTableBuilder.cpp index d56252663..cf1d0de4a 100644 --- a/src/symboltablebuilder/SymbolTableBuilder.cpp +++ b/src/symboltablebuilder/SymbolTableBuilder.cpp @@ -185,8 +185,8 @@ std::any SymbolTableBuilder::visitStructDef(StructDefNode *node) { rootScope->createChildScope(STRUCT_SCOPE_PREFIX + node->structName, ScopeType::STRUCT, &node->codeLoc); currentScope->isGenericScope = node->hasTemplateTypes; - // Visit struct field list - visit(node->fieldLst()); + // Visit children + visitChildren(node); // Leave the struct scope currentScope = node->structScope->parent; diff --git a/src/symboltablebuilder/SymbolType.cpp b/src/symboltablebuilder/SymbolType.cpp index 63d2d2dfc..4e9b47a52 100644 --- a/src/symboltablebuilder/SymbolType.cpp +++ b/src/symboltablebuilder/SymbolType.cpp @@ -343,7 +343,7 @@ bool SymbolType::isCoveredByGenericTypeList(std::vector &genericTyp // Check if the symbol type itself is generic if (baseType.is(TY_GENERIC)) { return std::ranges::any_of(genericTypeList, [&](GenericType &t) { - if (t == baseType) { + if (baseType.matches(t, true, true, true)) { t.used = true; return true; } @@ -384,6 +384,8 @@ std::string SymbolType::getName(bool withSize) const { // NOLINT(misc-no-recursi name << "public "; if (specifiers.isInline && !defaultForSuperType.isInline) name << "inline "; + if (specifiers.isComposition && !defaultForSuperType.isComposition) + name << "compose "; if (specifiers.isConst && !defaultForSuperType.isConst) name << "const "; if (specifiers.isHeap && !defaultForSuperType.isHeap) diff --git a/src/symboltablebuilder/SymbolType.h b/src/symboltablebuilder/SymbolType.h index 0946aaf68..11c464d18 100644 --- a/src/symboltablebuilder/SymbolType.h +++ b/src/symboltablebuilder/SymbolType.h @@ -153,7 +153,7 @@ class SymbolType { [[nodiscard]] ALWAYS_INLINE SymbolType removeReferenceWrapper() const { return isRef() ? getContainedTy() : *this; } [[nodiscard]] SymbolType getBaseType() const { assert(!typeChain.empty()); - return SymbolType({typeChain.front()}); + return SymbolType({typeChain.front()}, specifiers); } [[nodiscard]] bool hasAnyGenericParts() const; void setTemplateTypes(const std::vector &templateTypes); diff --git a/src/symboltablebuilder/TypeChain.cpp b/src/symboltablebuilder/TypeChain.cpp index 226404629..774eb196f 100644 --- a/src/symboltablebuilder/TypeChain.cpp +++ b/src/symboltablebuilder/TypeChain.cpp @@ -14,22 +14,20 @@ bool operator==(const SymbolType::TypeChainElement &lhs, const SymbolType::TypeC switch (lhs.superType) { case TY_ARRAY: return lhs.data.arraySize == rhs.data.arraySize; - case TY_STRUCT: { - assert(lhs.data.bodyScope != nullptr && rhs.data.bodyScope != nullptr); - const std::string lhsSubTypeSuffix = CommonUtil::getLastFragment(lhs.subType, SCOPE_ACCESS_TOKEN); - const std::string rhsSubTypeSuffix = CommonUtil::getLastFragment(rhs.subType, SCOPE_ACCESS_TOKEN); - return lhsSubTypeSuffix == rhsSubTypeSuffix && lhs.templateTypes == rhs.templateTypes; - } - case TY_INTERFACE: { - const std::string lhsSubTypeSuffix = CommonUtil::getLastFragment(lhs.subType, SCOPE_ACCESS_TOKEN); - const std::string rhsSubTypeSuffix = CommonUtil::getLastFragment(rhs.subType, SCOPE_ACCESS_TOKEN); - return lhsSubTypeSuffix == rhsSubTypeSuffix; - } + case TY_STRUCT: + case TY_INTERFACE: case TY_ENUM: { - assert(lhs.data.bodyScope != nullptr && rhs.data.bodyScope != nullptr); const std::string lhsSubTypeSuffix = CommonUtil::getLastFragment(lhs.subType, SCOPE_ACCESS_TOKEN); const std::string rhsSubTypeSuffix = CommonUtil::getLastFragment(rhs.subType, SCOPE_ACCESS_TOKEN); - return lhsSubTypeSuffix == rhsSubTypeSuffix && lhs.data.bodyScope == rhs.data.bodyScope; + if (lhs.superType == TY_STRUCT) { + assert(lhs.data.bodyScope != nullptr && rhs.data.bodyScope != nullptr); + return lhsSubTypeSuffix == rhsSubTypeSuffix && lhs.templateTypes == rhs.templateTypes; + } else if (lhs.superType == TY_INTERFACE) { + return lhsSubTypeSuffix == rhsSubTypeSuffix; + } else { + assert(lhs.data.bodyScope != nullptr && rhs.data.bodyScope != nullptr); + return lhsSubTypeSuffix == rhsSubTypeSuffix && lhs.data.bodyScope == rhs.data.bodyScope; + } } case TY_FUNCTION: case TY_PROCEDURE: diff --git a/src/symboltablebuilder/TypeSpecifiers.cpp b/src/symboltablebuilder/TypeSpecifiers.cpp index caf08a328..facaf8afe 100644 --- a/src/symboltablebuilder/TypeSpecifiers.cpp +++ b/src/symboltablebuilder/TypeSpecifiers.cpp @@ -13,7 +13,7 @@ TypeSpecifiers TypeSpecifiers::of(uint16_t superType) { case TY_INT: // fall-through case TY_SHORT: // fall-through case TY_LONG: - return {/*const*/ false, /*signed*/ true, /*unsigned*/ false, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ false, /*signed*/ true, /*unsigned*/ false, /*heap*/ false}; case TY_BYTE: // fall-through case TY_CHAR: // fall-through case TY_STRING: // fall-through @@ -21,26 +21,26 @@ TypeSpecifiers TypeSpecifiers::of(uint16_t superType) { case TY_PTR: // fall-through case TY_REF: // fall-through case TY_ARRAY: // fall-through - return {/*const*/ false, /*signed*/ false, /*unsigned*/ true, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ false, /*signed*/ false, /*unsigned*/ true, /*heap*/ false}; case TY_GENERIC: // Generics must be non-signed and non-unsigned at the same time to ensure a proper function matching - return {/*const*/ false, /*signed*/ false, /*unsigned*/ false, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ false, /*signed*/ false, /*unsigned*/ false, /*heap*/ false}; case TY_STRUCT: // fall-through case TY_INTERFACE: - return {/*const*/ false, /*signed*/ false, /*unsigned*/ true, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ false, /*signed*/ false, /*unsigned*/ true, /*heap*/ false}; case TY_ENUM: // fall-through case TY_ALIAS: - return {/*const*/ true, /*signed*/ false, /*unsigned*/ true, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ true, /*signed*/ false, /*unsigned*/ true, /*heap*/ false}; case TY_FUNCTION: // fall-through case TY_PROCEDURE: - return {/*const*/ false, /*signed*/ false, /*unsigned*/ true, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ false, /*signed*/ false, /*unsigned*/ true, /*heap*/ false}; case TY_IMPORT: - return {/*const*/ true, /*signed*/ false, /*unsigned*/ true, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ true, /*signed*/ false, /*unsigned*/ true, /*heap*/ false}; case TY_DYN: case TY_INVALID: case TY_UNRESOLVED: // Return all-false specifiers to not match anything - return {/*const*/ false, /*signed*/ false, /*unsigned*/ false, /*inline*/ false, /*public*/ false, /*heap*/ false}; + return {/*const*/ false, /*signed*/ false, /*unsigned*/ false, /*heap*/ false}; default: throw CompilerError(UNHANDLED_BRANCH, "Symbol specifier fallthrough"); // GCOV_EXCL_LINE } @@ -78,14 +78,6 @@ TypeSpecifiers TypeSpecifiers::merge(const TypeSpecifiers &other) const { bool TypeSpecifiers::match(TypeSpecifiers otherSpecifiers, bool allowConstify) const { TypeSpecifiers thisSpecifiers = *this; - // Zero out public specifier to ignore it while matching - thisSpecifiers.isPublic = false; - otherSpecifiers.isPublic = false; - - // Zero out inline specifier to ignore it while matching - thisSpecifiers.isInline = false; - otherSpecifiers.isInline = false; - // If allowConstify is enabled, only allow to match lhs=const and rhs=non-const if (allowConstify && thisSpecifiers.isConst && !otherSpecifiers.isConst) otherSpecifiers.isConst = true; @@ -111,10 +103,8 @@ bool operator==(const TypeSpecifiers &lhs, const TypeSpecifiers &rhs) { const bool isConst = lhs.isConst == rhs.isConst; const bool isSigned = lhs.isSigned == rhs.isSigned; const bool isUnsigned = lhs.isUnsigned == rhs.isUnsigned; - const bool isInline = lhs.isInline == rhs.isInline; - const bool isPublic = lhs.isPublic == rhs.isPublic; const bool isHeap = lhs.isHeap == rhs.isHeap; - return isConst && isSigned && isUnsigned && isInline && isPublic && isHeap; + return isConst && isSigned && isUnsigned && isHeap; } bool TypeSpecifiers::getBit(uint8_t index) const { @@ -125,12 +115,14 @@ bool TypeSpecifiers::getBit(uint8_t index) const { return isSigned; case BIT_INDEX_UNSIGNED: return isUnsigned; - case BIT_INDEX_INLINE: - return isInline; - case BIT_INDEX_PUBLIC: - return isPublic; case BIT_INDEX_HEAP: return isHeap; + case BIT_INDEX_PUBLIC: + return isPublic; + case BIT_INDEX_INLINE: + return isInline; + case BIT_INDEX_COMPOSITION: + return isComposition; default: throw CompilerError(UNHANDLED_BRANCH, "Bit index fallthrough"); // GCOV_EXCL_LINE } @@ -144,12 +136,14 @@ bool TypeSpecifiers::setBit(uint8_t index, bool value) { return isSigned = value; case BIT_INDEX_UNSIGNED: return isUnsigned = value; - case BIT_INDEX_INLINE: - return isInline = value; - case BIT_INDEX_PUBLIC: - return isPublic = value; case BIT_INDEX_HEAP: return isHeap = value; + case BIT_INDEX_PUBLIC: + return isPublic = value; + case BIT_INDEX_INLINE: + return isInline = value; + case BIT_INDEX_COMPOSITION: + return isComposition = value; default: throw CompilerError(UNHANDLED_BRANCH, "Bit index fallthrough"); // GCOV_EXCL_LINE } diff --git a/src/symboltablebuilder/TypeSpecifiers.h b/src/symboltablebuilder/TypeSpecifiers.h index 4ec61681d..e9fec5aad 100644 --- a/src/symboltablebuilder/TypeSpecifiers.h +++ b/src/symboltablebuilder/TypeSpecifiers.h @@ -7,20 +7,21 @@ namespace spice::compiler { // Bit indices from right to left -const uint8_t BIT_INDEX_HEAP = 0; -const uint8_t BIT_INDEX_PUBLIC = 1; -const uint8_t BIT_INDEX_INLINE = 2; -const uint8_t BIT_INDEX_UNSIGNED = 3; -const uint8_t BIT_INDEX_SIGNED = 4; -const uint8_t BIT_INDEX_CONST = 5; +const uint8_t BIT_INDEX_COMPOSITION = 0; +const uint8_t BIT_INDEX_INLINE = 1; +const uint8_t BIT_INDEX_PUBLIC = 2; +const uint8_t BIT_INDEX_HEAP = 3; +const uint8_t BIT_INDEX_UNSIGNED = 4; +const uint8_t BIT_INDEX_SIGNED = 5; +const uint8_t BIT_INDEX_CONST = 6; const uint8_t BIT_INDEX_MAX = BIT_INDEX_CONST; // Please adjust if something changes above class TypeSpecifiers { public: // Constructors TypeSpecifiers() = default; - TypeSpecifiers(bool isConst, bool isSigned, bool isUnsigned, bool isInline, bool isPublic, bool isHeap) - : isConst(isConst), isSigned(isSigned), isUnsigned(isUnsigned), isInline(isInline), isPublic(isPublic), isHeap(isHeap) {} + TypeSpecifiers(bool isConst, bool isSigned, bool isUnsigned, bool isHeap) + : isConst(isConst), isSigned(isSigned), isUnsigned(isUnsigned), isHeap(isHeap) {} // Public static methods static TypeSpecifiers of(uint16_t superType); @@ -38,9 +39,10 @@ class TypeSpecifiers { bool isConst : 1 = false; bool isSigned : 1 = false; bool isUnsigned : 1 = false; - bool isInline : 1 = false; - bool isPublic : 1 = false; bool isHeap : 1 = false; + bool isPublic : 1 = false; + bool isInline : 1 = false; + bool isComposition : 1 = false; private: // Private methods diff --git a/src/typechecker/FunctionManager.cpp b/src/typechecker/FunctionManager.cpp index 19769b2af..0ba9ae8d6 100644 --- a/src/typechecker/FunctionManager.cpp +++ b/src/typechecker/FunctionManager.cpp @@ -364,17 +364,15 @@ MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&mat const SymbolType &thisType = candidate.thisType; if (!thisType.is(TY_DYN)) { - // Update struct scope of 'this' type to the substantiated struct scope - std::vector concreteTemplateTypes; - if (!thisType.hasAnyGenericParts()) - concreteTemplateTypes = thisType.getTemplateTypes(); - const std::string structSignature = Struct::getSignature(thisType.getSubType(), concreteTemplateTypes); - Scope *substantiatedStructBodyScope = matchScope->parent->getChildScope(STRUCT_SCOPE_PREFIX + structSignature); - assert(substantiatedStructBodyScope != nullptr); - candidate.thisType.setBodyScope(substantiatedStructBodyScope); - - // Set match scope to the substantiated struct scope - matchScope = substantiatedStructBodyScope; + // If we only have the generic struct scope, lookup the concrete manifestation scope + if (matchScope->isGenericScope) { + const std::string structName = thisType.getOriginalSubType(); + Scope *scope = thisType.getBodyScope()->parent; + Struct *spiceStruct = StructManager::matchStruct(scope, structName, thisType.getTemplateTypes(), candidate.declNode); + assert(spiceStruct != nullptr); + matchScope = spiceStruct->structScope; + } + candidate.thisType.setBodyScope(matchScope); } // Clear template types of candidate, since they are not needed anymore diff --git a/src/typechecker/TypeChecker.cpp b/src/typechecker/TypeChecker.cpp index 2139c4d59..dd2394350 100644 --- a/src/typechecker/TypeChecker.cpp +++ b/src/typechecker/TypeChecker.cpp @@ -1221,7 +1221,7 @@ std::any TypeChecker::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { // Check if lhs is enum or strobj SymbolType lhsBaseTy = lhsType; - TypeChecker::autoDeReference(lhsBaseTy); + autoDeReference(lhsBaseTy); if (!lhsBaseTy.is(TY_STRUCT)) SOFT_ERROR_ER(node, INVALID_MEMBER_ACCESS, "Cannot apply member access operator on " + lhsType.getName()) @@ -1229,8 +1229,19 @@ std::any TypeChecker::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { const std::string &structName = lhsBaseTy.getSubType(); Scope *structScope = lhsBaseTy.getBodyScope(); + // If we only have the generic struct scope, lookup the concrete manifestation scope + if (structScope->isGenericScope) { + const std::string structName = lhsBaseTy.getOriginalSubType(); + Scope *matchScope = lhsBaseTy.getBodyScope()->parent; + Struct *spiceStruct = StructManager::matchStruct(matchScope, structName, lhsBaseTy.getTemplateTypes(), node); + assert(spiceStruct != nullptr); + structScope = spiceStruct->structScope; + } + assert(!structScope->isGenericScope); // At this point we always expect a substantiation scope + // Get accessed field - SymbolTableEntry *memberEntry = structScope->lookupStrict(fieldName); + std::vector indexPath; + SymbolTableEntry *memberEntry = structScope->symbolTable.lookupInComposedFields(fieldName, indexPath); if (!memberEntry) SOFT_ERROR_ER(node, REFERENCED_UNDEFINED_VARIABLE, "Field '" + node->identifier + "' not found in struct " + structName) SymbolType memberType = memberEntry->getType(); @@ -1774,7 +1785,7 @@ bool TypeChecker::visitMethodCall(FctCallNode *node, Scope *structScope) const { localArgType = mapLocalTypeToImportedScopeType(data.calleeParentScope, localArgType); // 'this' type SymbolType localThisType = data.thisType; - TypeChecker::autoDeReference(localThisType); + autoDeReference(localThisType); localThisType = mapLocalTypeToImportedScopeType(data.calleeParentScope, localThisType); // Retrieve function object @@ -2138,6 +2149,10 @@ std::any TypeChecker::visitDataType(DataTypeNode *node) { "The heap specifier can only be applied to symbols of pointer type, you provided " + baseType.getName()) type.specifiers.isHeap = true; + } else if (specifier->type == SpecifierNode::TY_COMPOSITION && node->isFieldType) { + if (!type.is(TY_STRUCT)) + SOFT_ERROR_ST(specifier, SPECIFIER_AT_ILLEGAL_CONTEXT, "The compose specifier can only be used on plain struct fields") + type.specifiers.isComposition = true; } else if (specifier->type == SpecifierNode::TY_PUBLIC && (node->isFieldType || node->isGlobalType)) { type.specifiers.isPublic = true; } else { diff --git a/src/typechecker/TypeCheckerPrepare.cpp b/src/typechecker/TypeCheckerPrepare.cpp index 0975b6421..00142d389 100644 --- a/src/typechecker/TypeCheckerPrepare.cpp +++ b/src/typechecker/TypeCheckerPrepare.cpp @@ -348,8 +348,8 @@ std::any TypeChecker::visitStructDefPrepare(StructDefNode *node) { // Retrieve field types std::vector fieldTypes; - fieldTypes.reserve(node->fieldLst()->fields().size()); - for (FieldNode *field : node->fieldLst()->fields()) { + fieldTypes.reserve(node->fields().size()); + for (FieldNode *field : node->fields()) { // Visit field type auto fieldType = std::any_cast(visit(field)); if (fieldType.is(TY_UNRESOLVED)) @@ -375,7 +375,7 @@ std::any TypeChecker::visitStructDefPrepare(StructDefNode *node) { // Check if all template types were used by at least one field if (std::ranges::any_of(templateTypesGeneric, [&](const GenericType &genericType) { return !genericType.used; })) - throw SemanticError(node->templateTypeLst(), GENERIC_TYPE_NOT_USED, "Generic type was not used by the struct fields"); + softError(node->templateTypeLst(), GENERIC_TYPE_NOT_USED, "Generic type was not used by the struct fields"); // Change to the root scope currentScope = rootScope; diff --git a/src/util/CommonUtil.h b/src/util/CommonUtil.h index 6ef7ff3df..4f74912a4 100644 --- a/src/util/CommonUtil.h +++ b/src/util/CommonUtil.h @@ -2,7 +2,6 @@ #pragma once -#include #include #include diff --git a/src/visualizer/ASTVisualizer.h b/src/visualizer/ASTVisualizer.h index a4fa4dfc4..950d60662 100644 --- a/src/visualizer/ASTVisualizer.h +++ b/src/visualizer/ASTVisualizer.h @@ -52,7 +52,6 @@ class ASTVisualizer : private CompilerPass, public AbstractASTVisitor { std::any visitArgLst(ArgLstNode *ctx) override { return buildNode(ctx); } std::any visitEnumItemLst(EnumItemLstNode *ctx) override { return buildNode(ctx); } std::any visitEnumItem(EnumItemNode *ctx) override { return buildNode(ctx); } - std::any visitFieldLst(FieldLstNode *ctx) override { return buildNode(ctx); } std::any visitField(FieldNode *ctx) override { return buildNode(ctx); } std::any visitSignature(SignatureNode *ctx) override { return buildNode(ctx); } std::any visitStmt(StmtNode *ctx) override { return buildNode(ctx); } diff --git a/src/visualizer/CSTVisualizer.h b/src/visualizer/CSTVisualizer.h index 2992ef2c7..9344cf0ed 100644 --- a/src/visualizer/CSTVisualizer.h +++ b/src/visualizer/CSTVisualizer.h @@ -49,7 +49,6 @@ class CSTVisualizer : private CompilerPass, public SpiceVisitor { std::any visitAssertStmt(SpiceParser::AssertStmtContext *ctx) override { return buildRule(ctx); } std::any visitAnonymousBlockStmt(SpiceParser::AnonymousBlockStmtContext *ctx) override { return buildRule(ctx); } std::any visitStmtLst(SpiceParser::StmtLstContext *ctx) override { return buildRule(ctx); } - std::any visitFieldLst(SpiceParser::FieldLstContext *ctx) override { return buildRule(ctx); } std::any visitField(SpiceParser::FieldContext *ctx) override { return buildRule(ctx); } std::any visitSignature(SpiceParser::SignatureContext *ctx) override { return buildRule(ctx); } std::any visitTypeLst(SpiceParser::TypeLstContext *ctx) override { return buildRule(ctx); } diff --git a/std/data/doubly-linked-list.spice b/std/data/doubly-linked-list.spice index 9819e10d7..a46b41e89 100644 --- a/std/data/doubly-linked-list.spice +++ b/std/data/doubly-linked-list.spice @@ -1,6 +1,5 @@ -// Link external functions -ext f malloc(long); -ext p free(heap byte*); +import "std/type/result"; +import "std/type/error"; // Add generic type definitions type T dyn; @@ -8,12 +7,17 @@ type T dyn; /** * Node of a DoublyLinkedList */ -public type Node struct { - heap Node* prev +type Node struct { T value + heap Node* prev heap Node* next } +p Node.ctor(const T& value) { + this.value = value; + this.next = nil*>; +} + /** * A doubly linked list is a common, dynamically resizable data structure to store uniform data in order. * It is characterized by the pointer for every item, pointing to the next one and the pointer, pointing @@ -22,60 +26,155 @@ public type Node struct { public type DoublyLinkedList struct { heap Node* head heap Node* tail + unsigned long size = 0l +} + +public p DoublyLinkedList.ctor() { + this.tail = nil*>; + this.head = nil*>; } -public p Node.dtor() { - if this.next != nil*> { - this.next.dtor(); - free((heap byte*) this.next); +public p DoublyLinkedList.pushBack(const T& value) { + heap Node* newNode = this.createNode(value); + if this.isEmpty() { + this.head = this.tail = newNode; + } else { + this.head.next = newNode; + newNode.prev = this.head; + this.head = newNode; } + this.size++; } -public p DoublyLinkedList.insert(T newValue, Node* prevNode = nil*>) { - // Create new node - heap Node* newNode; - unsafe { - newNode = (heap Node*) malloc(sizeof(type Node) / 8); +public p DoublyLinkedList.pushFront(const T& value) { + heap Node* newNode = this.createNode(value); + if this.isEmpty() { + this.head = this.tail = newNode; + } else { + newNode.next = this.tail; + this.tail.prev = newNode; + this.tail = newNode; } - newNode.value = newValue; - - if prevNode != nil*> { // Previous node was passed -> insert after this node - // Link the previous to this one - newNode.prev = prevNode; - // Link the next node to this one - newNode.next = prevNode.next; - // Link this node to the next node - prevNode.next.prev = newNode; - // Link this node to the previous node - prevNode.next = newNode; - - // Check if the previous node was the last node - if prevNode == tail { - this.tail = newNode; + this.size++; +} + +public p DoublyLinkedList.insertAt(unsigned long idx, const T& value) { + // Abort if the index is out of bounds + if idx < 0 || idx > this.size { panic(Error("Access index out of bound")); } + + heap Node* newNode = this.createNode(value); + if idx == 0 { + this.pushFront(value); + } else if idx == this.size { + this.pushBack(value); + } else { + heap Node* curr = this.tail; + for unsigned long i = 0l; i < idx - 1l; i++ { + curr = curr.next; } - } else { // No previous node was passed -> insert at head - newNode.next = this.head; - this.head = newNode; + newNode.next = curr.next; + newNode.prev = curr; + curr.next.prev = newNode; + curr.next = newNode; + this.size++; + } +} + +public p DoublyLinkedList.remove(const T& valueToRemove) { + heap Node* curr = this.tail; + while curr != nil*> { + if curr.value == valueToRemove { + if curr == this.tail { + this.tail = curr.next; + this.tail.prev = nil*>; + } else if curr == this.head { + this.head = curr.prev; + this.head.next = nil*>; + } else { + curr.prev.next = curr.next; + curr.next.prev = curr.prev; + } + this.size--; + break; + } + curr = curr.next; } } -public p DoublyLinkedList.insertHead(T newValue) { - this.insert(newValue); +public p DoublyLinkedList.removeAt(unsigned long idx) { + // Abort if the index is out of bounds + if idx < 0 || idx >= this.size { panic(Error("Access index out of bound")); } + + heap Node* curr = this.tail; + for unsigned long i = 0l; i < idx; i++ { + curr = curr.next; + } + if curr == this.tail { + this.tail = curr.next; + this.tail.prev = nil*>; + } else if curr == this.head { + this.head = curr.prev; + this.head.next = nil*>; + } else { + curr.prev.next = curr.next; + curr.next.prev = curr.prev; + } + this.size--; } -public inline p DoublyLinkedList.insertTail(T newValue) { - this.insert(newValue, this.tail); +public p DoublyLinkedList.removeAt(unsigned int idx) { + this.removeAt((unsigned long) idx); } -/*public f*> DoublyLinkedList.find(T value) { - heap Node* currentNode = this.head; - while currentNode != nil*> { - // Check condition - if currentNode.value == value { - return currentNode; - } - // Move to next node - currentNode = currentNode.next; +public p DoublyLinkedList.removeFront() { + this.removeAt(0l); +} + +public p DoublyLinkedList.removeBack() { + this.removeAt(this.size - 1l); +} + +public inline f DoublyLinkedList.getSize() { + return this.size; +} + +public inline f DoublyLinkedList.isEmpty() { + return this.size == 0l; +} + +public f DoublyLinkedList.get(unsigned long idx) { + // Abort if the index is out of bounds + if idx < 0 || idx >= this.size { panic(Error("Access index out of bound")); } + + heap Node* curr = this.tail; + for unsigned long i = 0l; i < idx; i++ { + curr = curr.next; + } + return curr.value; +} + +public f DoublyLinkedList.get(unsigned int idx) { + return this.get((unsigned long) idx); +} + +public inline f DoublyLinkedList.getFront() { + if this.isEmpty() { panic(Error("Access index out of bounds")); } + return this.tail.value; +} + +public inline f DoublyLinkedList.getBack() { + if this.isEmpty() { panic(Error("Access index out of bounds")); } + return this.head.value; +} + +f*> DoublyLinkedList.createNode(const T& value) { + heap Node* newNode; + unsafe { + Result allocResult = sAlloc(sizeof(type Node)); + newNode = (heap Node*) allocResult.unwrap(); } - return nil*>; -}*/ \ No newline at end of file + newNode.value = value; + newNode.prev = nil*>; + newNode.next = nil*>; + return newNode; +} \ No newline at end of file diff --git a/std/data/hash-table.spice b/std/data/hash-table.spice index 3afc4d0e9..1f8051aee 100644 --- a/std/data/hash-table.spice +++ b/std/data/hash-table.spice @@ -1,32 +1,40 @@ import "std/data/vector"; -import "std/data/doubly-linked-list"; +import "std/data/linked-list"; +import "std/runtime/iterator_rt"; import "std/data/pair"; - -type Size alias long; +import "std/math/hash"; // Generic types for key and value type K dyn; type V dyn; public type HashTable struct { - Vector>> table - Size bucketCount + Vector>> table + unsigned long bucketCount } -public p HashTable.ctor(Size bucketCount = 1000l) { +public p HashTable.ctor(unsigned long bucketCount = 1000l) { this.bucketCount = bucketCount; } public p HashTable.insert(const K& key, const V& value) { - Size index = hash(key); - this.table.at(index).pushBack(Pair(key, value)); + const unsigned long index = hash(key); + foreach dyn bucket : iterate(this.table) { + if bucket.getFirst() == key { + bucket.add(Pair(key, value)); + return; + } + } + this.table.pushBack(LinkedList>()); + LinkedList>& bucket = this.table.back(); + bucket.add(Pair(key, value)); } public p HashTable.delete(const K& key) { - Size index = hash(key); - const DoublyLinkedList>& bucket = this.table.at(index); + const unsigned long index = hash(key); + const LinkedList>& bucket = this.table.at(index); - for Size i = 0l; i < bucket.getSize(); i++ { + for unsigned long i = 0l; i < bucket.getSize(); i++ { Pair& candidate = bucket.at(i); if candidate.getFirst() == key { bucket.remove(i); @@ -36,10 +44,10 @@ public p HashTable.delete(const K& key) { } public f HashTable.search(const K& key) { - Size index = hash(key); - const DoublyLinkedList>& bucket = this.table.at(index); + unsigned long index = hash(key); + const LinkedList>& bucket = this.table.at(index); - for Size i = 0l; i < bucket.getSize(); i++ { + for unsigned long i = 0l; i < bucket.getSize(); i++ { Pair& candidate = bucket.at(i); if candidate.getFirst() == key { return &candidate.getSecond(); diff --git a/std/data/linked-list.spice b/std/data/linked-list.spice index d963bf1b6..b70e9ca63 100644 --- a/std/data/linked-list.spice +++ b/std/data/linked-list.spice @@ -1,6 +1,5 @@ -// Link external functions -ext f malloc(long); -ext p free(heap byte*); +import "std/type/result"; +import "std/type/error"; // Add generic type definitions type T dyn; @@ -8,11 +7,16 @@ type T dyn; /** * Node of a LinkedList */ -public type Node struct { +type Node struct { T value heap Node* next } +p Node.ctor(const T& value) { + this.value = value; + this.next = nil*>; +} + /** * A linked list is a common, dynamically resizable data structure to store uniform data in order. * It is characterized by the pointer for each item, pointing to the next one. @@ -31,6 +35,7 @@ public type Node struct { public type LinkedList struct { heap Node* tail heap Node* head + unsigned long size = 0l } public p LinkedList.ctor() { @@ -38,79 +43,174 @@ public p LinkedList.ctor() { this.head = nil*>; } -public p LinkedList.dtor() { - Node* curr = this.tail; - while curr != nil*> { - Node* next = curr.next; - free(curr); - curr = next; +public p LinkedList.pushBack(const T& value) { + // Create new node + heap Node* newNode = this.createNode(value); + + // Insert at head + if this.isEmpty() { + this.head = this.tail = newNode; + } else { + this.head.next = newNode; // Link the new node to the previous one + this.head = newNode; // Set the head to the new node } + this.size++; } -public p LinkedList.insert(const T& value) { +public p LinkedList.pushFront(const T& value) { // Create new node - heap Node* newNode; - unsafe { - newNode = (heap Node*) malloc(sizeof(type Node) / 8l); - } - newNode.value = value; - newNode.next = nil*>; + heap Node* newNode = this.createNode(value); - // Insert at head - this.head.next = newNode; - this.head = newNode; + // Insert at tail + if this.isEmpty() { + this.head = this.tail = newNode; + } else { + newNode.next = this.tail; // Link the next node to the new one + this.tail = newNode; // Set the tail to the new node + } + this.size++; } public p LinkedList.insertAt(unsigned long idx, const T& value) { + // Abort if the index is out of bounds + if idx < 0l || idx >= this.size { return; } + // Create new node - heap Node* newNode; - unsafe { - newNode = (heap Node*) malloc(sizeof(type Node) / 8); + heap Node* newNode = this.createNode(value); + + if this.isEmpty() { + this.head = this.tail = newNode; + } else if idx == 0l { + newNode.next = this.tail; // Link the next node to the new one + this.tail = newNode; // Set the tail to the new node + } else { + heap Node* curr = this.tail; + for unsigned long i = 0l; i < idx - 1l; i++ { + curr = curr.next; + } + newNode.next = curr.next; // Link the next node to the new one + curr.next = newNode; // Link the new node to the previous one + } + this.size++; +} + +public p LinkedList.insertAt(unsigned int idx, const T& value) { + this.insertAt((unsigned long) idx, value); +} + +public p LinkedList.remove(const T& valueToRemove) { + // Abort if the list is already empty + if this.isEmpty() { return; } + + if this.tail.value == valueToRemove { + heap Node* temp = this.tail; + this.tail = this.tail.next; + unsafe { + sDealloc((heap byte*) temp); + } + this.size--; + return; } - newNode.value = value; - // Search for item right before insert position - Node* prev = this.tail; - while curr != nil*> && idx > 1 { - prev = prev.next; - idx--; + heap Node* curr = this.tail; + while curr.next != nil*> && curr.next.value != valueToRemove { + curr = curr.next; + } + if curr.next == nil*> { return; } + + heap Node* temp = curr.next; + curr.next = curr.next.next; + unsafe { + sDealloc((heap byte*) temp); } - // Link the next node to the new one - newNode.next = prev.next; - // Link the new node to the previous one - prev.next = newNode; + this.size--; +} + +public p LinkedList.removeAt(unsigned long idx) { + // Abort if the index is out of bounds + if idx < 0l || idx >= this.size { return; } - // Check if we have a new tail if idx == 0l { - this.tail = newNode; + heap Node* temp = this.tail; + this.tail = this.tail.next; + unsafe { + sDealloc((heap byte*) temp); + } + this.size--; + return; + } + + heap Node* curr = this.tail; + for unsigned long i = 0l; i < idx - 1l; i++ { + curr = curr.next; + } + + heap Node* temp = curr.next; + curr.next = curr.next.next; + unsafe { + sDealloc((heap byte*) temp); } - // Check if the previous node was the last node - if this.head == prev { - this.head = newNode; + + if idx == this.size - 1l { + this.head = curr; } + + this.size--; } -public p LinkedList.remove(const T& valueToRemove) { +public p LinkedList.removeAt(unsigned int index) { + this.removeAt((unsigned long) index); +} + +public p LinkedList.removeFront() { + this.removeAt(0l); +} + +public p LinkedList.removeBack() { + this.removeAt(this.size - 1l); +} + +public inline f LinkedList.getSize() { + return this.size; +} + +public inline f LinkedList.isEmpty() { + return this.size == 0l; +} + +public f LinkedList.get(unsigned long idx) { + // Abort if the index is out of bounds + if idx < 0 || idx >= this.size { panic(Error("Access index out of bound")); } + heap Node* curr = this.tail; - heap Node* prev = nil; - while curr != nil*> && curr.value != valueToRemove { - prev = curr; + for unsigned long i = 0l; i < idx; i++ { curr = curr.next; } - // Check if the item was found. If yes, delete its node - if curr != nil*> { - // Set the next node of the previous node - prev.next = curr.next; - // Free the removed node - free(curr); - } + return curr.value; } -public inline f LinkedList.getFirst() { - return this.head.value; +public f LinkedList.get(unsigned int idx) { + return this.get((unsigned long) idx); } -public inline f LinkedList.getLast() { +public inline f LinkedList.getFront() { + if this.isEmpty() { panic(Error("Access index out of bounds")); } return this.tail.value; +} + +public inline f LinkedList.getBack() { + if this.isEmpty() { panic(Error("Access index out of bounds")); } + return this.head.value; +} + +f*> LinkedList.createNode(const T& value) { + heap Node* newNode; + unsafe { + Result allocResult = sAlloc(sizeof(type Node)); + newNode = (heap Node*) allocResult.unwrap(); + } + newNode.value = value; + newNode.next = nil*>; + return newNode; } \ No newline at end of file diff --git a/std/data/map.spice b/std/data/map.spice index 997e11914..5b4b56a9d 100644 --- a/std/data/map.spice +++ b/std/data/map.spice @@ -2,12 +2,6 @@ const unsigned long INITIAL_ALLOC_COUNT = 5l; const unsigned int RESIZE_FACTOR = 2; -// Link external functions -ext f malloc(long); -ext f realloc(heap byte*, int); -ext p free(heap byte*); -ext f memcpy(heap byte*, heap byte*, int); - // Add generic type definitions type K dyn; type V dyn; diff --git a/std/data/optional.spice b/std/data/optional.spice index 8db3e6cb7..0354ea43f 100644 --- a/std/data/optional.spice +++ b/std/data/optional.spice @@ -81,4 +81,12 @@ public inline p Optional.clear() { */ public inline f Optional.isPresent() { return this.isPresent; +} + +public f operator==(const Optional& lhs, const Optional& rhs) { + return lhs.isPresent == rhs.isPresent && lhs.data == rhs.data; +} + +public f operator!=(const Optional& lhs, const Optional& rhs) { + return !(lhs == rhs); } \ No newline at end of file diff --git a/std/data/pair.spice b/std/data/pair.spice index e51af3234..c69cdffc8 100644 --- a/std/data/pair.spice +++ b/std/data/pair.spice @@ -21,4 +21,12 @@ public f Pair.getFirst() { public f Pair.getSecond() { return this.second; +} + +public f operator==(const Pair& lhs, const Pair& rhs) { + return lhs.first == rhs.first && lhs.second == rhs.second; +} + +public f operator!=(const Pair& lhs, const Pair& rhs) { + return !(lhs == rhs); } \ No newline at end of file diff --git a/std/data/queue.spice b/std/data/queue.spice index bdfc15095..8c8564818 100644 --- a/std/data/queue.spice +++ b/std/data/queue.spice @@ -166,6 +166,20 @@ public p Queue.pack() { this.resize(this.size); } +public f operator==(const Queue& lhs, const Queue& rhs) { + // Compare the sizes + if lhs.size != rhs.size { return false; } + // Compare the contents + for unsigned long i = 0l; index < lhs.size; i++ { + if lhs.contents[i] != rhs.contents[i] { return false; } + } + return true; +} + +public f operator!=(const Queue& lhs, const Queue& rhs) { + return !(lhs == rhs); +} + /** * Re-allocates heap space for the queue contents */ diff --git a/std/data/red-black-tree.spice b/std/data/red-black-tree.spice index 5ffdae5ff..580ee6f4d 100644 --- a/std/data/red-black-tree.spice +++ b/std/data/red-black-tree.spice @@ -13,13 +13,28 @@ type NodeColor enum { RED, BLACK } */ type Node struct { T data + heap Node* parent heap Node* childLeft heap Node* childRight NodeColor color } -f*> createNode(T data) { - heap Node* newNode = (heap Node*) malloc(sizeof(type Node) / 8); +inline f Node.isRoot() { + return this.parent == nil*>; +} + +p Node.dump(unsigned int indent = 0) { + // Dump right child + if this.childRight != nil*> { this.childRight.dump(indent + 4); } + // Dump node itself + printf("%s%d %s", String(' ') * indent, this.data, this.color == NodeColor::RED ? "R" : "B"); + // Dump left child + if this.childLeft != nil*> { this.childLeft.dump(indent + 4); } +} + +f*> createNode(T data) { + Result*> allocResult = sAlloc(sizeof(type Node)); + heap Node* newNode = allocResult.unwrap(); newNode.data = data; newNode.childLeft = nil*>; newNode.childRight = nil*>; @@ -27,10 +42,6 @@ f*> createNode(T data) { return newNode; } -inline f Node.isRoot() { - return this.parent == nil*>; -} - /** * A Red-Black Tree is a self-balancing search tree, which is used e.g. in the implementation of maps. * @@ -47,14 +58,14 @@ public p RedBlackTree.ctor() { } public p RedBlackTree.insert(T newItem) { - this.insertAdd(newItem); - this.insertRebalance(newItem); + heap Node* newNode = createNode(newItem); + } -p RedBlackTree.insertAdd(T newItem) { +public p RedBlackTree.insertFixup(heap Node* node) { } -p RedBlackTree.insertRebalance(T newItem) { - +public p RedBlackTree.dump() { + if this.rootNode != nil*> { this.rootNode.dump(); } } \ No newline at end of file diff --git a/std/data/stack.spice b/std/data/stack.spice index 4a3c8e10d..3e8073405 100644 --- a/std/data/stack.spice +++ b/std/data/stack.spice @@ -124,6 +124,20 @@ public p Stack.pack() { this.resize(this.size); } +public f operator==(const Stack& lhs, const Stack& rhs) { + // Compare the sizes + if lhs.size != rhs.size { return false; } + // Compare the contents + for unsigned long i = 0l; index < lhs.size; i++ { + if lhs.contents[i] != rhs.contents[i] { return false; } + } + return true; +} + +public f operator!=(const Stack& lhs, const Stack& rhs) { + return !(lhs == rhs); +} + /** * Re-allocates heap space for the queue contents */ diff --git a/std/data/triple.spice b/std/data/triple.spice index 1334fc2bd..8c31fb225 100644 --- a/std/data/triple.spice +++ b/std/data/triple.spice @@ -28,4 +28,12 @@ public f Triple.getSecond() { public f Triple.getThird() { return this.third; +} + +public f operator==(const Triple& lhs, const Triple& rhs) { + return lhs.first == rhs.first && lhs.second == rhs.second && lhs.third == rhs.third; +} + +public f operator!=(const Triple& lhs, const Triple& rhs) { + return !(lhs == rhs); } \ No newline at end of file diff --git a/std/data/vector.spice b/std/data/vector.spice index aadc8936f..8970c4813 100644 --- a/std/data/vector.spice +++ b/std/data/vector.spice @@ -178,6 +178,20 @@ public p Vector.pack() { this.resize(this.size); } +public f operator==(const Vector& lhs, const Vector& rhs) { + // Compare the sizes + if lhs.size != rhs.size { return false; } + // Compare the contents + for unsigned long index = 0l; index < lhs.size; index++ { + if lhs.contents[index] != rhs.contents[index] { return false; } + } + return true; +} + +public f operator!=(const Vector& lhs, const Vector& rhs) { + return !(lhs == rhs); +} + /** * Re-allocates heap space for the queue contents */ diff --git a/std/math/hash.spice b/std/math/hash.spice new file mode 100644 index 000000000..12f8aab4e --- /dev/null +++ b/std/math/hash.spice @@ -0,0 +1,36 @@ +// Generic type defs +type TT int|long|short|bool; // Trivially hashable types +type TC byte|char; // Trivially hashable types with one additional cast +type T dyn; // Any type + +public type IHashable interface { + public f hash(const T&); +} + +public f hash(TT input) { + return (unsigned long) input; +} + +public f hash(TC input) { + return (unsigned long) ((unsigned int) input); +} + +public f hash(double input) { + unsafe { + return *((unsigned long*) &input); + } +} + +// djb2 hash +public f hash(string input) { + unsigned long hash = 5381l; + for unsigned long i = 0l; i < len(input); i++ { + unsigned long c = (unsigned long) ((unsigned int) input[i]); + hash = ((hash << 5l) + hash) + c; // hash * 33 + c + } + return hash; +} + +/*public f hash(const T& input) { + return input.hash(); +}*/ \ No newline at end of file diff --git a/std/runtime/iterator_rt.spice b/std/runtime/iterator_rt.spice index d99ccd1c4..f47c56aec 100644 --- a/std/runtime/iterator_rt.spice +++ b/std/runtime/iterator_rt.spice @@ -1,3 +1,5 @@ +#![core.compiler.alwaysKeepOnNameCollision = true] + import "std/iterator/array-iterator"; import "std/iterator/vector-iterator"; import "std/data/vector"; diff --git a/std/runtime/memory_rt.spice b/std/runtime/memory_rt.spice index feeea100b..25e491428 100644 --- a/std/runtime/memory_rt.spice +++ b/std/runtime/memory_rt.spice @@ -1,3 +1,5 @@ +#![core.compiler.alwaysKeepOnNameCollision = true] + import "std/type/result"; // Link external functions diff --git a/std/runtime/string_rt.spice b/std/runtime/string_rt.spice index 4629600cf..f46775c35 100644 --- a/std/runtime/string_rt.spice +++ b/std/runtime/string_rt.spice @@ -1,3 +1,5 @@ +#![core.compiler.alwaysKeepOnNameCollision = true] + import "std/type/result"; // Constants @@ -337,13 +339,45 @@ public f String.find(string needle, unsigned int startIndex) { } /** - * Searches for a substring in a string. Returns -1 if the string was not found. + * Searches for a substring in a string from the back. Returns -1 if the string was not found. * * @param startIndex Index where to start the search * @return Index, where the substring was found / -1 */ -public f String.find(string needle, unsigned short startIndex) { - return this.find(needle, (unsigned long) startIndex); +public f String.rfind(string needle, unsigned long startIndex = 0l) { + // Return -1 if the startIndex is out of bounds + if startIndex >= this.length { return (unsigned long) -1l; } + + const unsigned long needleLength = getRawLength(needle); + // Return false if the needle is longer than the haystack + if this.length < needleLength { return (unsigned long) -1l; } + + if startIndex == 0l { startIndex = this.length - needleLength; } + + // Search needle in haystack + for unsigned long idx = startIndex; idx >= 0; idx-- { + // Start matching at startIdx + for unsigned long charIdx = 0l; charIdx < needleLength; charIdx++ { + unsafe { + if this.contents[idx + charIdx] != needle[charIdx] { + continue 2; + } + } + } + // Whole string was matched + return idx; + } + return (unsigned long) -1l; +} + +/** + * Searches for a substring in a string from the back. Returns -1 if the string was not found. + * + * @param startIndex Index where to start the search + * @return Index, where the substring was found / -1 + */ +public f String.rfind(string needle, unsigned int startIndex) { + return this.rfind(needle, (unsigned long) startIndex); } /** diff --git a/test/test-files/irgenerator/debug-info/success-dbg-info-complex/ir-code.ll b/test/test-files/irgenerator/debug-info/success-dbg-info-complex/ir-code.ll index a3f59df29..976e45c3a 100644 --- a/test/test-files/irgenerator/debug-info/success-dbg-info-complex/ir-code.ll +++ b/test/test-files/irgenerator/debug-info/success-dbg-info-complex/ir-code.ll @@ -505,7 +505,7 @@ attributes #3 = { cold noreturn nounwind } !8 = !{i32 2, !"Debug Info Version", i32 3} !9 = !{i32 1, !"wchar_size", i32 4} !10 = !{i32 8, !"PIC Level", i32 2} -!11 = !{i32 7, !"PIE Level", i32 0} +!11 = !{i32 7, !"PIE Level", i32 2} !12 = !{i32 7, !"uwtable", i32 2} !13 = !{i32 7, !"frame-pointer", i32 2} !14 = !{!"spice version dev (https://github.com/spicelang/spice)"} 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 d51aad938..ba8a92adb 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 @@ -112,7 +112,7 @@ attributes #3 = { nofree nounwind } !17 = !{i32 2, !"Debug Info Version", i32 3} !18 = !{i32 1, !"wchar_size", i32 4} !19 = !{i32 8, !"PIC Level", i32 2} -!20 = !{i32 7, !"PIE Level", i32 0} +!20 = !{i32 7, !"PIE Level", i32 2} !21 = !{i32 7, !"uwtable", i32 2} !22 = !{i32 7, !"frame-pointer", i32 2} !23 = !{!"spice version dev (https://github.com/spicelang/spice)"} @@ -125,15 +125,15 @@ attributes #3 = { nofree nounwind } !30 = !DIDerivedType(tag: DW_TAG_member, name: "lng", scope: !28, file: !7, line: 2, baseType: !31, size: 64) !31 = !DIBasicType(name: "long", size: 64, encoding: DW_ATE_signed) !32 = !DIDerivedType(tag: DW_TAG_member, name: "str", scope: !28, file: !7, line: 3, baseType: !33, size: 192, align: 8, offset: 64) -!33 = !DICompositeType(tag: DW_TAG_structure_type, name: "String", scope: !34, file: !34, line: 11, size: 192, align: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !35, identifier: "struct.String") +!33 = !DICompositeType(tag: DW_TAG_structure_type, name: "String", scope: !34, file: !34, line: 13, size: 192, align: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !35, identifier: "struct.String") !34 = !DIFile(filename: "string_rt.spice", directory: "C:\\Users\\Marc\\Documents\\JustForFunGitHubClonesFast\\spice\\std\\runtime") !35 = !{!36, !39, !41} -!36 = !DIDerivedType(tag: DW_TAG_member, name: "contents", scope: !33, file: !34, line: 12, baseType: !37, size: 64) +!36 = !DIDerivedType(tag: DW_TAG_member, name: "contents", scope: !33, file: !34, line: 14, baseType: !37, size: 64) !37 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !38, size: 64) !38 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_unsigned_char) -!39 = !DIDerivedType(tag: DW_TAG_member, name: "capacity", scope: !33, file: !34, line: 13, baseType: !40, size: 64, offset: 64) +!39 = !DIDerivedType(tag: DW_TAG_member, name: "capacity", scope: !33, file: !34, line: 15, baseType: !40, size: 64, offset: 64) !40 = !DIBasicType(name: "unsigned long", size: 64, encoding: DW_ATE_unsigned) -!41 = !DIDerivedType(tag: DW_TAG_member, name: "length", scope: !33, file: !34, line: 14, baseType: !40, size: 64, offset: 128) +!41 = !DIDerivedType(tag: DW_TAG_member, name: "length", scope: !33, file: !34, line: 16, baseType: !40, size: 64, offset: 128) !42 = !DIDerivedType(tag: DW_TAG_member, name: "i", scope: !28, file: !7, line: 4, baseType: !43, size: 32, offset: 256) !43 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) !44 = !{} diff --git a/test/test-files/irgenerator/structs/success-access-composed-fields/ir-code.ll b/test/test-files/irgenerator/structs/success-access-composed-fields/ir-code.ll new file mode 100644 index 000000000..09bf2c9af --- /dev/null +++ b/test/test-files/irgenerator/structs/success-access-composed-fields/ir-code.ll @@ -0,0 +1,40 @@ +; 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.D = type { %struct.A, %struct.C, i32 } +%struct.A = type { i32 } +%struct.C = type { %struct.A, %struct.B, i32 } +%struct.B = type { i32 } + +@printf.str.0 = private unnamed_addr constant [12 x i8] c"%d, %d, %d\0A\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %result = alloca i32, align 4 + %d = alloca %struct.D, align 8 + store i32 0, ptr %result, align 4 + store %struct.D zeroinitializer, ptr %d, align 4 + %f1_addr = getelementptr inbounds %struct.D, ptr %d, i32 0, i32 0, i32 0 + store i32 1, ptr %f1_addr, align 4 + %f2_addr = getelementptr inbounds %struct.D, ptr %d, i32 0, i32 2 + store i32 2, ptr %f2_addr, align 4 + %f3_addr = getelementptr inbounds %struct.D, ptr %d, i32 0, i32 1, i32 1, i32 0 + store i32 3, ptr %f3_addr, align 4 + %f1_addr1 = getelementptr inbounds %struct.D, ptr %d, i32 0, i32 0, i32 0 + %1 = load i32, ptr %f1_addr1, align 4 + %f2_addr2 = getelementptr inbounds %struct.D, ptr %d, i32 0, i32 2 + %2 = load i32, ptr %f2_addr2, align 4 + %f3_addr3 = getelementptr inbounds %struct.D, ptr %d, i32 0, i32 1, i32 1, i32 0 + %3 = load i32, ptr %f3_addr3, align 4 + %4 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %1, i32 %2, i32 %3) + %5 = load i32, ptr %result, align 4 + ret i32 %5 +} + +; Function Attrs: nofree nounwind +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 + +attributes #0 = { noinline nounwind optnone uwtable } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/structs/success-access-composed-fields/source.spice b/test/test-files/irgenerator/structs/success-access-composed-fields/source.spice new file mode 100644 index 000000000..6fd97e7dc --- /dev/null +++ b/test/test-files/irgenerator/structs/success-access-composed-fields/source.spice @@ -0,0 +1,27 @@ +type A struct { + int f1 +} + +type B struct { + int f3 +} + +type C struct { + compose A _1 + compose B _2 + int f2 +} + +type D struct { + compose A _1 + compose C _2 + int f2 +} + +f main() { + D d; + d.f1 = 1; + d.f2 = 2; + d.f3 = 3; + printf("%d, %d, %d\n", d.f1, d.f2, d.f3); +} \ No newline at end of file diff --git a/test/test-files/irgenerator/structs/success-default-field-values3/ir-code.ll b/test/test-files/irgenerator/structs/success-default-field-values3/ir-code.ll index e293bae54..0db8c304a 100644 --- a/test/test-files/irgenerator/structs/success-default-field-values3/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-default-field-values3/ir-code.ll @@ -14,8 +14,10 @@ define private void @_ZN4Test4ctorEv(ptr noundef nonnull align 8 dereferenceable %this = alloca ptr, align 8 store ptr %0, ptr %this, align 8 %2 = load ptr, ptr %this, align 8 - %3 = getelementptr inbounds %struct.Test, ptr %2, i32 0, i32 1 - store ptr @anon.string.0, ptr %3, align 8 + %3 = getelementptr inbounds %struct.Test, ptr %2, i32 0, i32 0 + store i32 0, ptr %3, align 4 + %4 = getelementptr inbounds %struct.Test, ptr %2, i32 0, i32 1 + store ptr @anon.string.0, ptr %4, align 8 ret void } diff --git a/test/test-files/std/data/doubly-linked-list-normal-usecase/cout.out b/test/test-files/std/data/doubly-linked-list-normal-usecase/cout.out new file mode 100644 index 000000000..d8347e3cc --- /dev/null +++ b/test/test-files/std/data/doubly-linked-list-normal-usecase/cout.out @@ -0,0 +1 @@ +All assertions passed! diff --git a/test/test-files/std/data/doubly-linked-list-normal-usecase/source.spice b/test/test-files/std/data/doubly-linked-list-normal-usecase/source.spice new file mode 100644 index 000000000..f8687adf8 --- /dev/null +++ b/test/test-files/std/data/doubly-linked-list-normal-usecase/source.spice @@ -0,0 +1,37 @@ +import "std/data/doubly-linked-list"; + +f main() { + DoublyLinkedList list; + assert list.getSize() == 0; + assert list.isEmpty(); + list.pushBack(String("Hello")); + assert list.getSize() == 1; + assert !list.isEmpty(); + String var = String("World"); + list.pushBack(var); + assert list.getSize() == 2; + list.pushFront(String("Hi")); + assert list.getSize() == 3; + assert list.getFront() == String("Hi"); + assert list.getBack() == String("World"); + list.removeFront(); + assert list.getSize() == 2; + assert list.getFront() == String("Hello"); + list.removeBack(); + assert list.getSize() == 1; + assert list.getBack() == String("Hello"); + list.pushBack(String("World")); + list.pushFront(String("Hi")); + list.pushBack(String("Programmers")); + assert list.getSize() == 4; + list.remove(String("World")); + assert list.getSize() == 3; + assert list.get(0) == String("Hi"); + assert list.get(1) == String("Hello"); + assert list.get(2) == String("Programmers"); + list.removeAt(1); + assert list.getSize() == 2; + assert list.get(0) == String("Hi"); + assert list.get(1) == String("Programmers"); + printf("All assertions passed!\n"); +} \ No newline at end of file diff --git a/test/test-files/std/data/linked-list-normal-usecase/cout.out b/test/test-files/std/data/linked-list-normal-usecase/cout.out new file mode 100644 index 000000000..d8347e3cc --- /dev/null +++ b/test/test-files/std/data/linked-list-normal-usecase/cout.out @@ -0,0 +1 @@ +All assertions passed! diff --git a/test/test-files/std/data/linked-list-normal-usecase/source.spice b/test/test-files/std/data/linked-list-normal-usecase/source.spice new file mode 100644 index 000000000..0bc886750 --- /dev/null +++ b/test/test-files/std/data/linked-list-normal-usecase/source.spice @@ -0,0 +1,37 @@ +import "std/data/linked-list"; + +f main() { + LinkedList list; + assert list.getSize() == 0; + assert list.isEmpty(); + list.pushBack(String("Hello")); + assert list.getSize() == 1; + assert !list.isEmpty(); + String var = String("World"); + list.pushBack(var); + assert list.getSize() == 2; + list.pushFront(String("Hi")); + assert list.getSize() == 3; + assert list.getFront() == String("Hi"); + assert list.getBack() == String("World"); + list.removeFront(); + assert list.getSize() == 2; + assert list.getFront() == String("Hello"); + list.removeBack(); + assert list.getSize() == 1; + assert list.getBack() == String("Hello"); + list.pushBack(String("World")); + list.pushFront(String("Hi")); + list.pushBack(String("Programmers")); + assert list.getSize() == 4; + list.remove(String("World")); + assert list.getSize() == 3; + assert list.get(0) == String("Hi"); + assert list.get(1) == String("Hello"); + assert list.get(2) == String("Programmers"); + list.removeAt(1); + assert list.getSize() == 2; + assert list.get(0) == String("Hi"); + assert list.get(1) == String("Programmers"); + printf("All assertions passed!\n"); +} \ No newline at end of file diff --git a/test/test-files/std/math/hash-basic/cout.out b/test/test-files/std/math/hash-basic/cout.out new file mode 100644 index 000000000..516121888 --- /dev/null +++ b/test/test-files/std/math/hash-basic/cout.out @@ -0,0 +1,7 @@ +Hash (int): 123 +Hash (long): 123 +Hash (short): 123 +Hash (char): 0 +Hash (byte): 0 +Hash (string): -1763540338 +Hash (double): 0 diff --git a/test/test-files/std/math/hash-basic/source.spice b/test/test-files/std/math/hash-basic/source.spice new file mode 100644 index 000000000..006c07998 --- /dev/null +++ b/test/test-files/std/math/hash-basic/source.spice @@ -0,0 +1,13 @@ +import "std/math/hash"; + +f main() { + // Trivial hashes + printf("Hash (int): %d\n", hash(123)); + printf("Hash (long): %d\n", hash(123l)); + printf("Hash (short): %d\n", hash(123s)); + printf("Hash (char): %d\n", hash(123.0)); + printf("Hash (byte): %d\n", hash(123.0)); + printf("Hash (string): %d\n", hash("Hello, World!")); + // Complex hashes + printf("Hash (double): %d\n", hash(123.0)); +} \ No newline at end of file diff --git a/test/test-files/std/runtime/string-basic-methods-non-modifying/source.spice b/test/test-files/std/runtime/string-basic-methods-non-modifying/source.spice index a1edf85c3..0a21f53b1 100644 --- a/test/test-files/std/runtime/string-basic-methods-non-modifying/source.spice +++ b/test/test-files/std/runtime/string-basic-methods-non-modifying/source.spice @@ -18,6 +18,10 @@ f main() { assert s4.find("H") == 0; assert s4.find("!") == 11; assert s4.find(" ", 12) == -1; + assert s4.rfind("ell") == 1; + assert s4.rfind("o") == 7; + assert s4.rfind("!") == 11; + assert s4.rfind("o", 6) == 4; assert !s4.contains("abc"); assert s4.contains("Hello"); assert s4.contains("World!"); diff --git a/test/test-files/typechecker/structs/error-circular-composed-fields-lookup/exception.out b/test/test-files/typechecker/structs/error-circular-composed-fields-lookup/exception.out new file mode 100644 index 000000000..1eea6e412 --- /dev/null +++ b/test/test-files/typechecker/structs/error-circular-composed-fields-lookup/exception.out @@ -0,0 +1,20 @@ +[Error|Compiler]: +Unresolved soft errors: There are unresolved errors. Please fix them and recompile. + +[Error|Semantic] ./test-files/typechecker/structs/error-circular-composed-fields-lookup/source.spice:3:5: +Specifier at illegal context: The compose specifier can only be used on plain struct fields + +3 compose A* f2 + ^^^^^^^ + +[Error|Semantic] ./test-files/typechecker/structs/error-circular-composed-fields-lookup/source.spice:9:5: +Referenced undefined variable: Variable 'f2' was referenced before declared + +9 a.f2 = &a; + ^^^^ + +[Error|Semantic] ./test-files/typechecker/structs/error-circular-composed-fields-lookup/source.spice:10:5: +Referenced undefined variable: Field 'f3' not found in struct A + +10 a.f3 = 1; + ^^^^ \ No newline at end of file diff --git a/test/test-files/typechecker/structs/error-circular-composed-fields-lookup/source.spice b/test/test-files/typechecker/structs/error-circular-composed-fields-lookup/source.spice new file mode 100644 index 000000000..9352ac7dd --- /dev/null +++ b/test/test-files/typechecker/structs/error-circular-composed-fields-lookup/source.spice @@ -0,0 +1,11 @@ +type A struct { + int f1 + compose A* f2 +} + +f main() { + A a; + a.f1 = 1; + a.f2 = &a; + a.f3 = 1; +} \ No newline at end of file diff --git a/test/test-files/typechecker/structs/error-referenced-undefined-struct-field-composed/exception.out b/test/test-files/typechecker/structs/error-referenced-undefined-struct-field-composed/exception.out new file mode 100644 index 000000000..dc3415f5c --- /dev/null +++ b/test/test-files/typechecker/structs/error-referenced-undefined-struct-field-composed/exception.out @@ -0,0 +1,8 @@ +[Error|Compiler]: +Unresolved soft errors: There are unresolved errors. Please fix them and recompile. + +[Error|Semantic] ./test-files/typechecker/structs/error-referenced-undefined-struct-field-composed/source.spice:20:50: +Referenced undefined variable: Field 'f4' not found in struct C + +20 , c.f1, c.f2, c.f3, c.f4); + ^^^^ \ No newline at end of file diff --git a/test/test-files/typechecker/structs/error-referenced-undefined-struct-field-composed/source.spice b/test/test-files/typechecker/structs/error-referenced-undefined-struct-field-composed/source.spice new file mode 100644 index 000000000..bc9c82a2a --- /dev/null +++ b/test/test-files/typechecker/structs/error-referenced-undefined-struct-field-composed/source.spice @@ -0,0 +1,21 @@ +type A struct { + int f1 +} + +type B struct { + int f3 +} + +type C struct { + compose A _1 + compose B _2 + int f2 +} + +f main() { + C c; + c.f1 = 1; + c.f2 = 2; + c.f1 = 3; + printf("%d, %d, %d, %d\n", c.f1, c.f2, c.f3, c.f4); +} \ No newline at end of file