Skip to content

Commit

Permalink
Avoid functions with different names in the same manifestation list (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer authored Feb 24, 2024
1 parent 4efb183 commit 4ae6921
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 39 deletions.
2 changes: 1 addition & 1 deletion .run/spice.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -d -O2 ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -d -O0 -ir ../../media/test-project/test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" EMULATE_TERMINAL="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="LLVM_BUILD_INCLUDE_DIR" value="D:/LLVM/build-release/include" />
<env name="LLVM_INCLUDE_DIR" value="D:/LLVM/llvm/include" />
Expand Down
2 changes: 1 addition & 1 deletion src/model/GenericType.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <string>
#include <utility>

#include "symboltablebuilder/SymbolType.h"
#include <symboltablebuilder/SymbolType.h>

#include "../../lib/json/json.hpp"

Expand Down
21 changes: 13 additions & 8 deletions src/typechecker/FunctionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@

#include <ast/ASTNodes.h>
#include <exception/SemanticError.h>
#include <model/GenericType.h>
#include <symboltablebuilder/Scope.h>
#include <symboltablebuilder/SymbolTableBuilder.h>
#include <typechecker/ExprResult.h>
#include <typechecker/TypeMatcher.h>
#include <util/CodeLoc.h>

namespace spice::compiler {

Function *FunctionManager::insertFunction(Scope *insertScope, const Function &baseFunction,
std::vector<Function *> *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<Function> manifestations;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<Function *> 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) {
Expand Down Expand Up @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions src/typechecker/FunctionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
#include <vector>

#include <model/GenericType.h>
#include <typechecker/ExprResult.h>
#include <util/CodeLoc.h>

namespace spice::compiler {

// Forward declarations
struct CodeLoc;
struct ExprResult;
class Function;
class Scope;
class SymbolTableEntry;
Expand All @@ -23,7 +23,7 @@ class GenericType;

// Typedefs
using FunctionManifestationList = std::unordered_map</*mangledName=*/std::string, Function>;
using FunctionRegistry = std::map<CodeLoc, FunctionManifestationList>;
using FunctionRegistry = std::map</*fctId=*/std::string, /*manifestationList=*/FunctionManifestationList>;
using Arg = std::pair</*type=*/SymbolType, /*isTemporary=*/bool>;
using ArgList = std::vector<Arg>;

Expand Down
17 changes: 10 additions & 7 deletions src/typechecker/StructManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
#include <symboltablebuilder/Scope.h>
#include <symboltablebuilder/SymbolTableBuilder.h>
#include <typechecker/TypeMatcher.h>
#include <util/CodeLoc.h>

namespace spice::compiler {

Struct *StructManager::insertStruct(Scope *insertScope, Struct &spiceStruct, std::vector<Struct *> *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);
Expand All @@ -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();
Expand All @@ -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<Struct *> 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) {
Expand Down Expand Up @@ -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
}

Expand Down
5 changes: 3 additions & 2 deletions src/typechecker/StructManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@
#include <vector>

#include <model/GenericType.h>
#include <util/CodeLoc.h>

namespace spice::compiler {

// Forward declarations
struct CodeLoc;
class Struct;
class Scope;
class SymbolType;
class ASTNode;
class GenericType;

// Typedefs
using StructManifestationList = std::unordered_map</*mangledName=*/std::string, Struct>;
using StructRegistry = std::map<CodeLoc, StructManifestationList>;
using StructRegistry = std::map</*structId=*/std::string, /*manifestationList=*/StructManifestationList>;

class StructManager {
public:
Expand Down
1 change: 1 addition & 0 deletions src/typechecker/TypeChecker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/typechecker/TypeCheckerCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down
48 changes: 34 additions & 14 deletions src/typechecker/TypeCheckerImplicit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,16 @@ void TypeChecker::createDefaultCtorIfRequired(const Struct &spiceStruct, Scope *

if (auto fieldNode = dynamic_cast<FieldNode *>(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<DataTypeNode *>(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);
}
}

Expand Down Expand Up @@ -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<FieldNode *>(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
*
Expand Down
3 changes: 2 additions & 1 deletion test/TestRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 4ae6921

Please sign in to comment.