diff --git a/.github/workflows/ci-cpp.yml b/.github/workflows/ci-cpp.yml index 4ebf50f56..55deada77 100644 --- a/.github/workflows/ci-cpp.yml +++ b/.github/workflows/ci-cpp.yml @@ -78,7 +78,7 @@ jobs: echo "/usr/lib/ccache:/usr/local/opt/ccache/libexec" >> $GITHUB_PATH mkdir ./bin cd ./bin - cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DSPICE_BUILT_BY="ghactions" -DSPICE_LINK_STATIC=OFF -DSPICE_LTO=ON -DSPICE_RUN_COVERAGE=ON -GNinja -Wattributes .. + cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DSPICE_BUILT_BY="ghactions" -DSPICE_LINK_STATIC=OFF -DSPICE_LTO=ON -DSPICE_RUN_COVERAGE=ON -GNinja -Wattributes .. cmake --build . --target spicetest -j$(nproc) # - name: Build Test target diff --git a/media/specs/ctors-and-dtors.md b/media/specs/ctors-and-dtors.md index 6389e0016..0c89bafc1 100644 --- a/media/specs/ctors-and-dtors.md +++ b/media/specs/ctors-and-dtors.md @@ -3,8 +3,8 @@ ## Roadmap - [x] Produce error when using `heap` specifier on non-pointer types -- [ ] Add mechanism to generate functions and methods, that are not part of the AST -- [ ] Generate default dtor +- [x] Add mechanism to generate functions and methods, that are not part of the AST +- [x] Generate default dtor - [ ] Generate default ctor - [ ] Generate default copy ctor - [ ] (Generate default move ctor) @@ -70,7 +70,6 @@ with the following signature: ```spice p TestStruct.dtor() { - // Code for dtor body // Call the dtor of all fields, that are of type struct and have a dtor // Free all fields, that are marked with the `heap` specifier } diff --git a/media/test-project/test.spice b/media/test-project/test.spice index 10d5fad54..cac40c7fe 100644 --- a/media/test-project/test.spice +++ b/media/test-project/test.spice @@ -1,4 +1,25 @@ +import "std/data/vector" as vec; + f main() { - int t = 123; - heap int test = t; -} \ No newline at end of file + vec::Vector v1 = vec::Vector(3); + v1.pushBack(1.2); + v1.pushBack(7.4964598); + v1.pushBack(5.3); + v1.pushBack(-238974.23); + v1.pushBack(23234.2); + v1.pushBack(-1234.9); + v1.pushBack(0.0); + printf("Vector size: %d\n", v1.getSize()); + printf("Vector capacity: %d\n", v1.getCapacity()); + printf("Vector item 5: %f\n", v1.get(5)); +} + +/*type TestStruct struct { + int a = 123 + short b = 1s +} + +f main() { + TestStruct ts; + printf("%d %d\n", ts.a, ts.b); +}*/ \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 97cc7eb15..8b1348837 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -66,6 +66,7 @@ set(SOURCES typechecker/TypeChecker.h typechecker/TypeCheckerPrepare.cpp typechecker/TypeCheckerCheck.cpp + typechecker/TypeCheckerImplicit.cpp typechecker/OpRuleManager.cpp typechecker/OpRuleManager.h typechecker/FunctionManager.cpp @@ -86,6 +87,7 @@ set(SOURCES irgenerator/GenBuiltinFunctions.cpp irgenerator/GenExpressions.cpp irgenerator/GenValues.cpp + irgenerator/GenImplicit.cpp irgenerator/StdFunctionManager.cpp irgenerator/StdFunctionManager.h irgenerator/OpRuleConversionManager.cpp diff --git a/src/CompilerPass.h b/src/CompilerPass.h index 4173f64de..7f0b7e91b 100644 --- a/src/CompilerPass.h +++ b/src/CompilerPass.h @@ -26,6 +26,7 @@ class CompilerPass { const CliOptions &cliOptions; SourceFile *sourceFile; Scope *rootScope; + size_t manIdx = 0; public: // Public members diff --git a/src/SourceFile.cpp b/src/SourceFile.cpp index 528ee7a79..dd81cecc9 100644 --- a/src/SourceFile.cpp +++ b/src/SourceFile.cpp @@ -566,6 +566,10 @@ void SourceFile::addDependency(SourceFile *sourceFile, const ASTNode *declNode, sourceFile->dependants.push_back(this); } +bool SourceFile::imports(const SourceFile *sourceFile) const { + return std::ranges::any_of(dependencies, [=](const auto &dependency) { return dependency.second.first == sourceFile; }); +} + bool SourceFile::isAlreadyImported(const std::string &filePathSearch) const { // NOLINT(misc-no-recursion) // Check if the current source file corresponds to the path to search if (filePath == filePathSearch) @@ -574,12 +578,12 @@ bool SourceFile::isAlreadyImported(const std::string &filePathSearch) const { // return parent != nullptr && parent->isAlreadyImported(filePathSearch); } -void SourceFile::requestRuntimeModule(RuntimeModule runtimeModule) { +SourceFile *SourceFile::requestRuntimeModule(RuntimeModule runtimeModule) { // Check if the module was already imported if (importedRuntimeModules & runtimeModule) - return; + return resourceManager.runtimeModuleManager.getModule(runtimeModule); - resourceManager.runtimeModuleManager.requestModule(this, runtimeModule); + return resourceManager.runtimeModuleManager.requestModule(this, runtimeModule); } void SourceFile::addNameRegistryEntry(const std::string &symbolName, SymbolTableEntry *entry, Scope *scope, diff --git a/src/SourceFile.h b/src/SourceFile.h index ffa65efb3..cd914361f 100644 --- a/src/SourceFile.h +++ b/src/SourceFile.h @@ -133,8 +133,9 @@ class SourceFile { // Public methods void addDependency(SourceFile *sourceFile, const ASTNode *declNode, const std::string &dependencyName, const std::string &path); + [[nodiscard]] bool imports(const SourceFile *sourceFile) const; [[nodiscard]] bool isAlreadyImported(const std::string &filePathSearch) const; - void requestRuntimeModule(RuntimeModule runtimeModule); + SourceFile *requestRuntimeModule(RuntimeModule runtimeModule); void addNameRegistryEntry(const std::string &symbolName, SymbolTableEntry *entry, Scope *scope, bool keepNewOnCollision = true, SymbolTableEntry *importEntry = nullptr, const std::string &predecessorName = ""); [[nodiscard]] const NameRegistryEntry *getNameRegistryEntry(std::string symbolName) const; diff --git a/src/ast/ASTNodes.h b/src/ast/ASTNodes.h index ef01be3f3..d8305d8e8 100644 --- a/src/ast/ASTNodes.h +++ b/src/ast/ASTNodes.h @@ -152,8 +152,8 @@ class ASTNode { return children.size() == 1 && children.front()->returnsOnAllControlPaths(overrideUnreachable); } - [[nodiscard]] virtual std::vector *getFctManifestations() { - assert(false && "Must be called on a FctDefNode, ProcDefNode or ExtDeclNode"); + [[nodiscard]] virtual std::vector *getFctManifestations(const std::string &fctName) { + assert(false && "Must be called on a FctDefNode, ProcDefNode, ExtDeclNode, StructDefNode or SignatureNode"); return nullptr; } @@ -168,6 +168,7 @@ class ASTNode { } [[nodiscard]] virtual bool isFctOrProcDef() const { return false; } + [[nodiscard]] virtual bool isStructDef() const { return false; } [[nodiscard]] virtual bool isStmtNode() const { return false; } [[nodiscard]] virtual bool isAssignExpr() const { return false; } @@ -287,13 +288,9 @@ class FctDefNode : public ASTNode { // Other methods [[nodiscard]] std::string getScopeId() const { return "fct:" + codeLoc.toString(); } - [[nodiscard]] std::string getSymbolTableEntryName() const { - const FctNameNode *functionName = getChild(); - assert(functionName != nullptr); - return functionName->name + ":" + codeLoc.toPrettyLineAndColumn(); - } + [[nodiscard]] std::string getSymbolTableEntryName() const { return Function::getSymbolTableEntryName(fctName->name, codeLoc); } [[nodiscard]] bool returnsOnAllControlPaths(bool *overrideUnreachable) const override; - std::vector *getFctManifestations() override { return &fctManifestations; } + std::vector *getFctManifestations(const std::string &_) override { return &fctManifestations; } [[nodiscard]] bool isFctOrProcDef() const override { return true; } // Public members @@ -328,13 +325,9 @@ class ProcDefNode : public ASTNode { // Other methods [[nodiscard]] std::string getScopeId() const { return "proc:" + codeLoc.toString(); } - [[nodiscard]] std::string getSymbolTableEntryName() const { - const FctNameNode *functionName = getChild(); - assert(functionName != nullptr); - return functionName->name + ":" + codeLoc.toPrettyLineAndColumn(); - } + [[nodiscard]] std::string getSymbolTableEntryName() const { return Function::getSymbolTableEntryName(procName->name, codeLoc); } bool returnsOnAllControlPaths(bool *overrideUnreachable) const override; - std::vector *getFctManifestations() override { return &procManifestations; } + std::vector *getFctManifestations(const std::string &_) override { return &procManifestations; } [[nodiscard]] bool isFctOrProcDef() const override { return true; } // Public members @@ -369,6 +362,12 @@ class StructDefNode : public ASTNode { // Other methods std::vector *getStructManifestations() override { return &structManifestations; } + std::vector *getFctManifestations(const std::string &fctName) override { + if (!defaultFctManifestations.contains(fctName)) + defaultFctManifestations.insert({fctName, {}}); + return &defaultFctManifestations.at(fctName); + } + [[nodiscard]] bool isStructDef() const override { return true; } // Public members std::string structName; @@ -377,6 +376,7 @@ class StructDefNode : public ASTNode { SymbolTableEntry *entry = nullptr; TypeSpecifiers structSpecifiers = TypeSpecifiers::of(TY_STRUCT); std::vector structManifestations; + std::map> defaultFctManifestations; Scope *structScope = nullptr; }; @@ -511,7 +511,7 @@ class ExtDeclNode : public ASTNode { [[nodiscard]] TypeLstNode *argTypeLst() const { return getChild(); } // Other methods - std::vector *getFctManifestations() override { return &extFunctionManifestations; } + std::vector *getFctManifestations(const std::string &_) override { return &extFunctionManifestations; } // Public members std::string extFunctionName; @@ -903,7 +903,7 @@ class SignatureNode : public ASTNode { [[nodiscard]] TypeLstNode *paramTypeLst() const { return getChild(hasTemplateTypes ? 1 : 0); } // Other methods - std::vector *getFctManifestations() override { return &signatureManifestations; } + std::vector *getFctManifestations(const std::string &_) override { return &signatureManifestations; } // Public members Type signatureType = SignatureNode::TYPE_NONE; diff --git a/src/exception/SemanticError.cpp b/src/exception/SemanticError.cpp index fcfbbdd10..6acc8b542 100644 --- a/src/exception/SemanticError.cpp +++ b/src/exception/SemanticError.cpp @@ -8,6 +8,7 @@ namespace spice::compiler { SemanticError::SemanticError(const ASTNode *node, const SemanticErrorType &type, const std::string &message) { + assert(node != nullptr); errorMessage = "[Error|Semantic] " + node->codeLoc.toPrettyString() + ":\n"; errorMessage += getMessagePrefix(type) + ": " + message; if (!node->errorMessage.empty()) diff --git a/src/global/RuntimeModuleManager.cpp b/src/global/RuntimeModuleManager.cpp index d1c371620..0f811116a 100644 --- a/src/global/RuntimeModuleManager.cpp +++ b/src/global/RuntimeModuleManager.cpp @@ -29,6 +29,11 @@ SourceFile *RuntimeModuleManager::requestModule(SourceFile *parentSourceFile, Ru return rtFile; } +SourceFile *RuntimeModuleManager::getModule(RuntimeModule requestedModule) const { + assert(isModuleAvailable(requestedModule)); + return modules.at(requestedModule); +} + bool RuntimeModuleManager::isModuleAvailable(RuntimeModule requestedModule) const { return modules.contains(requestedModule); } SourceFile *RuntimeModuleManager::loadModule(SourceFile *parentSourceFile, RuntimeModule requestedModule) { diff --git a/src/global/RuntimeModuleManager.h b/src/global/RuntimeModuleManager.h index fc41bfe72..dc5db007f 100644 --- a/src/global/RuntimeModuleManager.h +++ b/src/global/RuntimeModuleManager.h @@ -35,6 +35,7 @@ class RuntimeModuleManager { // Public methods SourceFile *requestModule(SourceFile *parentSourceFile, RuntimeModule requestedModule); + [[nodiscard]] SourceFile *getModule(RuntimeModule requestedModule) const; [[nodiscard]] bool isModuleAvailable(RuntimeModule requestedModule) const; private: diff --git a/src/irgenerator/DebugInfoGenerator.cpp b/src/irgenerator/DebugInfoGenerator.cpp index c91e639c9..18310770a 100644 --- a/src/irgenerator/DebugInfoGenerator.cpp +++ b/src/irgenerator/DebugInfoGenerator.cpp @@ -340,7 +340,7 @@ llvm::DIType *DebugInfoGenerator::getDITypeForSymbolType(const ASTNode *node, co for (size_t i = 0; i < spiceStruct->fieldTypes.size(); i++) { // Get field entry SymbolTableEntry *fieldEntry = spiceStruct->structScope->symbolTable.lookupStrictByIndex(i); - assert(fieldEntry != nullptr); + assert(fieldEntry != nullptr && fieldEntry->isField()); const SymbolType fieldType = fieldEntry->getType(); const size_t fieldLineNo = fieldEntry->declNode->codeLoc.line; const size_t offsetInBits = structLayout->getElementOffsetInBits(i); diff --git a/src/irgenerator/GenImplicit.cpp b/src/irgenerator/GenImplicit.cpp new file mode 100644 index 000000000..01ea34e15 --- /dev/null +++ b/src/irgenerator/GenImplicit.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2021-2023 ChilliBits. All rights reserved. + +#include "IRGenerator.h" + +#include +#include +#include +#include +#include + +namespace spice::compiler { + +llvm::Value *IRGenerator::doImplicitCast(llvm::Value *src, SymbolType dstSTy, SymbolType srcSTy) { + assert(srcSTy != dstSTy); // We only need to cast implicitly, if the types do not match exactly + + // Unpack the pointers until a pointer of another type is met + size_t loadCounter = 0; + while (srcSTy.isPtr()) { + src = builder.CreateLoad(srcSTy.toLLVMType(context, currentScope), src); + srcSTy = srcSTy.getContainedTy(); + dstSTy = dstSTy.getContainedTy(); + loadCounter++; + } + // GEP or bit-cast + if (dstSTy.isArray() && srcSTy.isArray()) { // Special case that is used for passing arrays as pointer to functions + llvm::Value *indices[2] = {builder.getInt32(0), builder.getInt32(0)}; + src = builder.CreateInBoundsGEP(srcSTy.toLLVMType(context, currentScope), src, indices); + } else { + src = builder.CreateLoad(srcSTy.toLLVMType(context, currentScope), src); + src = builder.CreateBitCast(src, dstSTy.toLLVMType(context, currentScope)); + } + // Pack the pointers together again + for (; loadCounter > 0; loadCounter--) { + llvm::Value *newActualArg = insertAlloca(src->getType()); + builder.CreateStore(src, newActualArg); + src = newActualArg; + } + return src; +} + +void IRGenerator::generateScopeCleanup(const StmtLstNode *node) const { + // Do not clean up if the block is already terminated + if (blockAlreadyTerminated) + return; + + // Call all dtor functions + for (auto [entry, dtor] : node->dtorFunctions.at(manIdx)) + generateDtorCall(entry, dtor); +} + +void IRGenerator::generateDtorCall(SymbolTableEntry *entry, const Function *dtor) const { + assert(dtor != nullptr); + + // Retrieve metadata for the function + const std::string mangledName = NameMangling::mangleFunction(*dtor); + + // Function is not defined in the current module -> declare it + if (!module->getFunction(mangledName)) { + llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getVoidTy(), builder.getPtrTy(), false); + module->getOrInsertFunction(mangledName, fctType); + } + + // Get callee function + llvm::Function *callee = module->getFunction(mangledName); + assert(callee != nullptr); + + // Retrieve address of the struct variable. For fields this is the 'this' variable, otherwise use the normal address + llvm::Value *structPtr; + if (entry->isField()) { + // Take 'this' var as base pointer + const SymbolTableEntry *thisVar = currentScope->lookupStrict(THIS_VARIABLE_NAME); + assert(thisVar != nullptr); + assert(thisVar->getType().isPtr() && thisVar->getType().getContainedTy().is(TY_STRUCT)); + llvm::Type *thisType = thisVar->getType().getContainedTy().toLLVMType(context, currentScope); + llvm::Value *thisPtr = builder.CreateLoad(builder.getPtrTy(), thisVar->getAddress()); + // Add field offset + structPtr = builder.CreateInBoundsGEP(thisType, thisPtr, {builder.getInt32(0), builder.getInt32(entry->orderIndex)}); + } else { + structPtr = entry->getAddress(); + } + assert(structPtr != nullptr); + + // Generate function call + builder.CreateCall(callee, structPtr); +} + +void IRGenerator::generateDeallocCall(llvm::Value *variableAddress) const { + // Issue call + llvm::Function *deallocFct = stdFunctionManager.getDeallocBytePtrRefFct(); + builder.CreateCall(deallocFct, variableAddress); +} + +llvm::Function *IRGenerator::generateImplicitProcedure(const std::function &generateBody, const Function *spiceFunc) { + // Only focus on procedures and procedure methods without arguments for now + assert(spiceFunc->isProcedure() && spiceFunc->getParamTypes().empty()); + const ASTNode *node = spiceFunc->entry->declNode; + + // Only generate if used + if (!spiceFunc->used) + return nullptr; + + // Retrieve return type + llvm::Type *returnType = builder.getVoidTy(); + + // Get 'this' entry + std::vector> paramInfoList; + std::vector paramTypes; + SymbolTableEntry *thisEntry = nullptr; + if (spiceFunc->isMethod()) { + thisEntry = spiceFunc->bodyScope->lookupStrict(THIS_VARIABLE_NAME); + assert(thisEntry != nullptr); + paramInfoList.emplace_back(THIS_VARIABLE_NAME, thisEntry); + paramTypes.push_back(builder.getPtrTy()); + } + + // Get function linkage + const bool isPublic = spiceFunc->entry->getType().specifiers.isPublic; + llvm::GlobalValue::LinkageTypes linkage = isPublic ? llvm::Function::ExternalLinkage : llvm::Function::PrivateLinkage; + + // Create function + const std::string mangledName = NameMangling::mangleFunction(*spiceFunc); + llvm::FunctionType *fctType = llvm::FunctionType::get(returnType, paramTypes, false); + llvm::Function *fct = llvm::Function::Create(fctType, llvm::Function::ExternalLinkage, mangledName, module); + fct->setLinkage(linkage); + fct->setDoesNotRecurse(); + + // Set attributes to 'this' param + if (spiceFunc->isMethod()) { + fct->addParamAttr(0, llvm::Attribute::NoUndef); + fct->addParamAttr(0, llvm::Attribute::NonNull); + } + + // Add debug info + diGenerator.generateFunctionDebugInfo(fct, spiceFunc); + diGenerator.setSourceLocation(node); + + // Change to body scope + changeToScope(spiceFunc->getSignature(false), ScopeType::FUNC_PROC_BODY); + + // Create entry block + llvm::BasicBlock *bEntry = createBlock(); + switchToBlock(bEntry, fct); + + // Reset alloca insert markers to this block + allocaInsertBlock = bEntry; + allocaInsertInst = nullptr; + + // Store first argument to 'this' symbol + if (spiceFunc->isMethod()) { + assert(thisEntry != nullptr); + // Allocate space for the parameter + llvm::Value *thisAddress = insertAlloca(paramTypes.front(), THIS_VARIABLE_NAME); + // Update the symbol table entry + thisEntry->updateAddress(thisAddress); + // Generate debug info + diGenerator.generateLocalVarDebugInfo(THIS_VARIABLE_NAME, thisAddress, 1); + // Store the value at the new address + builder.CreateStore(fct->arg_begin(), thisAddress); + } + + // Generate body + generateBody(); + + // Create return instruction + builder.CreateRetVoid(); + + // Conclude debug info for function + diGenerator.concludeFunctionDebugInfo(); + + // Verify function + verifyFunction(fct, node->codeLoc); + + // Change to parent scope + changeToParentScope(ScopeType::FUNC_PROC_BODY); + + return fct; +} + +void IRGenerator::generateDefaultDefaultDtor(const Function *dtorFunction) { + assert(dtorFunction->implicitDefault && dtorFunction->name.starts_with(DTOR_FUNCTION_NAME)); + + const std::function generateBody = [&]() { + // Retrieve struct scope + Scope *structScope = dtorFunction->bodyScope->parent; + assert(structScope != nullptr); + + // Get struct address + SymbolTableEntry *thisEntry = dtorFunction->bodyScope->lookupStrict(THIS_VARIABLE_NAME); + assert(thisEntry != nullptr); + llvm::Value *thisAddress = thisEntry->getAddress(); + assert(thisAddress != nullptr); + llvm::Value *thisAddressLoaded = nullptr; + llvm::Type *structType = thisEntry->getType().getBaseType().toLLVMType(context, structScope); + + const size_t fieldCount = structScope->getFieldCount(); + for (size_t i = 0; i < fieldCount; i++) { + SymbolTableEntry *field = structScope->symbolTable.lookupStrictByIndex(i); + assert(field != nullptr && field->isField()); + const SymbolType &fieldType = field->getType(); + + // Call dtor for struct fields + if (fieldType.is(TY_STRUCT)) { + // Lookup dtor function + Scope *matchScope = fieldType.getBodyScope(); + const Function *dtorFunction = FunctionManager::lookupFunction(matchScope, DTOR_FUNCTION_NAME, fieldType, {}, false); + + // Generate call to dtor + generateDtorCall(field, dtorFunction); + } + + // Deallocate fields, that are stored on the heap + if (fieldType.isHeap()) { + // 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); + // Call dealloc function + generateDeallocCall(fieldAddress); + } + } + }; + generateImplicitProcedure(generateBody, dtorFunction); +} + +} // namespace spice::compiler \ No newline at end of file diff --git a/src/irgenerator/GenTopLevelDefinitions.cpp b/src/irgenerator/GenTopLevelDefinitions.cpp index 404b4f6cd..1a0bf92ab 100644 --- a/src/irgenerator/GenTopLevelDefinitions.cpp +++ b/src/irgenerator/GenTopLevelDefinitions.cpp @@ -472,8 +472,9 @@ std::any IRGenerator::visitStructDef(const StructDefNode *node) { llvm::StructType *structType = llvm::StructType::create(context, mangledName); // Set LLVM type to the struct entry - assert(spiceStruct->entry != nullptr); - spiceStruct->entry->setStructLLVMType(structType); + SymbolTableEntry *structEntry = spiceStruct->entry; + assert(structEntry != nullptr); + structEntry->setStructLLVMType(structType); // Collect concrete field types std::vector fieldTypes; @@ -487,6 +488,12 @@ std::any IRGenerator::visitStructDef(const StructDefNode *node) { // Set field types to struct type structType->setBody(fieldTypes); + // Generate default ctor/dtor, etc. + const SymbolType &thisType = structEntry->getType(); + const Function *spiceFunc = FunctionManager::lookupFunction(currentScope, DTOR_FUNCTION_NAME, thisType, {}, true); + if (spiceFunc != nullptr && spiceFunc->implicitDefault) + generateDefaultDefaultDtor(spiceFunc); + // Return to root scope currentScope = rootScope; assert(currentScope); diff --git a/src/irgenerator/GenValues.cpp b/src/irgenerator/GenValues.cpp index 03d964022..abe4a8948 100644 --- a/src/irgenerator/GenValues.cpp +++ b/src/irgenerator/GenValues.cpp @@ -336,22 +336,16 @@ std::any IRGenerator::visitStructInstantiation(const StructInstantiationNode *no canBeConstant &= fieldValue.constant != nullptr; } - LLVMExprResult result; if (canBeConstant) { // All field values are constants, so we can create a global constant struct instantiation // Collect constants std::vector constants; - for (const LLVMExprResult &exprResult : fieldValueResults) { - // Delete potential constant globals, that were already created a layer below - if (exprResult.constant->getType()->isStructTy()) - module->getNamedGlobal(exprResult.ptr->getName())->eraseFromParent(); + for (const LLVMExprResult &exprResult : fieldValueResults) constants.push_back(exprResult.constant); - } - // Create global struct + // Create global constant struct llvm::Constant *constantStruct = llvm::ConstantStruct::get(structType, constants); - llvm::Value *constantAddr = createGlobalConst(ANON_GLOBAL_STRUCT_NAME, constantStruct); - result = {.constant = constantStruct, .ptr = constantAddr}; + return LLVMExprResult{.constant = constantStruct}; } else { // We have at least one non-immediate value, so we need to take normal struct instantiation as fallback llvm::Value *structAddr = insertAlloca(structType); @@ -367,15 +361,13 @@ std::any IRGenerator::visitStructInstantiation(const StructInstantiationNode *no builder.CreateStore(itemValue, currentFieldAddress, storeVolatile); } - result = {.ptr = structAddr}; - } - - // Attach address to anonymous symbol to keep track of deallocation - SymbolTableEntry *returnSymbol = currentScope->symbolTable.lookupAnonymous(node->codeLoc); - if (returnSymbol != nullptr) - returnSymbol->updateAddress(result.ptr); + // Attach address to anonymous symbol to keep track of deallocation + SymbolTableEntry *returnSymbol = currentScope->symbolTable.lookupAnonymous(node->codeLoc); + if (returnSymbol != nullptr) + returnSymbol->updateAddress(structAddr); - return result; + return LLVMExprResult{.ptr = structAddr}; + } } std::any IRGenerator::visitLambdaFunc(const LambdaFuncNode *node) { diff --git a/src/irgenerator/IRGenerator.cpp b/src/irgenerator/IRGenerator.cpp index db005bf7e..f54a90db9 100644 --- a/src/irgenerator/IRGenerator.cpp +++ b/src/irgenerator/IRGenerator.cpp @@ -200,7 +200,7 @@ llvm::Constant *IRGenerator::getDefaultValueForSymbolType(const SymbolType &symb for (size_t i = 0; i < fieldCount; i++) { // Get entry of the field SymbolTableEntry *fieldEntry = structScope->symbolTable.lookupStrictByIndex(i); - assert(fieldEntry != nullptr); + assert(fieldEntry != nullptr && fieldEntry->isField()); // Retrieve field node const auto fieldNode = dynamic_cast(fieldEntry->declNode); @@ -329,7 +329,8 @@ LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, SymbolTableEnt const SymbolType &rhsSType, bool isDecl) { // Deduce some information about the assignment const bool isRefAssign = lhsEntry != nullptr && lhsEntry->getType().isRef(); - const bool needsShallowCopy = !isDecl && !isRefAssign && rhsSType.is(TY_STRUCT); + const bool isRhsTemporary = rhs.entry == nullptr && rhs.ptr == nullptr && rhs.refPtr == nullptr; + const bool needsShallowCopy = !isDecl && !isRefAssign && rhsSType.is(TY_STRUCT) && !isRhsTemporary; if (isRefAssign) { if (isDecl) { // Reference gets initially assigned @@ -380,15 +381,10 @@ LLVMExprResult IRGenerator::doAssignment(llvm::Value *lhsAddress, SymbolTableEnt materializeConstant(rhs); // Directly set the address to the lhs entry - lhsEntry->updateAddress(resolveAddress(rhs, lhsEntry->isVolatile)); - - // If we have value, store it to the address - if (rhs.value) { - assert(rhs.ptr != nullptr); - builder.CreateStore(rhs.value, rhs.ptr); - } - + llvm::Value *rhsAddress = resolveAddress(rhs); + lhsEntry->updateAddress(rhsAddress); rhs.entry = lhsEntry; + return rhs; } @@ -433,52 +429,6 @@ void IRGenerator::autoDeReferencePtr(llvm::Value *&ptr, SymbolType &symbolType, } } -void IRGenerator::generateScopeCleanup(const StmtLstNode *node) const { - // Do not clean up if the block is already terminated - if (blockAlreadyTerminated) - return; - - // Call all dtor functions - for (auto [entry, dtor] : node->dtorFunctions.at(manIdx)) - generateDtorCall(entry, dtor, node); -} - -void IRGenerator::generateDtorCall(SymbolTableEntry *entry, Function *dtor, const StmtLstNode *node) const { - assert(dtor != nullptr); - - // Retrieve metadata for the function - const std::string mangledName = NameMangling::mangleFunction(*dtor); - - // Function is not defined in the current module -> declare it - if (!module->getFunction(mangledName)) { - llvm::FunctionType *fctType = llvm::FunctionType::get(builder.getVoidTy(), builder.getPtrTy(), false); - module->getOrInsertFunction(mangledName, fctType); - } - - // Get callee function - llvm::Function *callee = module->getFunction(mangledName); - assert(callee != nullptr); - - // Retrieve address of the struct variable. For fields this is the 'this' variable, otherwise use the normal address - llvm::Value *structPtr; - if (entry->isField()) { - // Take 'this' var as base pointer - const SymbolTableEntry *thisVar = currentScope->lookupStrict(THIS_VARIABLE_NAME); - assert(thisVar != nullptr); - assert(thisVar->getType().isPtr() && thisVar->getType().getContainedTy().is(TY_STRUCT)); - llvm::Type *thisType = thisVar->getType().getContainedTy().toLLVMType(context, currentScope); - llvm::Value *thisPtr = builder.CreateLoad(builder.getPtrTy(), thisVar->getAddress()); - // Add field offset - structPtr = builder.CreateInBoundsGEP(thisType, thisPtr, {builder.getInt32(0), builder.getInt32(entry->orderIndex)}); - } else { - structPtr = entry->getAddress(); - } - assert(structPtr != nullptr); - - // Generate function call - builder.CreateCall(callee, structPtr); -} - llvm::GlobalVariable *IRGenerator::createGlobalConst(const std::string &baseName, llvm::Constant *constant) { // Get unused name const std::string globalName = getUnusedGlobalName(baseName); @@ -517,59 +467,11 @@ std::string IRGenerator::getUnusedGlobalName(const std::string &baseName) const return globalName; } -llvm::Value *IRGenerator::doImplicitCast(llvm::Value *src, SymbolType dstSTy, SymbolType srcSTy) { - assert(srcSTy != dstSTy); // We only need to cast implicitly, if the types do not match exactly - - // Unpack the pointers until a pointer of another type is met - size_t loadCounter = 0; - while (srcSTy.isPtr()) { - src = builder.CreateLoad(srcSTy.toLLVMType(context, currentScope), src); - srcSTy = srcSTy.getContainedTy(); - dstSTy = dstSTy.getContainedTy(); - loadCounter++; - } - // GEP or bit-cast - if (dstSTy.isArray() && srcSTy.isArray()) { // Special case that is used for passing arrays as pointer to functions - llvm::Value *indices[2] = {builder.getInt32(0), builder.getInt32(0)}; - src = builder.CreateInBoundsGEP(srcSTy.toLLVMType(context, currentScope), src, indices); - } else { - src = builder.CreateLoad(srcSTy.toLLVMType(context, currentScope), src); - src = builder.CreateBitCast(src, dstSTy.toLLVMType(context, currentScope)); - } - // Pack the pointers together again - for (; loadCounter > 0; loadCounter--) { - llvm::Value *newActualArg = insertAlloca(src->getType()); - builder.CreateStore(src, newActualArg); - src = newActualArg; - } - return src; -} - void IRGenerator::materializeConstant(LLVMExprResult &exprResult) { // Skip results, that do not contain a constant or already have a value - if (exprResult.constant == nullptr || exprResult.value != nullptr) + if (exprResult.value != nullptr || exprResult.constant == nullptr) return; - llvm::Type *constantType = exprResult.constant->getType(); - if (constantType->isArrayTy() || constantType->isStructTy()) { - // Insert alloca for local variable - llvm::Value *localAddr = insertAlloca(constantType); - - // If no address is given, we simply store the constant - if (exprResult.ptr != nullptr) { - // Get the size of the type in bytes - const size_t instanceSize = module->getDataLayout().getTypeAllocSize(constantType); - - // Copy the constant to the local address via llvm.memcpy - llvm::Function *memcpyIntrinsic = stdFunctionManager.getMemcpyIntrinsic(); - llvm::Value *args[4] = {localAddr, exprResult.ptr, builder.getInt64(instanceSize), builder.getFalse()}; - builder.CreateCall(memcpyIntrinsic, args); - } - - // Set the pointer to the newly created local variable - exprResult.ptr = localAddr; - } - // Default case: the value to the constant exprResult.value = exprResult.constant; } diff --git a/src/irgenerator/IRGenerator.h b/src/irgenerator/IRGenerator.h index 596b1c4c0..fabb8565b 100644 --- a/src/irgenerator/IRGenerator.h +++ b/src/irgenerator/IRGenerator.h @@ -127,17 +127,22 @@ class IRGenerator : private CompilerPass, public ParallelizableASTVisitor { const SymbolType &rhsSType, bool isDecl); llvm::Value *createShallowCopy(llvm::Value *oldAddress, llvm::Type *varType, llvm::Value *targetAddress, const std::string &name = "", bool isVolatile = false); - void generateScopeCleanup(const StmtLstNode *node) const; - void generateDtorCall(SymbolTableEntry *entry, Function *dtor, const StmtLstNode *node) const; void autoDeReferencePtr(llvm::Value *&ptr, SymbolType &symbolType, Scope *accessScope) const; llvm::GlobalVariable *createGlobalConst(const std::string &baseName, llvm::Constant *constant); llvm::Constant *createGlobalStringConst(const std::string &baseName, const std::string &value, const CodeLoc &codeLoc); [[nodiscard]] std::string getUnusedGlobalName(const std::string &baseName) const; void materializeConstant(LLVMExprResult &exprResult); - llvm::Value *doImplicitCast(llvm::Value *src, SymbolType dstSTy, SymbolType srcSTy); const std::vector &getOpFctPointers(const ASTNode *node) const; llvm::Value *buildFatFctPtr(Scope *bodyScope, llvm::StructType *capturesStructType, llvm::Value *lambda); + // Generate implicit + llvm::Value *doImplicitCast(llvm::Value *src, SymbolType dstSTy, SymbolType srcSTy); + void generateScopeCleanup(const StmtLstNode *node) const; + void generateDtorCall(SymbolTableEntry *entry, const Function *dtor) const; + void generateDeallocCall(llvm::Value *variableAddress) const; + llvm::Function *generateImplicitProcedure(const std::function &generateBody, const Function *spiceFunc); + void generateDefaultDefaultDtor(const Function *dtorFunction); + // Private members llvm::LLVMContext &context; llvm::IRBuilder<> &builder; @@ -153,7 +158,6 @@ class IRGenerator : private CompilerPass, public ParallelizableASTVisitor { llvm::BasicBlock *allocaInsertBlock = nullptr; llvm::Instruction *allocaInsertInst = nullptr; bool blockAlreadyTerminated = false; - size_t manIdx = 0; }; } // namespace spice::compiler \ No newline at end of file diff --git a/src/irgenerator/StdFunctionManager.cpp b/src/irgenerator/StdFunctionManager.cpp index f05cf12c0..407bedb15 100644 --- a/src/irgenerator/StdFunctionManager.cpp +++ b/src/irgenerator/StdFunctionManager.cpp @@ -49,18 +49,33 @@ llvm::Function *StdFunctionManager::getMemcmpIntrinsic() const { llvm::Function *StdFunctionManager::getStringGetRawLengthStringFct() const { const ParamList paramLst = {{SymbolType(TY_STRING), false}}; - const Function function("getRawLength", nullptr, SymbolType(TY_DYN), SymbolType(TY_LONG), paramLst, {}, nullptr, false); + const Function function("getRawLength", nullptr, SymbolType(TY_DYN), SymbolType(TY_LONG), paramLst, {}, nullptr); const std::string mangledName = NameMangling::mangleFunction(function); return getFunction(mangledName.c_str(), builder.getInt64Ty(), {builder.getPtrTy()}); } llvm::Function *StdFunctionManager::getStringIsRawEqualStringStringFct() const { const ParamList paramLst = {{SymbolType(TY_STRING), false}, {SymbolType(TY_STRING), false}}; - const Function function("isRawEqual", nullptr, SymbolType(TY_DYN), SymbolType(TY_BOOL), paramLst, {}, nullptr, false); + const Function function("isRawEqual", nullptr, SymbolType(TY_DYN), SymbolType(TY_BOOL), paramLst, {}, nullptr); const std::string mangledName = NameMangling::mangleFunction(function); return getFunction(mangledName.c_str(), builder.getInt1Ty(), {builder.getPtrTy(), builder.getPtrTy()}); } +llvm::Function *StdFunctionManager::getAllocLongFct() const { + const ParamList paramLst = {{SymbolType(TY_LONG), false}}; + const SymbolType bytePtrSTy = SymbolType(TY_BYTE).toPointer(nullptr); + const Function function("sAlloc", nullptr, SymbolType(TY_DYN), bytePtrSTy, paramLst, {}, nullptr); + const std::string mangledName = NameMangling::mangleFunction(function); + return getFunction(mangledName.c_str(), builder.getPtrTy(), {builder.getInt64Ty()}); +} + +llvm::Function *StdFunctionManager::getDeallocBytePtrRefFct() const { + const ParamList paramLst = {{SymbolType(TY_BYTE).toPointer(nullptr).toReference(nullptr), false}}; + const Function function("sDealloc", nullptr, SymbolType(TY_DYN), SymbolType(TY_DYN), paramLst, {}, nullptr); + const std::string mangledName = NameMangling::mangleFunction(function); + return getProcedure(mangledName.c_str(), {builder.getPtrTy()}); +} + llvm::Function *StdFunctionManager::getIteratorGetFct(const Function *spiceFunc) const { const std::string functionName = NameMangling::mangleFunction(*spiceFunc); return getFunction(functionName.c_str(), builder.getPtrTy(), builder.getPtrTy()); diff --git a/src/irgenerator/StdFunctionManager.h b/src/irgenerator/StdFunctionManager.h index e5a2492b3..b9e7e3c7b 100644 --- a/src/irgenerator/StdFunctionManager.h +++ b/src/irgenerator/StdFunctionManager.h @@ -26,6 +26,8 @@ class StdFunctionManager { [[nodiscard]] llvm::Function *getMemcmpIntrinsic() const; [[nodiscard]] llvm::Function *getStringGetRawLengthStringFct() const; [[nodiscard]] llvm::Function *getStringIsRawEqualStringStringFct() const; + [[nodiscard]] llvm::Function *getAllocLongFct() const; + [[nodiscard]] llvm::Function *getDeallocBytePtrRefFct() const; [[nodiscard]] llvm::Function *getIteratorGetFct(const Function *spiceFunc) const; [[nodiscard]] llvm::Function *getIteratorGetIdxFct(const Function *spiceFunc, Scope *accessScope) const; [[nodiscard]] llvm::Function *getIteratorIsValidFct(const Function *spiceFunc) const; diff --git a/src/model/Function.cpp b/src/model/Function.cpp index 927104b94..1445fdba0 100644 --- a/src/model/Function.cpp +++ b/src/model/Function.cpp @@ -98,6 +98,10 @@ std::string Function::getSignature(const std::string &name, const SymbolType &th return returnTyStr + thisTyStr.str() + name + templateTyStr.str() + "(" + paramTyStr.str() + ")"; } +std::string Function::getSymbolTableEntryName(const std::string &functionName, const CodeLoc &codeLoc) { + return functionName + ":" + codeLoc.toString(); +} + /** * Checks if a function contains optional parameters. * This would imply that the function is not substantiated by its optional parameters yet. diff --git a/src/model/Function.h b/src/model/Function.h index 148211764..f952a3aaf 100644 --- a/src/model/Function.h +++ b/src/model/Function.h @@ -35,9 +35,9 @@ class Function { public: // Constructors Function(std::string name, SymbolTableEntry *entry, SymbolType thisType, SymbolType returnType, ParamList paramList, - std::vector templateTypes, ASTNode *declNode, bool external) + std::vector templateTypes, ASTNode *declNode) : name(std::move(name)), entry(entry), thisType(std::move(thisType)), returnType(std::move(returnType)), - paramList(std::move(paramList)), templateTypes(std::move(templateTypes)), declNode(declNode), external(external) {} + paramList(std::move(paramList)), templateTypes(std::move(templateTypes)), declNode(declNode) {} Function() = default; // Public methods @@ -46,6 +46,7 @@ class Function { [[nodiscard]] static std::string getSignature(const std::string &name, const SymbolType &thisType, const SymbolType &returnType, const ParamList ¶mList, const std::vector &concreteTemplateTypes, bool withThisType = true); + [[nodiscard]] static std::string getSymbolTableEntryName(const std::string &functionName, const CodeLoc &codeLoc); [[nodiscard]] inline bool isMethod() const { return !thisType.is(TY_DYN); } [[nodiscard]] inline bool isFunction() const { return !returnType.is(TY_DYN); } [[nodiscard]] inline bool isProcedure() const { return returnType.is(TY_DYN); } @@ -73,8 +74,8 @@ class Function { std::string mangleSuffix; bool genericSubstantiation = false; bool alreadyTypeChecked = false; - bool external = false; bool used = false; + bool implicitDefault = false; // Json serializer/deserializer NLOHMANN_DEFINE_TYPE_INTRUSIVE(Function, name, thisType, returnType, paramList, templateTypes) diff --git a/src/model/Struct.h b/src/model/Struct.h index ffab16b95..3fcf34623 100644 --- a/src/model/Struct.h +++ b/src/model/Struct.h @@ -17,7 +17,6 @@ namespace spice::compiler { class Scope; class SymbolType; class SymbolTableEntry; -class Function; class ASTNode; struct CodeLoc; diff --git a/src/symboltablebuilder/Scope.cpp b/src/symboltablebuilder/Scope.cpp index d114b05ac..86c2f2e34 100644 --- a/src/symboltablebuilder/Scope.cpp +++ b/src/symboltablebuilder/Scope.cpp @@ -2,6 +2,7 @@ #include "Scope.h" +#include #include #include #include @@ -137,8 +138,12 @@ size_t Scope::getFieldCount() const { size_t fieldCount = 0; for (const auto &symbol : symbolTable.symbols) { const SymbolType &symbolType = symbol.second.getType(); - if (!symbolType.is(TY_IMPORT) && !symbol.second.declNode->isFctOrProcDef()) - fieldCount++; + if (symbolType.is(TY_IMPORT)) + continue; + const ASTNode *declNode = symbol.second.declNode; + if (declNode->isFctOrProcDef() || declNode->isStructDef()) + continue; + fieldCount++; } return fieldCount; } @@ -170,7 +175,8 @@ void Scope::collectWarnings(std::vector &warnings) const { // N std::string warningMessage; for (const auto &[_, entry] : symbolTable.symbols) { // Do not produce a warning if the symbol is used or has a special name - if (entry.used || entry.name.starts_with(UNUSED_VARIABLE_NAME)) + const std::string &name = entry.name; + if (entry.used || name.starts_with(UNUSED_VARIABLE_NAME)) continue; switch (entry.getType().getSuperType()) { @@ -180,8 +186,13 @@ void Scope::collectWarnings(std::vector &warnings) const { // N continue; if (type == ScopeType::GLOBAL) { + const std::vector *fctManifestations = entry.declNode->getFctManifestations(name); warningType = UNUSED_FUNCTION; - warningMessage = "The function '" + entry.declNode->getFctManifestations()->front()->getSignature() + "' is unused"; + warningMessage = "The function '" + fctManifestations->front()->getSignature() + "' is unused"; + } else if (type == ScopeType::STRUCT) { + const std::vector *fctManifestations = entry.declNode->getFctManifestations(name); + warningType = UNUSED_METHOD; + warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused"; } else { warningType = UNUSED_VARIABLE; warningMessage = "The variable '" + entry.name + "' is unused"; @@ -195,8 +206,17 @@ void Scope::collectWarnings(std::vector &warnings) const { // N continue; if (type == ScopeType::GLOBAL) { + const std::vector *fctManifestations = entry.declNode->getFctManifestations(name); warningType = UNUSED_PROCEDURE; - warningMessage = "The procedure '" + entry.declNode->getFctManifestations()->front()->getSignature() + "' is unused"; + warningMessage = "The procedure '" + fctManifestations->front()->getSignature() + "' is unused"; + } else if (type == ScopeType::STRUCT) { + // Check if this is a default instance method + const std::vector *fctManifestations = entry.declNode->getFctManifestations(name); + if (fctManifestations->empty() || fctManifestations->front()->implicitDefault) + continue; + + warningType = UNUSED_METHOD; + warningMessage = "The method '" + fctManifestations->front()->getSignature() + "' is unused"; } else { warningType = UNUSED_VARIABLE; warningMessage = "The variable '" + entry.name + "' is unused"; @@ -294,19 +314,7 @@ void Scope::checkSuccessfulTypeInference() const { * * @return Imported / not imported */ -bool Scope::isImportedBy(const Scope *askingScope) const { - // Get root scope of the source file where askingScope scope lives - const Scope *askingRootScope = askingScope; - while (askingRootScope->type != ScopeType::GLOBAL && askingRootScope->parent) - askingRootScope = askingRootScope->parent; - - // Get root scope of the source file where the current scope lives - const Scope *thisRootScope = this; - while (thisRootScope->type != ScopeType::GLOBAL && thisRootScope->parent) - thisRootScope = thisRootScope->parent; - - return askingRootScope != thisRootScope; -} +bool Scope::isImportedBy(const Scope *askingScope) const { return askingScope->sourceFile->imports(sourceFile); } /** * Check if unsafe operations are allowed in this scope diff --git a/src/symboltablebuilder/SymbolTableEntry.cpp b/src/symboltablebuilder/SymbolTableEntry.cpp index b1e26f606..e3e9f3012 100644 --- a/src/symboltablebuilder/SymbolTableEntry.cpp +++ b/src/symboltablebuilder/SymbolTableEntry.cpp @@ -121,7 +121,7 @@ void SymbolTableEntry::popAddress() { * * @return Struct field or not */ -bool SymbolTableEntry::isField() const { return scope->type == ScopeType::STRUCT && !type.isOneOf({TY_FUNCTION, TY_PROCEDURE}); } +bool SymbolTableEntry::isField() const { return scope->type == ScopeType::STRUCT && orderIndex < scope->getFieldCount(); } /** * Stringify the current symbol to a human-readable form. Used to dump whole symbol tables with their contents. diff --git a/src/symboltablebuilder/SymbolType.h b/src/symboltablebuilder/SymbolType.h index ab6e3834c..f329bf510 100644 --- a/src/symboltablebuilder/SymbolType.h +++ b/src/symboltablebuilder/SymbolType.h @@ -173,10 +173,7 @@ class SymbolType { assert(isPrimitive() /* Global variables */ || isOneOf({TY_FUNCTION, TY_PROCEDURE, TY_ENUM, TY_STRUCT, TY_INTERFACE})); return specifiers.isPublic; } - [[nodiscard]] inline bool isHeap() const { - assert(isPrimitive() /* Local variables */ || is(TY_STRUCT)); - return specifiers.isHeap; - } + [[nodiscard]] inline bool isHeap() const { return specifiers.isHeap; } inline void setBodyScope(Scope *bodyScope) { assert(isOneOf({TY_STRUCT, TY_INTERFACE})); typeChain.back().data.bodyScope = bodyScope; diff --git a/src/symboltablebuilder/TypeSpecifiers.cpp b/src/symboltablebuilder/TypeSpecifiers.cpp index 12a4ad602..caf08a328 100644 --- a/src/symboltablebuilder/TypeSpecifiers.cpp +++ b/src/symboltablebuilder/TypeSpecifiers.cpp @@ -94,6 +94,19 @@ bool TypeSpecifiers::match(TypeSpecifiers otherSpecifiers, bool allowConstify) c return thisSpecifiers == otherSpecifiers; } +/** + * Erase all specifiers that are set in the mask. This is used in type matching. + * + * @param mask Bitmask to erase with + */ +void TypeSpecifiers::eraseWithMask(const TypeSpecifiers &mask) { + // Zero out all bits that are set in the mask + for (uint8_t i = 0; i <= BIT_INDEX_MAX; i++) { + if (mask.getBit(i)) + setBit(i, false); + } +} + bool operator==(const TypeSpecifiers &lhs, const TypeSpecifiers &rhs) { const bool isConst = lhs.isConst == rhs.isConst; const bool isSigned = lhs.isSigned == rhs.isSigned; diff --git a/src/symboltablebuilder/TypeSpecifiers.h b/src/symboltablebuilder/TypeSpecifiers.h index 0152119c3..4ec61681d 100644 --- a/src/symboltablebuilder/TypeSpecifiers.h +++ b/src/symboltablebuilder/TypeSpecifiers.h @@ -28,6 +28,7 @@ class TypeSpecifiers { // Public methods [[nodiscard]] TypeSpecifiers merge(const TypeSpecifiers &other) const; [[nodiscard]] bool match(TypeSpecifiers other, bool allowConstify) const; + void eraseWithMask(const TypeSpecifiers &mask); // Overloaded operators friend bool operator==(const TypeSpecifiers &lhs, const TypeSpecifiers &rhs); diff --git a/src/typechecker/FunctionManager.cpp b/src/typechecker/FunctionManager.cpp index 4eaeebaa9..c680d1e86 100644 --- a/src/typechecker/FunctionManager.cpp +++ b/src/typechecker/FunctionManager.cpp @@ -95,7 +95,7 @@ Function FunctionManager::createMainFunction(SymbolTableEntry *entry, const std: ParamList paramList; for (const SymbolType ¶mType : paramTypes) paramList.push_back({paramType, false}); - return {MAIN_FUNCTION_NAME, entry, SymbolType(TY_DYN), SymbolType(TY_INT), paramList, {}, declNode, false}; + return {MAIN_FUNCTION_NAME, entry, SymbolType(TY_DYN), SymbolType(TY_INT), paramList, {}, declNode}; } Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode) { @@ -118,6 +118,59 @@ Function *FunctionManager::insertSubstantiation(Scope *insertScope, const Functi return &manifestationList.at(signature); } +/** + * Checks if a function exists by matching it, but not setting it to used + * + * @param matchScope Scope to match against + * @param requestedName Function name requirement + * @param requestedThisType This type requirement + * @param templateTypeHints Template types to substantiate generic types + * @param requestedParamTypes Argument types requirement + * @param strictSpecifierMatching Match argument and this type specifiers strictly + * @return Found function or nullptr + */ +const Function *FunctionManager::lookupFunction(Scope *matchScope, const std::string &requestedName, + const SymbolType &requestedThisType, + const std::vector &requestedParamTypes, + bool strictSpecifierMatching) { + assert(requestedThisType.isOneOf({TY_DYN, TY_STRUCT})); + + // Copy the registry to prevent iterating over items, that are created within the loop + 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) { + // 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) { + assert(presetFunction.hasSubstantiatedParams()); // No optional params are allowed at this point + + // Only match against fully substantiated versions to prevent double matching of a function + if (!presetFunction.genericSubstantiation) + continue; + + // Copy the function to be able to substantiate types + Function candidate = presetFunction; + + bool forceSubstantiation = false; + MatchResult matchResult = matchManifestation(candidate, matchScope, requestedName, requestedThisType, requestedParamTypes, + strictSpecifierMatching, forceSubstantiation); + if (matchResult == MatchResult::SKIP_FUNCTION) + break; // Leave the whole function + if (matchResult == MatchResult::SKIP_MANIFESTATION) + continue; // Leave this manifestation and try the next one + + // Add to matches + matches.push_back(&matchScope->functions.at(defCodeLocStr).at(signature)); + + break; // Leave the whole manifestation list to not double-match the manifestation + } + } + + // Return the very match or a nullptr + return !matches.empty() ? matches.front() : nullptr; +} + /** * Check if there is a function in the scope, fulfilling all given requirements and if found, return it. * If more than one function matches the requirement, an error gets thrown. @@ -153,46 +206,18 @@ Function *FunctionManager::matchFunction(Scope *matchScope, const std::string &r // Copy the function to be able to substantiate types Function candidate = presetFunction; - // Check name requirement - if (!matchName(candidate, requestedName)) - break; // Leave the whole manifestation list, because all manifestations in this list have the same name - - // Prepare mapping table from generic type name to concrete type - TypeMapping &typeMapping = candidate.typeMapping; - typeMapping.clear(); - typeMapping.reserve(candidate.templateTypes.size()); - - // Check 'this' type requirement - if (!matchThisType(candidate, requestedThisType, typeMapping, strictSpecifierMatching)) - continue; // Leave this manifestation and try the next one - - // Check arg types requirement bool forceSubstantiation = false; - if (!matchArgTypes(candidate, requestedParamTypes, typeMapping, strictSpecifierMatching, forceSubstantiation)) + MatchResult matchResult = matchManifestation(candidate, matchScope, requestedName, requestedThisType, requestedParamTypes, + strictSpecifierMatching, forceSubstantiation); + if (matchResult == MatchResult::SKIP_FUNCTION) + break; // Leave the whole function + if (matchResult == MatchResult::SKIP_MANIFESTATION) continue; // Leave this manifestation and try the next one - // Substantiate return type - substantiateReturnType(candidate, typeMapping); - // We found a match! -> Set the actual candidate and its entry to used candidate.used = true; candidate.entry->used = true; - 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; - } - // 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)); @@ -201,9 +226,6 @@ Function *FunctionManager::matchFunction(Scope *matchScope, const std::string &r continue; // Match was successful -> match the next function } - // Clear template types of candidate, since they are not needed anymore - candidate.templateTypes.clear(); - // Check if we already have this manifestation and can simply re-use it if (matchScope->functions.at(defCodeLocStr).contains(candidate.getSignature())) { matches.push_back(&matchScope->functions.at(defCodeLocStr).at(candidate.getSignature())); @@ -214,7 +236,7 @@ Function *FunctionManager::matchFunction(Scope *matchScope, const std::string &r Function *substantiatedFunction = insertSubstantiation(matchScope, candidate, presetFunction.declNode); substantiatedFunction->genericSubstantiation = true; substantiatedFunction->alreadyTypeChecked = false; - substantiatedFunction->declNode->getFctManifestations()->push_back(substantiatedFunction); + substantiatedFunction->declNode->getFctManifestations(requestedName)->push_back(substantiatedFunction); // Copy function entry const std::string newSignature = substantiatedFunction->getSignature(false); @@ -319,6 +341,51 @@ bool FunctionManager::matchInterfaceMethod(Scope *matchScope, const std::string return false; } +MatchResult FunctionManager::matchManifestation(Function &candidate, Scope *&matchScope, const std::string &requestedName, + const SymbolType &requestedThisType, + const std::vector &requestedParamTypes, bool strictSpecifierMatching, + bool &forceSubstantiation) { + // Check name requirement + if (!matchName(candidate, requestedName)) + return MatchResult::SKIP_FUNCTION; // Leave the whole manifestation list, because all have the same name + + // Prepare mapping table from generic type name to concrete type + TypeMapping &typeMapping = candidate.typeMapping; + typeMapping.clear(); + typeMapping.reserve(candidate.templateTypes.size()); + + // Check 'this' type requirement + if (!matchThisType(candidate, requestedThisType, typeMapping, strictSpecifierMatching)) + return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one + + // Check arg types requirement + if (!matchArgTypes(candidate, requestedParamTypes, typeMapping, strictSpecifierMatching, forceSubstantiation)) + return MatchResult::SKIP_MANIFESTATION; // Leave this manifestation and try the next one + + // Substantiate return type + substantiateReturnType(candidate, typeMapping); + + 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; + } + + // Clear template types of candidate, since they are not needed anymore + candidate.templateTypes.clear(); + + return MatchResult::MATCHED; +} + /** * Checks if the matching candidate fulfills the name requirement * @@ -344,7 +411,7 @@ bool FunctionManager::matchThisType(Function &candidate, const SymbolType &reque SymbolType &candidateThisType = candidate.thisType; // Give the type matcher a way to retrieve instances of GenericType by their name - TypeMatcher::ResolverFct genericTypeResolver = [=](const std::string &genericTypeName) { + TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) { return getGenericTypeOfCandidateByName(candidate, genericTypeName); }; @@ -376,7 +443,7 @@ bool FunctionManager::matchArgTypes(Function &candidate, const std::vector; using FunctionRegistry = std::unordered_map; +enum class MatchResult : uint8_t { MATCHED, SKIP_MANIFESTATION, SKIP_FUNCTION }; + class FunctionManager { public: // Friend classes @@ -34,6 +36,10 @@ class FunctionManager { static void substantiateOptionalParams(const Function &baseFunction, std::vector &manifestations); [[nodiscard]] static Function createMainFunction(SymbolTableEntry *entry, const std::vector ¶mTypes, ASTNode *declNode); + [[nodiscard]] static const Function *lookupFunction(Scope *matchScope, const std::string &requestedName, + const SymbolType &requestedThisType, + const std::vector &requestedParamTypes, + bool strictSpecifierMatching); [[nodiscard]] static Function *matchFunction(Scope *matchScope, const std::string &requestedName, const SymbolType &requestedThisType, const std::vector &requestedParamTypes, bool strictSpecifierMatching, @@ -46,6 +52,10 @@ class FunctionManager { // Private methods [[nodiscard]] static Function *insertSubstantiation(Scope *insertScope, const Function &newManifestation, const ASTNode *declNode); + [[nodiscard]] static MatchResult matchManifestation(Function &candidate, Scope *&matchScope, const std::string &requestedName, + const SymbolType &requestedThisType, + const std::vector &requestedParamTypes, + bool strictSpecifierMatching, bool &forceSubstantiation); [[nodiscard]] static bool matchName(const Function &candidate, const std::string &requestedName); [[nodiscard]] static bool matchThisType(Function &candidate, const SymbolType &requestedThisType, TypeMapping &typeMapping, bool strictSpecifierMatching); diff --git a/src/typechecker/InterfaceManager.cpp b/src/typechecker/InterfaceManager.cpp index c5801fd3b..0280082e0 100644 --- a/src/typechecker/InterfaceManager.cpp +++ b/src/typechecker/InterfaceManager.cpp @@ -177,7 +177,7 @@ bool InterfaceManager::matchTemplateTypes(Interface &candidate, const std::vecto return false; // Give the type matcher a way to retrieve instances of GenericType by their name - TypeMatcher::ResolverFct genericTypeResolver = [=](const std::string &genericTypeName) { + TypeMatcher::ResolverFct genericTypeResolver = [&](const std::string &genericTypeName) { return getGenericTypeOfCandidateByName(candidate, genericTypeName); }; diff --git a/src/typechecker/MacroDefs.h b/src/typechecker/MacroDefs.h index d395c3c92..c83a2564f 100644 --- a/src/typechecker/MacroDefs.h +++ b/src/typechecker/MacroDefs.h @@ -30,6 +30,12 @@ const char *const UNRESOLVED_TYPE_NAME = "unresolved type"; return UNRESOLVED_TYPE_NAME; \ } +#define SOFT_ERROR_VOID(node, type, message) \ + { \ + resourceManager.errorManager.addSoftError(node, type, message); \ + return; \ + } + #define HANDLE_UNRESOLVED_TYPE_ER(var) \ { \ if (var.is(TY_UNRESOLVED)) \ diff --git a/src/typechecker/OpRuleManager.cpp b/src/typechecker/OpRuleManager.cpp index a04b7c632..1b2b41895 100644 --- a/src/typechecker/OpRuleManager.cpp +++ b/src/typechecker/OpRuleManager.cpp @@ -9,6 +9,9 @@ namespace spice::compiler { +OpRuleManager::OpRuleManager(TypeChecker *typeChecker) + : typeChecker(typeChecker), resourceManager(typeChecker->resourceManager) {} + SymbolType OpRuleManager::getAssignResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx, bool isDecl /*=false*/, const char *errorMessagePrefix /*=""*/) { // Check if we try to assign a constant value @@ -27,11 +30,14 @@ SymbolType OpRuleManager::getAssignResultType(const ASTNode *node, SymbolType lh if (lhs.isRef() && lhs.getContainedTy().matches(rhs, false, false, true)) return lhs; // Allow ref type to type of the same contained type straight away - if (rhs.isRef() && rhs.getContainedTy().matches(rhs, false, false, true)) - return lhs; - // Allow ref type to type of the same contained type straight away - if (rhs.isRef() && lhs.matches(rhs.getContainedTy(), false, !lhs.isRef(), true)) - return lhs; + if (rhs.isRef()) { + // If this is const ref, remove both: the reference and the constness + SymbolType rhsModified = rhs.getContainedTy(); + rhsModified.specifiers.isConst = false; + + if (lhs.matches(rhsModified, false, !lhs.isRef(), true)) + return lhs; + } // Allow arrays, structs, functions, procedures of the same type straight away if (lhs.isOneOf({TY_ARRAY, TY_STRUCT, TY_FUNCTION, TY_PROCEDURE}) && lhs.matches(rhs, false, true, true)) return rhs; @@ -544,6 +550,10 @@ SymbolType OpRuleManager::getCastResultType(const ASTNode *node, SymbolType lhs, lhs = lhs.removeReferenceWrapper(); rhs = rhs.removeReferenceWrapper(); + // Only allow to cast the 'heap' specifier away, if we are in unsafe mode + if (lhs.specifiers.isHeap != rhs.specifiers.isHeap) + ensureUnsafeAllowed(node, "(cast)", lhs, rhs); + // Allow casts string -> char* and string -> char[] if (lhs.isOneOf({TY_PTR, TY_ARRAY}) && lhs.getContainedTy().is(TY_CHAR) && rhs.is(TY_STRING)) return lhs; @@ -670,7 +680,7 @@ void OpRuleManager::ensureUnsafeAllowed(const ASTNode *node, const char *name, c const std::string rhsName = rhs.getName(true); const std::string errorMsg = "Cannot apply '" + std::string(name) + "' operator on types " + lhsName + " and " + rhsName + " as this is an unsafe operation. Please use unsafe blocks if you know what you are doing."; - throw SemanticError(node, UNSAFE_OPERATION_IN_SAFE_CONTEXT, errorMsg); + SOFT_ERROR_VOID(node, UNSAFE_OPERATION_IN_SAFE_CONTEXT, errorMsg) } void OpRuleManager::ensureNoConstAssign(const ASTNode *node, const SymbolType &lhs) { diff --git a/src/typechecker/OpRuleManager.h b/src/typechecker/OpRuleManager.h index 72eaf6007..53d42e20f 100644 --- a/src/typechecker/OpRuleManager.h +++ b/src/typechecker/OpRuleManager.h @@ -617,22 +617,22 @@ const BinaryOpRule CAST_OP_RULES[] = { class OpRuleManager { public: // Constructors - explicit OpRuleManager(TypeChecker *typeChecker) : typeChecker(typeChecker) {} + explicit OpRuleManager(TypeChecker *typeChecker); // Public methods - static SymbolType getAssignResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx, bool isDecl = false, - const char *errorMessagePrefix = ""); - static SymbolType getFieldAssignResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx, bool imm); + SymbolType getAssignResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx, bool isDecl = false, + const char *errorMessagePrefix = ""); + SymbolType getFieldAssignResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx, bool imm); ExprResult getPlusEqualResultType(ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); ExprResult getMinusEqualResultType(ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); ExprResult getMulEqualResultType(ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); ExprResult getDivEqualResultType(ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); - static SymbolType getRemEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); - static SymbolType getSHLEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); - static SymbolType getSHREqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); - static SymbolType getAndEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); - static SymbolType getOrEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); - static SymbolType getXorEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); + SymbolType getRemEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); + SymbolType getSHLEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); + SymbolType getSHREqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); + SymbolType getAndEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); + SymbolType getOrEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); + SymbolType getXorEqualResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); static SymbolType getLogicalOrResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); static SymbolType getLogicalAndResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); static SymbolType getBitwiseOrResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); @@ -652,8 +652,8 @@ class OpRuleManager { ExprResult getDivResultType(ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); static ExprResult getRemResultType(const ASTNode *node, SymbolType lhs, SymbolType rhs, size_t opIdx); static SymbolType getPrefixMinusResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); - static SymbolType getPrefixPlusPlusResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); - static SymbolType getPrefixMinusMinusResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); + SymbolType getPrefixPlusPlusResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); + SymbolType getPrefixMinusMinusResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); static SymbolType getPrefixNotResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); static SymbolType getPrefixBitwiseNotResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); static SymbolType getPrefixMulResultType(const ASTNode *node, SymbolType lhs, size_t opIdx); @@ -665,6 +665,7 @@ class OpRuleManager { private: // Members TypeChecker *typeChecker; + GlobalResourceManager &resourceManager; // Private methods template @@ -679,7 +680,7 @@ class OpRuleManager { static SemanticError getExceptionBinary(const ASTNode *node, const char *name, const SymbolType &lhs, const SymbolType &rhs, const char *messagePrefix); void ensureUnsafeAllowed(const ASTNode *node, const char *name, const SymbolType &lhs, const SymbolType &rhs) const; - static void ensureNoConstAssign(const ASTNode *node, const SymbolType &lhs); + void ensureNoConstAssign(const ASTNode *node, const SymbolType &lhs); }; } // namespace spice::compiler \ No newline at end of file diff --git a/src/typechecker/StructManager.cpp b/src/typechecker/StructManager.cpp index 920e1ae03..758df2bd6 100644 --- a/src/typechecker/StructManager.cpp +++ b/src/typechecker/StructManager.cpp @@ -130,7 +130,7 @@ Struct *StructManager::matchStruct(Scope *matchScope, const std::string &request for (size_t i = 0; i < substantiatedStruct->fieldTypes.size(); i++) { // Replace field type with concrete template type SymbolTableEntry *fieldEntry = substantiatedStruct->structScope->symbolTable.lookupStrictByIndex(i); - assert(fieldEntry != nullptr); + assert(fieldEntry != nullptr && fieldEntry->isField()); fieldEntry->updateType(substantiatedStruct->fieldTypes.at(i), /*overwriteExistingType=*/true); } @@ -176,7 +176,7 @@ bool StructManager::matchTemplateTypes(Struct &candidate, const std::vectoritemVarDecl(), itemType, iteratorItemType, manIdx, true, ERROR_FOREACH_ITEM); + opRuleManager.getAssignResultType(node->itemVarDecl(), itemType, iteratorItemType, manIdx, true, ERROR_FOREACH_ITEM); } // Update type of item @@ -411,7 +411,7 @@ std::any TypeChecker::visitSignature(SignatureNode *node) { } // Build signature object - Function signature(node->methodName, nullptr, SymbolType(TY_DYN), returnType, paramList, usedGenericTypes, node, false); + Function signature(node->methodName, nullptr, SymbolType(TY_DYN), returnType, paramList, usedGenericTypes, node); // Add signature to current scope Function *manifestation = FunctionManager::insertFunction(currentScope, signature, &node->signatureManifestations); @@ -453,7 +453,7 @@ std::any TypeChecker::visitDeclStmt(DeclStmtNode *node) { if (localVarType.is(TY_UNRESOLVED) || rhsTy.is(TY_UNRESOLVED)) localVarType = SymbolType(TY_UNRESOLVED); else - localVarType = OpRuleManager::getAssignResultType(node, localVarType, rhsTy, 0, true); + localVarType = opRuleManager.getAssignResultType(node, localVarType, rhsTy, 0, true); // If this is a struct type, check if the type is known. If not, error out if (localVarType.isBaseType(TY_STRUCT) && !sourceFile->getNameRegistryEntry(localVarType.getBaseType().getSubType())) { @@ -520,7 +520,7 @@ std::any TypeChecker::visitReturnStmt(ReturnStmtNode *node) { HANDLE_UNRESOLVED_TYPE_ST(rhs.type) // Check if types match - OpRuleManager::getAssignResultType(node->assignExpr(), returnType, rhs.type, 0, false, ERROR_MSG_RETURN); + opRuleManager.getAssignResultType(node->assignExpr(), returnType, rhs.type, 0, false, ERROR_MSG_RETURN); // Manager dtor call if (rhs.entry != nullptr) { @@ -717,7 +717,7 @@ std::any TypeChecker::visitAssignExpr(AssignExprNode *node) { // Take a look at the operator if (node->op == AssignExprNode::OP_ASSIGN) { - rhsType = OpRuleManager::getAssignResultType(node, lhsType, rhsType, 0); + rhsType = opRuleManager.getAssignResultType(node, lhsType, rhsType, 0); // If there is an anonymous entry attached (e.g. for struct instantiation), delete it if (rhsEntry != nullptr && rhsEntry->anonymous) @@ -731,17 +731,17 @@ std::any TypeChecker::visitAssignExpr(AssignExprNode *node) { } else if (node->op == AssignExprNode::OP_DIV_EQUAL) { rhsType = opRuleManager.getDivEqualResultType(node, lhsType, rhsType, 0).type; } else if (node->op == AssignExprNode::OP_REM_EQUAL) { - rhsType = OpRuleManager::getRemEqualResultType(node, lhsType, rhsType, 0); + rhsType = opRuleManager.getRemEqualResultType(node, lhsType, rhsType, 0); } else if (node->op == AssignExprNode::OP_SHL_EQUAL) { - rhsType = OpRuleManager::getSHLEqualResultType(node, lhsType, rhsType, 0); + rhsType = opRuleManager.getSHLEqualResultType(node, lhsType, rhsType, 0); } else if (node->op == AssignExprNode::OP_SHR_EQUAL) { - rhsType = OpRuleManager::getSHREqualResultType(node, lhsType, rhsType, 0); + rhsType = opRuleManager.getSHREqualResultType(node, lhsType, rhsType, 0); } else if (node->op == AssignExprNode::OP_AND_EQUAL) { - rhsType = OpRuleManager::getAndEqualResultType(node, lhsType, rhsType, 0); + rhsType = opRuleManager.getAndEqualResultType(node, lhsType, rhsType, 0); } else if (node->op == AssignExprNode::OP_OR_EQUAL) { - rhsType = OpRuleManager::getOrEqualResultType(node, lhsType, rhsType, 0); + rhsType = opRuleManager.getOrEqualResultType(node, lhsType, rhsType, 0); } else if (node->op == AssignExprNode::OP_XOR_EQUAL) { - rhsType = OpRuleManager::getXorEqualResultType(node, lhsType, rhsType, 0); + rhsType = opRuleManager.getXorEqualResultType(node, lhsType, rhsType, 0); } if (lhsVar) { // Variable is involved on the left side @@ -1081,7 +1081,7 @@ std::any TypeChecker::visitPrefixUnaryExpr(PrefixUnaryExprNode *node) { operandType = OpRuleManager::getPrefixMinusResultType(node, operandType, 0); break; case PrefixUnaryExprNode::OP_PLUS_PLUS: - operandType = OpRuleManager::getPrefixPlusPlusResultType(node, operandType, 0); + operandType = opRuleManager.getPrefixPlusPlusResultType(node, operandType, 0); if (operandEntry) { // In case the lhs is captured, notify the capture about the write access @@ -1094,7 +1094,7 @@ std::any TypeChecker::visitPrefixUnaryExpr(PrefixUnaryExprNode *node) { break; case PrefixUnaryExprNode::OP_MINUS_MINUS: - operandType = OpRuleManager::getPrefixMinusMinusResultType(node, operandType, 0); + operandType = opRuleManager.getPrefixMinusMinusResultType(node, operandType, 0); if (operandEntry) { // In case the lhs is captured, notify the capture about the write access @@ -1173,6 +1173,10 @@ std::any TypeChecker::visitPostfixUnaryExpr(PostfixUnaryExprNode *node) { // Get item type lhsType = lhsType.getContainedTy(); + + // Remove heap specifier + lhsType.specifiers.isHeap = false; + break; } case PostfixUnaryExprNode::OP_MEMBER_ACCESS: { @@ -1302,7 +1306,7 @@ std::any TypeChecker::visitAtomicExpr(AtomicExprNode *node) { if (varType.isOneOf({TY_FUNCTION, TY_PROCEDURE}) && varEntry->global) { // Check if overloaded function was referenced - const std::vector *manifestations = varEntry->declNode->getFctManifestations(); + const std::vector *manifestations = varEntry->declNode->getFctManifestations(varEntry->name); if (manifestations->size() > 1) SOFT_ERROR_ER(node, REFERENCED_OVERLOADED_FCT, "Overloaded functions or functions with optional parameters cannot be referenced") @@ -1347,7 +1351,7 @@ std::any TypeChecker::visitAtomicExpr(AtomicExprNode *node) { // Check if the entry is public if it is imported assert(nameRegistryEntry->targetEntry != nullptr); - if (!nameRegistryEntry->targetEntry->getType().isPublic() && accessScope->parent->isImportedBy(currentScope)) + if (!nameRegistryEntry->targetEntry->getType().isPublic() && accessScope->parent->isImportedBy(rootScope)) SOFT_ERROR_ER(node, INSUFFICIENT_VISIBILITY, "Cannot access struct '" + nameRegistryEntry->targetEntry->name + "' due to its private visibility") } @@ -1688,7 +1692,7 @@ bool TypeChecker::visitFctPtrCall(FctCallNode *node, const SymbolType &functionT SOFT_ERROR_BOOL(node, REFERENCED_UNDEFINED_FUNCTION, "Expected and actual number of arguments do not match") // Create resolver function, that always returns a nullptr - TypeMatcher::ResolverFct resolverFct = [=](const std::string &genericTypeName) { return nullptr; }; + TypeMatcher::ResolverFct resolverFct = [](const std::string &genericTypeName) { return nullptr; }; for (size_t i = 0; i < actualArgTypes.size(); i++) { const SymbolType &actualType = actualArgTypes.at(i); @@ -1855,7 +1859,7 @@ std::any TypeChecker::visitStructInstantiation(StructInstantiationNode *node) { const bool rhsIsImmediate = assignExpr->hasCompileTimeValue(); // Check if actual type matches expected type - OpRuleManager::getFieldAssignResultType(assignExpr, expectedType, fieldResult.type, 0, rhsIsImmediate); + opRuleManager.getFieldAssignResultType(assignExpr, expectedType, fieldResult.type, 0, rhsIsImmediate); // If there is an anonymous entry attached (e.g. for struct instantiation), delete it if (fieldResult.entry != nullptr && fieldResult.entry->anonymous) @@ -1872,8 +1876,11 @@ std::any TypeChecker::visitStructInstantiation(StructInstantiationNode *node) { // Update type of struct entry structEntry->updateType(structType, true); - // Insert anonymous symbol to keep track of dtor calls for de-allocation - SymbolTableEntry *anonymousEntry = currentScope->symbolTable.insertAnonymous(structType, node); + // If not all values are constant, insert anonymous symbol to keep track of dtor calls for de-allocation + SymbolTableEntry *anonymousEntry = nullptr; + if (node->fieldLst() != nullptr) + if (std::ranges::any_of(node->fieldLst()->args(), [](AssignExprNode *field) { return !field->hasCompileTimeValue(); })) + anonymousEntry = currentScope->symbolTable.insertAnonymous(structType, node); // Remove public specifier to not have public local variables structType.specifiers.isPublic = false; @@ -1931,7 +1938,7 @@ std::any TypeChecker::visitLambdaFunc(LambdaFuncNode *node) { // Create function object const std::string fctName = "lambda." + node->codeLoc.toPrettyLineAndColumn(); - node->lambdaFunction.at(manIdx) = Function(fctName, nullptr, SymbolType(TY_DYN), returnType, paramList, {}, node, false); + node->lambdaFunction.at(manIdx) = Function(fctName, nullptr, SymbolType(TY_DYN), returnType, paramList, {}, node); node->lambdaFunction.at(manIdx).bodyScope = bodyScope; node->lambdaFunction.at(manIdx).mangleSuffix = "." + std::to_string(manIdx); @@ -1971,8 +1978,7 @@ std::any TypeChecker::visitLambdaProc(LambdaProcNode *node) { // Create function object const std::string fctName = "lambda." + node->codeLoc.toPrettyLineAndColumn(); - node->lambdaProcedure.at(manIdx) = - Function(fctName, nullptr, SymbolType(TY_DYN), SymbolType(TY_DYN), paramList, {}, node, false); + node->lambdaProcedure.at(manIdx) = Function(fctName, nullptr, SymbolType(TY_DYN), SymbolType(TY_DYN), paramList, {}, node); node->lambdaProcedure.at(manIdx).bodyScope = bodyScope; node->lambdaProcedure.at(manIdx).mangleSuffix = "." + std::to_string(manIdx); @@ -2018,7 +2024,7 @@ std::any TypeChecker::visitLambdaExpr(LambdaExprNode *node) { // Create function object const std::string fctName = "lambda." + node->codeLoc.toPrettyLineAndColumn(); - node->lambdaFunction.at(manIdx) = Function(fctName, nullptr, SymbolType(TY_DYN), returnType, paramList, {}, node, false); + node->lambdaFunction.at(manIdx) = Function(fctName, nullptr, SymbolType(TY_DYN), returnType, paramList, {}, node); node->lambdaFunction.at(manIdx).bodyScope = bodyScope; node->lambdaFunction.at(manIdx).mangleSuffix = "." + std::to_string(manIdx); @@ -2090,7 +2096,7 @@ std::any TypeChecker::visitDataType(DataTypeNode *node) { type.specifiers.isUnsigned = true; } else if (specifier->type == SpecifierNode::TY_HEAP) { // Heap variables can only be pointers - if (!type.isPtr()) + if (!type.removeReferenceWrapper().isOneOf({TY_PTR, TY_STRING})) SOFT_ERROR_ST(specifier, SPECIFIER_AT_ILLEGAL_CONTEXT, "The heap specifier can only be applied to symbols of pointer type, you provided " + baseType.getName()) @@ -2354,49 +2360,6 @@ void TypeChecker::autoDeReference(SymbolType &symbolType) { symbolType = symbolType.getContainedTy(); } -/** - * Consider calls to destructors for the given scope - * - * @param node StmtLstNode for the current scope - */ -void TypeChecker::doScopeCleanup(StmtLstNode *node) { - // Get all variables, that are approved for deallocation - std::vector vars = currentScope->getVarsGoingOutOfScope(); - for (SymbolTableEntry *var : vars) { - // Only generate dtor call for structs and if not omitted - if (!var->getType().is(TY_STRUCT) || var->omitDtorCall) - continue; - // Variable must be either initialized or a struct field - if (!var->isInitialized() && var->scope->type != ScopeType::STRUCT) - continue; - // Call dtor - callStructDtor(var, node); - } -} - -/** - * Prepare the generation of a call to the dtor of a given struct - * - * @param entry Symbol entry to use as 'this' pointer for the dtor call - * @param node StmtLstNode for the current scope - */ -void TypeChecker::callStructDtor(SymbolTableEntry *entry, StmtLstNode *node) { - SymbolType thisType = entry->getType(); - assert(thisType.is(TY_STRUCT)); - Scope *matchScope = thisType.getBodyScope(); - assert(matchScope->type == ScopeType::STRUCT); - - // Search for dtor - const bool isImported = matchScope->isImportedBy(rootScope); - if (isImported) - thisType = mapLocalTypeToImportedScopeType(matchScope, thisType); - Function *spiceFunc = FunctionManager::matchFunction(matchScope, DTOR_FUNCTION_NAME, thisType, {}, true, node); - - // Add the dtor to the stmt list node to call it later in codegen - if (spiceFunc != nullptr) - node->dtorFunctions.at(manIdx).emplace_back(entry, spiceFunc); -} - /** * Returns the operator function list for the current manifestation and the given node * diff --git a/src/typechecker/TypeChecker.h b/src/typechecker/TypeChecker.h index 19846cb72..77c06ac32 100644 --- a/src/typechecker/TypeChecker.h +++ b/src/typechecker/TypeChecker.h @@ -122,7 +122,6 @@ class TypeChecker : private CompilerPass, public ASTVisitor { std::vector &warnings; std::unordered_map typeMapping; bool typeCheckedMainFct = false; - size_t manIdx = 0; // Private methods std::string visitOrdinaryFctCall(FctCallNode *node); @@ -131,11 +130,14 @@ class TypeChecker : private CompilerPass, public ASTVisitor { [[nodiscard]] SymbolType mapLocalTypeToImportedScopeType(const Scope *targetScope, const SymbolType &symbolType) const; [[nodiscard]] SymbolType mapImportedScopeTypeToLocalType(const Scope *sourceScope, const SymbolType &symbolType) const; static void autoDeReference(SymbolType &symbolType); - void doScopeCleanup(StmtLstNode *node); - void callStructDtor(SymbolTableEntry *entry, StmtLstNode *node); std::vector &getOpFctPointers(ASTNode *node) const; [[nodiscard]] bool isStringRT() const; void softError(const ASTNode *node, SemanticErrorType errorType, const std::string &message) const; + + // Implicit code generation + void createDefaultDtorIfRequired(Struct &spiceStruct, Scope *structScope); + void implicitlyCallStructDtor(SymbolTableEntry *entry, StmtLstNode *node); + void doScopeCleanup(StmtLstNode *node); }; } // namespace spice::compiler \ No newline at end of file diff --git a/src/typechecker/TypeCheckerImplicit.cpp b/src/typechecker/TypeCheckerImplicit.cpp new file mode 100644 index 000000000..25db958fa --- /dev/null +++ b/src/typechecker/TypeCheckerImplicit.cpp @@ -0,0 +1,137 @@ +// Copyright (c) 2021-2023 ChilliBits. All rights reserved. + +#include "TypeChecker.h" + +#include +#include +#include + +namespace spice::compiler { + +static const char *const FCT_NAME_DEALLOC = "sDealloc"; + +/** + * Checks if the given struct scope already has a default constructor and creates a default one if not + * + * @param entry SymbolTableEntry of the struct + * @param structScope Scope of the struct + */ +void TypeChecker::createDefaultDtorIfRequired(Struct &spiceStruct, Scope *structScope) { + ASTNode *node = spiceStruct.declNode; + assert(structScope != nullptr && structScope->type == ScopeType::STRUCT); + + // Abort if the struct already has a user-defined destructor + const SymbolTableEntry *structEntry = spiceStruct.entry; + const SymbolType &thisType = structEntry->getType(); + const std::string fqFctName = thisType.getOriginalSubType() + MEMBER_ACCESS_TOKEN + DTOR_FUNCTION_NAME; + if (sourceFile->getNameRegistryEntry(fqFctName)) + return; + + // Check we have field types, that require use to do anything in the destructor + const size_t fieldCount = structScope->getFieldCount(); + bool hasHeapFields = false; + bool hasFieldsToDestruct = false; + for (size_t i = 0; i < fieldCount; i++) { + SymbolTableEntry *fieldSymbol = structScope->symbolTable.lookupStrictByIndex(i); + hasHeapFields |= fieldSymbol->getType().isHeap(); + if (fieldSymbol->getType().is(TY_STRUCT)) { + // Lookup dtor function + const SymbolType &thisType = fieldSymbol->getType(); + const Function *dtorFct = FunctionManager::matchFunction(structScope, DTOR_FUNCTION_NAME, thisType, {}, true, node); + hasFieldsToDestruct |= dtorFct != nullptr; + } + } + + // If we don't have any fields, that require us to do anything in the dtor, we can skip it + if (!hasHeapFields && !hasFieldsToDestruct) + return; + + // Procedure type + SymbolType procedureType(TY_PROCEDURE); + procedureType.specifiers.isPublic = spiceStruct.entry->getType().specifiers.isPublic; + + // Insert symbol for function into the symbol table + const std::string entryName = Function::getSymbolTableEntryName(DTOR_FUNCTION_NAME, node->codeLoc); + SymbolTableEntry *fctEntry = structScope->insert(entryName, structEntry->declNode); + fctEntry->updateType(procedureType, true); + + // Add to external name registry + assert(sourceFile->getNameRegistryEntry(fqFctName) == nullptr); + sourceFile->addNameRegistryEntry(fqFctName, fctEntry, structScope, /*keepNewOnCollision=*/true); + + // Create the default destructor function + const std::vector templateTypes = spiceStruct.templateTypes; + Function defaultDtor(DTOR_FUNCTION_NAME, fctEntry, thisType, SymbolType(TY_DYN), {}, templateTypes, structEntry->declNode); + defaultDtor.implicitDefault = true; + + // Create function scope + Scope *fctScope = structScope->createChildScope(defaultDtor.getSignature(false), ScopeType::FUNC_PROC_BODY, &node->codeLoc); + defaultDtor.bodyScope = fctScope; + + // Create 'this' symbol in the function scope + SymbolTableEntry *thisEntry = fctScope->insert(THIS_VARIABLE_NAME, node); + thisEntry->updateType(thisType.toPointer(node), true); + thisEntry->used = true; // Always set to used to not print warnings for non-existing code + + // Hand it off to the function manager to register the function + FunctionManager::insertFunction(structScope, defaultDtor, structEntry->declNode->getFctManifestations(DTOR_FUNCTION_NAME)); + + // Request memory runtime if we have fields, that are allocated on the heap + if (hasHeapFields) { + SourceFile *memoryRT = sourceFile->requestRuntimeModule(MEMORY_RT); + assert(memoryRT != nullptr); + Scope *matchScope = memoryRT->globalScope.get(); + // Set dealloc function to used + const SymbolType thisType(TY_DYN); + SymbolType bytePtrRefType = SymbolType(TY_BYTE).toPointer(node).toReference(node); + bytePtrRefType.specifiers.isHeap = true; + const std::vector paramTypes = {bytePtrRefType}; + Function *deallocFct = FunctionManager::matchFunction(matchScope, FCT_NAME_DEALLOC, thisType, paramTypes, true, node); + assert(deallocFct != nullptr); + } +} + +/** + * Prepare the generation of a call to the dtor of a given struct + * + * @param entry Symbol entry to use as 'this' pointer for the dtor call + * @param node StmtLstNode for the current scope + */ +void TypeChecker::implicitlyCallStructDtor(SymbolTableEntry *entry, StmtLstNode *node) { + SymbolType thisType = entry->getType(); + assert(thisType.is(TY_STRUCT)); + Scope *matchScope = thisType.getBodyScope(); + assert(matchScope->type == ScopeType::STRUCT); + + // Search for dtor + const bool isImported = matchScope->isImportedBy(rootScope); + if (isImported) + thisType = mapLocalTypeToImportedScopeType(matchScope, thisType); + Function *spiceFunc = FunctionManager::matchFunction(matchScope, DTOR_FUNCTION_NAME, thisType, {}, true, node); + + // Add the dtor to the stmt list node to call it later in codegen + if (spiceFunc != nullptr) + node->dtorFunctions.at(manIdx).emplace_back(entry, spiceFunc); +} + +/** + * Consider calls to destructors for the given scope + * + * @param node StmtLstNode for the current scope + */ +void TypeChecker::doScopeCleanup(StmtLstNode *node) { + // Get all variables, that are approved for deallocation + std::vector vars = currentScope->getVarsGoingOutOfScope(); + for (SymbolTableEntry *var : vars) { + // Only generate dtor call for structs and if not omitted + if (!var->getType().is(TY_STRUCT) || var->omitDtorCall) + continue; + // Variable must be either initialized or a struct field + if (!var->isInitialized() && var->scope->type != ScopeType::STRUCT) + continue; + // Call dtor + implicitlyCallStructDtor(var, node); + } +} + +} // namespace spice::compiler \ No newline at end of file diff --git a/src/typechecker/TypeCheckerPrepare.cpp b/src/typechecker/TypeCheckerPrepare.cpp index 84f0f6552..97193ea03 100644 --- a/src/typechecker/TypeCheckerPrepare.cpp +++ b/src/typechecker/TypeCheckerPrepare.cpp @@ -152,7 +152,7 @@ std::any TypeChecker::visitFctDefPrepare(FctDefNode *node) { functionEntry->updateType(functionType, false); // Build function object - Function spiceFunc(node->fctName->name, functionEntry, thisType, returnType, paramList, usedGenericTypes, node, false); + Function spiceFunc(node->fctName->name, functionEntry, thisType, returnType, paramList, usedGenericTypes, node); spiceFunc.bodyScope = node->fctScope; FunctionManager::insertFunction(currentScope, spiceFunc, &node->fctManifestations); @@ -270,8 +270,7 @@ std::any TypeChecker::visitProcDefPrepare(ProcDefNode *node) { procedureEntry->updateType(procedureType, false); // Build procedure object - Function spiceProc(node->procName->name, procedureEntry, thisType, SymbolType(TY_DYN), paramList, usedGenericTypes, node, - false); + Function spiceProc(node->procName->name, procedureEntry, thisType, SymbolType(TY_DYN), paramList, usedGenericTypes, node); spiceProc.bodyScope = node->procScope; FunctionManager::insertFunction(currentScope, spiceProc, &node->procManifestations); @@ -387,6 +386,9 @@ std::any TypeChecker::visitStructDefPrepare(StructDefNode *node) { StructManager::insertStruct(currentScope, spiceStruct, &node->structManifestations); spiceStruct.structScope = node->structScope; + // Check for default ctor/dtor, etc. + createDefaultDtorIfRequired(spiceStruct, node->structScope); + return nullptr; } @@ -605,7 +607,7 @@ std::any TypeChecker::visitExtDeclPrepare(ExtDeclNode *node) { } // Add function to current scope - Function spiceFunc = Function(node->extFunctionName, node->entry, SymbolType(TY_DYN), returnType, argList, {}, node, true); + Function spiceFunc = Function(node->extFunctionName, node->entry, SymbolType(TY_DYN), returnType, argList, {}, node); node->extFunction = FunctionManager::insertFunction(currentScope, spiceFunc, &node->extFunctionManifestations); node->extFunction->mangleFunctionName = false; diff --git a/src/typechecker/TypeMatcher.cpp b/src/typechecker/TypeMatcher.cpp index 753c35268..f5c25518c 100644 --- a/src/typechecker/TypeMatcher.cpp +++ b/src/typechecker/TypeMatcher.cpp @@ -29,7 +29,7 @@ bool TypeMatcher::matchRequestedToCandidateTypes(const std::vector & } bool TypeMatcher::matchRequestedToCandidateType(SymbolType candidateType, SymbolType requestedType, TypeMapping &typeMapping, - ResolverFct &resolverFct, bool strictSpecifiers) { + ResolverFct &resolverFct, bool strictSpecifierMatching) { // Unwrap both types as far as possible while (candidateType.isSameContainerTypeAs(requestedType)) { requestedType = requestedType.getContainedTy(); @@ -46,31 +46,38 @@ bool TypeMatcher::matchRequestedToCandidateType(SymbolType candidateType, Symbol // If the candidate does not contain any generic parts, we can simply check for type equality if (!candidateType.hasAnyGenericParts()) - return candidateType.matches(requestedType, true, !strictSpecifiers, true); + return candidateType.matches(requestedType, true, !strictSpecifierMatching, true); // Check if the candidate type itself is generic if (candidateType.isBaseType(TY_GENERIC)) { // The candidate type itself is generic const std::string genericTypeName = candidateType.getBaseType().getSubType(); // Check if we know the concrete type for that generic type name already - if (typeMapping.contains(genericTypeName)) { + if (typeMapping.contains(genericTypeName)) { // This is a known generic type SymbolType knownConcreteType = typeMapping.at(genericTypeName); + // Merge specifiers of candidate type and known concrete type together + knownConcreteType.specifiers = knownConcreteType.specifiers.merge(candidateType.specifiers); + // Remove reference wrapper of candidate type if required if (!requestedType.isRef()) knownConcreteType = knownConcreteType.removeReferenceWrapper(); // Check if the known concrete type matches the requested type - return knownConcreteType.matches(requestedType, true, !strictSpecifiers, true); - } else { + return knownConcreteType.matches(requestedType, true, !strictSpecifierMatching, true); + } else { // This is an unknown generic type // Retrieve generic candidate type by its name const GenericType *genericCandidateType = resolverFct(genericTypeName); assert(genericCandidateType != nullptr); // Check if the requested type fulfills all conditions of the generic candidate type - if (!genericCandidateType->checkConditionsOf(requestedType, true, !strictSpecifiers)) + if (!genericCandidateType->checkConditionsOf(requestedType, true, !strictSpecifierMatching)) return false; + // Zero out all specifiers in the requested type, that are present in the candidate type + // This is to set all specifiers that are not present in the candidate type to the generic type replacement + requestedType.specifiers.eraseWithMask(candidateType.specifiers); + // Add to type mapping const SymbolType newMappingType = requestedType.hasAnyGenericParts() ? candidateType : requestedType; typeMapping.insert({genericTypeName, newMappingType}); @@ -81,24 +88,24 @@ bool TypeMatcher::matchRequestedToCandidateType(SymbolType candidateType, Symbol // Check if supertype and subtype are equal if (requestedType.getSuperType() != candidateType.getSuperType()) return false; - const bool isFctType = candidateType.isOneOf({TY_FUNCTION, TY_PROCEDURE}); - if (!isFctType && requestedType.getOriginalSubType() != candidateType.getOriginalSubType()) - return false; - if (!isFctType && requestedType.getBodyScope()->parent != candidateType.getBodyScope()->parent) - return false; // If we have a function/procedure type, check the param and return types. Otherwise, check the template types - if (isFctType) { + if (candidateType.isOneOf({TY_FUNCTION, TY_PROCEDURE})) { // Check param and return types const std::vector &candidatePRTypes = candidateType.getFunctionParamAndReturnTypes(); const std::vector &requestedPRTypes = requestedType.getFunctionParamAndReturnTypes(); - if (!matchRequestedToCandidateTypes(candidatePRTypes, requestedPRTypes, typeMapping, resolverFct, strictSpecifiers)) + if (!matchRequestedToCandidateTypes(candidatePRTypes, requestedPRTypes, typeMapping, resolverFct, strictSpecifierMatching)) return false; } else { + if (requestedType.getOriginalSubType() != candidateType.getOriginalSubType()) + return false; + if (requestedType.getBodyScope()->parent != candidateType.getBodyScope()->parent) + return false; + // Check template types const std::vector &candidateTTypes = candidateType.getTemplateTypes(); const std::vector &requestedTTypes = requestedType.getTemplateTypes(); - if (!matchRequestedToCandidateTypes(candidateTTypes, requestedTTypes, typeMapping, resolverFct, strictSpecifiers)) + if (!matchRequestedToCandidateTypes(candidateTTypes, requestedTTypes, typeMapping, resolverFct, strictSpecifierMatching)) return false; } diff --git a/src/typechecker/TypeMatcher.h b/src/typechecker/TypeMatcher.h index 3ba2d4b8a..ff5cb5af5 100644 --- a/src/typechecker/TypeMatcher.h +++ b/src/typechecker/TypeMatcher.h @@ -23,7 +23,7 @@ class TypeMatcher { const std::vector &requestedType, TypeMapping &typeMapping, ResolverFct &resolverFct, bool strictSpecifiers); static bool matchRequestedToCandidateType(SymbolType candidateType, SymbolType requestedType, TypeMapping &typeMapping, - ResolverFct &resolverFct, bool strictSpecifiers); + ResolverFct &resolverFct, bool strictSpecifierMatching); static void substantiateTypeWithTypeMapping(SymbolType &symbolType, const TypeMapping &typeMapping); }; diff --git a/src/util/CompilerWarning.cpp b/src/util/CompilerWarning.cpp index 8024eb3c6..bace7e3e1 100644 --- a/src/util/CompilerWarning.cpp +++ b/src/util/CompilerWarning.cpp @@ -44,6 +44,8 @@ std::string CompilerWarning::getMessagePrefix(CompilerWarningType type) { return "Unused function"; case UNUSED_PROCEDURE: return "Unused procedure"; + case UNUSED_METHOD: + return "Unused method"; case UNUSED_STRUCT: return "Unused struct"; case UNUSED_INTERFACE: diff --git a/src/util/CompilerWarning.h b/src/util/CompilerWarning.h index 562ba44fc..33f4da162 100644 --- a/src/util/CompilerWarning.h +++ b/src/util/CompilerWarning.h @@ -12,6 +12,7 @@ struct CodeLoc; enum CompilerWarningType { UNUSED_FUNCTION, UNUSED_PROCEDURE, + UNUSED_METHOD, UNUSED_STRUCT, UNUSED_INTERFACE, UNUSED_IMPORT, diff --git a/std/data/binary-tree.spice b/std/data/binary-tree.spice index cc2997b43..99d10f13e 100644 --- a/std/data/binary-tree.spice +++ b/std/data/binary-tree.spice @@ -1,6 +1,6 @@ // Link external functions -ext f malloc(long); -ext p free(byte*); +ext f malloc(long); +ext p free(heap byte*); // Add generic type definitions type T dyn; @@ -9,8 +9,8 @@ type T dyn; * Node of a BinaryTree */ public type Node struct { - Node* childLeft - Node* childRight + heap Node* childLeft + heap Node* childRight T value } @@ -24,34 +24,34 @@ public type Node struct { * Search: O(log n) */ public type BinaryTree struct { - Node* rootNode + heap Node* rootNode bool isBalanced } public p BinaryTree.ctor() { - this.rootNode = nil*>; + this.rootNode = nil*>; this.isBalanced = false; } public p BinaryTree.dtor() { - if this.rootNode != nil*> { + if this.rootNode != nil*> { this.rootNode.dtor(); - free((byte*) this.rootNode); + free((heap byte*) this.rootNode); } } public p Node.dtor() { - if this.childLeft != nil*> { + if this.childLeft != nil*> { this.childLeft.dtor(); - free((byte*) this.childLeft); + free((heap byte*) this.childLeft); } - if this.childRight != nil*> { + if this.childRight != nil*> { this.childRight.dtor(); - free((byte*) this.childRight); + free((heap byte*) this.childRight); } } -public p BinaryTree.insert(T newValue, Node* baseNode = nil*>) { +public p BinaryTree.insert(T newValue, heap Node* baseNode = nil*>) { // Search position where to insert // ToDo } \ No newline at end of file diff --git a/std/data/doubly-linked-list.spice b/std/data/doubly-linked-list.spice index d988ed4ba..9819e10d7 100644 --- a/std/data/doubly-linked-list.spice +++ b/std/data/doubly-linked-list.spice @@ -1,6 +1,6 @@ // Link external functions -ext f malloc(long); -ext p free(byte*); +ext f malloc(long); +ext p free(heap byte*); // Add generic type definitions type T dyn; @@ -9,9 +9,9 @@ type T dyn; * Node of a DoublyLinkedList */ public type Node struct { - Node* prev + heap Node* prev T value - Node* next + heap Node* next } /** @@ -20,26 +20,26 @@ public type Node struct { * to the previous one. */ public type DoublyLinkedList struct { - Node* head - Node* tail + heap Node* head + heap Node* tail } public p Node.dtor() { - if this.next != nil*> { + if this.next != nil*> { this.next.dtor(); - free((byte*) this.next); + free((heap byte*) this.next); } } public p DoublyLinkedList.insert(T newValue, Node* prevNode = nil*>) { // Create new node - Node* newNode; + heap Node* newNode; unsafe { - newNode = (Node*) malloc(sizeof(type Node) / 8); + newNode = (heap Node*) malloc(sizeof(type Node) / 8); } newNode.value = newValue; - if prevNode != nil*> { // Previous node was passed -> insert after this node + 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 @@ -67,9 +67,9 @@ public inline p DoublyLinkedList.insertTail(T newValue) { this.insert(newValue, this.tail); } -/*public f*> DoublyLinkedList.find(T value) { - Node* currentNode = this.head; - while currentNode != nil*> { +/*public f*> DoublyLinkedList.find(T value) { + heap Node* currentNode = this.head; + while currentNode != nil*> { // Check condition if currentNode.value == value { return currentNode; @@ -77,5 +77,5 @@ public inline p DoublyLinkedList.insertTail(T newValue) { // Move to next node currentNode = currentNode.next; } - return nil*>; + return nil*>; }*/ \ No newline at end of file diff --git a/std/data/linked-list.spice b/std/data/linked-list.spice index 9d03a36c7..d963bf1b6 100644 --- a/std/data/linked-list.spice +++ b/std/data/linked-list.spice @@ -1,6 +1,6 @@ // Link external functions -ext f malloc(long); -ext p free(byte*); +ext f malloc(long); +ext p free(heap byte*); // Add generic type definitions type T dyn; @@ -10,7 +10,7 @@ type T dyn; */ public type Node struct { T value - Node* next + heap Node* next } /** @@ -29,18 +29,18 @@ public type Node struct { * Beware that each add operation allocates memory and every remove operation frees memory. */ public type LinkedList struct { - Node* tail - Node* head + heap Node* tail + heap Node* head } public p LinkedList.ctor() { - this.tail = nil*>; - this.head = nil*>; + this.tail = nil*>; + this.head = nil*>; } public p LinkedList.dtor() { Node* curr = this.tail; - while curr != nil*> { + while curr != nil*> { Node* next = curr.next; free(curr); curr = next; @@ -49,12 +49,12 @@ public p LinkedList.dtor() { public p LinkedList.insert(const T& value) { // Create new node - Node* newNode; + heap Node* newNode; unsafe { - newNode = (Node*) malloc(sizeof(type Node) / 8l); + newNode = (heap Node*) malloc(sizeof(type Node) / 8l); } newNode.value = value; - newNode.next = nil*>; + newNode.next = nil*>; // Insert at head this.head.next = newNode; @@ -63,15 +63,15 @@ public p LinkedList.insert(const T& value) { public p LinkedList.insertAt(unsigned long idx, const T& value) { // Create new node - Node* newNode; + heap Node* newNode; unsafe { - newNode = (Node*) malloc(sizeof(type Node) / 8); + newNode = (heap Node*) malloc(sizeof(type Node) / 8); } newNode.value = value; // Search for item right before insert position Node* prev = this.tail; - while curr != nil*> && idx > 1 { + while curr != nil*> && idx > 1 { prev = prev.next; idx--; } @@ -92,14 +92,14 @@ public p LinkedList.insertAt(unsigned long idx, const T& value) { } public p LinkedList.remove(const T& valueToRemove) { - Node* curr = this.tail; - Node* prev = nil; - while curr != nil*> && curr.value != valueToRemove { + heap Node* curr = this.tail; + heap Node* prev = nil; + while curr != nil*> && curr.value != valueToRemove { prev = curr; curr = curr.next; } // Check if the item was found. If yes, delete its node - if curr != nil*> { + if curr != nil*> { // Set the next node of the previous node prev.next = curr.next; // Free the removed node diff --git a/std/data/map.spice b/std/data/map.spice index 7977ed544..997e11914 100644 --- a/std/data/map.spice +++ b/std/data/map.spice @@ -3,10 +3,10 @@ const unsigned long INITIAL_ALLOC_COUNT = 5l; const unsigned int RESIZE_FACTOR = 2; // Link external functions -ext f malloc(long); -ext f realloc(byte*, int); -ext p free(byte*); -ext f memcpy(byte*, byte*, int); +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; diff --git a/std/data/queue.spice b/std/data/queue.spice index 298e7ecd1..bdfc15095 100644 --- a/std/data/queue.spice +++ b/std/data/queue.spice @@ -1,15 +1,10 @@ +import "std/type/result"; import "std/type/error"; // Constants const unsigned long INITIAL_ALLOC_COUNT = 5l; const unsigned int RESIZE_FACTOR = 2; -// Link external functions -ext f malloc(long); -ext f realloc(byte*, int); -ext p free(byte*); -ext f memcpy(byte*, byte*, int); - // Add generic type definition type T dyn; @@ -25,7 +20,7 @@ type T dyn; * with every item pushed. */ public type Queue struct { - T* contents // Pointer to the first data element + heap T* contents // Pointer to the first data element unsigned long capacity // Allocated number of items unsigned long size // Current number of items unsigned long idxFront // Index for front access @@ -53,7 +48,8 @@ public p Queue.ctor(unsigned long initAllocItems = INITIAL_ALLOC_COUNT) { // Allocate space for the initial number of elements const long itemSize = sizeof(type T) / 8l; unsafe { - this.contents = (T*) malloc(itemSize * initAllocItems); + Result allocResult = sAlloc(itemSize * initAllocItems); + this.contents = (heap T*) allocResult.unwrap(); } this.size = 0l; this.capacity = initAllocItems; @@ -61,13 +57,6 @@ public p Queue.ctor(unsigned long initAllocItems = INITIAL_ALLOC_COUNT) { this.idxBack = 0l; } -public p Queue.dtor() { - // Free all the memory - unsafe { - free((byte*) this.contents); - } -} - /** * Add an item at the end of the queue */ @@ -184,10 +173,10 @@ p Queue.resize(unsigned long itemCount) { // Allocate the new memory const long itemSize = sizeof(type T) / 8l; unsafe { - byte* oldAddress = (byte*) this.contents; - int newSize = (int) (itemSize * itemCount); - T* newMemory = (T*) realloc(oldAddress, newSize); - this.contents = newMemory; + heap byte* oldAddress = (heap byte*) this.contents; + unsigned long newSize = (unsigned long) (itemSize * itemCount); + Result allocResult = sRealloc(oldAddress, newSize); + this.contents = (heap T*) allocResult.unwrap(); } // Set new capacity this.capacity = itemCount; diff --git a/std/data/red-black-tree.spice b/std/data/red-black-tree.spice index 930e08edc..5ffdae5ff 100644 --- a/std/data/red-black-tree.spice +++ b/std/data/red-black-tree.spice @@ -1,6 +1,6 @@ // Link external functions -ext f malloc(long); -ext p free(byte*); +ext f malloc(long); +ext p free(heap byte*); // Add generic type definitions type T dyn; @@ -13,22 +13,22 @@ type NodeColor enum { RED, BLACK } */ type Node struct { T data - Node* childLeft - Node* childRight + heap Node* childLeft + heap Node* childRight NodeColor color } f*> createNode(T data) { - Node* newNode = malloc(sizeof(type Node) / 8); + heap Node* newNode = (heap Node*) malloc(sizeof(type Node) / 8); newNode.data = data; - newNode.childLeft = nil*>; - newNode.childRight = nil*>; + newNode.childLeft = nil*>; + newNode.childRight = nil*>; newNode.color = NodeColor::RED; return newNode; } inline f Node.isRoot() { - return this.parent == nil*>; + return this.parent == nil*>; } /** @@ -39,11 +39,11 @@ inline f Node.isRoot() { * Deletion time: O(log n) */ public type RedBlackTree struct { - Node* rootNode + heap Node* rootNode } public p RedBlackTree.ctor() { - this.rootNode = nil*>; + this.rootNode = nil*>; } public p RedBlackTree.insert(T newItem) { diff --git a/std/data/stack.spice b/std/data/stack.spice index dedc5da1c..4a3c8e10d 100644 --- a/std/data/stack.spice +++ b/std/data/stack.spice @@ -1,13 +1,10 @@ +import "std/type/result"; +import "std/type/error"; + // Constants const unsigned long INITIAL_ALLOC_COUNT = 5l; const unsigned int RESIZE_FACTOR = 2; -// Link external functions -ext f malloc(long); -ext f realloc(byte*, int); -ext p free(byte*); -ext f memcpy(byte*, byte*, int); - // Add generic type definition type T dyn; @@ -23,7 +20,7 @@ type T dyn; * with every item pushed. */ public type Stack struct { - T* contents // Pointer to the first data element + heap T* contents // Pointer to the first data element unsigned long capacity // Allocated number of items unsigned long size // Current number of items } @@ -46,21 +43,15 @@ public p Stack.ctor(unsigned int initAllocItems) { public p Stack.ctor(unsigned long initAllocItems = INITIAL_ALLOC_COUNT) { // Allocate space for the initial number of elements - const long itemSize = sizeof(type T) / 8l; + const unsigned long itemSize = sizeof(type T) / 8l; unsafe { - this.contents = (T*) malloc(itemSize * initAllocItems); + Result allocResult = sAlloc(itemSize * initAllocItems); + this.contents = (heap T*) allocResult.unwrap(); } this.size = 0l; this.capacity = initAllocItems; } -public p Stack.dtor() { - // Free all the memory - unsafe { - free((byte*) this.contents); - } -} - /** * Add an item to the stack */ @@ -80,6 +71,7 @@ public p Stack.push(const T& item) { * Retrieve item and remove it from the stack */ public f Stack.pop() { + if this.isEmpty() { panic(Error("The stack is empty")); } // Pop the element from the stack unsafe { return this.contents[(int) --this.size]; @@ -137,12 +129,12 @@ public p Stack.pack() { */ p Stack.resize(unsigned long itemCount) { // Allocate the new memory - const long itemSize = sizeof(type T) / 8l; + const unsigned long itemSize = sizeof(type T) / 8l; unsafe { - byte* oldAddress = (byte*) this.contents; - int newSize = (int) (itemSize * itemCount); - T* newMemory = (T*) realloc(oldAddress, newSize); - this.contents = newMemory; + heap byte* oldAddress = (heap byte*) this.contents; + unsigned long newSize = (unsigned long) (itemSize * itemCount); + Result allocResult = sRealloc(oldAddress, newSize); + this.contents = (heap T*) allocResult.unwrap(); } // Set new capacity this.capacity = itemCount; diff --git a/std/data/vector.spice b/std/data/vector.spice index 15c4158f7..aadc8936f 100644 --- a/std/data/vector.spice +++ b/std/data/vector.spice @@ -1,15 +1,10 @@ +import "std/type/result"; import "std/type/error"; // Constants const unsigned long INITIAL_ALLOC_COUNT = 5l; const unsigned int RESIZE_FACTOR = 2; -// Link external functions -ext f malloc(long); -ext f realloc(byte*, int); -ext p free(byte*); -ext f memcpy(byte*, byte*, int); - // Add generic type definition type T dyn; @@ -25,7 +20,7 @@ type T dyn; * with every item pushed. */ public type Vector struct { - T* contents // Pointer to the first data element + heap T* contents // Pointer to the first data element unsigned long capacity // Allocated number of items unsigned long size // Current number of items } @@ -35,7 +30,8 @@ public p Vector.ctor(unsigned long initAllocItems = INITIAL_ALLOC_COUNT) { const long itemSize = sizeof(type T) / 8l; assert itemSize != 0l; unsafe { - this.contents = (T*) malloc(itemSize * initAllocItems); + Result allocResult = sAlloc(itemSize * initAllocItems); + this.contents = (heap T*) allocResult.unwrap(); } this.size = 0l; this.capacity = initAllocItems; @@ -57,13 +53,6 @@ public p Vector.ctor(unsigned long initAllocItems, const T& defaultValue) { this.size = initAllocItems; } -public p Vector.dtor() { - // Free all the memory - unsafe { - free((byte*) this.contents); - } -} - /** * Checks if the vector contains any items at the moment * @@ -197,10 +186,10 @@ p Vector.resize(unsigned long itemCount) { const long itemSize = sizeof(type T) / 8l; assert itemSize != 0l; unsafe { - byte* oldAddress = (byte*) this.contents; - int newSize = (int) (itemSize * itemCount); - T* newMemory = (T*) realloc(oldAddress, newSize); - this.contents = newMemory; + heap byte* oldAddress = (heap byte*) this.contents; + unsigned long newSize = (unsigned long) (itemSize * itemCount); + Result allocResult = sRealloc(oldAddress, newSize); + this.contents = (heap T*) allocResult.unwrap(); } // Set new capacity this.capacity = itemCount; diff --git a/std/os/allocator.spice b/std/os/allocator.spice index caebcb2ed..73b140e26 100644 --- a/std/os/allocator.spice +++ b/std/os/allocator.spice @@ -14,8 +14,8 @@ const UInt32 LEAF_BINS_INDEX_MASK = 0x7; const UInt32 NUM_LEAF_BINS = 256; // NUM_TOP_BINS * BINS_PER_LEAF // External functions -ext f malloc(long); -ext p free(byte*); +ext f malloc(long); +ext p free(heap byte*); type Allocation struct { UInt32 offset diff --git a/std/runtime/memory_rt.spice b/std/runtime/memory_rt.spice index 03ad3ed4d..769f4e92d 100644 --- a/std/runtime/memory_rt.spice +++ b/std/runtime/memory_rt.spice @@ -1,10 +1,10 @@ import "std/type/result"; // Link external functions -ext f malloc(long); -ext f realloc(heap byte*, int); +ext f malloc(unsigned long); +ext f realloc(heap byte*, unsigned long); ext p free(heap byte*); -ext p memcpy(heap byte*, heap byte*, long); +ext p memcpy(heap byte*, heap byte*, unsigned long); /** * Allocates a new block of memory of the given size. @@ -12,9 +12,9 @@ ext p memcpy(heap byte*, heap byte*, long); * @param size The size of the block to allocate. * @return A pointer to the allocated block, or an error if the allocation failed. */ -public f> alloc(long size) { +public f> sAlloc(unsigned long size) { heap byte* ptr = malloc(size); - return ptr != nil ? ptr : err(nil, "Out of memory occurred!"); + return ptr != nil ? ok(ptr) : err(nil, "Out of memory occurred!"); } /** @@ -24,9 +24,9 @@ public f> alloc(long size) { * @param size The new size of the block. * @return A pointer to the reallocated block, or an error if the reallocation failed. */ -public f> realloc(heap byte* ptr, long size) { +public f> sRealloc(heap byte* ptr, unsigned long size) { heap byte* newPtr = realloc(ptr, size); - return newPtr != nil ? newPtr : err(nil, "Out of memory occurred!"); + return newPtr != nil ? ok(newPtr) : err(nil, "Out of memory occurred!"); } /** @@ -36,13 +36,13 @@ public f> realloc(heap byte* ptr, long size) { * @param size The size of the block to copy. * @return A pointer to the copied block, or an error if the copy failed. */ -public f> copy(heap byte* ptr, long size) { +public f> sCopy(heap byte* ptr, unsigned long size) { heap byte* newPtr = malloc(size); if newPtr == nil { return err(nil, "Out of memory occurred!"); } memcpy(newPtr, ptr, size); - return newPtr; + return ok(newPtr); } /** @@ -51,7 +51,7 @@ public f> copy(heap byte* ptr, long size) { * * @param ptr The pointer to the block to free. */ -public p dealloc(heap byte*& ptr) { +public p sDealloc(heap byte*& ptr) { free(ptr); ptr = nil; // Zero out to prevent accidental double frees } \ No newline at end of file diff --git a/std/runtime/string_rt.spice b/std/runtime/string_rt.spice index c3c8624cc..d0b79d243 100644 --- a/std/runtime/string_rt.spice +++ b/std/runtime/string_rt.spice @@ -255,7 +255,9 @@ public f operator!=(const String& a, const String& b) { * @return Raw immutable string */ public inline f String.getRaw() { - return (string) this.contents; + unsafe { + return (string) this.contents; + } } /** diff --git a/test/test-files/irgenerator/arrays/success-arrays2/ir-code.ll b/test/test-files/irgenerator/arrays/success-arrays2/ir-code.ll index b4fc50396..e1229fda1 100644 --- a/test/test-files/irgenerator/arrays/success-arrays2/ir-code.ll +++ b/test/test-files/irgenerator/arrays/success-arrays2/ir-code.ll @@ -11,30 +11,24 @@ target triple = "x86_64-w64-windows-gnu" ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { %result = alloca i32, align 4 - %1 = alloca [10 x i32], align 4 %intArray = alloca [10 x i32], align 4 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 40, i1 false) store [10 x i32] [i32 1, i32 2, i32 4, i32 8, i32 16, i32 32, i32 64, i32 128, i32 256, i32 512], ptr %intArray, align 4 - %2 = getelementptr inbounds [10 x i32], ptr %intArray, i32 0, i32 3 - %3 = load i32, ptr %2, align 4 - %4 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %3) - %5 = getelementptr inbounds [10 x i32], ptr %intArray, i32 0, i32 7 - %6 = load i32, ptr %5, align 4 - %7 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %6) - %8 = getelementptr inbounds [10 x i32], ptr %intArray, i32 0, i32 9 - %9 = load i32, ptr %8, align 4 - %10 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.2, i32 %9) - %11 = load i32, ptr %result, align 4 - ret i32 %11 + %1 = getelementptr inbounds [10 x i32], ptr %intArray, i32 0, i32 3 + %2 = load i32, ptr %1, align 4 + %3 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %2) + %4 = getelementptr inbounds [10 x i32], ptr %intArray, i32 0, i32 7 + %5 = load i32, ptr %4, align 4 + %6 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %5) + %7 = getelementptr inbounds [10 x i32], ptr %intArray, i32 0, i32 9 + %8 = load i32, ptr %7, align 4 + %9 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.2, i32 %8) + %10 = load i32, ptr %result, align 4 + ret i32 %10 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/arrays/success-arrays3/ir-code.ll b/test/test-files/irgenerator/arrays/success-arrays3/ir-code.ll index a22ca5890..ebb9dca9a 100644 --- a/test/test-files/irgenerator/arrays/success-arrays3/ir-code.ll +++ b/test/test-files/irgenerator/arrays/success-arrays3/ir-code.ll @@ -10,24 +10,18 @@ target triple = "x86_64-w64-windows-gnu" define dso_local i32 @main() #0 { %result = alloca i32, align 4 %intArray = alloca [2 x i32], align 4 - %1 = alloca [2 x i32], align 4 store i32 0, ptr %result, align 4 store [2 x i32] zeroinitializer, ptr %intArray, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 8, i1 false) store [2 x i32] [i32 1, i32 2], ptr %intArray, align 4 - %2 = getelementptr inbounds [2 x i32], ptr %intArray, i32 0, i32 1 - %3 = load i32, ptr %2, align 4 - %4 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %3) - %5 = load i32, ptr %result, align 4 - ret i32 %5 + %1 = getelementptr inbounds [2 x i32], ptr %intArray, i32 0, i32 1 + %2 = load i32, ptr %1, align 4 + %3 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %2) + %4 = load i32, ptr %result, align 4 + ret i32 %4 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/builtins/success-len/ir-code.ll b/test/test-files/irgenerator/builtins/success-len/ir-code.ll index e919d62be..4518d99e7 100644 --- a/test/test-files/irgenerator/builtins/success-len/ir-code.ll +++ b/test/test-files/irgenerator/builtins/success-len/ir-code.ll @@ -9,22 +9,16 @@ target triple = "x86_64-w64-windows-gnu" ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { %result = alloca i32, align 4 - %1 = alloca [4 x i32], align 4 %testIntArray = alloca [4 x i32], align 4 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 16, i1 false) store [4 x i32] [i32 1, i32 2, i32 3, i32 4], ptr %testIntArray, align 4 - %2 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i64 4) - %3 = load i32, ptr %result, align 4 - ret i32 %3 + %1 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i64 4) + %2 = load i32, ptr %result, align 4 + ret i32 %2 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/builtins/success-sizeof/cout.out b/test/test-files/irgenerator/builtins/success-sizeof/cout.out index 75d24fc4d..68400c00f 100644 --- a/test/test-files/irgenerator/builtins/success-sizeof/cout.out +++ b/test/test-files/irgenerator/builtins/success-sizeof/cout.out @@ -8,4 +8,4 @@ Size of string: 64 Size of bool: 1 Size of int[]: 224 Size of int*: 64 -Size of struct instance: 128 +Size of struct type: 128 diff --git a/test/test-files/irgenerator/builtins/success-sizeof/ir-code-O2.ll b/test/test-files/irgenerator/builtins/success-sizeof/ir-code-O2.ll index 8c0a0ec3e..d0acf8a62 100644 --- a/test/test-files/irgenerator/builtins/success-sizeof/ir-code-O2.ll +++ b/test/test-files/irgenerator/builtins/success-sizeof/ir-code-O2.ll @@ -13,7 +13,7 @@ target triple = "x86_64-w64-windows-gnu" @printf.str.7 = private unnamed_addr constant [18 x i8] c"Size of bool: %d\0A\00", align 1 @printf.str.8 = private unnamed_addr constant [19 x i8] c"Size of int[]: %d\0A\00", align 1 @printf.str.9 = private unnamed_addr constant [18 x i8] c"Size of int*: %d\0A\00", align 1 -@printf.str.10 = private unnamed_addr constant [29 x i8] c"Size of struct instance: %d\0A\00", align 1 +@printf.str.10 = private unnamed_addr constant [25 x i8] c"Size of struct type: %d\0A\00", align 1 ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() local_unnamed_addr #0 { diff --git a/test/test-files/irgenerator/builtins/success-sizeof/ir-code.ll b/test/test-files/irgenerator/builtins/success-sizeof/ir-code.ll index 29a798f64..d6e28ac5d 100644 --- a/test/test-files/irgenerator/builtins/success-sizeof/ir-code.ll +++ b/test/test-files/irgenerator/builtins/success-sizeof/ir-code.ll @@ -13,7 +13,7 @@ target triple = "x86_64-w64-windows-gnu" @printf.str.7 = private unnamed_addr constant [18 x i8] c"Size of bool: %d\0A\00", align 1 @printf.str.8 = private unnamed_addr constant [19 x i8] c"Size of int[]: %d\0A\00", align 1 @printf.str.9 = private unnamed_addr constant [18 x i8] c"Size of int*: %d\0A\00", align 1 -@printf.str.10 = private unnamed_addr constant [29 x i8] c"Size of struct instance: %d\0A\00", align 1 +@printf.str.10 = private unnamed_addr constant [25 x i8] c"Size of struct type: %d\0A\00", align 1 ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { diff --git a/test/test-files/irgenerator/builtins/success-sizeof/source.spice b/test/test-files/irgenerator/builtins/success-sizeof/source.spice index 4a662c1ce..b25a00e42 100644 --- a/test/test-files/irgenerator/builtins/success-sizeof/source.spice +++ b/test/test-files/irgenerator/builtins/success-sizeof/source.spice @@ -16,5 +16,5 @@ f main() { printf("Size of int[]: %d\n", sizeof([1, 2, 3, 4, 5, 6, 7])); int intVariable = 123; printf("Size of int*: %d\n", sizeof(&intVariable)); - printf("Size of struct instance: %d\n", sizeof(type Struct)); + printf("Size of struct type: %d\n", sizeof(type Struct)); } \ No newline at end of file diff --git a/test/test-files/irgenerator/debug-info/success-dgb-info-complex/cout.out b/test/test-files/irgenerator/debug-info/success-dbg-info-complex/cout.out similarity index 100% rename from test/test-files/irgenerator/debug-info/success-dgb-info-complex/cout.out rename to test/test-files/irgenerator/debug-info/success-dbg-info-complex/cout.out diff --git a/test/test-files/irgenerator/debug-info/success-dgb-info-complex/ir-code.ll b/test/test-files/irgenerator/debug-info/success-dbg-info-complex/ir-code.ll similarity index 98% rename from test/test-files/irgenerator/debug-info/success-dgb-info-complex/ir-code.ll rename to test/test-files/irgenerator/debug-info/success-dbg-info-complex/ir-code.ll index 103131a8e..e7b7017ba 100644 --- a/test/test-files/irgenerator/debug-info/success-dgb-info-complex/ir-code.ll +++ b/test/test-files/irgenerator/debug-info/success-dbg-info-complex/ir-code.ll @@ -84,7 +84,6 @@ assert.then.L12: ; preds = %2 assert.exit.L12: ; preds = %2 %18 = call %struct.VectorIterator @"_Z7iterateR22std/data/vector.VectorIiE"(ptr %vi), !dbg !44 - store %struct.VectorIterator %18, ptr %it, align 8, !dbg !44 call void @llvm.dbg.declare(metadata ptr %it, metadata !45, metadata !DIExpression()), !dbg !52 store %struct.VectorIterator %18, ptr %it, align 8, !dbg !44 %19 = call i1 @_ZN14VectorIteratorIiE7isValidEv(ptr %it), !dbg !53 @@ -141,7 +140,6 @@ assert.then.L21: ; preds = %assert.exit.L20 assert.exit.L21: ; preds = %assert.exit.L20 call void @_ZN14VectorIteratorIiE4nextEv(ptr %it), !dbg !62 %35 = call %struct.Pair @_ZN14VectorIteratorIiE6getIdxEv(ptr %it), !dbg !63 - store %struct.Pair %35, ptr %pair, align 8, !dbg !63 call void @llvm.dbg.declare(metadata ptr %pair, metadata !64, metadata !DIExpression()), !dbg !70 store %struct.Pair %35, ptr %pair, align 8, !dbg !63 %36 = call ptr @_ZN4PairImRiE8getFirstEv(ptr %pair), !dbg !71 @@ -499,9 +497,9 @@ attributes #3 = { cold noreturn nounwind } !0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) !1 = distinct !DIGlobalVariable(name: "printf.str.0", linkageName: "printf.str.0", scope: !2, file: !5, line: 70, type: !6, isLocal: true, isDefinition: true) !2 = distinct !DICompileUnit(language: DW_LANG_C17, file: !3, producer: "spice version dev (https://github.com/spicelang/spice)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, globals: !4, splitDebugInlining: false, nameTableKind: None) -!3 = !DIFile(filename: "C:\\Users\\Marc\\Documents\\JustForFunGitHubClonesFast\\spice\\cmake-build-debug\\test\\test-files\\irgenerator\\debug-info\\success-dgb-info-complex\\source.spice", directory: ".\\test-files\\irgenerator\\debug-info\\success-dgb-info-complex") +!3 = !DIFile(filename: "C:\\Users\\Marc\\Documents\\JustForFunGitHubClonesFast\\spice\\cmake-build-debug\\test\\test-files\\irgenerator\\debug-info\\success-dbg-info-complex\\source.spice", directory: ".\\test-files\\irgenerator\\debug-info\\success-dbg-info-complex") !4 = !{!0} -!5 = !DIFile(filename: "source.spice", directory: ".\\test-files\\irgenerator\\debug-info\\success-dgb-info-complex") +!5 = !DIFile(filename: "source.spice", directory: ".\\test-files\\irgenerator\\debug-info\\success-dbg-info-complex") !6 = !DIStringType(name: "printf.str.0", size: 184) !7 = !{i32 7, !"Dwarf Version", i32 4} !8 = !{i32 2, !"Debug Info Version", i32 3} @@ -524,14 +522,14 @@ attributes #3 = { cold noreturn nounwind } !25 = !DILocalVariable(name: "argc", arg: 1, scope: !15, file: !5, line: 6, type: !18) !26 = !DILocalVariable(name: "argv", arg: 2, scope: !15, file: !5, line: 6, type: !19) !27 = !DILocalVariable(name: "vi", scope: !15, file: !5, line: 8, type: !28) -!28 = !DICompositeType(tag: DW_TAG_structure_type, name: "Vector", scope: !29, file: !29, line: 27, size: 192, align: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !30, identifier: "struct.Vector") +!28 = !DICompositeType(tag: DW_TAG_structure_type, name: "Vector", scope: !29, file: !29, line: 22, size: 192, align: 8, flags: DIFlagTypePassByValue | DIFlagNonTrivial, elements: !30, identifier: "struct.Vector") !29 = !DIFile(filename: "vector.spice", directory: "C:\\Users\\Marc\\Documents\\JustForFunGitHubClonesFast\\spice\\std\\data") !30 = !{!31, !33, !35} -!31 = !DIDerivedType(tag: DW_TAG_member, name: "contents", scope: !28, file: !29, line: 28, baseType: !32, size: 64) +!31 = !DIDerivedType(tag: DW_TAG_member, name: "contents", scope: !28, file: !29, line: 23, baseType: !32, size: 64) !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !18, size: 64) -!33 = !DIDerivedType(tag: DW_TAG_member, name: "capacity", scope: !28, file: !29, line: 29, baseType: !34, size: 64, offset: 64) +!33 = !DIDerivedType(tag: DW_TAG_member, name: "capacity", scope: !28, file: !29, line: 24, baseType: !34, size: 64, offset: 64) !34 = !DIBasicType(name: "unsigned long", size: 64, encoding: DW_ATE_unsigned) -!35 = !DIDerivedType(tag: DW_TAG_member, name: "size", scope: !28, file: !29, line: 30, baseType: !34, size: 64, offset: 128) +!35 = !DIDerivedType(tag: DW_TAG_member, name: "size", scope: !28, file: !29, line: 25, baseType: !34, size: 64, offset: 128) !36 = !DILocation(line: 8, column: 5, scope: !15) !37 = !DILocation(line: 8, column: 22, scope: !15) !38 = !DILocation(line: 9, column: 17, scope: !15) diff --git a/test/test-files/irgenerator/debug-info/success-dgb-info-complex/source.spice b/test/test-files/irgenerator/debug-info/success-dbg-info-complex/source.spice similarity index 100% rename from test/test-files/irgenerator/debug-info/success-dgb-info-complex/source.spice rename to test/test-files/irgenerator/debug-info/success-dbg-info-complex/source.spice diff --git a/test/test-files/irgenerator/debug-info/success-dgb-info-complex/with-debug-info b/test/test-files/irgenerator/debug-info/success-dbg-info-complex/with-debug-info similarity index 100% rename from test/test-files/irgenerator/debug-info/success-dgb-info-complex/with-debug-info rename to test/test-files/irgenerator/debug-info/success-dbg-info-complex/with-debug-info 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 d80d9bf8d..dc8831c5d 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 @@ -49,7 +49,6 @@ define dso_local i32 @main() #1 !dbg !53 { call void @llvm.dbg.declare(metadata ptr %test, metadata !58, metadata !DIExpression()), !dbg !59 store i32 987654, ptr %test, align 4, !dbg !60 %1 = call %struct.TestStruct @_Z3fctRi(ptr %test), !dbg !61 - store %struct.TestStruct %1, ptr %res, align 8, !dbg !61 call void @llvm.dbg.declare(metadata ptr %res, metadata !62, metadata !DIExpression()), !dbg !63 store %struct.TestStruct %1, ptr %res, align 8, !dbg !61 %lng_addr = getelementptr inbounds %struct.TestStruct, ptr %res, i32 0, i32 0, !dbg !64 diff --git a/test/test-files/irgenerator/ext-decl/success-ext-decl/source.spice b/test/test-files/irgenerator/ext-decl/success-ext-decl/source.spice index a06847eb1..b5206931d 100644 --- a/test/test-files/irgenerator/ext-decl/success-ext-decl/source.spice +++ b/test/test-files/irgenerator/ext-decl/success-ext-decl/source.spice @@ -1,8 +1,8 @@ -ext f malloc(long); -ext p free(byte*); +ext f malloc(long); +ext p free(heap byte*); f main() { - byte* address = malloc(1l); + heap byte* address = malloc(1l); *address = (byte) 12; free(address); } \ No newline at end of file diff --git a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code-O2.ll b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code-O2.ll index 72fb48a65..4cdca3ead 100644 --- a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code-O2.ll +++ b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code-O2.ll @@ -16,12 +16,12 @@ define dso_local i32 @main() local_unnamed_addr #0 { %1 = alloca %struct.NumberIterator.0, align 8 %2 = tail call %struct.NumberIterator @_Z5rangess(i16 3, i16 8) #2 %.fca.0.extract2 = extractvalue %struct.NumberIterator %2, 0 + store i16 %.fca.0.extract2, ptr %shortIterator, align 8 %.fca.1.extract4 = extractvalue %struct.NumberIterator %2, 1 %.fca.1.gep5 = getelementptr inbounds %struct.NumberIterator, ptr %shortIterator, i64 0, i32 1 + store i16 %.fca.1.extract4, ptr %.fca.1.gep5, align 2 %.fca.2.extract6 = extractvalue %struct.NumberIterator %2, 2 %.fca.2.gep7 = getelementptr inbounds %struct.NumberIterator, ptr %shortIterator, i64 0, i32 2 - store i16 %.fca.0.extract2, ptr %shortIterator, align 8 - store i16 %.fca.1.extract4, ptr %.fca.1.gep5, align 2 store i16 %.fca.2.extract6, ptr %.fca.2.gep7, align 4 %3 = call i1 @_ZN14NumberIteratorIsE7isValidEv(ptr nonnull %shortIterator) #2 br i1 %3, label %foreach.body.L5.lr.ph, label %foreach.exit.L5 diff --git a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code.ll b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code.ll index ffba58625..01fd34345 100644 --- a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code.ll +++ b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-break/ir-code.ll @@ -21,7 +21,6 @@ define dso_local i32 @main() #0 { store i32 0, ptr %result, align 4 %3 = call %struct.NumberIterator @_Z5rangess(i16 3, i16 8) store %struct.NumberIterator %3, ptr %shortIterator, align 2 - store %struct.NumberIterator %3, ptr %shortIterator, align 2 br label %foreach.head.L5 foreach.head.L5: ; preds = %foreach.tail.L5, %0 diff --git a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code-O2.ll b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code-O2.ll index e633b143e..c0e1e3383 100644 --- a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code-O2.ll +++ b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code-O2.ll @@ -16,12 +16,12 @@ define dso_local i32 @main() local_unnamed_addr #0 { %1 = alloca %struct.NumberIterator.0, align 8 %2 = tail call %struct.NumberIterator @_Z5rangess(i16 3, i16 8) #2 %.fca.0.extract2 = extractvalue %struct.NumberIterator %2, 0 + store i16 %.fca.0.extract2, ptr %shortIterator, align 8 %.fca.1.extract4 = extractvalue %struct.NumberIterator %2, 1 %.fca.1.gep5 = getelementptr inbounds %struct.NumberIterator, ptr %shortIterator, i64 0, i32 1 + store i16 %.fca.1.extract4, ptr %.fca.1.gep5, align 2 %.fca.2.extract6 = extractvalue %struct.NumberIterator %2, 2 %.fca.2.gep7 = getelementptr inbounds %struct.NumberIterator, ptr %shortIterator, i64 0, i32 2 - store i16 %.fca.0.extract2, ptr %shortIterator, align 8 - store i16 %.fca.1.extract4, ptr %.fca.1.gep5, align 2 store i16 %.fca.2.extract6, ptr %.fca.2.gep7, align 4 %3 = call i1 @_ZN14NumberIteratorIsE7isValidEv(ptr nonnull %shortIterator) #2 br i1 %3, label %foreach.body.L5.lr.ph, label %foreach.exit.L5 diff --git a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code.ll b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code.ll index 369914516..2e1da347c 100644 --- a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code.ll +++ b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-continue/ir-code.ll @@ -21,7 +21,6 @@ define dso_local i32 @main() #0 { store i32 0, ptr %result, align 4 %3 = call %struct.NumberIterator @_Z5rangess(i16 3, i16 8) store %struct.NumberIterator %3, ptr %shortIterator, align 2 - store %struct.NumberIterator %3, ptr %shortIterator, align 2 br label %foreach.head.L5 foreach.head.L5: ; preds = %foreach.tail.L5, %0 diff --git a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-indexed/ir-code.ll b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-indexed/ir-code.ll index 84956d608..ddd88099f 100644 --- a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-indexed/ir-code.ll +++ b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-indexed/ir-code.ll @@ -12,52 +12,47 @@ target triple = "x86_64-w64-windows-gnu" ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { %result = alloca i32, align 4 - %1 = alloca [7 x i32], align 4 %intArray = alloca [7 x i32], align 4 - %2 = alloca %struct.ArrayIterator, align 8 + %1 = alloca %struct.ArrayIterator, align 8 %index = alloca i64, align 8 %item = alloca i32, align 4 %pair_addr = alloca %struct.Pair, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 28, i1 false) store [7 x i32] [i32 1, i32 5, i32 4, i32 0, i32 12, i32 12345, i32 9], ptr %intArray, align 4 - %3 = getelementptr inbounds [7 x i32], ptr %intArray, i32 0, i32 0 - %4 = call %struct.ArrayIterator @_Z7iteratePim(ptr %3, i64 7) - store %struct.ArrayIterator %4, ptr %2, align 8 + %2 = getelementptr inbounds [7 x i32], ptr %intArray, i32 0, i32 0 + %3 = call %struct.ArrayIterator @_Z7iteratePim(ptr %2, i64 7) + store %struct.ArrayIterator %3, ptr %1, align 8 store i64 0, ptr %index, align 8 br label %foreach.head.L5 foreach.head.L5: ; preds = %foreach.tail.L5, %0 - %5 = call i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr %2) - br i1 %5, label %foreach.body.L5, label %foreach.exit.L5 + %4 = call i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr %1) + br i1 %4, label %foreach.body.L5, label %foreach.exit.L5 foreach.body.L5: ; preds = %foreach.head.L5 - %pair = call %struct.Pair @_ZN13ArrayIteratorIiE6getIdxEv(ptr %2) + %pair = call %struct.Pair @_ZN13ArrayIteratorIiE6getIdxEv(ptr %1) store %struct.Pair %pair, ptr %pair_addr, align 8 %idx_addr = getelementptr inbounds %struct.Pair, ptr %pair_addr, i32 0, i32 0 - %6 = load i64, ptr %idx_addr, align 8 - store i64 %6, ptr %index, align 8 + %5 = load i64, ptr %idx_addr, align 8 + store i64 %5, ptr %index, align 8 %item_addr = getelementptr inbounds %struct.Pair, ptr %pair_addr, i32 0, i32 1 - %7 = load ptr, ptr %item_addr, align 8 - %8 = load i32, ptr %7, align 4 - store i32 %8, ptr %item, align 4 - %9 = load i64, ptr %index, align 8 - %10 = load i32, ptr %item, align 4 - %11 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i64 %9, i32 %10) + %6 = load ptr, ptr %item_addr, align 8 + %7 = load i32, ptr %6, align 4 + store i32 %7, ptr %item, align 4 + %8 = load i64, ptr %index, align 8 + %9 = load i32, ptr %item, align 4 + %10 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i64 %8, i32 %9) br label %foreach.tail.L5 foreach.tail.L5: ; preds = %foreach.body.L5 - call void @_ZN13ArrayIteratorIiE4nextEv(ptr %2) + call void @_ZN13ArrayIteratorIiE4nextEv(ptr %1) br label %foreach.head.L5 foreach.exit.L5: ; preds = %foreach.head.L5 - %12 = load i32, ptr %result, align 4 - ret i32 %12 + %11 = load i32, ptr %result, align 4 + ret i32 %11 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - declare %struct.ArrayIterator @_Z7iteratePim(ptr, i64) declare i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr) @@ -65,10 +60,9 @@ declare i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr) declare %struct.Pair @_ZN13ArrayIteratorIiE6getIdxEv(ptr) ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 declare void @_ZN13ArrayIteratorIiE4nextEv(ptr) attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-normal/ir-code.ll b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-normal/ir-code.ll index 3e80d6a63..1d1600157 100644 --- a/test/test-files/irgenerator/foreach-loops/success-foreach-loop-normal/ir-code.ll +++ b/test/test-files/irgenerator/foreach-loops/success-foreach-loop-normal/ir-code.ll @@ -11,42 +11,37 @@ target triple = "x86_64-w64-windows-gnu" ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { %result = alloca i32, align 4 - %1 = alloca [7 x i32], align 4 %intArray = alloca [7 x i32], align 4 - %2 = alloca %struct.ArrayIterator, align 8 + %1 = alloca %struct.ArrayIterator, align 8 %item = alloca i32, align 4 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 28, i1 false) store [7 x i32] [i32 1, i32 5, i32 4, i32 0, i32 12, i32 12345, i32 9], ptr %intArray, align 4 - %3 = getelementptr inbounds [7 x i32], ptr %intArray, i32 0, i32 0 - %4 = call %struct.ArrayIterator @_Z7iteratePim(ptr %3, i64 7) - store %struct.ArrayIterator %4, ptr %2, align 8 + %2 = getelementptr inbounds [7 x i32], ptr %intArray, i32 0, i32 0 + %3 = call %struct.ArrayIterator @_Z7iteratePim(ptr %2, i64 7) + store %struct.ArrayIterator %3, ptr %1, align 8 br label %foreach.head.L5 foreach.head.L5: ; preds = %foreach.tail.L5, %0 - %5 = call i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr %2) - br i1 %5, label %foreach.body.L5, label %foreach.exit.L5 + %4 = call i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr %1) + br i1 %4, label %foreach.body.L5, label %foreach.exit.L5 foreach.body.L5: ; preds = %foreach.head.L5 - %6 = call ptr @_ZN13ArrayIteratorIiE3getEv(ptr %2) - %7 = load i32, ptr %6, align 4 - store i32 %7, ptr %item, align 4 - %8 = load i32, ptr %item, align 4 - %9 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %8) + %5 = call ptr @_ZN13ArrayIteratorIiE3getEv(ptr %1) + %6 = load i32, ptr %5, align 4 + store i32 %6, ptr %item, align 4 + %7 = load i32, ptr %item, align 4 + %8 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, i32 %7) br label %foreach.tail.L5 foreach.tail.L5: ; preds = %foreach.body.L5 - call void @_ZN13ArrayIteratorIiE4nextEv(ptr %2) + call void @_ZN13ArrayIteratorIiE4nextEv(ptr %1) br label %foreach.head.L5 foreach.exit.L5: ; preds = %foreach.head.L5 - %10 = load i32, ptr %result, align 4 - ret i32 %10 + %9 = load i32, ptr %result, align 4 + ret i32 %9 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - declare %struct.ArrayIterator @_Z7iteratePim(ptr, i64) declare i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr) @@ -54,10 +49,9 @@ declare i1 @_ZN13ArrayIteratorIiE7isValidEv(ptr) declare ptr @_ZN13ArrayIteratorIiE3getEv(ptr) ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 declare void @_ZN13ArrayIteratorIiE4nextEv(ptr) attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/generics/success-external-generic-functions/ir-code.ll b/test/test-files/irgenerator/generics/success-external-generic-functions/ir-code.ll index 44afbff69..7f72465c9 100644 --- a/test/test-files/irgenerator/generics/success-external-generic-functions/ir-code.ll +++ b/test/test-files/irgenerator/generics/success-external-generic-functions/ir-code.ll @@ -11,42 +11,37 @@ target triple = "x86_64-w64-windows-gnu" ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { %result = alloca i32, align 4 - %1 = alloca [2 x ptr], align 8 %test = alloca i32, align 4 %i = alloca i32, align 4 %iPtr = alloca ptr, align 8 store i32 0, ptr %result, align 4 call void @_Z11printFormatd(double 1.123000e+00) call void @_Z11printFormati(i32 543) - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 16, i1 false) call void @_Z11printFormatA2Pc([2 x ptr] [ptr @anon.string.0, ptr @anon.string.1]) store i32 1234, ptr %test, align 4 call void @_Z11printFormatPi(ptr %test) store i32 12, ptr %i, align 4 - %2 = call ptr @_Z7getAIncPi(ptr %i) - store ptr %2, ptr %iPtr, align 8 - %3 = load ptr, ptr %iPtr, align 8 - %4 = load i32, ptr %3, align 4 - %5 = icmp eq i32 %4, 13 - br i1 %5, label %assert.exit.L12, label %assert.then.L12, !prof !0 + %1 = call ptr @_Z7getAIncPi(ptr %i) + store ptr %1, ptr %iPtr, align 8 + %2 = load ptr, ptr %iPtr, align 8 + %3 = load i32, ptr %2, align 4 + %4 = icmp eq i32 %3, 13 + br i1 %4, label %assert.exit.L12, label %assert.then.L12, !prof !0 assert.then.L12: ; preds = %0 - %6 = call i32 (ptr, ...) @printf(ptr @anon.string.2) + %5 = call i32 (ptr, ...) @printf(ptr @anon.string.2) call void @exit(i32 1) unreachable assert.exit.L12: ; preds = %0 - %7 = load i32, ptr %result, align 4 - ret i32 %7 + %6 = load i32, ptr %result, align 4 + ret i32 %6 } declare void @_Z11printFormatd(double) declare void @_Z11printFormati(i32) -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - declare void @_Z11printFormatA2Pc([2 x ptr]) declare void @_Z11printFormatPi(ptr) @@ -54,14 +49,13 @@ declare void @_Z11printFormatPi(ptr) declare ptr @_Z7getAIncPi(ptr) ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 ; Function Attrs: cold noreturn nounwind -declare void @exit(i32) #3 +declare void @exit(i32) #2 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } -attributes #3 = { cold noreturn nounwind } +attributes #1 = { nofree nounwind } +attributes #2 = { cold noreturn nounwind } !0 = !{!"branch_weights", i32 2000, i32 1} diff --git a/test/test-files/irgenerator/generics/success-generic-functions2/ir-code.ll b/test/test-files/irgenerator/generics/success-generic-functions2/ir-code.ll index 09090e9b8..ea6e30ada 100644 --- a/test/test-files/irgenerator/generics/success-generic-functions2/ir-code.ll +++ b/test/test-files/irgenerator/generics/success-generic-functions2/ir-code.ll @@ -124,45 +124,37 @@ declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #0 ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #1 { %result = alloca i32, align 4 - %1 = alloca [7 x i16], align 2 %numberList1 = alloca [7 x i16], align 2 %result1 = alloca i32, align 4 - %2 = alloca [4 x i64], align 8 %numberList2 = alloca [4 x i64], align 8 %result2 = alloca i32, align 4 - %3 = alloca [2 x i32], align 4 + %1 = alloca [2 x i32], align 4 %resultList = alloca [2 x i32], align 4 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 14, i1 false) store [7 x i16] [i16 1, i16 2, i16 3, i16 4, i16 5, i16 6, i16 7], ptr %numberList1, align 2 - %4 = getelementptr inbounds [7 x i16], ptr %numberList1, i32 0, i32 0 - %5 = call i32 @_Z10sumNumbersPsl(ptr %4, i64 7) - store i32 %5, ptr %result1, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %2, ptr @anon.array.1, i64 32, i1 false) + %2 = getelementptr inbounds [7 x i16], ptr %numberList1, i32 0, i32 0 + %3 = call i32 @_Z10sumNumbersPsl(ptr %2, i64 7) + store i32 %3, ptr %result1, align 4 store [4 x i64] [i64 10, i64 12, i64 14, i64 16], ptr %numberList2, align 8 - %6 = getelementptr inbounds [4 x i64], ptr %numberList2, i32 0, i32 0 - %7 = call i32 @_Z10sumNumbersPll(ptr %6, i64 4) - store i32 %7, ptr %result2, align 4 - %8 = getelementptr inbounds [2 x i32], ptr %3, i32 0 - %9 = load i32, ptr %result1, align 4 - store i32 %9, ptr %8, align 4 - %10 = load i32, ptr %result2, align 4 - %11 = getelementptr inbounds i32, ptr %8, i32 1 - store i32 %10, ptr %11, align 4 - %12 = load [2 x i32], ptr %3, align 4 - store [2 x i32] %12, ptr %resultList, align 4 - %13 = load [2 x i32], ptr %resultList, align 4 - call void @_Z9printDatalA2i(i64 2, [2 x i32] %13) - %14 = load i32, ptr %result1, align 4 - %15 = load i32, ptr %result2, align 4 - %16 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %14, i32 %15) - %17 = load i32, ptr %result, align 4 - ret i32 %17 + %4 = getelementptr inbounds [4 x i64], ptr %numberList2, i32 0, i32 0 + %5 = call i32 @_Z10sumNumbersPll(ptr %4, i64 4) + store i32 %5, ptr %result2, align 4 + %6 = getelementptr inbounds [2 x i32], ptr %1, i32 0 + %7 = load i32, ptr %result1, align 4 + store i32 %7, ptr %6, align 4 + %8 = load i32, ptr %result2, align 4 + %9 = getelementptr inbounds i32, ptr %6, i32 1 + store i32 %8, ptr %9, align 4 + %10 = load [2 x i32], ptr %1, align 4 + store [2 x i32] %10, ptr %resultList, align 4 + %11 = load [2 x i32], ptr %resultList, align 4 + call void @_Z9printDatalA2i(i64 2, [2 x i32] %11) + %12 = load i32, ptr %result1, align 4 + %13 = load i32, ptr %result2, align 4 + %14 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %12, i32 %13) + %15 = load i32, ptr %result, align 4 + ret i32 %15 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #2 - attributes #0 = { nofree nounwind } attributes #1 = { noinline nounwind optnone uwtable } -attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/test/test-files/irgenerator/interfaces/success-generic-interfaces/ir-code.ll b/test/test-files/irgenerator/interfaces/success-generic-interfaces/ir-code.ll index 6ea73ad46..bdcec767c 100644 --- a/test/test-files/irgenerator/interfaces/success-generic-interfaces/ir-code.ll +++ b/test/test-files/irgenerator/interfaces/success-generic-interfaces/ir-code.ll @@ -7,7 +7,6 @@ target triple = "x86_64-w64-windows-gnu" @anon.string.0 = private unnamed_addr constant [5 x i8] c"Mike\00", align 1 @anon.string.1 = private unnamed_addr constant [7 x i8] c"Miller\00", align 1 -@anon.struct.0 = private unnamed_addr constant %struct.Person { ptr @anon.string.0, ptr @anon.string.1, i32 43 } @printf.str.0 = private unnamed_addr constant [3 x i8] c"%d\00", align 1 define private i32 @_ZN6Person7compareERKlRKl(ptr noundef nonnull %0, ptr %1, ptr %2) { @@ -51,7 +50,6 @@ define dso_local i32 @main() #0 { %2 = alloca i64, align 8 %isEqual = alloca i1, align 1 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %mike, ptr @anon.struct.0, i64 24, i1 false) store %struct.Person { ptr @anon.string.0, ptr @anon.string.1, i32 43 }, ptr %mike, align 8 store i64 22, ptr %1, align 8 store i64 22, ptr %2, align 8 @@ -65,12 +63,8 @@ define dso_local i32 @main() #0 { ret i32 %8 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/lambdas/success-expression-lambda/ir-code.ll b/test/test-files/irgenerator/lambdas/success-expression-lambda/ir-code.ll index 78c29a128..197532696 100644 --- a/test/test-files/irgenerator/lambdas/success-expression-lambda/ir-code.ll +++ b/test/test-files/irgenerator/lambdas/success-expression-lambda/ir-code.ll @@ -107,24 +107,19 @@ for.exit.L8: ; preds = %for.head.L8 ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { %result = alloca i32, align 4 - %1 = alloca [10 x i32], align 4 %array = alloca [10 x i32], align 4 %fat.ptr = alloca { ptr, ptr }, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 40, i1 false) store [10 x i32] [i32 10, i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2, i32 1], ptr %array, align 4 - %2 = getelementptr inbounds { ptr, ptr }, ptr %fat.ptr, i32 0, i32 0 - store ptr @_Z15lambda.L19C17.0ii, ptr %2, align 8 - %3 = load { ptr, ptr }, ptr %fat.ptr, align 8 - call void @_Z4sortRA10iPFbiiE(ptr %array, { ptr, ptr } %3) + %1 = getelementptr inbounds { ptr, ptr }, ptr %fat.ptr, i32 0, i32 0 + store ptr @_Z15lambda.L19C17.0ii, ptr %1, align 8 + %2 = load { ptr, ptr }, ptr %fat.ptr, align 8 + call void @_Z4sortRA10iPFbiiE(ptr %array, { ptr, ptr } %2) call void @_Z10printArrayRA10i(ptr %array) - %4 = load i32, ptr %result, align 4 - ret i32 %4 + %3 = load i32, ptr %result, align 4 + ret i32 %3 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - define private i1 @_Z15lambda.L19C17.0ii(i32 %0, i32 %1) { %a = alloca i32, align 4 %b = alloca i32, align 4 @@ -169,8 +164,7 @@ for.exit.L24: ; preds = %for.head.L24 } ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/methods/success-method-call-on-return-value/ir-code.ll b/test/test-files/irgenerator/methods/success-method-call-on-return-value/ir-code.ll index 0326e3688..ff1ce9b63 100644 --- a/test/test-files/irgenerator/methods/success-method-call-on-return-value/ir-code.ll +++ b/test/test-files/irgenerator/methods/success-method-call-on-return-value/ir-code.ll @@ -3,12 +3,11 @@ 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.Letter = type { ptr, %struct.Stamp } %struct.Stamp = type { double, i1 } +%struct.Letter = type { ptr, %struct.Stamp } @printf.str.0 = private unnamed_addr constant [21 x i8] c"Value: %f, glued: %d\00", align 1 @anon.string.0 = private unnamed_addr constant [17 x i8] c"This is a letter\00", align 1 -@anon.struct.0 = private unnamed_addr constant %struct.Letter { ptr @anon.string.0, %struct.Stamp { double 3.400000e+00, i1 true } } @printf.str.1 = private unnamed_addr constant [17 x i8] c"Stamp glued: %d\0A\00", align 1 define private void @_ZN5Stamp5printEv(ptr noundef nonnull %0) { @@ -44,7 +43,6 @@ define dso_local i32 @main() #1 { %letter = alloca %struct.Letter, align 8 %stamp = alloca %struct.Stamp, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %letter, ptr @anon.struct.0, i64 24, i1 false) store %struct.Letter { ptr @anon.string.0, %struct.Stamp { double 3.400000e+00, i1 true } }, ptr %letter, align 8 %stamp_addr = getelementptr inbounds %struct.Letter, ptr %letter, i32 0, i32 1 %glued_addr = getelementptr inbounds %struct.Stamp, ptr %stamp_addr, i32 0, i32 1 @@ -53,15 +51,10 @@ define dso_local i32 @main() #1 { %3 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, i32 %2) %4 = call %struct.Stamp @_ZN6Letter8getStampEv(ptr %letter) store %struct.Stamp %4, ptr %stamp, align 8 - store %struct.Stamp %4, ptr %stamp, align 8 call void @_ZN5Stamp5printEv(ptr %stamp) %5 = load i32, ptr %result, align 4 ret i32 %5 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #2 - attributes #0 = { nofree nounwind } attributes #1 = { noinline nounwind optnone uwtable } -attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/test/test-files/irgenerator/methods/success-method-down-up-call/ir-code.ll b/test/test-files/irgenerator/methods/success-method-down-up-call/ir-code.ll index 9f134ef55..79d8bbfc2 100644 --- a/test/test-files/irgenerator/methods/success-method-down-up-call/ir-code.ll +++ b/test/test-files/irgenerator/methods/success-method-down-up-call/ir-code.ll @@ -5,7 +5,6 @@ target triple = "x86_64-w64-windows-gnu" %struct.TestStruct = type { i8, i32 } -@anon.struct.0 = private unnamed_addr constant %struct.TestStruct { i8 97, i32 1 } @printf.str.0 = private unnamed_addr constant [10 x i8] c"Test: %d\0A\00", align 1 ; Function Attrs: noinline nounwind optnone uwtable @@ -13,16 +12,12 @@ define dso_local i32 @main() #0 { %result = alloca i32, align 4 %s = alloca %struct.TestStruct, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %s, ptr @anon.struct.0, i64 8, i1 false) store %struct.TestStruct { i8 97, i32 1 }, ptr %s, align 4 call void @_ZN10TestStructIhE9printTestEv(ptr %s) %1 = load i32, ptr %result, align 4 ret i32 %1 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - define private void @_ZN10TestStructIhE9printTestEv(ptr noundef nonnull %0) { %this = alloca ptr, align 8 store ptr %0, ptr %this, align 8 @@ -33,7 +28,7 @@ define private void @_ZN10TestStructIhE9printTestEv(ptr noundef nonnull %0) { } ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 define private i32 @_ZN10TestStructIhE7getTestEv(ptr noundef nonnull %0) { %result = alloca i32, align 4 @@ -63,5 +58,4 @@ if.exit.L18: ; preds = %if.then.L18, %1 } attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/methods/success-methods/ir-code.ll b/test/test-files/irgenerator/methods/success-methods/ir-code.ll index b72a73046..5cced4e92 100644 --- a/test/test-files/irgenerator/methods/success-methods/ir-code.ll +++ b/test/test-files/irgenerator/methods/success-methods/ir-code.ll @@ -6,7 +6,6 @@ target triple = "x86_64-w64-windows-gnu" %struct.Letter = type { ptr } @anon.string.0 = private unnamed_addr constant [11 x i8] c"No content\00", align 1 -@anon.struct.0 = private unnamed_addr constant %struct.Letter { ptr @anon.string.0 } @anon.string.1 = private unnamed_addr constant [13 x i8] c"Hello World!\00", align 1 @printf.str.0 = private unnamed_addr constant [13 x i8] c"Content: %s\0A\00", align 1 @@ -37,7 +36,6 @@ define dso_local i32 @main() #0 { %result = alloca i32, align 4 %letter = alloca %struct.Letter, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %letter, ptr @anon.struct.0, i64 8, i1 false) store %struct.Letter { ptr @anon.string.0 }, ptr %letter, align 8 call void @_ZN6Letter10setContentEPc(ptr %letter, ptr @anon.string.1) %1 = call ptr @_ZN6Letter10getContentEv(ptr %letter) @@ -46,12 +44,8 @@ define dso_local i32 @main() #0 { ret i32 %3 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/operators/success-operator-overloading-binary/ir-code.ll b/test/test-files/irgenerator/operators/success-operator-overloading-binary/ir-code.ll index aec9b4464..59d099c81 100644 --- a/test/test-files/irgenerator/operators/success-operator-overloading-binary/ir-code.ll +++ b/test/test-files/irgenerator/operators/success-operator-overloading-binary/ir-code.ll @@ -66,7 +66,6 @@ define dso_local i32 @main() #0 { %6 = load %struct.Counter, ptr %counter2, align 8 %7 = call %struct.Counter @_Z7op.plus7Counter7Counter(%struct.Counter %5, %struct.Counter %6) store %struct.Counter %7, ptr %counter3, align 8 - store %struct.Counter %7, ptr %counter3, align 8 %8 = call i64 @_ZN7Counter8getValueEv(ptr %counter3) %9 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.2, i64 %8) %10 = load i32, ptr %result, align 4 diff --git a/test/test-files/irgenerator/operators/success-operator-overloading-unary/ir-code.ll b/test/test-files/irgenerator/operators/success-operator-overloading-unary/ir-code.ll index b93f0f0da..2cc3f7422 100644 --- a/test/test-files/irgenerator/operators/success-operator-overloading-unary/ir-code.ll +++ b/test/test-files/irgenerator/operators/success-operator-overloading-unary/ir-code.ll @@ -5,7 +5,6 @@ target triple = "x86_64-w64-windows-gnu" %struct.TestStruct = type { i64 } -@anon.struct.0 = private unnamed_addr constant %struct.TestStruct { i64 123 } @anon.string.0 = private unnamed_addr constant [70 x i8] c"Assertion failed: Condition 'output.test == 125l' evaluated to false.\00", align 1 @anon.string.1 = private unnamed_addr constant [66 x i8] c"Assertion failed: Condition 'ts.test == 125l' evaluated to false.\00", align 1 @printf.str.0 = private unnamed_addr constant [23 x i8] c"All assertions passed!\00", align 1 @@ -29,7 +28,6 @@ define dso_local i32 @main() #0 { %ts = alloca %struct.TestStruct, align 8 %output = alloca ptr, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %ts, ptr @anon.struct.0, i64 8, i1 false) store %struct.TestStruct { i64 123 }, ptr %ts, align 8 %1 = load %struct.TestStruct, ptr %ts, align 8 %2 = call ptr @_Z16op.plusplus.postR10TestStruct(ptr %ts) @@ -66,18 +64,14 @@ assert.exit.L15: ; preds = %assert.exit.L14 ret i32 %13 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 ; Function Attrs: cold noreturn nounwind -declare void @exit(i32) #3 +declare void @exit(i32) #2 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } -attributes #3 = { cold noreturn nounwind } +attributes #1 = { nofree nounwind } +attributes #2 = { cold noreturn nounwind } !0 = !{!"branch_weights", i32 2000, i32 1} diff --git a/test/test-files/irgenerator/pointers/success-nested-pointers/ir-code.ll b/test/test-files/irgenerator/pointers/success-nested-pointers/ir-code.ll index 4649a1a17..fb28e6f07 100644 --- a/test/test-files/irgenerator/pointers/success-nested-pointers/ir-code.ll +++ b/test/test-files/irgenerator/pointers/success-nested-pointers/ir-code.ll @@ -45,26 +45,20 @@ declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #0 ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #1 { %result = alloca i32, align 4 - %1 = alloca [4 x i32], align 4 %intArray = alloca [4 x i32], align 4 %intArray1 = alloca ptr, align 8 %intArray2 = alloca ptr, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 16, i1 false) store [4 x i32] [i32 1, i32 2, i32 3, i32 4], ptr %intArray, align 4 - %2 = getelementptr inbounds [4 x i32], ptr %intArray, i32 0, i32 1 - %3 = load i32, ptr %2, align 4 - %4 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.4, i32 %3) + %1 = getelementptr inbounds [4 x i32], ptr %intArray, i32 0, i32 1 + %2 = load i32, ptr %1, align 4 + %3 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.4, i32 %2) store ptr %intArray, ptr %intArray1, align 8 store ptr %intArray1, ptr %intArray2, align 8 call void @_Z8testProcPPPA4i(ptr %intArray2) - %5 = load i32, ptr %result, align 4 - ret i32 %5 + %4 = load i32, ptr %result, align 4 + ret i32 %4 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #2 - attributes #0 = { nofree nounwind } attributes #1 = { noinline nounwind optnone uwtable } -attributes #2 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/test/test-files/irgenerator/structs/success-destructors1/ir-code.ll b/test/test-files/irgenerator/structs/success-destructors1/ir-code.ll index fbd72f414..3543e8ee1 100644 --- a/test/test-files/irgenerator/structs/success-destructors1/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-destructors1/ir-code.ll @@ -10,7 +10,6 @@ target triple = "x86_64-w64-windows-gnu" @anon.string.1 = private unnamed_addr constant [5 x i8] c"Test\00", align 1 @anon.string.2 = private unnamed_addr constant [72 x i8] c"Assertion failed: Condition 'this.field2 == \22Test\22' evaluated to false.\00", align 1 @anon.string.3 = private unnamed_addr constant [5 x i8] c"Test\00", align 1 -@anon.struct.0 = private unnamed_addr constant %struct.Vector { i1 true, ptr @anon.string.3 } @printf.str.1 = private unnamed_addr constant [16 x i8] c"Fields: %d, %s\0A\00", align 1 define private void @_ZN6Vector4dtorEv(ptr noundef nonnull %0) { @@ -62,7 +61,6 @@ define dso_local i32 @main() #3 { %result = alloca i32, align 4 %vec = alloca %struct.Vector, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %vec, ptr @anon.struct.0, i64 16, i1 false) store %struct.Vector { i1 true, ptr @anon.string.3 }, ptr %vec, align 8 %field1_addr = getelementptr inbounds %struct.Vector, ptr %vec, i32 0, i32 0 %1 = load i1, ptr %field1_addr, align 1 @@ -75,13 +73,9 @@ define dso_local i32 @main() #3 { ret i32 %5 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #4 - attributes #0 = { nofree nounwind } attributes #1 = { nounwind } attributes #2 = { cold noreturn nounwind } attributes #3 = { noinline nounwind optnone uwtable } -attributes #4 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } !0 = !{!"branch_weights", i32 2000, i32 1} diff --git a/test/test-files/irgenerator/structs/success-destructors2/ir-code.ll b/test/test-files/irgenerator/structs/success-destructors2/ir-code.ll index 5e806ace9..abeff3fe5 100644 --- a/test/test-files/irgenerator/structs/success-destructors2/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-destructors2/ir-code.ll @@ -6,7 +6,6 @@ target triple = "x86_64-w64-windows-gnu" %struct.Vector = type { i1, ptr } @anon.string.0 = private unnamed_addr constant [5 x i8] c"Test\00", align 1 -@anon.struct.0 = private unnamed_addr constant %struct.Vector { i1 true, ptr @anon.string.0 } @printf.str.0 = private unnamed_addr constant [16 x i8] c"Fields: %d, %s\0A\00", align 1 @printf.str.1 = private unnamed_addr constant [19 x i8] c"Destructor called!\00", align 1 @anon.string.1 = private unnamed_addr constant [70 x i8] c"Assertion failed: Condition 'this.field1 == true' evaluated to false.\00", align 1 @@ -18,7 +17,6 @@ define dso_local i32 @main() #0 { %result = alloca i32, align 4 %vec = alloca %struct.Vector, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %vec, ptr @anon.struct.0, i64 16, i1 false) store %struct.Vector { i1 true, ptr @anon.string.0 }, ptr %vec, align 8 %field1_addr = getelementptr inbounds %struct.Vector, ptr %vec, i32 0, i32 0 %1 = load i1, ptr %field1_addr, align 1 @@ -31,11 +29,8 @@ define dso_local i32 @main() #0 { ret i32 %5 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 define private void @_ZN6Vector4dtorEv(ptr noundef nonnull %0) { %this = alloca ptr, align 8 @@ -71,17 +66,16 @@ assert.exit.L14: ; preds = %assert.exit.L13 } ; Function Attrs: nounwind -declare i32 @memcmp(ptr, ptr, i64) #3 +declare i32 @memcmp(ptr, ptr, i64) #2 ; Function Attrs: cold noreturn nounwind -declare void @exit(i32) #4 +declare void @exit(i32) #3 declare i1 @_Z10isRawEqualPcPc(ptr, ptr) attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } -attributes #3 = { nounwind } -attributes #4 = { cold noreturn nounwind } +attributes #1 = { nofree nounwind } +attributes #2 = { nounwind } +attributes #3 = { cold noreturn nounwind } !0 = !{!"branch_weights", i32 2000, i32 1} diff --git a/test/test-files/irgenerator/structs/success-external-nested-struct/ir-code.ll b/test/test-files/irgenerator/structs/success-external-nested-struct/ir-code.ll index b493d1de3..bdbd2ae28 100644 --- a/test/test-files/irgenerator/structs/success-external-nested-struct/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-external-nested-struct/ir-code.ll @@ -16,7 +16,6 @@ define dso_local i32 @main() #0 { store i32 0, ptr %result, align 4 %1 = call %struct.Socket @_Z16openServerSockett(i16 8080) store %struct.Socket %1, ptr %s, align 8 - store %struct.Socket %1, ptr %s, align 8 %n = getelementptr inbounds %struct.Socket, ptr %s, i32 0, i32 2 %testString_addr = getelementptr inbounds %struct.NestedSocket, ptr %n, i32 0, i32 0 %2 = load ptr, ptr %testString_addr, align 8 diff --git a/test/test-files/irgenerator/structs/success-external-structs/ir-code.ll b/test/test-files/irgenerator/structs/success-external-structs/ir-code.ll index da9ced492..5b0aba530 100644 --- a/test/test-files/irgenerator/structs/success-external-structs/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-external-structs/ir-code.ll @@ -5,15 +5,12 @@ target triple = "x86_64-w64-windows-gnu" %struct.Vec = type { i32, i1 } -@anon.struct.0 = private unnamed_addr constant %struct.Vec { i32 11, i1 false } - ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @main() #0 { %result = alloca i32, align 4 %v = alloca %struct.Vec, align 8 %v1 = alloca %struct.Vec, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %v, ptr @anon.struct.0, i64 8, i1 false) store %struct.Vec { i32 11, i1 false }, ptr %v, align 4 call void @_ZN3Vec5printEv(ptr %v) store %struct.Vec zeroinitializer, ptr %v1, align 4 @@ -22,10 +19,6 @@ define dso_local i32 @main() #0 { ret i32 %1 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - declare void @_ZN3Vec5printEv(ptr) attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } diff --git a/test/test-files/irgenerator/structs/success-struct-field-access/ir-code.ll b/test/test-files/irgenerator/structs/success-struct-field-access/ir-code.ll index 501bc80ba..83d2b341b 100644 --- a/test/test-files/irgenerator/structs/success-struct-field-access/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-struct-field-access/ir-code.ll @@ -7,7 +7,6 @@ target triple = "x86_64-w64-windows-gnu" @anon.string.0 = private unnamed_addr constant [5 x i8] c"John\00", align 1 @anon.string.1 = private unnamed_addr constant [4 x i8] c"Doe\00", align 1 -@anon.struct.0 = private unnamed_addr constant %struct.Person { ptr @anon.string.0, ptr @anon.string.1, i32 46 } @printf.str.0 = private unnamed_addr constant [15 x i8] c"John's age: %d\00", align 1 ; Function Attrs: noinline nounwind optnone uwtable @@ -15,7 +14,6 @@ define dso_local i32 @main() #0 { %result = alloca i32, align 4 %john = alloca %struct.Person, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %john, ptr @anon.struct.0, i64 24, i1 false) store %struct.Person { ptr @anon.string.0, ptr @anon.string.1, i32 46 }, ptr %john, align 8 %age_addr = getelementptr inbounds %struct.Person, ptr %john, i32 0, i32 2 store i32 47, ptr %age_addr, align 4 @@ -26,12 +24,8 @@ define dso_local i32 @main() #0 { ret i32 %3 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/structs/success-struct-in-place/ir-code.ll b/test/test-files/irgenerator/structs/success-struct-in-place/ir-code.ll index 0ffb6a36c..bce0efc6b 100644 --- a/test/test-files/irgenerator/structs/success-struct-in-place/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-struct-in-place/ir-code.ll @@ -10,23 +10,17 @@ target triple = "x86_64-w64-windows-gnu" @1 = private unnamed_addr constant [1 x i8] zeroinitializer, align 1 @anon.string.0 = private unnamed_addr constant [10 x i8] c"Spaghetti\00", align 1 @anon.string.1 = private unnamed_addr constant [2 x i8] c"g\00", align 1 -@anon.struct.0 = private unnamed_addr constant %struct.ShoppingItem { ptr @anon.string.0, double 1.000000e+02, ptr @anon.string.1 } @anon.string.2 = private unnamed_addr constant [5 x i8] c"Rice\00", align 1 @anon.string.3 = private unnamed_addr constant [2 x i8] c"g\00", align 1 -@anon.struct.1 = private unnamed_addr constant %struct.ShoppingItem { ptr @anon.string.2, double 1.255000e+02, ptr @anon.string.3 } @anon.string.4 = private unnamed_addr constant [9 x i8] c"Doughnut\00", align 1 @anon.string.5 = private unnamed_addr constant [4 x i8] c"pcs\00", align 1 -@anon.struct.2 = private unnamed_addr constant %struct.ShoppingItem { ptr @anon.string.4, double 6.000000e+00, ptr @anon.string.5 } @anon.string.6 = private unnamed_addr constant [14 x i8] c"Shopping Cart\00", align 1 @anon.string.7 = private unnamed_addr constant [10 x i8] c"Spaghetti\00", align 1 @anon.string.8 = private unnamed_addr constant [2 x i8] c"g\00", align 1 -@anon.struct.3 = private unnamed_addr constant %struct.ShoppingItem { ptr @anon.string.7, double 1.000000e+02, ptr @anon.string.8 } @anon.string.9 = private unnamed_addr constant [5 x i8] c"Rice\00", align 1 @anon.string.10 = private unnamed_addr constant [2 x i8] c"g\00", align 1 -@anon.struct.4 = private unnamed_addr constant %struct.ShoppingItem { ptr @anon.string.9, double 1.255000e+02, ptr @anon.string.10 } @anon.string.11 = private unnamed_addr constant [9 x i8] c"Doughnut\00", align 1 @anon.string.12 = private unnamed_addr constant [4 x i8] c"pcs\00", align 1 -@anon.struct.5 = private unnamed_addr constant %struct.ShoppingItem { ptr @anon.string.11, double 6.000000e+00, ptr @anon.string.12 } @anon.array.0 = private unnamed_addr constant [3 x %struct.ShoppingItem] [%struct.ShoppingItem { ptr @anon.string.7, double 1.000000e+02, ptr @anon.string.8 }, %struct.ShoppingItem { ptr @anon.string.9, double 1.255000e+02, ptr @anon.string.10 }, %struct.ShoppingItem { ptr @anon.string.11, double 6.000000e+00, ptr @anon.string.12 }] @anon.string.13 = private unnamed_addr constant [13 x i8] c"Another Cart\00", align 1 @printf.str.0 = private unnamed_addr constant [26 x i8] c"Shopping cart item 1: %s\0A\00", align 1 @@ -38,11 +32,11 @@ define private %struct.ShoppingCart @_Z15newShoppingCartv() { %1 = alloca %struct.ShoppingCart, align 8 store [3 x %struct.ShoppingItem] [%struct.ShoppingItem { ptr @0, double 0.000000e+00, ptr @1 }, %struct.ShoppingItem { ptr @0, double 0.000000e+00, ptr @1 }, %struct.ShoppingItem { ptr @0, double 0.000000e+00, ptr @1 }], ptr %items, align 8 %2 = getelementptr inbounds [3 x %struct.ShoppingItem], ptr %items, i32 0, i32 0 - call void @llvm.memcpy.p0.p0.i64(ptr %2, ptr @anon.struct.0, i64 24, i1 false) + store %struct.ShoppingItem { ptr @anon.string.0, double 1.000000e+02, ptr @anon.string.1 }, ptr %2, align 8 %3 = getelementptr inbounds [3 x %struct.ShoppingItem], ptr %items, i32 0, i32 1 - call void @llvm.memcpy.p0.p0.i64(ptr %3, ptr @anon.struct.1, i64 24, i1 false) + store %struct.ShoppingItem { ptr @anon.string.2, double 1.255000e+02, ptr @anon.string.3 }, ptr %3, align 8 %4 = getelementptr inbounds [3 x %struct.ShoppingItem], ptr %items, i32 0, i32 2 - call void @llvm.memcpy.p0.p0.i64(ptr %4, ptr @anon.struct.2, i64 24, i1 false) + store %struct.ShoppingItem { ptr @anon.string.4, double 6.000000e+00, ptr @anon.string.5 }, ptr %4, align 8 %5 = getelementptr inbounds %struct.ShoppingCart, ptr %1, i32 0, i32 0 store ptr @anon.string.6, ptr %5, align 8 %6 = load [3 x %struct.ShoppingItem], ptr %items, align 8 @@ -52,54 +46,45 @@ define private %struct.ShoppingCart @_Z15newShoppingCartv() { ret %struct.ShoppingCart %8 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 - define private %struct.ShoppingCart @_Z19anotherShoppingCartv() { %result = alloca %struct.ShoppingCart, align 8 - %1 = alloca [3 x %struct.ShoppingItem], align 8 %items = alloca [3 x %struct.ShoppingItem], align 8 - %2 = alloca %struct.ShoppingCart, align 8 - call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @anon.array.0, i64 72, i1 false) + %1 = alloca %struct.ShoppingCart, align 8 store [3 x %struct.ShoppingItem] [%struct.ShoppingItem { ptr @anon.string.7, double 1.000000e+02, ptr @anon.string.8 }, %struct.ShoppingItem { ptr @anon.string.9, double 1.255000e+02, ptr @anon.string.10 }, %struct.ShoppingItem { ptr @anon.string.11, double 6.000000e+00, ptr @anon.string.12 }], ptr %items, align 8 - %3 = getelementptr inbounds %struct.ShoppingCart, ptr %2, i32 0, i32 0 - store ptr @anon.string.13, ptr %3, align 8 - %4 = load [3 x %struct.ShoppingItem], ptr %items, align 8 - %5 = getelementptr inbounds %struct.ShoppingCart, ptr %2, i32 0, i32 1 - store [3 x %struct.ShoppingItem] %4, ptr %5, align 8 - %6 = load %struct.ShoppingCart, ptr %2, align 8 - ret %struct.ShoppingCart %6 + %2 = getelementptr inbounds %struct.ShoppingCart, ptr %1, i32 0, i32 0 + store ptr @anon.string.13, ptr %2, align 8 + %3 = load [3 x %struct.ShoppingItem], ptr %items, align 8 + %4 = getelementptr inbounds %struct.ShoppingCart, ptr %1, i32 0, i32 1 + store [3 x %struct.ShoppingItem] %3, ptr %4, align 8 + %5 = load %struct.ShoppingCart, ptr %1, align 8 + ret %struct.ShoppingCart %5 } ; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @main() #1 { +define dso_local i32 @main() #0 { %result = alloca i32, align 4 %shoppingCart = alloca %struct.ShoppingCart, align 8 - %1 = alloca %struct.ShoppingCart, align 8 store i32 0, ptr %result, align 4 - %2 = call %struct.ShoppingCart @_Z15newShoppingCartv() - store %struct.ShoppingCart %2, ptr %shoppingCart, align 8 - store %struct.ShoppingCart %2, ptr %shoppingCart, align 8 + %1 = call %struct.ShoppingCart @_Z15newShoppingCartv() + store %struct.ShoppingCart %1, ptr %shoppingCart, align 8 %items_addr = getelementptr inbounds %struct.ShoppingCart, ptr %shoppingCart, i32 0, i32 1 - %3 = getelementptr inbounds [3 x %struct.ShoppingItem], ptr %items_addr, i32 0, i32 1 - %name_addr = getelementptr inbounds %struct.ShoppingItem, ptr %3, i32 0, i32 0 - %4 = load ptr, ptr %name_addr, align 8 - %5 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, ptr %4) - %6 = call %struct.ShoppingCart @_Z19anotherShoppingCartv() - store %struct.ShoppingCart %6, ptr %1, align 8 - call void @llvm.memcpy.p0.p0.i64(ptr %shoppingCart, ptr %1, i64 80, i1 false) + %2 = getelementptr inbounds [3 x %struct.ShoppingItem], ptr %items_addr, i32 0, i32 1 + %name_addr = getelementptr inbounds %struct.ShoppingItem, ptr %2, i32 0, i32 0 + %3 = load ptr, ptr %name_addr, align 8 + %4 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.0, ptr %3) + %5 = call %struct.ShoppingCart @_Z19anotherShoppingCartv() + store %struct.ShoppingCart %5, ptr %shoppingCart, align 8 %items_addr1 = getelementptr inbounds %struct.ShoppingCart, ptr %shoppingCart, i32 0, i32 1 - %7 = getelementptr inbounds [3 x %struct.ShoppingItem], ptr %items_addr1, i32 0, i32 2 - %unit_addr = getelementptr inbounds %struct.ShoppingItem, ptr %7, i32 0, i32 2 - %8 = load ptr, ptr %unit_addr, align 8 - %9 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, ptr %8) - %10 = load i32, ptr %result, align 4 - ret i32 %10 + %6 = getelementptr inbounds [3 x %struct.ShoppingItem], ptr %items_addr1, i32 0, i32 2 + %unit_addr = getelementptr inbounds %struct.ShoppingItem, ptr %6, i32 0, i32 2 + %7 = load ptr, ptr %unit_addr, align 8 + %8 = call i32 (ptr, ...) @printf(ptr noundef @printf.str.1, ptr %7) + %9 = load i32, ptr %result, align 4 + ret i32 %9 } ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 -attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #1 = { noinline nounwind optnone uwtable } -attributes #2 = { nofree nounwind } +attributes #0 = { noinline nounwind optnone uwtable } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/irgenerator/structs/success-struct-self-ref/ir-code.ll b/test/test-files/irgenerator/structs/success-struct-self-ref/ir-code.ll index a1c465ba7..5a86e06db 100644 --- a/test/test-files/irgenerator/structs/success-struct-self-ref/ir-code.ll +++ b/test/test-files/irgenerator/structs/success-struct-self-ref/ir-code.ll @@ -5,7 +5,6 @@ target triple = "x86_64-w64-windows-gnu" %struct.TreeNode = type { ptr, i32 } -@anon.struct.0 = private unnamed_addr constant %struct.TreeNode zeroinitializer @printf.str.0 = private unnamed_addr constant [21 x i8] c"Root node number: %d\00", align 1 ; Function Attrs: noinline nounwind optnone uwtable @@ -17,7 +16,6 @@ define dso_local i32 @main() #0 { %childNode21 = alloca %struct.TreeNode, align 8 %curNode = alloca ptr, align 8 store i32 0, ptr %result, align 4 - call void @llvm.memcpy.p0.p0.i64(ptr %rootNode, ptr @anon.struct.0, i64 16, i1 false) store %struct.TreeNode zeroinitializer, ptr %rootNode, align 8 %1 = getelementptr inbounds %struct.TreeNode, ptr %_childNode1, i32 0, i32 0 store ptr %rootNode, ptr %1, align 8 @@ -57,12 +55,8 @@ while.exit.L21: ; preds = %while.head.L21 ret i32 %15 } -; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) -declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #1 - ; Function Attrs: nofree nounwind -declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #2 +declare noundef i32 @printf(ptr nocapture noundef readonly, ...) #1 attributes #0 = { noinline nounwind optnone uwtable } -attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } -attributes #2 = { nofree nounwind } +attributes #1 = { nofree nounwind } diff --git a/test/test-files/typechecker/unsafe/error-pointer-cast/exception.out b/test/test-files/typechecker/unsafe/error-pointer-cast/exception.out index 9b7a20d48..cc8086f71 100644 --- a/test/test-files/typechecker/unsafe/error-pointer-cast/exception.out +++ b/test/test-files/typechecker/unsafe/error-pointer-cast/exception.out @@ -1,3 +1,6 @@ +[Error|Compiler]: +Unresolved soft errors: There are unresolved errors. Please fix them and recompile. + [Error|Semantic] ./test-files/typechecker/unsafe/error-pointer-cast/source.spice:4:17: Unsafe operation in safe context: Cannot apply '(cast)' operator on types int* and long* as this is an unsafe operation. Please use unsafe blocks if you know what you are doing. diff --git a/test/test-files/typechecker/warnings/warning-unused-method/source.spice b/test/test-files/typechecker/warnings/warning-unused-method/source.spice new file mode 100644 index 000000000..f4c5c1c2c --- /dev/null +++ b/test/test-files/typechecker/warnings/warning-unused-method/source.spice @@ -0,0 +1,16 @@ +type TestStruct struct { + int a +} + +p TestStruct.unusedProcedure() { + printf("Hello World: %d", this.a); +} + +f TestStruct.unusedFunction() { + printf("%d", this.a); + return "Hello World"; +} + +f main() { + TestStruct _ts; +} \ No newline at end of file diff --git a/test/test-files/typechecker/warnings/warning-unused-method/warning.out b/test/test-files/typechecker/warnings/warning-unused-method/warning.out new file mode 100644 index 000000000..b30dc0772 --- /dev/null +++ b/test/test-files/typechecker/warnings/warning-unused-method/warning.out @@ -0,0 +1,2 @@ +[Warning] ./test-files/typechecker/warnings/warning-unused-method/source.spice:9:1: Unused method: The method 'string TestStruct.unusedFunction()' is unused +[Warning] ./test-files/typechecker/warnings/warning-unused-method/source.spice:5:1: Unused method: The method 'TestStruct.unusedProcedure()' is unused