diff --git a/.run/spice.run.xml b/.run/spice.run.xml index 19b93f127..925f10ae2 100644 --- a/.run/spice.run.xml +++ b/.run/spice.run.xml @@ -1,5 +1,5 @@ - + diff --git a/src/model/GenericType.h b/src/model/GenericType.h index 8059152ae..fa57c487d 100644 --- a/src/model/GenericType.h +++ b/src/model/GenericType.h @@ -5,7 +5,7 @@ #include #include -#include "symboltablebuilder/SymbolType.h" +#include #include "../../lib/json/json.hpp" diff --git a/src/typechecker/FunctionManager.cpp b/src/typechecker/FunctionManager.cpp index 187293694..431187820 100644 --- a/src/typechecker/FunctionManager.cpp +++ b/src/typechecker/FunctionManager.cpp @@ -4,16 +4,20 @@ #include #include +#include #include #include +#include #include +#include namespace spice::compiler { Function *FunctionManager::insertFunction(Scope *insertScope, const Function &baseFunction, std::vector *nodeFunctionList) { // Open a new manifestation list for the function definition - insertScope->functions.insert({baseFunction.declNode->codeLoc, FunctionManifestationList()}); + const std::string fctId = baseFunction.name + ":" + baseFunction.declNode->codeLoc.toPrettyLineAndColumn(); + insertScope->functions.insert({fctId, FunctionManifestationList()}); // Collect substantiations std::vector manifestations; @@ -110,8 +114,9 @@ Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Functi throw SemanticError(declNode, FUNCTION_DECLARED_TWICE, "The function/procedure '" + signature + "' is declared twice"); // Retrieve the matching manifestation list of the scope - assert(insertScope->functions.contains(declNode->codeLoc)); - FunctionManifestationList &manifestationList = insertScope->functions.at(declNode->codeLoc); + const std::string fctId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn(); + assert(insertScope->functions.contains(fctId)); + FunctionManifestationList &manifestationList = insertScope->functions.at(fctId); // Add substantiated function manifestationList.emplace(signature, newManifestation); @@ -193,7 +198,7 @@ Function *FunctionManager::matchFunction(Scope *matchScope, const std::string &r FunctionRegistry functionRegistry = matchScope->functions; // Loop over function registry to find functions, that match the requirements of the call std::vector matches; - for (const auto &[defCodeLocStr, m] : functionRegistry) { + for (const auto &[fctId, m] : functionRegistry) { // Copy the manifestation list to prevent iterating over items, that are created within the loop const FunctionManifestationList manifestations = m; for (const auto &[signature, presetFunction] : manifestations) { @@ -229,16 +234,16 @@ Function *FunctionManager::matchFunction(Scope *matchScope, const std::string &r // Check if the function is generic needs to be substantiated if (presetFunction.templateTypes.empty() && !forceSubstantiation) { - assert(matchScope->functions.contains(defCodeLocStr) && matchScope->functions.at(defCodeLocStr).contains(signature)); - matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature)); + assert(matchScope->functions.contains(fctId) && matchScope->functions.at(fctId).contains(signature)); + matches.push_back(&matchScope->functions.at(fctId).at(signature)); matches.back()->used = true; continue; // Match was successful -> match the next function } // Check if we already have this manifestation and can simply re-use it const std::string nonGenericSignature = candidate.getSignature(); - if (matchScope->functions.at(defCodeLocStr).contains(nonGenericSignature)) { - matches.push_back(&matchScope->functions.at(defCodeLocStr).at(nonGenericSignature)); + if (matchScope->functions.at(fctId).contains(nonGenericSignature)) { + matches.push_back(&matchScope->functions.at(fctId).at(nonGenericSignature)); break; // Leave the whole manifestation list to not double-match the manifestation } diff --git a/src/typechecker/FunctionManager.h b/src/typechecker/FunctionManager.h index 4fb7fb4a9..75bba042d 100644 --- a/src/typechecker/FunctionManager.h +++ b/src/typechecker/FunctionManager.h @@ -8,12 +8,12 @@ #include #include -#include -#include namespace spice::compiler { // Forward declarations +struct CodeLoc; +struct ExprResult; class Function; class Scope; class SymbolTableEntry; @@ -23,7 +23,7 @@ class GenericType; // Typedefs using FunctionManifestationList = std::unordered_map; -using FunctionRegistry = std::map; +using FunctionRegistry = std::map; using Arg = std::pair; using ArgList = std::vector; diff --git a/src/typechecker/StructManager.cpp b/src/typechecker/StructManager.cpp index 8d146be88..a2a96ac3b 100644 --- a/src/typechecker/StructManager.cpp +++ b/src/typechecker/StructManager.cpp @@ -7,12 +7,14 @@ #include #include #include +#include namespace spice::compiler { Struct *StructManager::insertStruct(Scope *insertScope, Struct &spiceStruct, std::vector *nodeStructList) { // Open a new manifestation list. Which gets filled by the substantiated manifestations of the struct - insertScope->structs.insert({spiceStruct.declNode->codeLoc, StructManifestationList()}); + const std::string structId = spiceStruct.name + ":" + spiceStruct.declNode->codeLoc.toPrettyLineAndColumn(); + insertScope->structs.insert({structId, StructManifestationList()}); // Save substantiation in declaration node Struct *substantiation = insertSubstantiation(insertScope, spiceStruct, spiceStruct.declNode); @@ -31,8 +33,9 @@ Struct *StructManager::insertSubstantiation(Scope *insertScope, Struct &newManif #endif // Retrieve the matching manifestation list of the scope - assert(insertScope->structs.contains(declNode->codeLoc)); - StructManifestationList &manifestationList = insertScope->structs.at(declNode->codeLoc); + const std::string structId = newManifestation.name + ":" + declNode->codeLoc.toPrettyLineAndColumn(); + assert(insertScope->structs.contains(structId)); + StructManifestationList &manifestationList = insertScope->structs.at(structId); // Add substantiated struct newManifestation.manifestationIndex = manifestationList.size(); @@ -56,7 +59,7 @@ Struct *StructManager::matchStruct(Scope *matchScope, const std::string &reqName StructRegistry structRegistry = matchScope->structs; // Loop over struct registry to find structs, that match the requirements of the instantiation std::vector matches; - for (const auto &[defCodeLocStr, m] : structRegistry) { + for (const auto &[structId, m] : structRegistry) { // Copy the manifestation list to prevent iterating over items, that are created within the loop const StructManifestationList manifestations = m; for (const auto &[mangledName, presetStruct] : manifestations) { @@ -89,15 +92,15 @@ Struct *StructManager::matchStruct(Scope *matchScope, const std::string &reqName // Check if it needs to be substantiated if (presetStruct.templateTypes.empty()) { - assert(matchScope->structs.contains(defCodeLocStr) && matchScope->structs.at(defCodeLocStr).contains(mangledName)); - matches.push_back(&matchScope->structs.at(defCodeLocStr).at(mangledName)); + assert(matchScope->structs.contains(structId) && matchScope->structs.at(structId).contains(mangledName)); + matches.push_back(&matchScope->structs.at(structId).at(mangledName)); matches.back()->used = true; continue; // Match was successful -> match the next struct } // Check if we already have this manifestation and can simply re-use it if (manifestations.contains(candidate.getSignature())) { - matches.push_back(&matchScope->structs.at(defCodeLocStr).at(candidate.getSignature())); + matches.push_back(&matchScope->structs.at(structId).at(candidate.getSignature())); break; // Leave the whole manifestation list to not double-match the manifestation } diff --git a/src/typechecker/StructManager.h b/src/typechecker/StructManager.h index 3b580569b..321be9846 100644 --- a/src/typechecker/StructManager.h +++ b/src/typechecker/StructManager.h @@ -8,19 +8,20 @@ #include #include -#include namespace spice::compiler { // Forward declarations +struct CodeLoc; class Struct; class Scope; class SymbolType; class ASTNode; +class GenericType; // Typedefs using StructManifestationList = std::unordered_map; -using StructRegistry = std::map; +using StructRegistry = std::map; class StructManager { public: diff --git a/src/typechecker/TypeChecker.h b/src/typechecker/TypeChecker.h index 2ccf16745..9b90db0f9 100644 --- a/src/typechecker/TypeChecker.h +++ b/src/typechecker/TypeChecker.h @@ -148,6 +148,7 @@ class TypeChecker : private CompilerPass, public ASTVisitor { void createDefaultCtorIfRequired(const Struct &spiceStruct, Scope *structScope); void createDefaultCopyCtorIfRequired(const Struct &spiceStruct, Scope *structScope); void createDefaultDtorIfRequired(const Struct &spiceStruct, Scope *structScope); + void createCtorBodyPreamble(Scope *bodyScope); Function *implicitlyCallStructMethod(SymbolTableEntry *entry, const std::string &methodName, const ArgList &args, const ASTNode *node); void implicitlyCallStructCopyCtor(SymbolTableEntry *entry, const ASTNode *node); diff --git a/src/typechecker/TypeCheckerCheck.cpp b/src/typechecker/TypeCheckerCheck.cpp index 30e46b970..cdbde4db6 100644 --- a/src/typechecker/TypeCheckerCheck.cpp +++ b/src/typechecker/TypeCheckerCheck.cpp @@ -115,6 +115,10 @@ std::any TypeChecker::visitProcDefCheck(ProcDefNode *node) { if (node->hasParams) visit(node->paramLst()); + // Prepare generation of special ctor preamble to store VTable, default field values, etc. if required + if (node->isCtor) + createCtorBodyPreamble(node->scope); + // Visit statements in new scope visit(node->body()); diff --git a/src/typechecker/TypeCheckerImplicit.cpp b/src/typechecker/TypeCheckerImplicit.cpp index 4af0e6642..3a1fbbf5c 100644 --- a/src/typechecker/TypeCheckerImplicit.cpp +++ b/src/typechecker/TypeCheckerImplicit.cpp @@ -88,22 +88,16 @@ void TypeChecker::createDefaultCtorIfRequired(const Struct &spiceStruct, Scope * if (auto fieldNode = dynamic_cast(fieldSymbol->declNode)) { hasFieldsWithDefaultValue |= fieldNode->defaultValue() != nullptr; - if (fieldSymbol->getType().is(TY_STRUCT)) { - Scope *fieldScope = fieldSymbol->getType().getBodyScope(); - // Lookup ctor function - const Function *ctorFct = FunctionManager::matchFunction(fieldScope, CTOR_FUNCTION_NAME, thisType, {}, {}, true, node); - hasFieldsToConstruct |= ctorFct != nullptr; - requestRevisitIfRequired(ctorFct); - } } else { assert(dynamic_cast(fieldSymbol->declNode) != nullptr); - if (fieldSymbol->getType().is(TY_STRUCT)) { - Scope *fieldScope = fieldSymbol->getType().getBodyScope(); - // Lookup ctor function - const Function *ctorFct = FunctionManager::matchFunction(fieldScope, CTOR_FUNCTION_NAME, thisType, {}, {}, true, node); - hasFieldsToConstruct |= ctorFct != nullptr; - requestRevisitIfRequired(ctorFct); - } + } + + if (fieldSymbol->getType().is(TY_STRUCT)) { + Scope *fieldScope = fieldSymbol->getType().getBodyScope(); + // Lookup ctor function + const Function *ctorFct = FunctionManager::matchFunction(fieldScope, CTOR_FUNCTION_NAME, thisType, {}, {}, true, node); + hasFieldsToConstruct |= ctorFct != nullptr; + requestRevisitIfRequired(ctorFct); } } @@ -220,6 +214,32 @@ void TypeChecker::createDefaultDtorIfRequired(const Struct &spiceStruct, Scope * } } +/** + * Prepare the generation of the ctor body preamble. This preamble is used to initialize the VTable, construct or initialize + * fields. + */ +void TypeChecker::createCtorBodyPreamble(Scope *bodyScope) { + // Retrieve struct scope + Scope *structScope = bodyScope->parent; + assert(structScope != nullptr); + + const size_t fieldCount = structScope->getFieldCount(); + for (size_t i = 0; i < fieldCount; i++) { + SymbolTableEntry *fieldSymbol = structScope->symbolTable.lookupStrictByIndex(i); + assert(fieldSymbol != nullptr && fieldSymbol->isField()); + if (fieldSymbol->isImplicitField) + continue; + const SymbolType &fieldType = fieldSymbol->getType(); + + if (fieldType.is(TY_STRUCT)) { + auto fieldNode = spice_pointer_cast(fieldSymbol->declNode); + // Match ctor function, create the concrete manifestation and set it to used + Scope *matchScope = fieldType.getBodyScope(); + FunctionManager::matchFunction(matchScope, CTOR_FUNCTION_NAME, fieldType, {}, {}, false, fieldNode); + } + } +} + /** * Prepare the generation of a call to a method of a given struct * diff --git a/test/TestRunner.cpp b/test/TestRunner.cpp index eec4515aa..0ee60f107 100644 --- a/test/TestRunner.cpp +++ b/test/TestRunner.cpp @@ -213,8 +213,9 @@ void execTestCase(const TestCase &testCase) { const bool exitRefFileFound = TestUtil::checkRefMatch(testCase.testPath / REF_NAME_EXIT_CODE, [&]() { return std::to_string(result.exitCode); }); // If no exit code ref file exists, check against 0 - if (!exitRefFileFound) + if (!exitRefFileFound) { EXPECT_EQ(0, result.exitCode) << "Program exited with non-zero exit code"; + } #endif return result.output; diff --git a/test/test-files/irgenerator/structs/success-default-dtor-nested/ir-code.ll b/test/test-files/irgenerator/structs/success-default-dtor-nested/ir-code.ll index 0fd1e88b7..0eb5fe9d2 100644 --- a/test/test-files/irgenerator/structs/success-default-dtor-nested/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-default-dtor-nested/ir-code.ll @@ -7,7 +7,6 @@ source_filename = "source.spice" @anon.string.0 = private unnamed_addr constant [12 x i8] c"Hello World\00", align 1 @printf.str.0 = private unnamed_addr constant [19 x i8] c"Inner dtor called\0A\00", align 1 -@anon.string.1 = private unnamed_addr constant [12 x i8] c"Hello World\00", align 1 declare ptr @malloc(i64 noundef) @@ -42,6 +41,16 @@ define private void @_ZN5Inner4dtorEv(ptr noundef nonnull align 8 dereferenceabl ; Function Attrs: nofree nounwind declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #0 +; Function Attrs: norecurse +define void @_ZN6Middle4ctorEv(ptr noundef nonnull align 8 dereferenceable(16) %0) #1 { + %this = alloca ptr, align 8 + store ptr %0, ptr %this, align 8 + %2 = load ptr, ptr %this, align 8 + %3 = getelementptr inbounds %struct.Middle, ptr %2, i32 0, i32 0 + call void @_ZN5Inner4ctorEv(ptr %3) + ret void +} + ; Function Attrs: norecurse define void @_ZN6Middle4dtorEv(ptr noundef nonnull align 8 dereferenceable(16) %0) #1 { %this = alloca ptr, align 8 @@ -52,6 +61,16 @@ define void @_ZN6Middle4dtorEv(ptr noundef nonnull align 8 dereferenceable(16) % ret void } +; Function Attrs: norecurse +define void @_ZN5Outer4ctorEv(ptr noundef nonnull align 8 dereferenceable(16) %0) #1 { + %this = alloca ptr, align 8 + store ptr %0, ptr %this, align 8 + %2 = load ptr, ptr %this, align 8 + %3 = getelementptr inbounds %struct.Outer, ptr %2, i32 0, i32 0 + call void @_ZN6Middle4ctorEv(ptr %3) + ret void +} + ; Function Attrs: norecurse define void @_ZN5Outer4dtorEv(ptr noundef nonnull align 8 dereferenceable(16) %0) #1 { %this = alloca ptr, align 8 @@ -67,7 +86,7 @@ define dso_local i32 @main() #2 { %result = alloca i32, align 4 %outer = alloca %struct.Outer, align 8 store i32 0, ptr %result, align 4 - store %struct.Outer { %struct.Middle { %struct.Inner { ptr @anon.string.1, ptr null } } }, ptr %outer, align 8 + call void @_ZN5Outer4ctorEv(ptr %outer) call void @_ZN5Outer4dtorEv(ptr %outer) %1 = load i32, ptr %result, align 4 ret i32 %1