Skip to content

Commit

Permalink
Fix bug in struct substantiation
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Jan 20, 2023
1 parent b24f52e commit 6ae44c4
Show file tree
Hide file tree
Showing 19 changed files with 178 additions and 142 deletions.
2 changes: 1 addition & 1 deletion media/specs/name-mangling.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,4 @@ Here is the scheme, how Spice mangles struct names:
### Implementation

To see the implementation for struct name mangling, have a look here:
[Struct::gerMangledName](../../src/model/Struct.cpp#:~:text=Struct::getMangledName)
[Struct::getMangledName](../../src/model/Struct.cpp#:~:text=Struct::getMangledName)
83 changes: 8 additions & 75 deletions media/test-project/os-test.spice
Original file line number Diff line number Diff line change
@@ -1,78 +1,11 @@
type T int|long;

type TestStruct<T> struct {
T f1
unsigned long length
}

p TestStruct.ctor(const unsigned long initialLength) {
this.length = initialLength;
}

p TestStruct.printLength() {
printf("%d\n", this.length);
}

type Alias alias TestStruct<long>;

f<int> main() {
Alias a = Alias{12345l, 54321l};
a.printLength();
dyn b = Alias(12l);
b.printLength();
}

/*type T dyn;

type Vector<T> struct {
T data
}

p Vector.ctor() {
this.data = 12;
}

f<int> main() {
Vector<int> intVec = Vector<int>();
}*/

/*type T string|char;

type TestStruct<T> struct {
T base
int test
}

f<int> main() {
TestStruct<char> s = TestStruct<char>{ 'a', 1 };
s.printTest();
}

p TestStruct.printTest() {
printf("Test: %d\n", this.getTest());
}

f<int> TestStruct.getTest() {
if this.test == 1 {
this.test++;
this.printTest();
}
return this.test;
}*/

/*ext<byte*> malloc(long);
ext free(byte*);
import "std/data/vector" as vec;
import "std/data/pair" as pair;

f<int> main() {
byte* address = malloc(1l);
*address = (byte) 12;
free(address);
}*/
Vector<Pair<int, string>> pairVector = Vector<Pair<int, string>>();
pairVector.pushBack(Pair<int, string>(0, "Hello"));
pairVector.pushBack(Pair<int, string>(1, "World"));

/*f<int> main() {
int[10] a;
for int i = 0; i < len(a); i++ {
a[i] = 1;
}
printf("Cell [3]: %d", a[3]);
}*/
Pair<int, string> p1 = pairVector.get(1);
printf("Hello %s!\n", p1.getSecond());
}
12 changes: 5 additions & 7 deletions src/irgenerator/GenTopLevelDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ std::any IRGenerator::visitFctDef(const FctDefNode *node) {
}

// Change scope
currentScope = currentScope->getChildScope(manifestation->getSignature());
currentScope = currentScope->getChildScope(manifestation->getSignature(false));
assert(currentScope != nullptr);

// Get 'this' entry
Expand Down Expand Up @@ -321,7 +321,7 @@ std::any IRGenerator::visitProcDef(const ProcDefNode *node) {
}

// Change scope
currentScope = currentScope->getChildScope(manifestation->getSignature());
currentScope = currentScope->getChildScope(manifestation->getSignature(false));
assert(currentScope != nullptr);

// Get 'this' entry
Expand Down Expand Up @@ -462,10 +462,8 @@ std::any IRGenerator::visitStructDef(const StructDefNode *node) {
llvm::StructType *structType = llvm::StructType::create(context, mangledName);

// Set LLVM type to the struct entry
const std::string structSignature = spiceStruct.getSignature();
SymbolTableEntry *structEntry = rootScope->lookupStrict(structSignature);
assert(structEntry != nullptr);
structEntry->setStructLLVMType(structType);
assert(spiceStruct.entry != nullptr);
spiceStruct.entry->setStructLLVMType(structType);

// Collect concrete field types
std::vector<llvm::Type *> fieldTypes;
Expand Down Expand Up @@ -501,7 +499,7 @@ std::any IRGenerator::visitGenericTypeDef(const GenericTypeDefNode *node) {
}

std::any IRGenerator::visitAliasDef(const AliasDefNode *node) {
return nullptr; // Noop (alias defs are high-level semantic-only structures)
return nullptr; // Noop (alias definitions are high-level semantic-only structures)
}

std::any IRGenerator::visitGlobalVarDef(const GlobalVarDefNode *node) {
Expand Down
1 change: 1 addition & 0 deletions src/irgenerator/IRGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ std::string IRGenerator::getIRString() const {
void IRGenerator::changeToScope(Scope *scope, const ScopeType scopeType) {
assert(scope != nullptr);
assert(scope->type == scopeType);
assert(!scope->isGenericScope);
// Adjust members of the new scope
scope->parent = currentScope;
scope->symbolTable.parent = &currentScope->symbolTable;
Expand Down
28 changes: 25 additions & 3 deletions src/model/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,28 @@ std::string Function::getMangledName() const {
* @return String representation as function signature
*/
std::string Function::getSignature(bool withThisType /*=true*/) const {
std::vector<SymbolType> templateSymbolTypes;
templateSymbolTypes.reserve(templateTypes.size());
for (const GenericType &genericType : templateTypes)
templateSymbolTypes.push_back(genericType);

return Function::getSignature(name, thisType, returnType, paramList, templateSymbolTypes, withThisType);
}

/**
* Get a string representation of the current function.
*
* @param name Function name
* @param thisType This symbol type
* @param returnType Return symbol type
* @param paramList Param symbol types
* @param concreteTemplateTypes Concrete template symbol types
* @param withThisType Include 'this' type in signature
* @return Function signature
*/
std::string Function::getSignature(const std::string &name, const SymbolType &thisType, const SymbolType &returnType,
const ParamList &paramList, const std::vector<SymbolType> &concreteTemplateTypes,
bool withThisType /*=true*/) {
// Build this type string
std::stringstream thisTyStr;
if (withThisType && !thisType.is(TY_DYN)) {
Expand Down Expand Up @@ -128,12 +150,12 @@ std::string Function::getSignature(bool withThisType /*=true*/) const {

// Build template type string
std::stringstream templateTyStr;
if (!templateTypes.empty()) {
if (!concreteTemplateTypes.empty()) {
templateTyStr << "<";
for (size_t i = 0; i < templateTypes.size(); i++) {
for (size_t i = 0; i < concreteTemplateTypes.size(); i++) {
if (i > 0)
templateTyStr << ",";
templateTyStr << templateTypes.at(i).getName();
templateTyStr << concreteTemplateTypes.at(i).getName();
}
templateTyStr << ">";
}
Expand Down
3 changes: 3 additions & 0 deletions src/model/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class Function {
[[nodiscard]] std::vector<SymbolType> getParamTypes() const;
[[nodiscard]] std::string getMangledName() const;
[[nodiscard]] std::string getSignature(bool withThisType = true) const;
[[nodiscard]] static std::string getSignature(const std::string &name, const SymbolType &thisType, const SymbolType &returnType,
const ParamList &paramList, const std::vector<SymbolType> &concreteTemplateTypes,
bool withThisType = true);
[[nodiscard]] inline bool isMethod() const { return !thisType.is(TY_DYN); }
[[nodiscard]] inline bool isFunction() const { return !isMethod() && !returnType.is(TY_DYN); }
[[nodiscard]] inline bool isProcedure() const { return !isMethod() && returnType.is(TY_DYN); }
Expand Down
44 changes: 22 additions & 22 deletions src/model/Struct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,36 +52,17 @@ std::string Struct::getSignature() const {
return getSignature(name, templateSymbolTypes);
}

/**
* Checks if a struct contains template types.
* This would imply that the struct is not substantiated by its generic types yet.
*
* @return Substantiated generics or not
*/
bool Struct::hasSubstantiatedGenerics() const {
return std::none_of(templateTypes.begin(), templateTypes.end(),
[](const GenericType &genericType) { return genericType.isBaseType(TY_GENERIC); });
}

/**
* Checks if a struct has generic types present.
* This would imply that the struct is not fully substantiated yet.
*
* @return Fully substantiated or not
*/
bool Struct::isFullySubstantiated() const { return hasSubstantiatedGenerics(); }

/**
* Get the signature from the struct name and the concrete template types
*
* Example:
* Pair<int,double>
*
* @param structName Struct name
* @param name Struct name
* @param concreteTemplateTypes Concrete template types
* @return Signature
*/
std::string Struct::getSignature(const std::string &structName, const std::vector<SymbolType> &concreteTemplateTypes) {
std::string Struct::getSignature(const std::string &name, const std::vector<SymbolType> &concreteTemplateTypes) {
// Build template type string
std::stringstream templateTyStr;
if (!concreteTemplateTypes.empty()) {
Expand All @@ -94,9 +75,28 @@ std::string Struct::getSignature(const std::string &structName, const std::vecto
templateTyStr << ">";
}

return CommonUtil::getLastFragment(structName, SCOPE_ACCESS_TOKEN) + templateTyStr.str();
return CommonUtil::getLastFragment(name, SCOPE_ACCESS_TOKEN) + templateTyStr.str();
}

/**
* Checks if a struct contains template types.
* This would imply that the struct is not substantiated by its generic types yet.
*
* @return Substantiated generics or not
*/
bool Struct::hasSubstantiatedGenerics() const {
return std::none_of(templateTypes.begin(), templateTypes.end(),
[](const GenericType &genericType) { return genericType.isBaseType(TY_GENERIC); });
}

/**
* Checks if a struct has generic types present.
* This would imply that the struct is not fully substantiated yet.
*
* @return Fully substantiated or not
*/
bool Struct::isFullySubstantiated() const { return hasSubstantiatedGenerics(); }

/**
* Checks if the current struct is of infinite size.
*
Expand Down
4 changes: 2 additions & 2 deletions src/model/Struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ class Struct {
// Public methods
[[nodiscard]] std::string getMangledName() const;
[[nodiscard]] std::string getSignature() const;
static std::string getSignature(const std::string &name, const std::vector<SymbolType> &concreteTemplateTypes);
[[nodiscard]] bool hasSubstantiatedGenerics() const;
[[nodiscard]] bool isFullySubstantiated() const;
static std::string getSignature(const std::string &structName, const std::vector<SymbolType> &concreteTemplateTypes);
//bool hasInfiniteSize(Scope *anchorScope = nullptr) const;
// bool hasInfiniteSize(Scope *anchorScope = nullptr) const;

// Public members
std::string name;
Expand Down
12 changes: 6 additions & 6 deletions src/symboltablebuilder/Scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // N
continue;

warningType = UNUSED_FUNCTION;
warningMessage = "The function '" + key + "' is unused";
warningMessage = "The function '" + entry.declNode->getFctManifestations()->front()->getSignature() + "' is unused";
break;
}
case TY_PROCEDURE: {
Expand All @@ -235,15 +235,15 @@ void Scope::collectWarnings(std::vector<CompilerWarning> &warnings) const { // N
continue;

warningType = UNUSED_PROCEDURE;
warningMessage = "The procedure '" + key + "' is unused";
warningMessage = "The procedure '" + entry.declNode->getFctManifestations()->front()->getSignature() + "' is unused";
break;
}
case TY_STRUCT: {
// Skip generic struct entries
if (!entry.getType().getTemplateTypes().empty())
continue;

if (entry.scope->type == SCOPE_GLOBAL) {
// Skip generic struct entries
if (!entry.getType().getTemplateTypes().empty())
continue;

warningType = UNUSED_STRUCT;
warningMessage = "The struct '" + entry.name + "' is unused";
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/symboltablebuilder/SymbolTableBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ std::any SymbolTableBuilder::visitFctDef(FctDefNode *node) {

// Create scope for the function
node->fctScope = currentScope = currentScope->createChildScope(node->getScopeId(), SCOPE_FUNC_PROC_BODY, &node->codeLoc);
currentScope->isGenericScope = node->hasTemplateTypes;
currentScope->isGenericScope = node->hasTemplateTypes || (node->structScope && node->structScope->isGenericScope);

// Create symbol for 'this' variable
if (node->isMethod) {
Expand Down Expand Up @@ -143,7 +143,7 @@ std::any SymbolTableBuilder::visitProcDef(ProcDefNode *node) {

// Create scope for the procedure
node->procScope = currentScope = currentScope->createChildScope(node->getScopeId(), SCOPE_FUNC_PROC_BODY, &node->codeLoc);
currentScope->isGenericScope = node->hasTemplateTypes;
currentScope->isGenericScope = node->hasTemplateTypes || (node->structScope && node->structScope->isGenericScope);

// Create symbol for 'this' variable
if (node->isMethod) {
Expand Down
12 changes: 7 additions & 5 deletions src/typechecker/FunctionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ Function *FunctionManager::matchFunction(Scope *matchScope, const std::string &r
if (presetFunction.templateTypes.empty()) {
assert(matchScope->functions.contains(defCodeLocStr) && matchScope->functions.at(defCodeLocStr).contains(mangledName));
matches.push_back(&matchScope->functions.at(defCodeLocStr).at(mangledName));
matches.back()->used = true;
continue; // Match was successful -> match the next function
}

Expand All @@ -216,19 +217,20 @@ Function *FunctionManager::matchFunction(Scope *matchScope, const std::string &r
substantiatedFunction->declNode->getFctManifestations()->push_back(substantiatedFunction);

// Copy function entry
const std::string newSignature = substantiatedFunction->getSignature();
matchScope->symbolTable.copySymbol(presetFunction.entry->name, newSignature);
const std::string newSignature = substantiatedFunction->getSignature(false);
matchScope->lookupStrict(presetFunction.entry->name)->used = true;
candidate.entry = matchScope->lookupStrict(newSignature);
candidate.entry->used = true;
matchScope->symbolTable.copySymbol(presetFunction.entry->name, newSignature);
substantiatedFunction->entry = matchScope->lookupStrict(newSignature);
assert(substantiatedFunction->entry != nullptr);

// Copy function scope
matchScope->copyChildScope(presetFunction.getSignature(false), newSignature);
Scope *childScope = matchScope->getChildScope(newSignature);
assert(childScope != nullptr);
childScope->isGenericScope = false;

// Insert symbols for generic type names with concrete types into the child block
for (const auto &[typeName, concreteType] : candidate.typeMapping)
for (const auto &[typeName, concreteType] : substantiatedFunction->typeMapping)
childScope->insertGenericType(typeName, GenericType(concreteType));

// Substantiate the 'this' symbol table entry in the new function scope
Expand Down

0 comments on commit 6ae44c4

Please sign in to comment.