From afacb3ea560c4ab5b956ce0f0734d61f9944e89b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 12 Aug 2025 12:38:57 +0200 Subject: [PATCH 01/27] Drop LLVMTypeAnalyzer --- src/engine/internal/llvm/CMakeLists.txt | 2 - .../internal/llvm/instructions/lists.cpp | 24 +- .../internal/llvm/instructions/variables.cpp | 8 +- src/engine/internal/llvm/llvmbuildutils.cpp | 5 - src/engine/internal/llvm/llvmbuildutils.h | 3 - src/engine/internal/llvm/llvmtypeanalyzer.cpp | 565 --- src/engine/internal/llvm/llvmtypeanalyzer.h | 87 - test/llvm/CMakeLists.txt | 5 - test/llvm/type_analyzer/listtype_test.cpp | 4022 ----------------- .../listtypeafterbranch_test.cpp | 2678 ----------- test/llvm/type_analyzer/mixed_test.cpp | 492 -- test/llvm/type_analyzer/variabletype_test.cpp | 2542 ----------- .../variabletypeafterbranch_test.cpp | 2515 ----------- 13 files changed, 16 insertions(+), 12932 deletions(-) delete mode 100644 src/engine/internal/llvm/llvmtypeanalyzer.cpp delete mode 100644 src/engine/internal/llvm/llvmtypeanalyzer.h delete mode 100644 test/llvm/type_analyzer/listtype_test.cpp delete mode 100644 test/llvm/type_analyzer/listtypeafterbranch_test.cpp delete mode 100644 test/llvm/type_analyzer/mixed_test.cpp delete mode 100644 test/llvm/type_analyzer/variabletype_test.cpp delete mode 100644 test/llvm/type_analyzer/variabletypeafterbranch_test.cpp diff --git a/src/engine/internal/llvm/CMakeLists.txt b/src/engine/internal/llvm/CMakeLists.txt index 629d1a28..eb37a95a 100644 --- a/src/engine/internal/llvm/CMakeLists.txt +++ b/src/engine/internal/llvm/CMakeLists.txt @@ -19,8 +19,6 @@ target_sources(scratchcpp llvmtypes.h llvmfunctions.cpp llvmfunctions.h - llvmtypeanalyzer.cpp - llvmtypeanalyzer.h llvmcompilercontext.cpp llvmcompilercontext.h llvmexecutablecode.cpp diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 5d83856b..b8695409 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -111,8 +111,8 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) Compiler::StaticType listType = Compiler::StaticType::Unknown; - if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + /*if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ // Check if enough space is allocated llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); @@ -153,8 +153,8 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) Compiler::StaticType listType = Compiler::StaticType::Unknown; - if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + /*if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ // Range check llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -191,8 +191,8 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) Compiler::StaticType listType = Compiler::StaticType::Unknown; - if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + /*if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ // Range check llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); @@ -234,8 +234,8 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) Compiler::StaticType listType = Compiler::StaticType::Unknown; - if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + /*if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ llvm::Value *min = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -271,8 +271,8 @@ LLVMInstruction *Lists::buildGetListItemIndex(LLVMInstruction *ins) Compiler::StaticType listType = Compiler::StaticType::Unknown; - if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + /*if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ ins->functionReturnReg->value = m_builder.CreateSIToFP(m_utils.getListItemIndex(listPtr, listType, arg.second), m_builder.getDoubleTy()); return ins->next; @@ -286,8 +286,8 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) Compiler::StaticType listType = Compiler::StaticType::Unknown; - if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false); + /*if (m_utils.warp()) + listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ llvm::Value *index = m_utils.getListItemIndex(listPtr, listType, arg.second); ins->functionReturnReg->value = m_builder.CreateICmpSGT(index, llvm::ConstantInt::get(m_builder.getInt64Ty(), -1, true)); diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 204fee06..4d5fa1e1 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -115,8 +115,8 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) Compiler::StaticType varType = Compiler::StaticType::Unknown; - if (m_utils.warp()) - varType = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown); + /*if (m_utils.warp()) + varType = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown);*/ // Initialize stack variable on first assignment // TODO: Use stack in the top level (outside loops and if statements) @@ -149,8 +149,8 @@ LLVMInstruction *Variables::buildReadVariable(LLVMInstruction *ins) LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->workVariable); Compiler::StaticType type = Compiler::StaticType::Unknown; - if (m_utils.warp()) - type = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown); + /*if (m_utils.warp()) + type = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown);*/ ins->functionReturnReg->value = varPtr.onStack && !(ins->loopCondition && !m_utils.warp()) ? varPtr.stackPtr : varPtr.heapPtr; ins->functionReturnReg->setType(type); diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 771bb665..f0636b42 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -172,11 +172,6 @@ LLVMFunctions &LLVMBuildUtils::functions() return m_functions; } -LLVMTypeAnalyzer &LLVMBuildUtils::typeAnalyzer() -{ - return m_typeAnalyzer; -} - std::string LLVMBuildUtils::scriptFunctionName(BlockPrototype *procedurePrototype) { std::string name; diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 6bfeed3c..14c99d4a 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -7,7 +7,6 @@ #include "llvmfunctions.h" #include "llvmvariableptr.h" #include "llvmlistptr.h" -#include "llvmtypeanalyzer.h" #include "llvmcoroutine.h" #include "llvmifstatement.h" #include "llvmloop.h" @@ -39,7 +38,6 @@ class LLVMBuildUtils llvm::IRBuilder<> &builder(); llvm::Function *function() const; LLVMFunctions &functions(); - LLVMTypeAnalyzer &typeAnalyzer(); std::string scriptFunctionName(BlockPrototype *procedurePrototype); llvm::FunctionType *scriptFunctionType(BlockPrototype *procedurePrototype); @@ -115,7 +113,6 @@ class LLVMBuildUtils llvm::LLVMContext &m_llvmCtx; llvm::IRBuilder<> &m_builder; LLVMFunctions m_functions; - LLVMTypeAnalyzer m_typeAnalyzer; Target *m_target = nullptr; llvm::Function *m_function = nullptr; diff --git a/src/engine/internal/llvm/llvmtypeanalyzer.cpp b/src/engine/internal/llvm/llvmtypeanalyzer.cpp deleted file mode 100644 index 3445c783..00000000 --- a/src/engine/internal/llvm/llvmtypeanalyzer.cpp +++ /dev/null @@ -1,565 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#include "llvmtypeanalyzer.h" -#include "llvminstruction.h" - -using namespace libscratchcpp; - -static const std::unordered_set - BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginWhileLoop, LLVMInstruction::Type::BeginRepeatUntilLoop }; - -static const std::unordered_set LIST_WRITE_INSTRUCTIONS = { LLVMInstruction::Type::AppendToList, LLVMInstruction::Type::InsertToList, LLVMInstruction::Type::ListReplace }; - -Compiler::StaticType LLVMTypeAnalyzer::variableType(const Variable *var, const LLVMInstruction *pos, Compiler::StaticType previousType) const -{ - InstructionSet visitedInstructions; - std::unordered_map listTypes; - return variableType(var, pos, previousType, listTypes, visitedInstructions); -} - -Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType) const -{ - InstructionSet visitedInstructions; - return variableTypeAfterBranch(var, start, previousType, visitedInstructions); -} - -Compiler::StaticType LLVMTypeAnalyzer::listType(const List *list, const LLVMInstruction *pos, Compiler::StaticType previousType, bool isEmpty) const -{ - InstructionSet visitedInstructions; - std::unordered_map listTypes; - return listType(list, pos, previousType, isEmpty, listTypes, visitedInstructions); -} - -Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch(const List *list, const LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty) const -{ - bool write = false; // only used internally (the compiler doesn't need this) - InstructionSet visitedInstructions; - std::unordered_map listTypes; - return listTypeAfterBranch(list, start, previousType, isEmpty, nullptr, write, listTypes, visitedInstructions); -} - -Compiler::StaticType LLVMTypeAnalyzer::variableType( - const Variable *var, - const LLVMInstruction *pos, - Compiler::StaticType previousType, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const -{ - if (!var || !pos) - return Compiler::StaticType::Unknown; - - /* - * If the given instruction has already been processed, - * it means there's a case like this: - * x = x - * - * or this: - * x = y - * ... - * y = x - */ - if (visitedInstructions.find(pos) != visitedInstructions.cend()) { - // Circular dependencies are rare (and bad) so don't optimize them - return Compiler::StaticType::Unknown; - } - - visitedInstructions.insert(pos); - - // Check the last write operation before the instruction - const LLVMInstruction *ins = pos->previous; - const LLVMInstruction *write = nullptr; - std::pair firstBranch = { nullptr, 0 }; - std::pair firstElseBranch = { nullptr, 0 }; - const LLVMInstruction *ourBranch = nullptr; - int level = 0; - - while (ins) { - if (isLoopEnd(ins) || isIfEnd(ins)) - level++; - else if (isLoopStart(ins) || isIfStart(ins)) { - if (!ourBranch && level == 0) - ourBranch = ins; - - if (!firstBranch.first || level < firstBranch.second) - firstBranch = { ins, level }; - - level--; - } else if (isElse(ins)) { - // Skip if branch if coming from else - if (!firstElseBranch.first || level < firstElseBranch.second) - firstElseBranch = { ins, level }; - - ins = branchStart(ins); - continue; - } else if (isVariableWrite(ins, var) && !isWriteNoOp(ins)) { - if (level <= 0) { // ignore nested branches (they're handled by the branch analyzer) - write = ins; - break; - } - } - - ins = ins->previous; - } - - if (firstBranch.first) { - // Analyze the first branch and else branch - bool ignoreWriteAfterPos = (isIfStart(firstBranch.first) && firstBranch.first == ourBranch); - - if (write) - previousType = writeValueType(write, listTypes, visitedInstructions); // write operation overrides previous type - - Compiler::StaticType firstBranchType = previousType; - Compiler::StaticType elseBranchType = previousType; - - if (!ignoreWriteAfterPos) { - firstBranchType = variableTypeAfterBranch(var, firstBranch.first, previousType, visitedInstructions); - elseBranchType = variableTypeAfterBranch(var, firstElseBranch.first, previousType, visitedInstructions); - } - - if (typesMatch(firstBranchType, elseBranchType)) - return firstBranchType; - else - return Compiler::StaticType::Unknown; - } else if (write) { - // There wasn't any branch found, so we can just check the last write operation - return writeValueType(write, listTypes, visitedInstructions); - } - - // No write operation found - return previousType; -} - -Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const -{ - if (!var || !start) - return previousType; - - const LLVMInstruction *end = branchEnd(start); - - if (!end) - return Compiler::StaticType::Unknown; - - // Process the branch from end - bool write = false; // only used internally (the compiler doesn't need this) - std::unordered_map listTypes; - return variableTypeAfterBranchFromEnd(var, end, previousType, write, listTypes, visitedInstructions); -} - -Compiler::StaticType LLVMTypeAnalyzer::variableTypeAfterBranchFromEnd( - const Variable *var, - const LLVMInstruction *end, - Compiler::StaticType previousType, - bool &write, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const -{ - // Find the last write instruction - const LLVMInstruction *ins = end->previous; - bool typeMightReset = false; - - while (ins && !isLoopStart(ins) && !isIfStart(ins)) { - if (isLoopEnd(ins) || isIfEnd(ins) || isElse(ins)) { - // Process the nested loop or if statement - Compiler::StaticType ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write, listTypes, visitedInstructions); - - if (typesMatch(ret, previousType)) { - if (write) - typeMightReset = true; - } else - return Compiler::StaticType::Unknown; - - ins = branchStart(ins); - - if (isElse(ins)) { - // Process if branch (the else branch is already processed) - ret = variableTypeAfterBranchFromEnd(var, ins, previousType, write, listTypes, visitedInstructions); - - if (typesMatch(ret, previousType)) { - if (write) { - if (typeMightReset) - return previousType; - - typeMightReset = true; - } - } else - return Compiler::StaticType::Unknown; - - ins = branchStart(ins); - } - } else if (isVariableWrite(ins, var) && !isWriteNoOp(ins)) { - // Variable write instruction - Compiler::StaticType writeType = writeValueType(ins, listTypes, visitedInstructions); - write = true; - - if (typesMatch(writeType, previousType)) - return writeType; - else - return typeMightReset ? Compiler::StaticType::Unknown : writeType; - } - - ins = ins->previous; - } - - write = false; - return previousType; -} - -Compiler::StaticType LLVMTypeAnalyzer::listType( - const List *list, - const LLVMInstruction *pos, - Compiler::StaticType previousType, - bool isEmpty, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const -{ - if (!list || !pos) - return Compiler::StaticType::Unknown; - - /* - * If the given instruction has already been processed, - * it means there's a circular dependency with an unknown type. - * See variableType() - */ - if (visitedInstructions.find(pos) != visitedInstructions.cend()) { - // Circular dependencies are rare (and bad) so don't optimize them - // TODO: The (previousType != Compiler::StaticType::Unknown) case isn't handled in tests - // Add a test case for it if you find it... - assert(previousType == Compiler::StaticType::Unknown); - return /*Compiler::StaticType::Unknown*/ previousType; - } - - visitedInstructions.insert(pos); - listTypes[list] = previousType; - - const LLVMInstruction *ins = pos; - const LLVMInstruction *previous = nullptr; - std::pair firstBranch = { nullptr, 0 }; - std::pair lastClear = { nullptr, 0 }; - int level = 0; - - // Find a start instruction (list clear in the top level or the first instruction) - while (ins) { - if (isLoopEnd(ins) || isIfEnd(ins)) - level++; - else if (isLoopStart(ins) || isIfStart(ins) || isElse(ins)) { - if (!isElse(ins)) - level--; - - if (!firstBranch.first || level < firstBranch.second) - firstBranch = { ins, level }; - } else if (isListClear(ins, list)) { - if (!lastClear.first || level < lastClear.second) - lastClear = { ins, level }; - } - - previous = ins; - ins = ins->previous; - } - - if (firstBranch.first) { - assert(firstBranch.second == level); - - // The first branch must be above the query point level - if (firstBranch.second == 0) - firstBranch.first = nullptr; - } - - // Clear must be in the top level - if (lastClear.second != level) - lastClear.first = nullptr; - - if (lastClear.first) { - ins = lastClear.first; - isEmpty = true; - } else - ins = previous; - - // Process from the start instruction - while (ins && ins != pos) { - if (isLoopStart(ins) || isIfStart(ins)) { - do { - bool write; - Compiler::StaticType type = listTypeAfterBranch(list, ins, previousType, isEmpty, pos, write, listTypes, visitedInstructions); - - // If this branch contains the query point, return the final type - if (ins == firstBranch.first) - return type; - - // If there was a write, the list is no longer empty - if (write) { - isEmpty = false; - - // The write could change the type - if (!typesMatch(type, previousType)) - previousType = type; - } - - // Skip the branch - ins = branchEnd(ins); - } while (isElse(ins)); // handle else branch - } else if (isListWrite(ins, list)) { - // List write instruction - Compiler::StaticType writeType = writeValueType(ins, listTypes, visitedInstructions); - - if (!handleListWrite(writeType, previousType, isEmpty)) - return Compiler::StaticType::Unknown; - } - - ins = ins->next; - } - - assert(ins); - return previousType; -} - -Compiler::StaticType LLVMTypeAnalyzer::listTypeAfterBranch( - const List *list, - const LLVMInstruction *start, - Compiler::StaticType previousType, - bool isEmpty, - const LLVMInstruction *query, - bool &write, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const -{ - write = false; - - if (!list || !start) - return previousType; - - assert(isLoopStart(start) || isIfStart(start) || isElse(start)); - - bool isLoop = isLoopStart(start); - bool clearBeforeQueryPoint = false; - const LLVMInstruction *ins = start->next; - - while (ins && !(isLoopEnd(ins) || isIfEnd(ins) || isElse(ins))) { - if (isLoopStart(ins) || isIfStart(ins)) { - do { - Compiler::StaticType type = listTypeAfterBranch(list, ins, previousType, isEmpty, query, write, listTypes, visitedInstructions); - - // If there was a write, the list is no longer empty - if (write) { - isEmpty = false; - - // The write could change the type - if (!typesMatch(type, previousType)) - return Compiler::StaticType::Unknown; - } - - // Skip the branch - ins = branchEnd(ins); - } while (isElse(ins)); // handle else branch - } else if (isListWrite(ins, list)) { - // List write instruction - Compiler::StaticType writeType = writeValueType(ins, listTypes, visitedInstructions); - write = true; - - if (!handleListWrite(writeType, previousType, isEmpty)) - return Compiler::StaticType::Unknown; - } else if (isListClear(ins, list)) { - // The list is now empty - isEmpty = true; - clearBeforeQueryPoint = true; - write = false; // the write variable is only used to check if the list is still empty - } else if (ins == query) { - if (!isLoop || clearBeforeQueryPoint) - break; - - clearBeforeQueryPoint = false; - } - - ins = ins->next; - } - - assert(ins); - return previousType; -} - -bool LLVMTypeAnalyzer::handleListWrite(Compiler::StaticType writeType, Compiler::StaticType &previousType, bool &isEmpty) const -{ - if (isEmpty) { - // In empty lists, writes of the same type determine the final type - // This is the first write found, it might determine the final type - previousType = writeType; - - // The list is no longer empty - isEmpty = false; - } else if (!typesMatch(writeType, previousType)) { - // For non-empty lists, we can just check the write type - return false; - } - - return true; -} - -const LLVMInstruction *LLVMTypeAnalyzer::branchEnd(const LLVMInstruction *start) const -{ - assert(start); - assert(isLoopStart(start) || isIfStart(start) || isElse(start)); - - // Find loop/if statement end or else branch - LLVMInstruction *ins = start->next; - int level = 0; - - while (ins && !((isLoopEnd(ins) || isIfEnd(ins) || isElse(ins)) && level == 0)) { - if (isLoopStart(ins) || isIfStart(ins)) - level++; - else if (isLoopEnd(ins) || isIfEnd(ins)) { - assert(level > 0); - level--; - } - - ins = ins->next; - } - - assert(ins); - return ins; -} - -const LLVMInstruction *LLVMTypeAnalyzer::branchStart(const LLVMInstruction *end) const -{ - assert(end); - assert(isLoopEnd(end) || isIfEnd(end) || isElse(end)); - - // Find loop/if statement/else branch start - LLVMInstruction *ins = end->previous; - int level = 0; - - while (ins && !((isLoopStart(ins) || isIfStart(ins) || isElse(ins)) && level == 0)) { - if (isLoopStart(ins) || isIfStart(ins)) { - assert(level > 0); - level--; - } - - if (isLoopEnd(ins) || isIfEnd(ins)) - level++; - - ins = ins->previous; - }; - - assert(ins); - return ins; -} - -bool LLVMTypeAnalyzer::isLoopStart(const LLVMInstruction *ins) const -{ - return (BEGIN_LOOP_INSTRUCTIONS.find(ins->type) != BEGIN_LOOP_INSTRUCTIONS.cend()); -} - -bool LLVMTypeAnalyzer::isLoopEnd(const LLVMInstruction *ins) const -{ - return (ins->type == LLVMInstruction::Type::EndLoop); -} - -bool LLVMTypeAnalyzer::isIfStart(const LLVMInstruction *ins) const -{ - return (ins->type == LLVMInstruction::Type::BeginIf); -} - -bool LLVMTypeAnalyzer::isElse(const LLVMInstruction *ins) const -{ - return (ins->type == LLVMInstruction::Type::BeginElse); -} - -bool LLVMTypeAnalyzer::isIfEnd(const LLVMInstruction *ins) const -{ - return (ins->type == LLVMInstruction::Type::EndIf); -} - -bool LLVMTypeAnalyzer::isVariableRead(const LLVMInstruction *ins) const -{ - return (ins->type == LLVMInstruction::Type::ReadVariable); -} - -bool LLVMTypeAnalyzer::isVariableWrite(const LLVMInstruction *ins, const Variable *var) const -{ - return (ins->type == LLVMInstruction::Type::WriteVariable && ins->workVariable == var); -} - -bool LLVMTypeAnalyzer::isListRead(const LLVMInstruction *ins) const -{ - return (ins->type == LLVMInstruction::Type::GetListItem); -} - -bool LLVMTypeAnalyzer::isListWrite(const LLVMInstruction *ins, const List *list) const -{ - return (LIST_WRITE_INSTRUCTIONS.find(ins->type) != LIST_WRITE_INSTRUCTIONS.cend() && ins->workList == list); -} - -bool LLVMTypeAnalyzer::isListClear(const LLVMInstruction *ins, const List *list) const -{ - return (ins->type == LLVMInstruction::Type::ClearList && ins->workList == list); -} - -Compiler::StaticType LLVMTypeAnalyzer::optimizeRegisterType(LLVMRegister *reg) const -{ - // TODO: Move this method out if it's used in LLVMCodeBuilder too - assert(reg); - - Compiler::StaticType ret = reg->type(); - - // Optimize string constants that represent numbers - if (reg->isConst() && reg->type() == Compiler::StaticType::String && reg->constValue().isValidNumber()) - ret = Compiler::StaticType::Number; - - return ret; -} - -bool LLVMTypeAnalyzer::isWriteNoOp(const LLVMInstruction *ins) const -{ - assert(ins); - assert(!ins->args.empty()); - const auto arg = ins->args.back().second; // value is always the last argument in variable/list write instructions - - if (arg->instruction) { - // TODO: Handle list item - if (isVariableRead(arg->instruction.get())) { - // Self-assignment is a no-op - return (ins->workVariable == arg->instruction->workVariable); - } - } - - return false; -} - -Compiler::StaticType LLVMTypeAnalyzer::writeValueType(const LLVMInstruction *ins, std::unordered_map listTypes, InstructionSet &visitedInstructions) const -{ - assert(ins); - assert(!ins->args.empty()); - const auto arg = ins->args.back().second; // value is always the last argument in variable/list write instructions - - if (arg->instruction) { - // TODO: Handle list item - if (isVariableRead(arg->instruction.get())) { - // If this is a variable read instruction, recursively get the variable type - return variableType(arg->instruction->workVariable, arg->instruction.get(), Compiler::StaticType::Unknown, listTypes, visitedInstructions); - } else if (isListRead(arg->instruction.get())) { - // If this is a list read instruction, recursively get the list type - Compiler::StaticType type = Compiler::StaticType::Unknown; - auto it = listTypes.find(arg->instruction->workList); - - if (it != listTypes.cend()) - type = it->second; - - // NOTE: The isEmpty parameter is useless here - return listType(arg->instruction->workList, arg->instruction.get(), type, false, listTypes, visitedInstructions); - } else { - // The write argument already has the instruction return type - return optimizeRegisterType(arg); - } - } else - return optimizeRegisterType(arg); -} - -bool LLVMTypeAnalyzer::typesMatch(Compiler::StaticType a, Compiler::StaticType b) const -{ - // Equal unknown types are not considered a match - return (a == b) && (a != Compiler::StaticType::Unknown); -} - -bool LLVMTypeAnalyzer:: - writeTypesMatch(const LLVMInstruction *ins, Compiler::StaticType expectedType, std::unordered_map listTypes, InstructionSet &visitedInstructions) const -{ - return typesMatch(writeValueType(ins, listTypes, visitedInstructions), expectedType); -} diff --git a/src/engine/internal/llvm/llvmtypeanalyzer.h b/src/engine/internal/llvm/llvmtypeanalyzer.h deleted file mode 100644 index a1d55377..00000000 --- a/src/engine/internal/llvm/llvmtypeanalyzer.h +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include - -namespace libscratchcpp -{ - -struct LLVMInstruction; -struct LLVMRegister; - -class LLVMTypeAnalyzer -{ - public: - Compiler::StaticType variableType(const Variable *var, const LLVMInstruction *pos, Compiler::StaticType previousType) const; - Compiler::StaticType variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType) const; - - Compiler::StaticType listType(const List *list, const LLVMInstruction *pos, Compiler::StaticType previousType, bool isEmpty) const; - Compiler::StaticType listTypeAfterBranch(const List *list, const LLVMInstruction *start, Compiler::StaticType previousType, bool isEmpty) const; - - private: - using InstructionSet = std::unordered_set; - - Compiler::StaticType variableType( - const Variable *var, - const LLVMInstruction *pos, - Compiler::StaticType previousType, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const; - - Compiler::StaticType variableTypeAfterBranch(const Variable *var, const LLVMInstruction *start, Compiler::StaticType previousType, InstructionSet &visitedInstructions) const; - - Compiler::StaticType variableTypeAfterBranchFromEnd( - const Variable *var, - const LLVMInstruction *end, - Compiler::StaticType previousType, - bool &write, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const; - - Compiler::StaticType listType( - const List *list, - const LLVMInstruction *pos, - Compiler::StaticType previousType, - bool isEmpty, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const; - - Compiler::StaticType listTypeAfterBranch( - const List *list, - const LLVMInstruction *start, - Compiler::StaticType previousType, - bool isEmpty, - const LLVMInstruction *query, - bool &write, - std::unordered_map listTypes, - InstructionSet &visitedInstructions) const; - - bool handleListWrite(Compiler::StaticType writeType, Compiler::StaticType &previousType, bool &isEmpty) const; - - const LLVMInstruction *branchEnd(const LLVMInstruction *start) const; - const LLVMInstruction *branchStart(const LLVMInstruction *end) const; - - bool isLoopStart(const LLVMInstruction *ins) const; - bool isLoopEnd(const LLVMInstruction *ins) const; - bool isIfStart(const LLVMInstruction *ins) const; - bool isElse(const LLVMInstruction *ins) const; - bool isIfEnd(const LLVMInstruction *ins) const; - - bool isVariableRead(const LLVMInstruction *ins) const; - bool isVariableWrite(const LLVMInstruction *ins, const Variable *var) const; - - bool isListRead(const LLVMInstruction *ins) const; - bool isListWrite(const LLVMInstruction *ins, const List *list) const; - bool isListClear(const LLVMInstruction *ins, const List *list) const; - - Compiler::StaticType optimizeRegisterType(LLVMRegister *reg) const; - bool isWriteNoOp(const LLVMInstruction *ins) const; - Compiler::StaticType writeValueType(const LLVMInstruction *ins, std::unordered_map listTypes, InstructionSet &visitedInstructions) const; - bool typesMatch(Compiler::StaticType a, Compiler::StaticType b) const; - bool - writeTypesMatch(const LLVMInstruction *ins, Compiler::StaticType expectedType, std::unordered_map listTypes, InstructionSet &visitedInstructions) const; -}; - -} // namespace libscratchcpp diff --git a/test/llvm/CMakeLists.txt b/test/llvm/CMakeLists.txt index 80ac82f6..62167f0d 100644 --- a/test/llvm/CMakeLists.txt +++ b/test/llvm/CMakeLists.txt @@ -55,11 +55,6 @@ add_executable( operators/string/string_concat_test.cpp operators/string/string_char_test.cpp operators/string/string_length_test.cpp - type_analyzer/variabletypeafterbranch_test.cpp - type_analyzer/variabletype_test.cpp - type_analyzer/listtypeafterbranch_test.cpp - type_analyzer/listtype_test.cpp - type_analyzer/mixed_test.cpp ) target_link_libraries( diff --git a/test/llvm/type_analyzer/listtype_test.cpp b/test/llvm/type_analyzer/listtype_test.cpp deleted file mode 100644 index a1183dc9..00000000 --- a/test/llvm/type_analyzer/listtype_test.cpp +++ /dev/null @@ -1,4022 +0,0 @@ -#include -#include -#include -#include -#include -#include - -using namespace libscratchcpp; - -TEST(LLVMTypeAnalyzer_ListType, NullParams) -{ - LLVMTypeAnalyzer analyzer; - ASSERT_EQ(analyzer.listType(nullptr, nullptr, Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NullList) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(nullptr, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NullPos) -{ - LLVMTypeAnalyzer analyzer; - List list("", ""); - ASSERT_EQ(analyzer.listType(&list, nullptr, Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NoWriteOperationsKnownType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, NoWriteOperationsKnownType_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, NoWriteOperationsUnknownType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Unknown, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NoWriteOperationsUnknownType_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Unknown, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, QueryPointIsWrite) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - ASSERT_EQ(analyzer.listType(&list, appendList.get(), Compiler::StaticType::String, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteSameTypeNumber_Before_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteSameTypeNumber_Before_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteStringOptimization_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "1.25"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // String "1.25" gets optimized to Number type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteMultipleLists) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list1("", ""); - List list2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Append to list1 - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 6); - appendList1->workList = &list1; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Append to list2 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list2; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list1, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteFromUnknownType_Before_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMRegister value(Compiler::StaticType::Unknown); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteFromUnknownType_Before_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMRegister value(Compiler::StaticType::Unknown); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteToUnknownType_Before_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // The type is known because a number is appended before the point - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Unknown, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteToUnknownType_Before_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Unknown, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteDifferentTypeNumberToString_Before_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteDifferentTypeNumberToString_Before_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteDifferentTypeBoolToNumber_Before_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, Loop_SingleWriteDifferentType_BeforeAndAfter) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Bool, true); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, IfStatement_SingleWriteDifferentType_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, IfStatement_SingleWriteDifferentType_After_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - // Since this isn't a loop, the write after the point doesn't affect the type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, IfStatement_SingleWriteDifferentType_After_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - // Since this isn't a loop, the write after the point doesn't affect the type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, WriteSameTypeInLoop_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, WriteSameTypeInLoop_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, WriteDifferentTypeInLoop_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Type is not known because the loop might not run at all - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, WriteDifferentTypeInLoop_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Type is String because empty list can establish new type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ChangeTypeBeforeLoop_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, OverrideUnknownTypeBeforeLoop_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Unknown, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, WriteSameTypeInIfStatement_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, WriteDifferentTypeInIfStatement_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Type is not known because the if statement might not run at all - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, OverrideDifferentTypeBeforeIfElseStatement_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2.5); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, OverrideUnknownTypeBeforeIfElseStatement_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2.5); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Unknown, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesSameTypeNumber_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // First write: string "abc" (incompatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second write: number 5.25 (incompatible with previous String) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown type because of type conflict - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesSameTypeString_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // First write: number 42 (establishes Number type for empty list) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second write: string "hello" (incompatible with Number) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown type because of type conflict - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesSameTypeUnknown_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // First write: unknown type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMRegister value1(Compiler::StaticType::Unknown); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second write: string "hello" - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown type because first write is unknown - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesWithStringOptimization_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // First write: number 5 - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second write: string "3.14" (optimized to Number) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "3.14"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Number type because the string gets optimized to number - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesNumberToString_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // First write: number 42 (compatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second write: string "hello" (incompatible with Number) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown type because of type conflict - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesNumberToString_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // First write: number 42 (establishes Number type for empty list) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second write: string "hello" (incompatible with Number) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown type because of type conflict - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesBoolToNumber_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 42.5); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown type because Number conflicts with Bool - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesBoolToNumber_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 42.5); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Number type because empty list establishes new type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesBoolToString_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "hello"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown type because String conflicts with Bool - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesBoolToString_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "hello"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return String type because empty list establishes new type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesBoolToStringNumberOptimization_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "3.14"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // String "3.14" gets optimized to Number, which conflicts with Bool - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleWritesBoolToStringNumberOptimization_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "3.14"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // String "3.14" gets optimized to Number type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, TypeChangeInIfBranch_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - changes type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - does not change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Returns unknown type because both branches might run - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, TypeChangeInIfBranch_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - changes type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - different type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Returns unknown type because different types in branches - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, TypeChangeInElseBranch_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - does not change type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - changes type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Returns unknown type because both branches might run - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, TypeChangeInElseBranch_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - establishes Number type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - establishes String type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Returns unknown type because different types in branches - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, IfElseWithEqualTypes_SameType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - does not change type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - does not change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, IfElseWithEqualTypes_SameType_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - establishes Number type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - establishes same Number type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, IfElseWithEqualTypes_DifferentTypes_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - changes type to string - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "Lorem"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - changes type to string - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "ipsum"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, IfElseWithEqualTypes_DifferentTypes_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - establishes String type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "Lorem"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - establishes same String type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "ipsum"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_SameTypes_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // First write - does not change type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Second write - does not change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_SameTypes_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // First write - establishes type for empty list - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Second write - same type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_DifferentTypes_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // First write - does not change type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Second write - changes type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, IfStatementAfterLoop_DifferentTypes_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // First write - establishes Number type for empty list - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Second write - adds string - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, TwoIfStatements_DifferentTypes_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - does not change type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - does not change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 4); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Third write - adds string (in else branch) - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "test"); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, TwoIfStatements_DifferentTypes_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First write - establishes Number type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second write - same Number type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 4); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Third write - adds String (in else branch) - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "test"); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_Before_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_Before_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_BeforeInner_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Returns Unknown because a string might be added - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_BeforeInner_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Returns Unknown because a string might be added - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_AfterWriteInsideInner_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Returns Unknown because a string might be added - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_AfterWriteInsideInner_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Returns Unknown because a string might be added - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_After_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Type is not known because the inner loop might not run at all - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithTypeChange_After_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Type is not known because the inner loop might not run at all - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithoutTypeChange_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.5); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopWithoutTypeChange_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.5); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopTypeChangesMultipleTimes_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // List write after inner loop - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, NestedLoopTypeChangesMultipleTimes_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - instructionList.addInstruction(innerLoop); - - // List write inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // List write after inner loop - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleNestedLoopsWithTypeChange_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop1); - - // List write inside inner loop 1 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop2); - - // List write inside inner loop 2 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd2); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleNestedLoopsWithTypeChange_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop1); - - // List write inside inner loop 1 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop2); - - // List write inside inner loop 2 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd2); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleNestedLoopsWithMultipleTypeChanges_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop1); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop2); - - // List write inside inner loop 2 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "abc"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd2); - - // List write inside inner loop 1 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2.75); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleNestedLoopsWithMultipleTypeChanges_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List write inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop1); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop2); - - // List write inside inner loop 2 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "abc"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd2); - - // List write inside inner loop 1 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2.75); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, SameTypeIfElseInLoopWithTypeChange_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // Inner if statement begin - auto innerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf); - - // List write inside inner if statement if branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner if statement else branch - auto innerElse = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(innerElse); - - // List write inside inner if statement else branch - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "def"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner if statement end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // A type conflict always occurs - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, InsertToList_SameType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::Number, 42.5); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &index }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(insertList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, InsertToList_DifferentType_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - LLVMConstantRegister value(Compiler::StaticType::String, "hello"); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &index }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(insertList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, InsertToList_InLoop_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto insertList1 = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index1(Compiler::StaticType::Number, 0); - LLVMConstantRegister value1(Compiler::StaticType::Number, 123); - insertList1->workList = &list; - insertList1->args.push_back({ Compiler::StaticType::Number, &index1 }); - insertList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(insertList1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto insertList2 = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 1); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - insertList2->workList = &list; - insertList2->args.push_back({ Compiler::StaticType::Number, &index2 }); - insertList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(insertList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, ListReplace_SameType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - LLVMConstantRegister value(Compiler::StaticType::String, "replaced"); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(replaceList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ListReplace_DifferentType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(replaceList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, ListReplace_WithStringOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - LLVMConstantRegister value(Compiler::StaticType::String, "42.75"); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(replaceList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // String "42.75" gets optimized to Number type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, MixedWriteOperations_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Append a number - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList); - - // Insert a string - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &index }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(insertList); - - // Replace with a boolean - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 1); - LLVMConstantRegister value3(Compiler::StaticType::Bool, true); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index2 }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(replaceList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should be Unknown due to multiple type conflicts - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MixedWriteOperations_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Append a string - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "hello"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList); - - // Insert another string - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - LLVMConstantRegister value2(Compiler::StaticType::String, "world"); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &index }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(insertList); - - // Replace with another string - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 1); - LLVMConstantRegister value3(Compiler::StaticType::String, "test"); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index2 }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(replaceList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should be String type since all operations use strings - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ComplexMixedOperations) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Append a number - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 123); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList); - - // Insert another number - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index1(Compiler::StaticType::Number, 0); - LLVMConstantRegister value2(Compiler::StaticType::Number, 456); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &index1 }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(insertList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Replace with a string (creates type conflict) - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 0); - LLVMConstantRegister value3(Compiler::StaticType::String, "conflict"); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index2 }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(replaceList); - - // Should be Number type at the function call point, before the conflicting replace - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, ComplexMixedOperations_InLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Append a number - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 123); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList); - - // Insert another number - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index1(Compiler::StaticType::Number, 0); - LLVMConstantRegister value2(Compiler::StaticType::Number, 456); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &index1 }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(insertList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Replace with a string (creates type conflict) - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 0); - LLVMConstantRegister value3(Compiler::StaticType::String, "conflict"); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index2 }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(replaceList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, MultipleClearsInLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearAndRebuild_WithDifferentTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Initial append - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "initial"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Rebuild with different operations and types - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - LLVMConstantRegister value2(Compiler::StaticType::Bool, true); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &index }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(insertList); - - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 0); - LLVMConstantRegister value3(Compiler::StaticType::Bool, false); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &index2 }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(replaceList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should be Bool type since clear resets and all subsequent operations use booleans - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_NewTypeAfterClear_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list in the middle of the loop - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Append after clear - this should set the new type - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return String because the list was empty and append sets the type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_NewTypeAfterClear_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list in the middle of the loop - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Append after clear - this should set the new type - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return String because clear makes the list empty, then append sets the type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_MultipleAppendsAfterClear) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // First append after clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append after clear (same type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 3.14); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Number because all appends after clear are the same type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_TypeConflictAfterClear) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // First append after clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append after clear (different type - creates conflict) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown because of type conflict after clear - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_WithInsertAndReplace) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Insert after clear - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister insertIndex(Compiler::StaticType::Number, 0); - LLVMConstantRegister insertValue(Compiler::StaticType::Bool, true); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &insertIndex }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &insertValue }); - instructionList.addInstruction(insertList); - - // Replace after insert - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister replaceIndex(Compiler::StaticType::Number, 0); - LLVMConstantRegister replaceValue(Compiler::StaticType::Bool, false); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &replaceIndex }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &replaceValue }); - instructionList.addInstruction(replaceList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Bool because all operations after clear use Bool type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_ConditionalClear) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear the list conditionally - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Append after clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "cleared"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Else branch - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Append without clearing - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown due to type conflict between branches - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendAfterClearingInIfBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown because the clearing branch might or might not run - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendAfterClearingInIfBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 42); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Number because there are no type conflicts - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendAfterClearingInElseBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Else branch - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown because the clearing branch might or might not run - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendAfterClearingInElseBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Else branch - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 42); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Number because there are no type conflicts - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_MultipleClearsAndWrites) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First clear - auto clearList1 = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList1->workList = &list; - instructionList.addInstruction(clearList1); - - // Append after first clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "first"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second clear - auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList2->workList = &list; - instructionList.addInstruction(clearList2); - - // Append after second clear (different type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Number because the last clear+append sequence determines the final type - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_ClearWithoutSubsequentWrite) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list but don't write anything after - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return previous type since no writes occurred after clear - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_ClearInNestedIf) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Nested if statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear and write in nested if - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown because the type-changing branch might or might not run - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendToEmptyListAndClearInIfBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Append in if branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in if branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendToEmptyListAndClearInIfBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Append in if branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in if branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendToEmptyListAndClearInElseBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Append in else branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in else branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, ClearListInLoop_AppendToEmptyListAndClearInElseBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Append in else branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in else branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_SimpleRead_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("source", ""); - List targetList("target", ""); - - // Establish source list type - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &sourceList; - instructionList.addInstruction(clearList); - - auto appendSource = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister sourceValue(Compiler::StaticType::String, "hello"); - appendSource->workList = &sourceList; - appendSource->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); - instructionList.addInstruction(appendSource); - - // Read from source list and write to target list - auto readSource = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readSourceReg = std::make_shared(Compiler::StaticType::Unknown); - readSourceReg->instruction = readSource; - readSourceReg->isRawValue = false; - readSource->functionReturnReg = readSourceReg.get(); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - readSource->workList = &sourceList; - readSource->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(readSource); - - auto appendTarget = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendTarget->workList = &targetList; - appendTarget->args.push_back({ Compiler::StaticType::Unknown, readSourceReg.get() }); - instructionList.addInstruction(appendTarget); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&targetList, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_SimpleRead_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("source", ""); - List targetList("target", ""); - - // Establish source list type - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &sourceList; - instructionList.addInstruction(clearList); - - auto appendSource = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister sourceValue(Compiler::StaticType::Bool, true); - appendSource->workList = &sourceList; - appendSource->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); - instructionList.addInstruction(appendSource); - - // Read from source list and write to target list - auto readSource = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readSourceReg = std::make_shared(Compiler::StaticType::Unknown); - readSourceReg->instruction = readSource; - readSourceReg->isRawValue = false; - readSource->functionReturnReg = readSourceReg.get(); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - readSource->workList = &sourceList; - readSource->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(readSource); - - auto appendTarget = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendTarget->workList = &targetList; - appendTarget->args.push_back({ Compiler::StaticType::Unknown, readSourceReg.get() }); - instructionList.addInstruction(appendTarget); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&targetList, funcCall.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_InLoop_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("source", ""); - List targetList("target", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // Clear source list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &sourceList; - instructionList.addInstruction(clearList); - - // First iteration: append string to source - auto appendSource1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister sourceValue1(Compiler::StaticType::String, "text"); - appendSource1->workList = &sourceList; - appendSource1->args.push_back({ Compiler::StaticType::Unknown, &sourceValue1 }); - instructionList.addInstruction(appendSource1); - - // Read from source and write to target - auto readSource1 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readSource1Reg = std::make_shared(Compiler::StaticType::Unknown); - readSource1Reg->instruction = readSource1; - readSource1Reg->isRawValue = false; - readSource1->functionReturnReg = readSource1Reg.get(); - LLVMConstantRegister index1(Compiler::StaticType::Number, 0); - readSource1->workList = &sourceList; - readSource1->args.push_back({ Compiler::StaticType::Number, &index1 }); - instructionList.addInstruction(readSource1); - - auto appendTarget1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendTarget1->workList = &targetList; - appendTarget1->args.push_back({ Compiler::StaticType::Unknown, readSource1Reg.get() }); - instructionList.addInstruction(appendTarget1); - - // Second iteration: append number to source (creates type conflict) - auto appendSource2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister sourceValue2(Compiler::StaticType::Number, 42); - appendSource2->workList = &sourceList; - appendSource2->args.push_back({ Compiler::StaticType::Unknown, &sourceValue2 }); - instructionList.addInstruction(appendSource2); - - // Read from source and write to target - auto readSource2 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readSource2Reg = std::make_shared(Compiler::StaticType::Unknown); - readSource2Reg->instruction = readSource2; - readSource2Reg->isRawValue = false; - readSource2->functionReturnReg = readSource2Reg.get(); - LLVMConstantRegister index2(Compiler::StaticType::Number, 1); - readSource2->workList = &sourceList; - readSource2->args.push_back({ Compiler::StaticType::Number, &index2 }); - instructionList.addInstruction(readSource2); - - auto appendTarget2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendTarget2->workList = &targetList; - appendTarget2->args.push_back({ Compiler::StaticType::Unknown, readSource2Reg.get() }); - instructionList.addInstruction(appendTarget2); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&targetList, funcCall.get(), Compiler::StaticType::String, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_InLoop_ConsistentType_NonEmptySource) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("source", ""); - List targetList("target", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // Append number to source - auto appendSource = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister sourceValue(Compiler::StaticType::Number, 3.14); - appendSource->workList = &sourceList; - appendSource->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); - instructionList.addInstruction(appendSource); - - // Read from source and write to target - auto readSource = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readSourceReg = std::make_shared(Compiler::StaticType::Unknown); - readSourceReg->instruction = readSource; - readSourceReg->isRawValue = false; - readSource->functionReturnReg = readSourceReg.get(); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - readSource->workList = &sourceList; - readSource->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(readSource); - - auto appendTarget = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendTarget->workList = &targetList; - appendTarget->args.push_back({ Compiler::StaticType::Unknown, readSourceReg.get() }); - instructionList.addInstruction(appendTarget); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&targetList, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_InLoop_ConsistentType_EmptySource) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("source", ""); - List targetList("target", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &sourceList; - instructionList.addInstruction(clearList); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // Append number to source - auto appendSource = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister sourceValue(Compiler::StaticType::Number, 3.14); - appendSource->workList = &sourceList; - appendSource->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); - instructionList.addInstruction(appendSource); - - // Read from source and write to target - auto readSource = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readSourceReg = std::make_shared(Compiler::StaticType::Unknown); - readSourceReg->instruction = readSource; - readSourceReg->isRawValue = false; - readSource->functionReturnReg = readSourceReg.get(); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - readSource->workList = &sourceList; - readSource->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(readSource); - - auto appendTarget = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendTarget->workList = &targetList; - appendTarget->args.push_back({ Compiler::StaticType::Unknown, readSourceReg.get() }); - instructionList.addInstruction(appendTarget); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&targetList, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_Circular_TypeSafety) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List listA("listA", ""); - List listB("listB", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listA; - instructionList.addInstruction(clearList); - - clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listB; - instructionList.addInstruction(clearList); - - // Initialize listA with a number - auto appendA1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister valueA1(Compiler::StaticType::Number, 10); - appendA1->workList = &listA; - appendA1->args.push_back({ Compiler::StaticType::Unknown, &valueA1 }); - instructionList.addInstruction(appendA1); - - // Read from listA and write to listB - auto readA = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readAReg = std::make_shared(Compiler::StaticType::Unknown); - readAReg->instruction = readA; - readAReg->isRawValue = false; - readA->functionReturnReg = readAReg.get(); - LLVMConstantRegister indexA(Compiler::StaticType::Number, 0); - readA->workList = &listA; - readA->args.push_back({ Compiler::StaticType::Number, &indexA }); - instructionList.addInstruction(readA); - - auto appendB = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendB->workList = &listB; - appendB->args.push_back({ Compiler::StaticType::Unknown, readAReg.get() }); - instructionList.addInstruction(appendB); - - // Read from listB and write back to listA (circular dependency) - auto readB = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readBReg = std::make_shared(Compiler::StaticType::Unknown); - readBReg->instruction = readB; - readBReg->isRawValue = false; - readB->functionReturnReg = readBReg.get(); - LLVMConstantRegister indexB(Compiler::StaticType::Number, 0); - readB->workList = &listB; - readB->args.push_back({ Compiler::StaticType::Number, &indexB }); - instructionList.addInstruction(readB); - - auto appendA2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendA2->workList = &listA; - appendA2->args.push_back({ Compiler::StaticType::Unknown, readBReg.get() }); - instructionList.addInstruction(appendA2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&listA, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_Circular_TypeSafety_InLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List listA("listA", ""); - List listB("listB", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listA; - instructionList.addInstruction(clearList); - - clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listB; - instructionList.addInstruction(clearList); - - // Initialize listA with a number - auto appendA1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister valueA1(Compiler::StaticType::Number, 10); - appendA1->workList = &listA; - appendA1->args.push_back({ Compiler::StaticType::Unknown, &valueA1 }); - instructionList.addInstruction(appendA1); - - // Read from listA and write to listB - auto readA = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readAReg = std::make_shared(Compiler::StaticType::Unknown); - readAReg->instruction = readA; - readAReg->isRawValue = false; - readA->functionReturnReg = readAReg.get(); - LLVMConstantRegister indexA(Compiler::StaticType::Number, 0); - readA->workList = &listA; - readA->args.push_back({ Compiler::StaticType::Number, &indexA }); - instructionList.addInstruction(readA); - - auto appendB = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendB->workList = &listB; - appendB->args.push_back({ Compiler::StaticType::Unknown, readAReg.get() }); - instructionList.addInstruction(appendB); - - // Read from listB and write back to listA (circular dependency) - auto readB = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readBReg = std::make_shared(Compiler::StaticType::Unknown); - readBReg->instruction = readB; - readBReg->isRawValue = false; - readB->functionReturnReg = readBReg.get(); - LLVMConstantRegister indexB(Compiler::StaticType::Number, 0); - readB->workList = &listB; - readB->args.push_back({ Compiler::StaticType::Number, &indexB }); - instructionList.addInstruction(readB); - - auto appendA2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendA2->workList = &listA; - appendA2->args.push_back({ Compiler::StaticType::Unknown, readBReg.get() }); - instructionList.addInstruction(appendA2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listType(&listA, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_Circular_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List listA("listA", ""); - List listB("listB", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listA; - instructionList.addInstruction(clearList); - - clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listB; - instructionList.addInstruction(clearList); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // Initialize listA with a number - auto appendA1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister valueA1(Compiler::StaticType::Number, 10); - appendA1->workList = &listA; - appendA1->args.push_back({ Compiler::StaticType::Unknown, &valueA1 }); - instructionList.addInstruction(appendA1); - - // Read from listA and write to listB - auto readA = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readAReg = std::make_shared(Compiler::StaticType::Unknown); - readAReg->instruction = readA; - readAReg->isRawValue = false; - readA->functionReturnReg = readAReg.get(); - LLVMConstantRegister indexA(Compiler::StaticType::Number, 0); - readA->workList = &listA; - readA->args.push_back({ Compiler::StaticType::Number, &indexA }); - instructionList.addInstruction(readA); - - auto appendB = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendB->workList = &listB; - appendB->args.push_back({ Compiler::StaticType::Unknown, readAReg.get() }); - instructionList.addInstruction(appendB); - - // Create type conflict by appending string to listB - auto appendB2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister valueB2(Compiler::StaticType::String, "conflict"); - appendB2->workList = &listB; - appendB2->args.push_back({ Compiler::StaticType::Unknown, &valueB2 }); - instructionList.addInstruction(appendB2); - - // Read from listB and write back to listA (circular dependency with type conflict) - auto readB = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readBReg = std::make_shared(Compiler::StaticType::Unknown); - readBReg->instruction = readB; - readBReg->isRawValue = false; - readB->functionReturnReg = readBReg.get(); - LLVMConstantRegister indexB(Compiler::StaticType::Number, 0); - readB->workList = &listB; - readB->args.push_back({ Compiler::StaticType::Number, &indexB }); - instructionList.addInstruction(readB); - - auto appendA2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendA2->workList = &listA; - appendA2->args.push_back({ Compiler::StaticType::Unknown, readBReg.get() }); - instructionList.addInstruction(appendA2); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown due to circular dependency with type conflicts - ASSERT_EQ(analyzer.listType(&listA, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_Circular_SingleList_KnownType_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Initialize list with a number - auto append1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 10); - append1->workList = &list; - append1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(append1); - - // Read from list and write to list - auto read = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readReg = std::make_shared(Compiler::StaticType::Unknown); - readReg->instruction = read; - readReg->isRawValue = false; - read->functionReturnReg = readReg.get(); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - read->workList = &list; - read->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(read); - - auto append2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - append2->workList = &list; - append2->args.push_back({ Compiler::StaticType::Unknown, readReg.get() }); - instructionList.addInstruction(append2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_Circular_SingleList_KnownType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Read from list and write to list - auto read = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readReg = std::make_shared(Compiler::StaticType::Unknown); - readReg->instruction = read; - readReg->isRawValue = false; - read->functionReturnReg = readReg.get(); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - read->workList = &list; - read->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(read); - - auto append2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - append2->workList = &list; - append2->args.push_back({ Compiler::StaticType::Unknown, readReg.get() }); - instructionList.addInstruction(append2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_Circular_SingleList_UnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Read from list and write to list - auto read = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readReg = std::make_shared(Compiler::StaticType::Unknown); - readReg->instruction = read; - readReg->isRawValue = false; - read->functionReturnReg = readReg.get(); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - read->workList = &list; - read->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(read); - - auto append2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - append2->workList = &list; - append2->args.push_back({ Compiler::StaticType::Unknown, readReg.get() }); - instructionList.addInstruction(append2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Unknown, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListType, CrossListDependency_ChainedReads_TypePropagation) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List listA("listA", ""); - List listB("listB", ""); - List listC("listC", ""); - - // Clear lists - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listA; - instructionList.addInstruction(clearList); - - clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &listB; - instructionList.addInstruction(clearList); - - // Initialize listA with a boolean - auto appendA = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister valueA(Compiler::StaticType::Bool, false); - appendA->workList = &listA; - appendA->args.push_back({ Compiler::StaticType::Unknown, &valueA }); - instructionList.addInstruction(appendA); - - // Read from listA and write to listB - auto readA = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readAReg = std::make_shared(Compiler::StaticType::Unknown); - readAReg->instruction = readA; - readAReg->isRawValue = false; - readA->functionReturnReg = readAReg.get(); - LLVMConstantRegister indexA(Compiler::StaticType::Number, 0); - readA->workList = &listA; - readA->args.push_back({ Compiler::StaticType::Number, &indexA }); - instructionList.addInstruction(readA); - - auto appendB = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendB->workList = &listB; - appendB->args.push_back({ Compiler::StaticType::Unknown, readAReg.get() }); - instructionList.addInstruction(appendB); - - // Read from listB and write to listC - auto readB = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readBReg = std::make_shared(Compiler::StaticType::Unknown); - readBReg->instruction = readB; - readBReg->isRawValue = false; - readB->functionReturnReg = readBReg.get(); - LLVMConstantRegister indexB(Compiler::StaticType::Number, 0); - readB->workList = &listB; - readB->args.push_back({ Compiler::StaticType::Number, &indexB }); - instructionList.addInstruction(readB); - - auto appendC = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendC->workList = &listC; - appendC->args.push_back({ Compiler::StaticType::Unknown, readBReg.get() }); - instructionList.addInstruction(appendC); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Type should propagate through the chain: A -> B -> C - ASSERT_EQ(analyzer.listType(&listC, funcCall.get(), Compiler::StaticType::String, true), Compiler::StaticType::Bool); -} diff --git a/test/llvm/type_analyzer/listtypeafterbranch_test.cpp b/test/llvm/type_analyzer/listtypeafterbranch_test.cpp deleted file mode 100644 index bf9c667b..00000000 --- a/test/llvm/type_analyzer/listtypeafterbranch_test.cpp +++ /dev/null @@ -1,2678 +0,0 @@ -#include -#include -#include -#include -#include -#include - -using namespace libscratchcpp; - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, NullParams) -{ - LLVMTypeAnalyzer analyzer; - ASSERT_EQ(analyzer.listTypeAfterBranch(nullptr, nullptr, Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, NullList) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(nullptr, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, NullStartInstruction) -{ - LLVMTypeAnalyzer analyzer; - List list("", ""); - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, nullptr, Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, EmptyLoop) -{ - LLVMTypeAnalyzer analyzer; - List list("", ""); - LLVMInstructionList instructionList; - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, EmptyIfStatement) -{ - LLVMTypeAnalyzer analyzer; - List list("", ""); - LLVMInstructionList instructionList; - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, EmptyIfElseStatement_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - List list("", ""); - LLVMInstructionList instructionList; - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, EmptyIfElseStatement_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - List list("", ""); - LLVMInstructionList instructionList; - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, elseStart.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, EmptyLoopUnknownType) -{ - LLVMTypeAnalyzer analyzer; - List list("", ""); - LLVMInstructionList instructionList; - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Unknown, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, NoWriteOperations) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendSameTypeNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendSameTypeBool) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendSameTypeString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendStringOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "1.25"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Although string is appended, it's constant and represents a number - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendMultipleLists) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list1("", ""); - List list2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Append to list1 - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 6); - appendList1->workList = &list1; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Append to list2 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list2; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list1, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendFromUnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMRegister value(Compiler::StaticType::Unknown); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendToUnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Unknown, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendUnknownToUnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMRegister value(Compiler::StaticType::Unknown); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Unknown, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleInsertSameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Number, &index }); - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleReplaceSameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Number, &index }); - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeNumberToString_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeNumberToString_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeStringToNumber_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeStringToNumber_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeNumberToBool_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeNumberToBool_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeBoolToNumber_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Bool, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeBoolToNumber_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeBoolToString_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "abc"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Bool, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeBoolToString_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "abc"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeStringToBool_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, false); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, true), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentTypeStringToBool_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, false); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendSameType_IfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentType_IfStatement_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleAppendDifferentType_IfStatement_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleInsertDifferentType_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Number, &index }); - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Insert might be a no-op depending on the index - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleInsertDifferentType_Empty_UnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Number, &index }); - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Insert might be a no-op depending on the index - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Unknown, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleInsertDifferentType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Number, &index }); - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleReplaceDifferentType_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Number, &index }); - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Since the list is empty, it's safe to return any type - // Let's return string in this case as the replace instruction indicates that string is used later - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, SingleReplaceDifferentType_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto appendList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 1); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Number, &index }); - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleAppendsDifferentTypes_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First append: string "abc" (incompatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append: number 5.25 (compatible with Number pre-loop type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because first append creates type conflict with pre-loop Number type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleAppendsDifferentTypes_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First append: string "abc" (incompatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append: number 5.25 (compatible with Number pre-loop type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because first append creates type conflict with pre-loop Number type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleAppendsWithStringOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First append: number 2 (compatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 2); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append: string "3.14" (optimized to Number, compatible with pre-loop type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "3.14"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Number because final append is optimized to Number type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleAppendsDifferentTypes_IfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - // First append: string "abc" (incompatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append: number 5.25 (compatible with Number type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - // Should return Unknown because first append creates type conflict with Number type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleAppendsDifferentTypes_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - // First append: string "abc" (incompatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append: number 5.25 (compatible with Number type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Else branch append: string "test" (incompatible with Number type) - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "test"); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - // Should return Unknown because first append in if branch creates type conflict - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleAppendsSameType_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(start); - - // First append: string "abc" (incompatible with Number) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Else branch first append: string "abc" (incompatible with Number) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "abc"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Else branch second append: number 5.25 (compatible with Number type) - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 5.25); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(end); - - // Should return Unknown because first append in else branch creates type conflict - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, elseStart.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, IfStatementInLoop_TypeChangeInIfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First append - changes type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second append - does not change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Returns unknown type because the type-changing branch might or might not run - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, IfStatementInLoop_TypeChangeInElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First append - does not change type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second append - changes type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Returns unknown type because the type-changing branch might or might not run - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, IfStatementInLoop_IfElseWithoutTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // First append - does not change type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Second append - does not change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleInsertsDifferentTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First insert: number - auto insertList1 = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index1(Compiler::StaticType::Number, 1); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - insertList1->workList = &list; - insertList1->args.push_back({ Compiler::StaticType::Number, &index1 }); - insertList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(insertList1); - - // Second insert: string (creates type conflict) - auto insertList2 = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 1); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - insertList2->workList = &list; - insertList2->args.push_back({ Compiler::StaticType::Number, &index2 }); - insertList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(insertList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleReplacesDifferentTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First replace: compatible with Bool - auto replaceList1 = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index1(Compiler::StaticType::Number, 1); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - replaceList1->workList = &list; - replaceList1->args.push_back({ Compiler::StaticType::Number, &index1 }); - replaceList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(replaceList1); - - // Second replace: incompatible with Bool - auto replaceList2 = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 2); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - replaceList2->workList = &list; - replaceList2->args.push_back({ Compiler::StaticType::Number, &index2 }); - replaceList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(replaceList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CombinedAppendInsertReplace_SameTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First: append number (compatible with Number) - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister appendValue(Compiler::StaticType::Number, 42); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &appendValue }); - instructionList.addInstruction(appendList); - - // Second: insert number (compatible with Number) - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister insertIndex(Compiler::StaticType::Number, 1); - LLVMConstantRegister insertValue(Compiler::StaticType::Number, 5); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &insertIndex }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &insertValue }); - instructionList.addInstruction(insertList); - - // Third: replace with number (compatible with Number) - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister replaceIndex(Compiler::StaticType::Number, 2); - LLVMConstantRegister replaceValue(Compiler::StaticType::Number, 3.14); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &replaceIndex }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &replaceValue }); - instructionList.addInstruction(replaceList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Number because only numbers are used - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CombinedAppendInsertReplace_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First: append number (compatible with Number) - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister appendValue(Compiler::StaticType::Number, 42); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &appendValue }); - instructionList.addInstruction(appendList); - - // Second: insert string (creates type conflict) - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister insertIndex(Compiler::StaticType::Number, 0); - LLVMConstantRegister insertValue(Compiler::StaticType::String, "hello"); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &insertIndex }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &insertValue }); - instructionList.addInstruction(insertList); - - // Third: replace with number (still conflicted) - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister replaceIndex(Compiler::StaticType::Number, 1); - LLVMConstantRegister replaceValue(Compiler::StaticType::Number, 3.14); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &replaceIndex }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &replaceValue }); - instructionList.addInstruction(replaceList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because insert created type conflict - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, NestedLoopWithTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List append inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List append inside inner loop (creates type conflict) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerLoop.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, NestedLoopWithoutTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); - instructionList.addInstruction(outerLoop); - - // List append inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List append inside inner loop (same type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.5); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerLoop.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, NestedLoopWithTypeChangeBeforeLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - instructionList.addInstruction(outerLoop); - - // List append inside outer loop (creates type conflict) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop); - - // List append inside inner loop - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Returns unknown type because first append creates type conflict - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerLoop.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, IfStatementInLoopWithTypeChangeBeforeLoop_TypeChangeInIfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - instructionList.addInstruction(outerLoop); - - // List append inside outer loop (creates type conflict) - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner if statement begin - auto innerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf); - - // List append inside inner if statement if branch - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner if statement else branch begin - auto innerElse = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(innerElse); - - // Inner if statement end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - // Returns unknown type because first append already created type conflict - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerLoop.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleNestedLoopsWithTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoop); - - // List append inside outer loop - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop1); - - // List append inside inner loop 1 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoop2); - - // List append inside inner loop 2 (creates type conflict) - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd2); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerEnd1); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerLoop.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleNestedIfStatementsWithTypeChange_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer if statement begin - auto outerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(outerIf); - - // List append inside outer if statement - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner if statement 1 begin - auto innerIf1 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf1); - - // List append inside inner if statement 1 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner if statement 2 begin - auto innerIf2 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf2); - - // List append inside inner if statement 2 (creates type conflict) - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner if statement 2 else branch - auto innerElse2 = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(innerElse2); - - // List append inside inner if statement 2 else branch - auto appendList4 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value4(Compiler::StaticType::Number, 4); - appendList4->workList = &list; - appendList4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); - instructionList.addInstruction(appendList4); - - // Inner if statement 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd2); - - // Inner if statement 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd1); - - // Outer if statement end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerIf.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleNestedIfStatementsWithTypeChange_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer if statement begin - auto outerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(outerIf); - - // List append inside outer if statement - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner if statement 1 begin - auto innerIf1 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf1); - - // List append inside inner if statement 1 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner if statement 2 begin - auto innerIf2 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf2); - - // List append inside inner if statement 2 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 4); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner if statement 2 else branch - auto innerElse2 = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(innerElse2); - - // List append inside inner if statement 2 else branch (creates type conflict) - auto appendList4 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value4(Compiler::StaticType::String, "abc"); - appendList4->workList = &list; - appendList4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); - instructionList.addInstruction(appendList4); - - // Inner if statement 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd2); - - // Inner if statement 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd1); - - // Outer if statement end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerIf.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, MultipleNestedIfStatementsWithoutTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - // Outer if statement begin - auto outerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(outerIf); - - // List append inside outer if statement - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Inner if statement 1 begin - auto innerIf1 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf1); - - // List append inside inner if statement 1 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - // Inner if statement 2 begin - auto innerIf2 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(innerIf2); - - // List append inside inner if statement 2 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 4); - appendList3->workList = &list; - appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - instructionList.addInstruction(appendList3); - - // Inner if statement 2 else branch - auto innerElse2 = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(innerElse2); - - // List append inside inner if statement 2 else branch (same type) - auto appendList4 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value4(Compiler::StaticType::Number, 0); - appendList4->workList = &list; - appendList4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); - instructionList.addInstruction(appendList4); - - // Inner if statement 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd2); - - // Inner if statement 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(innerEnd1); - - // Outer if statement end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, outerIf.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, AppendToEmptyListInIfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Append in if branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - the list might not be empty anymore - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because the list might not be empty when the type changes - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, AppendToEmptyListInElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Append in else branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - the list might not be empty anymore - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because the list might not be empty when the type changes - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_NewTypeAfterClear_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list in the middle of the loop - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Append after clear - this should set the new type - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return String because the list was empty and append sets the type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_NewTypeAfterClear_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list in the middle of the loop - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Append after clear - this should set the new type - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return String because clear makes the list empty, then append sets the type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_MultipleAppendsAfterClear) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // First append after clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append after clear (same type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 3.14); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Number because all appends after clear are the same type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_TypeConflictAfterClear) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // First append after clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second append after clear (different type - creates conflict) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because of type conflict after clear - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_WithInsertAndReplace) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Insert after clear - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister insertIndex(Compiler::StaticType::Number, 0); - LLVMConstantRegister insertValue(Compiler::StaticType::Bool, true); - insertList->workList = &list; - insertList->args.push_back({ Compiler::StaticType::Number, &insertIndex }); - insertList->args.push_back({ Compiler::StaticType::Unknown, &insertValue }); - instructionList.addInstruction(insertList); - - // Replace after insert - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister replaceIndex(Compiler::StaticType::Number, 0); - LLVMConstantRegister replaceValue(Compiler::StaticType::Bool, false); - replaceList->workList = &list; - replaceList->args.push_back({ Compiler::StaticType::Number, &replaceIndex }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, &replaceValue }); - instructionList.addInstruction(replaceList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Bool because all operations after clear use Bool type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_ConditionalClear) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear the list conditionally - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Append after clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "cleared"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Else branch - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Append without clearing - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown due to type conflict between branches - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendAfterClearingInIfBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because the clearing branch might or might not run - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendAfterClearingInIfBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 42); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Number because there are no type conflicts - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendAfterClearingInElseBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Else branch - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because the clearing branch might or might not run - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendAfterClearingInElseBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Else branch - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Clear the list - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 42); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Number because there are no type conflicts - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_MultipleClearsAndWrites) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // First clear - auto clearList1 = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList1->workList = &list; - instructionList.addInstruction(clearList1); - - // Append after first clear - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "first"); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Second clear - auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList2->workList = &list; - instructionList.addInstruction(clearList2); - - // Append after second clear (different type) - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Number because the last clear+append sequence determines the final type - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_ClearWithoutSubsequentWrite) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear the list but don't write anything after - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return previous type since no writes occurred after clear - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_ClearInNestedIf) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Nested if statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Clear and write in nested if - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); - instructionList.addInstruction(appendList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown because the type-changing branch might or might not run - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendToEmptyListAndClearInIfBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Append in if branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in if branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendToEmptyListAndClearInIfBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Append in if branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in if branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendToEmptyListAndClearInElseBranch_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Append in else branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in else branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, ClearListInLoop_AppendToEmptyListAndClearInElseBranch_DifferentType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Append in else branch - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Clear the list in else branch - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - // Append after if statement - should change type - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return String because the list remains empty after the if statement - ASSERT_EQ(analyzer.listTypeAfterBranch(&list, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CrossListDependency_ReadFromTypedList) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("", ""); - List targetList("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear sourceList - auto clear = std::make_shared(LLVMInstruction::Type::ClearList, false); - clear->workList = &sourceList; - instructionList.addInstruction(clear); - - // Set sourceList type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &sourceList; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Read from sourceList - auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - getItem->workList = &sourceList; - getItem->args.push_back({ Compiler::StaticType::Number, &index }); - auto itemRegister = std::make_shared(Compiler::StaticType::Unknown); - itemRegister->instruction = getItem; - itemRegister->isRawValue = false; - getItem->functionReturnReg = itemRegister.get(); - instructionList.addInstruction(getItem); - - // Append the read value to targetList - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList2->workList = &targetList; - appendList2->args.push_back({ Compiler::StaticType::Unknown, itemRegister.get() }); - instructionList.addInstruction(appendList2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Target list type depends on source list type - ASSERT_EQ(analyzer.listTypeAfterBranch(&targetList, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CrossListDependency_ReadFromUnknownList) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("", ""); - List targetList("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Read from sourceList - auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - getItem->workList = &sourceList; - getItem->args.push_back({ Compiler::StaticType::Number, &index }); - auto itemRegister = std::make_shared(Compiler::StaticType::Unknown); - itemRegister->instruction = getItem; - itemRegister->isRawValue = false; - getItem->functionReturnReg = itemRegister.get(); - instructionList.addInstruction(getItem); - - // Append the read value to targetList - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList->workList = &targetList; - appendList->args.push_back({ Compiler::StaticType::Unknown, itemRegister.get() }); - instructionList.addInstruction(appendList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&targetList, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CrossListDependency_ChainedReads_KnownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list1("", ""); - List list2("", ""); - List list3("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Clear list1 - auto clear = std::make_shared(LLVMInstruction::Type::ClearList, false); - clear->workList = &list1; - instructionList.addInstruction(clear); - - // Clear list2 - clear = std::make_shared(LLVMInstruction::Type::ClearList, false); - clear->workList = &list2; - instructionList.addInstruction(clear); - - // Set list1 type - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - appendList1->workList = &list1; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(appendList1); - - // Read from list1 - auto getItem1 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index1(Compiler::StaticType::Number, 0); - getItem1->workList = &list1; - getItem1->args.push_back({ Compiler::StaticType::Number, &index1 }); - auto itemRegister1 = std::make_shared(Compiler::StaticType::Unknown); - itemRegister1->instruction = getItem1; - itemRegister1->isRawValue = false; - getItem1->functionReturnReg = itemRegister1.get(); - instructionList.addInstruction(getItem1); - - // Append to list2 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList2->workList = &list2; - appendList2->args.push_back({ Compiler::StaticType::Unknown, itemRegister1.get() }); - instructionList.addInstruction(appendList2); - - // Read from list2 - auto getItem2 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 0); - getItem2->workList = &list2; - getItem2->args.push_back({ Compiler::StaticType::Number, &index2 }); - auto itemRegister2 = std::make_shared(Compiler::StaticType::Unknown); - itemRegister2->instruction = getItem2; - itemRegister2->isRawValue = false; - getItem2->functionReturnReg = itemRegister2.get(); - instructionList.addInstruction(getItem2); - - // Append to list3 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList3->workList = &list3; - appendList3->args.push_back({ Compiler::StaticType::Unknown, itemRegister2.get() }); - instructionList.addInstruction(appendList3); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - ASSERT_EQ(analyzer.listTypeAfterBranch(&list3, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CrossListDependency_ChainedReads_UnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List list1("", ""); - List list2("", ""); - List list3("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Read from list1 - auto getItem1 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index1(Compiler::StaticType::Number, 0); - getItem1->workList = &list1; - getItem1->args.push_back({ Compiler::StaticType::Number, &index1 }); - auto itemRegister1 = std::make_shared(Compiler::StaticType::Unknown); - itemRegister1->instruction = getItem1; - itemRegister1->isRawValue = false; - getItem1->functionReturnReg = itemRegister1.get(); - instructionList.addInstruction(getItem1); - - // Append to list2 - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList2->workList = &list2; - appendList2->args.push_back({ Compiler::StaticType::Unknown, itemRegister1.get() }); - instructionList.addInstruction(appendList2); - - // Read from list2 - auto getItem2 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 0); - getItem2->workList = &list2; - getItem2->args.push_back({ Compiler::StaticType::Number, &index2 }); - auto itemRegister2 = std::make_shared(Compiler::StaticType::Unknown); - itemRegister2->instruction = getItem2; - itemRegister2->isRawValue = false; - getItem2->functionReturnReg = itemRegister2.get(); - instructionList.addInstruction(getItem2); - - // Append to list3 - auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList3->workList = &list3; - appendList3->args.push_back({ Compiler::StaticType::Unknown, itemRegister2.get() }); - instructionList.addInstruction(appendList3); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // list3 should have Unknown type due to chained cross-list dependencies - ASSERT_EQ(analyzer.listTypeAfterBranch(&list3, start.get(), Compiler::StaticType::Number, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CrossListDependency_ConditionalRead) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("", ""); - List targetList("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // If statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // Read from sourceList in if branch - auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - getItem->workList = &sourceList; - getItem->args.push_back({ Compiler::StaticType::Number, &index }); - auto itemRegister = std::make_shared(Compiler::StaticType::Unknown); - itemRegister->instruction = getItem; - itemRegister->isRawValue = false; - getItem->functionReturnReg = itemRegister.get(); - instructionList.addInstruction(getItem); - - // Append to targetList - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList->workList = &targetList; - appendList->args.push_back({ Compiler::StaticType::Unknown, itemRegister.get() }); - instructionList.addInstruction(appendList); - - // Else branch - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Direct append of constant value - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister directValue(Compiler::StaticType::Number, 42); - appendList2->workList = &targetList; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &directValue }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should return Unknown due to cross-list dependency and conditional execution - ASSERT_EQ(analyzer.listTypeAfterBranch(&targetList, start.get(), Compiler::StaticType::String, true), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_ListTypeAfterBranch, CrossListDependency_ReadWithInsertAndReplace) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("", ""); - List targetList("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(start); - - // Read from sourceList - auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - getItem->workList = &sourceList; - getItem->args.push_back({ Compiler::StaticType::Number, &index }); - auto itemRegister = std::make_shared(Compiler::StaticType::Unknown); - itemRegister->instruction = getItem; - itemRegister->isRawValue = false; - getItem->functionReturnReg = itemRegister.get(); - instructionList.addInstruction(getItem); - - // Insert the read value to targetList - auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); - LLVMConstantRegister insertIndex(Compiler::StaticType::Number, 0); - insertList->workList = &targetList; - insertList->args.push_back({ Compiler::StaticType::Number, &insertIndex }); - insertList->args.push_back({ Compiler::StaticType::Unknown, itemRegister.get() }); - instructionList.addInstruction(insertList); - - // Replace with another read from the same list - auto getItem2 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - LLVMConstantRegister index2(Compiler::StaticType::Number, 1); - getItem2->workList = &sourceList; - getItem2->args.push_back({ Compiler::StaticType::Number, &index2 }); - auto itemRegister2 = std::make_shared(Compiler::StaticType::Unknown); - itemRegister2->instruction = getItem2; - itemRegister2->isRawValue = false; - getItem2->functionReturnReg = itemRegister2.get(); - instructionList.addInstruction(getItem2); - - auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); - LLVMConstantRegister replaceIndex(Compiler::StaticType::Number, 0); - replaceList->workList = &targetList; - replaceList->args.push_back({ Compiler::StaticType::Number, &replaceIndex }); - replaceList->args.push_back({ Compiler::StaticType::Unknown, itemRegister2.get() }); - instructionList.addInstruction(replaceList); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(end); - - // Should have Unknown type due to cross-list dependencies - ASSERT_EQ(analyzer.listTypeAfterBranch(&targetList, start.get(), Compiler::StaticType::String, true), Compiler::StaticType::Unknown); -} diff --git a/test/llvm/type_analyzer/mixed_test.cpp b/test/llvm/type_analyzer/mixed_test.cpp deleted file mode 100644 index 7d7af430..00000000 --- a/test/llvm/type_analyzer/mixed_test.cpp +++ /dev/null @@ -1,492 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -using namespace libscratchcpp; - -TEST(LLVMTypeAnalyzer_MixedTest, VariableToList_SimpleTransfer_NonEmpty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - Variable sourceVar("sourceVar", ""); - List targetList("targetList", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &targetList; - instructionList.addInstruction(clearList); - - // Write to variable - auto writeVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister varValue(Compiler::StaticType::String, "hello"); - writeVar->workVariable = &sourceVar; - writeVar->args.push_back({ Compiler::StaticType::Unknown, &varValue }); - instructionList.addInstruction(writeVar); - - // Read from variable and write to list - auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVarReg = std::make_shared(Compiler::StaticType::Unknown); - readVar->functionReturnReg = readVarReg.get(); - readVarReg->instruction = readVar; - readVar->workVariable = &sourceVar; - instructionList.addInstruction(readVar); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList->workList = &targetList; - appendList->args.push_back({ Compiler::StaticType::Unknown, readVarReg.get() }); - instructionList.addInstruction(appendList); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // List type should match variable type - ASSERT_EQ(analyzer.listType(&targetList, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_MixedTest, ListToVariable_SimpleTransfer_Empty) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("sourceList", ""); - Variable targetVar("targetVar", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &sourceList; - instructionList.addInstruction(clearList); - - // Write to list - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister listValue(Compiler::StaticType::Bool, true); - appendList->workList = &sourceList; - appendList->args.push_back({ Compiler::StaticType::Unknown, &listValue }); - instructionList.addInstruction(appendList); - - // Read from list and write to variable - auto readList = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readListReg = std::make_shared(Compiler::StaticType::Unknown); - readList->functionReturnReg = readListReg.get(); - readListReg->instruction = readList; - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - readList->workList = &sourceList; - readList->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(readList); - - auto writeVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - writeVar->workVariable = &targetVar; - writeVar->args.push_back({ Compiler::StaticType::Unknown, readListReg.get() }); - instructionList.addInstruction(writeVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Variable type should match list type - ASSERT_EQ(analyzer.variableType(&targetVar, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_MixedTest, CircularVarListDependency_TypeSafety) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - Variable var("var", ""); - List list("list", ""); - - // Initialize variable with number - auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - writeVar1->workVariable = &var; - writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(writeVar1); - - // Read from variable and write to list - auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVarReg = std::make_shared(Compiler::StaticType::Unknown); - readVar->functionReturnReg = readVarReg.get(); - readVarReg->instruction = readVar; - readVar->workVariable = &var; - instructionList.addInstruction(readVar); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, readVarReg.get() }); - instructionList.addInstruction(appendList); - - // Read from list and write back to variable (circular dependency) - auto readList = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readListReg = std::make_shared(Compiler::StaticType::Unknown); - readList->functionReturnReg = readListReg.get(); - readListReg->instruction = readList; - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - readList->workList = &list; - readList->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(readList); - - auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - writeVar2->workVariable = &var; - writeVar2->args.push_back({ Compiler::StaticType::Unknown, readListReg.get() }); - instructionList.addInstruction(writeVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should handle circular dependency gracefully - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_MixedTest, LoopWithVarListInteraction_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - Variable var("var", ""); - List list("list", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // First iteration: write string to variable - auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "text"); - writeVar1->workVariable = &var; - writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(writeVar1); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(loopStart); - - // Read from variable and write to list - auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVarReg = std::make_shared(Compiler::StaticType::Unknown); - readVar->functionReturnReg = readVarReg.get(); - readVarReg->instruction = readVar; - readVar->workVariable = &var; - instructionList.addInstruction(readVar); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, readVarReg.get() }); - instructionList.addInstruction(appendList); - - // Second iteration: write number to variable (creates type conflict) - auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - writeVar2->workVariable = &var; - writeVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(writeVar2); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(loopEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown due to type conflict - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_MixedTest, ConditionalVarListTransfer_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - Variable var("var", ""); - List list("list", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - instructionList.addInstruction(ifStart); - - // If branch: write number to variable, transfer to list - auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 123); - writeVar1->workVariable = &var; - writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(writeVar1); - - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVar1Reg = std::make_shared(Compiler::StaticType::Unknown); - readVar1->functionReturnReg = readVar1Reg.get(); - readVar1Reg->instruction = readVar1; - readVar1->workVariable = &var; - instructionList.addInstruction(readVar1); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, readVar1Reg.get() }); - instructionList.addInstruction(appendList1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - instructionList.addInstruction(elseStart); - - // Else branch: write string to variable, transfer to list - auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - writeVar2->workVariable = &var; - writeVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(writeVar2); - - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVar2Reg = std::make_shared(Compiler::StaticType::Unknown); - readVar2->functionReturnReg = readVar2Reg.get(); - readVar2Reg->instruction = readVar2; - readVar2->workVariable = &var; - instructionList.addInstruction(readVar2); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, readVar2Reg.get() }); - instructionList.addInstruction(appendList2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - instructionList.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown due to conditional type conflicts - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Number, false), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_MixedTest, MultipleVarsToSingleList_TypePropagation) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - Variable var1("var1", ""); - Variable var2("var2", ""); - List list("list", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - // Initialize first variable with boolean - auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - writeVar1->workVariable = &var1; - writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(writeVar1); - - // Initialize second variable with same type - auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Bool, false); - writeVar2->workVariable = &var2; - writeVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(writeVar2); - - // Read from first variable and write to list - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVar1Reg = std::make_shared(Compiler::StaticType::Unknown); - readVar1->functionReturnReg = readVar1Reg.get(); - readVar1Reg->instruction = readVar1; - readVar1->workVariable = &var1; - instructionList.addInstruction(readVar1); - - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList1->workList = &list; - appendList1->args.push_back({ Compiler::StaticType::Unknown, readVar1Reg.get() }); - instructionList.addInstruction(appendList1); - - // Read from second variable and write to list - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVar2Reg = std::make_shared(Compiler::StaticType::Unknown); - readVar2->functionReturnReg = readVar2Reg.get(); - readVar2Reg->instruction = readVar2; - readVar2->workVariable = &var2; - instructionList.addInstruction(readVar2); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList2->workList = &list; - appendList2->args.push_back({ Compiler::StaticType::Unknown, readVar2Reg.get() }); - instructionList.addInstruction(appendList2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // List should have Bool type from both variables - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_MixedTest, SingleListToMultipleVars_TypePropagation) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - List sourceList("sourceList", ""); - Variable var1("var1", ""); - Variable var2("var2", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &sourceList; - instructionList.addInstruction(clearList); - - // Initialize list with string values - auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister listValue1(Compiler::StaticType::String, "first"); - appendList1->workList = &sourceList; - appendList1->args.push_back({ Compiler::StaticType::Unknown, &listValue1 }); - instructionList.addInstruction(appendList1); - - auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); - LLVMConstantRegister listValue2(Compiler::StaticType::String, "second"); - appendList2->workList = &sourceList; - appendList2->args.push_back({ Compiler::StaticType::Unknown, &listValue2 }); - instructionList.addInstruction(appendList2); - - // Read first item and write to first variable - auto readList1 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readList1Reg = std::make_shared(Compiler::StaticType::Unknown); - readList1->functionReturnReg = readList1Reg.get(); - readList1Reg->instruction = readList1; - LLVMConstantRegister index1(Compiler::StaticType::Number, 0); - readList1->workList = &sourceList; - readList1->args.push_back({ Compiler::StaticType::Number, &index1 }); - instructionList.addInstruction(readList1); - - auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - writeVar1->workVariable = &var1; - writeVar1->args.push_back({ Compiler::StaticType::Unknown, readList1Reg.get() }); - instructionList.addInstruction(writeVar1); - - // Read second item and write to second variable - auto readList2 = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readList2Reg = std::make_shared(Compiler::StaticType::Unknown); - readList2->functionReturnReg = readList2Reg.get(); - readList2Reg->instruction = readList2; - LLVMConstantRegister index2(Compiler::StaticType::Number, 1); - readList2->workList = &sourceList; - readList2->args.push_back({ Compiler::StaticType::Number, &index2 }); - instructionList.addInstruction(readList2); - - auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - writeVar2->workVariable = &var2; - writeVar2->args.push_back({ Compiler::StaticType::Unknown, readList2Reg.get() }); - instructionList.addInstruction(writeVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Both variables should have String type from the list - ASSERT_EQ(analyzer.variableType(&var1, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); - ASSERT_EQ(analyzer.variableType(&var2, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_MixedTest, ComplexChain_VarToListToVar_TypePropagation) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - Variable sourceVar("sourceVar", ""); - List intermediateList("intermediateList", ""); - Variable targetVar("targetVar", ""); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &intermediateList; - instructionList.addInstruction(clearList); - - // Initialize source variable - auto writeSourceVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister sourceValue(Compiler::StaticType::Number, 3.14159); - writeSourceVar->workVariable = &sourceVar; - writeSourceVar->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); - instructionList.addInstruction(writeSourceVar); - - // Read from source variable and write to intermediate list - auto readSourceVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readSourceVarReg = std::make_shared(Compiler::StaticType::Unknown); - readSourceVar->functionReturnReg = readSourceVarReg.get(); - readSourceVarReg->instruction = readSourceVar; - readSourceVar->workVariable = &sourceVar; - instructionList.addInstruction(readSourceVar); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList->workList = &intermediateList; - appendList->args.push_back({ Compiler::StaticType::Unknown, readSourceVarReg.get() }); - instructionList.addInstruction(appendList); - - // Read from intermediate list and write to target variable - auto readList = std::make_shared(LLVMInstruction::Type::GetListItem, false); - auto readListReg = std::make_shared(Compiler::StaticType::Unknown); - readList->functionReturnReg = readListReg.get(); - readListReg->instruction = readList; - LLVMConstantRegister index(Compiler::StaticType::Number, 0); - readList->workList = &intermediateList; - readList->args.push_back({ Compiler::StaticType::Number, &index }); - instructionList.addInstruction(readList); - - auto writeTargetVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - writeTargetVar->workVariable = &targetVar; - writeTargetVar->args.push_back({ Compiler::StaticType::Unknown, readListReg.get() }); - instructionList.addInstruction(writeTargetVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Type should propagate through the chain: sourceVar -> intermediateList -> targetVar - ASSERT_EQ(analyzer.variableType(&targetVar, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Number); - ASSERT_EQ(analyzer.listType(&intermediateList, funcCall.get(), Compiler::StaticType::String, false), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_MixedTest, NestedLoopWithMixedDependencies_TypeConflict) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList instructionList; - Variable var("var", ""); - List list("list", ""); - - auto outerLoopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(outerLoopStart); - - // Write to variable in outer loop - auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - writeVar1->workVariable = &var; - writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - instructionList.addInstruction(writeVar1); - - auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); - clearList->workList = &list; - instructionList.addInstruction(clearList); - - auto innerLoopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - instructionList.addInstruction(innerLoopStart); - - // Read from variable and write to list in inner loop - auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - auto readVarReg = std::make_shared(Compiler::StaticType::Unknown); - readVar->functionReturnReg = readVarReg.get(); - readVarReg->instruction = readVar; - readVar->workVariable = &var; - instructionList.addInstruction(readVar); - - auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); - appendList->workList = &list; - appendList->args.push_back({ Compiler::StaticType::Unknown, readVarReg.get() }); - instructionList.addInstruction(appendList); - - // Write different type to variable in inner loop (creates conflict) - auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "conflict"); - writeVar2->workVariable = &var; - writeVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - instructionList.addInstruction(writeVar2); - - auto innerLoopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(innerLoopEnd); - - auto outerLoopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - instructionList.addInstruction(outerLoopEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - instructionList.addInstruction(funcCall); - - // Should return Unknown due to type conflicts in nested loops - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Unknown); - ASSERT_EQ(analyzer.listType(&list, funcCall.get(), Compiler::StaticType::Bool, false), Compiler::StaticType::Unknown); -} diff --git a/test/llvm/type_analyzer/variabletype_test.cpp b/test/llvm/type_analyzer/variabletype_test.cpp deleted file mode 100644 index 63256c9d..00000000 --- a/test/llvm/type_analyzer/variabletype_test.cpp +++ /dev/null @@ -1,2542 +0,0 @@ -#include -#include -#include -#include -#include -#include - -using namespace libscratchcpp; - -TEST(LLVMTypeAnalyzer_VariableType, NullParams) -{ - LLVMTypeAnalyzer analyzer; - ASSERT_EQ(analyzer.variableType(nullptr, nullptr, Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, NullVariable) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(nullptr, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, NullPos) -{ - LLVMTypeAnalyzer analyzer; - Variable var("", ""); - ASSERT_EQ(analyzer.variableType(&var, nullptr, Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, NoWriteOperationsKnownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, NoWriteOperationsUnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, QueryPointIsWrite) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - ASSERT_EQ(analyzer.variableType(&var, setVar.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteSameTypeNumber_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteSameTypeNumber_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteSameTypeBool_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteSameTypeBool_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteSameTypeString_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteSameTypeString_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteStringOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "1.25"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Although string is assigned, it's constant and represents a number - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteMultipleVariables) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - Variable var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Set var1 - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 6); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Set var2 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var1, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteFromUnknownType_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value(Compiler::StaticType::Unknown); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteFromUnknownType_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value(Compiler::StaticType::Unknown); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteToUnknownType_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // The type is known because a number is assigned before the point - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteToUnknownType_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // The type is not known because a number is assigned after the point - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteUnknownToUnknownType_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value(Compiler::StaticType::Unknown); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteUnknownToUnknownType_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value(Compiler::StaticType::Unknown); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeNumberToString_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeNumberToString_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeStringToNumber_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeNumberToBool_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeBoolToNumber_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeBoolToNumber_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeBoolToString_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "abc"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeStringToBool_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, false); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentTypeStringToBool_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, false); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, Loop_SingleWriteDifferentType_BeforeAndAfter) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Bool, true); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, IfStatement_SingleWriteDifferentType_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, IfStatement_SingleWriteDifferentType_After_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // Since this isn't a loop, the write after the point doesn't affect the type - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, IfStatement_SingleWriteDifferentType_After_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // Since this isn't a loop, the write after the point doesn't affect the type - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, WriteSameTypeInLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, WriteDifferentTypeInLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Type is not known because the loop might not run at all - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, OverrideDifferentTypeBeforeLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, OverrideUnknownTypeBeforeLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, WriteSameTypeInIfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, WriteDifferentTypeInIfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Type is not known because the if statement might not run at all - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, OverrideDifferentTypeBeforeIfElseStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2.5); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, OverrideUnknownTypeBeforeIfElseStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 1.25); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2.5); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesSameTypeNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // First write: string "abc" (incompatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: number 5.25 (compatible with Number pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Should return Number type because final assignment writes a number - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesSameTypeString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // First write: number 42 (incompatible with String) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (compatible with String pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Should return String type because final assignment writes a string - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesSameTypeUnknown) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // First write: unknown type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value1(Compiler::StaticType::Unknown); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (compatible with String pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Should return String type because final assignment writes a string - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesWithStringOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // First write: string "abc" (incompatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "3.14" (optimized to Number, compatible with pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "3.14"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Should return Number type because final assignment writes a string which is optimized to Number type - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesNumberToString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // First write: number 42 (compatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (incompatible with Number pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Should return String type because final assignment writes a string - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesBoolToNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 42.5); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Should return Number type because final assignment writes a number - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesBoolToString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "hello"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Should return String type because final assignment writes a string - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleWritesBoolToStringNumberOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "3.14"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // String "3.14" gets optimized to Number, which is still different from Bool - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Bool), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, TypeChangeInIfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - changes type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - does not change type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Returns unknown type because both branches might run - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, TypeChangeInElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - does not change type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - changes type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Returns unknown type because both branches might run - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, IfElseWithEqualTypes_SameType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - does not change type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - does not change type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, IfElseWithEqualTypes_DifferentTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - changes type to string - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "Lorem"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - changes type to string - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "ipsum"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, IfStatementAfterLoop_SameTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(loopStart); - - // First write - does not change type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(loopEnd); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // Second write - does not change type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, IfStatementAfterLoop_DifferentTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(loopStart); - - // First write - does not change type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(loopEnd); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // Second write - changes type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, TwoIfStatements_DifferentTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - does not change type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - does not change type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 4); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Third write - changes type (in else branch) - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "test"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, NestedLoopWithTypeChange_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, NestedLoopWithTypeChange_BeforeInner) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, NestedLoopWithTypeChange_AfterWriteInsideInner) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, NestedLoopWithTypeChange_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - // Type is not known because the inner loop might not run at all - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, NestedLoopWithoutTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.5); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, NestedLoopTypeChangesButResets) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Variable write after inner loop - resets type - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, NestedLoopTypeChangesAndDoesNotReset) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Variable write after inner loop - keeps the different type - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleNestedLoopsWithTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop1); - - // Variable write inside inner loop 1 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop2); - - // Variable write inside inner loop 2 - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd2); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, MultipleNestedLoopsWithTypeChangeButTypeReset) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop1); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop2); - - // Variable write inside inner loop 2 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "abc"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd2); - - // Variable write inside inner loop 1 - resets type - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2.75); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd1); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, SameTypeIfElseInLoopWithTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Inner if statement begin - auto innerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf); - - // Variable write inside inner if statement if branch - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner if statement else branch - auto innerElse = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(innerElse); - - // Variable write inside inner if statement else branch - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "def"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner if statement end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - // The type always changes to string - ASSERT_EQ(analyzer.variableType(&var, funcCall.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, CrossVariableDependency_SimpleAssignment) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - // First write: var2 = "test" (String type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have String type due to cross-variable dependency - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, CrossVariableDependency_TypeMismatch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - // First write: var2 = "test" (String type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency with type mismatch) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have String type (different from Number previousType) - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, CrossVariableDependency_QueryBeforeAssignment) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - // Query position before any assignments - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // First write: var2 = "test" (String type) - after query position - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency) - after query position - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // var1 should have the previous type since assignments are after query position - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, CrossVariableDependency_ChainedAssignments) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""), var3("", ""); - - // First write: var3 = 42 (Number type) - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar3->workVariable = &var3; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar3); - - // Second write: var2 = var3 (cross-variable dependency) - auto readVar3 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar3); - readVar3->workVariable = &var3; - - LLVMRegister var3Value(Compiler::StaticType::Unknown); - var3Value.isRawValue = false; - var3Value.instruction = readVar3; - readVar3->functionReturnReg = &var3Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var3Value }); - list.addInstruction(setVar2); - - // Third write: var1 = var2 (chained cross-variable dependency) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Query position after all assignments - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have Number type through the chain var1 = var2 = var3 = 42 - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Bool), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, CrossVariableDependency_CircularReference) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - // First write: var1 = var2 (circular dependency setup) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Second write: var2 = var1 (completes circular dependency) - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar2); - - // Query position after circular assignments - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // Should return Unknown due to circular dependency - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, CrossVariableDependency_InIfStatement_QueryInside) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: var2 = 3.14 (Number type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency in if statement) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Query position inside if statement after assignments - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // var1 should have Number type from cross-variable dependency - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, CrossVariableDependency_InIfStatement_QueryOutside) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: var2 = 3.14 (Number type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency in if statement) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // Query position outside if statement after it ends - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should return Unknown because the write is inside an if statement that may not execute - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableType, SelfAssignment) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // No-op: var1 = var1 - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar1); - - // Query position after self-assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // Should return the previous type because the write is a no-op - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, SelfAssignmentWithTypeChange_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // First write: var1 = 3.14 (Number type) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // No-op: var1 = var1 - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var1; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar2); - - // Query position after self-assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // Should return the previous type (number) because the write is a no-op - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, SelfAssignmentWithTypeChange_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // No-op: var1 = var1 - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar1); - - // var1 = 3.14 (Number type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 3.14); - setVar2->workVariable = &var1; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Query position after real assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // Should return number because it's assigned after the no-op - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, InstructionReturnType_AddNumbers) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // Create add instruction: 5 + 3 - auto addInstruction = std::make_shared(LLVMInstruction::Type::Add, false); - LLVMConstantRegister operand1(Compiler::StaticType::Number, 5); - LLVMConstantRegister operand2(Compiler::StaticType::Number, 3); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand1 }); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand2 }); - - // Set up return register for add instruction - LLVMRegister addResult(Compiler::StaticType::Number); - addResult.isRawValue = true; - addResult.instruction = addInstruction; - addInstruction->functionReturnReg = &addResult; - list.addInstruction(addInstruction); - - // Assign add result to variable: var1 = (5 + 3) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &addResult }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have Number type from add instruction return - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, InstructionReturnType_StringConcat) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // Create string concatenation instruction: "hello" + "world" - auto concatInstruction = std::make_shared(LLVMInstruction::Type::StringConcat, false); - LLVMConstantRegister str1(Compiler::StaticType::String, "hello"); - LLVMConstantRegister str2(Compiler::StaticType::String, "world"); - concatInstruction->args.push_back({ Compiler::StaticType::String, &str1 }); - concatInstruction->args.push_back({ Compiler::StaticType::String, &str2 }); - - // Set up return register for concat instruction - LLVMRegister concatResult(Compiler::StaticType::String); - concatResult.isRawValue = true; - concatResult.instruction = concatInstruction; - concatInstruction->functionReturnReg = &concatResult; - list.addInstruction(concatInstruction); - - // Assign concat result to variable: var1 = ("hello" + "world") - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &concatResult }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have String type from concat instruction return - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, InstructionReturnType_MathFunction) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // Create sqrt instruction: sqrt(16) - auto sqrtInstruction = std::make_shared(LLVMInstruction::Type::Sqrt, false); - LLVMConstantRegister operand(Compiler::StaticType::Number, 16); - sqrtInstruction->args.push_back({ Compiler::StaticType::Number, &operand }); - - // Set up return register for sqrt instruction - LLVMRegister sqrtResult(Compiler::StaticType::Number); - sqrtResult.isRawValue = true; - sqrtResult.instruction = sqrtInstruction; - sqrtInstruction->functionReturnReg = &sqrtResult; - list.addInstruction(sqrtInstruction); - - // Assign sqrt result to variable: var1 = sqrt(16) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &sqrtResult }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have Number type from sqrt instruction return - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, InstructionReturnType_Comparison) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // Create comparison instruction: 5 > 3 - auto cmpInstruction = std::make_shared(LLVMInstruction::Type::CmpGT, false); - LLVMConstantRegister operand1(Compiler::StaticType::Number, 5); - LLVMConstantRegister operand2(Compiler::StaticType::Number, 3); - cmpInstruction->args.push_back({ Compiler::StaticType::Number, &operand1 }); - cmpInstruction->args.push_back({ Compiler::StaticType::Number, &operand2 }); - - // Set up return register for comparison instruction - LLVMRegister cmpResult(Compiler::StaticType::Bool); - cmpResult.isRawValue = true; - cmpResult.instruction = cmpInstruction; - cmpInstruction->functionReturnReg = &cmpResult; - list.addInstruction(cmpInstruction); - - // Assign comparison result to variable: var1 = (5 > 3) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &cmpResult }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have Bool type from comparison instruction return - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Number), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableType, InstructionReturnType_FunctionCall) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // Create function call instruction - auto funcInstruction = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - funcInstruction->functionName = "some_reporter_function"; - LLVMConstantRegister arg(Compiler::StaticType::Number, 42); - funcInstruction->args.push_back({ Compiler::StaticType::Number, &arg }); - - // Set up return register for function call - LLVMRegister funcResult(Compiler::StaticType::String); - funcResult.isRawValue = true; - funcResult.instruction = funcInstruction; - funcInstruction->functionReturnReg = &funcResult; - list.addInstruction(funcInstruction); - - // Assign function result to variable: var1 = some_reporter_function(42) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &funcResult }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have String type from function call return - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableType, InstructionReturnType_ChainedOperations) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // First operation: 5 + 3 - auto addInstruction = std::make_shared(LLVMInstruction::Type::Add, false); - LLVMConstantRegister operand1(Compiler::StaticType::Number, 5); - LLVMConstantRegister operand2(Compiler::StaticType::Number, 3); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand1 }); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand2 }); - - LLVMRegister addResult(Compiler::StaticType::Number); - addResult.isRawValue = true; - addResult.instruction = addInstruction; - addInstruction->functionReturnReg = &addResult; - list.addInstruction(addInstruction); - - // Second operation: (5 + 3) * 2 - auto mulInstruction = std::make_shared(LLVMInstruction::Type::Mul, false); - LLVMConstantRegister multiplier(Compiler::StaticType::Number, 2); - mulInstruction->args.push_back({ Compiler::StaticType::Unknown, &addResult }); - mulInstruction->args.push_back({ Compiler::StaticType::Number, &multiplier }); - - LLVMRegister mulResult(Compiler::StaticType::Number); - mulResult.isRawValue = true; - mulResult.instruction = mulInstruction; - mulInstruction->functionReturnReg = &mulResult; - list.addInstruction(mulInstruction); - - // Assign final result to variable: var1 = ((5 + 3) * 2) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &mulResult }); - list.addInstruction(setVar1); - - // Query position after the assignment - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // var1 should have Number type from chained operations - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableType, InstructionReturnType_QueryBeforeAssignment) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - // Query position before any operations - auto queryPos = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(queryPos); - - // Create add instruction: 5 + 3 (after query position) - auto addInstruction = std::make_shared(LLVMInstruction::Type::Add, false); - LLVMConstantRegister operand1(Compiler::StaticType::Number, 5); - LLVMConstantRegister operand2(Compiler::StaticType::Number, 3); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand1 }); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand2 }); - - LLVMRegister addResult(Compiler::StaticType::Number); - addResult.isRawValue = true; - addResult.instruction = addInstruction; - addInstruction->functionReturnReg = &addResult; - list.addInstruction(addInstruction); - - // Assign add result to variable: var1 = (5 + 3) (after query position) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &addResult }); - list.addInstruction(setVar1); - - // var1 should have the previous type since assignment is after query position - ASSERT_EQ(analyzer.variableType(&var1, queryPos.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} diff --git a/test/llvm/type_analyzer/variabletypeafterbranch_test.cpp b/test/llvm/type_analyzer/variabletypeafterbranch_test.cpp deleted file mode 100644 index 54d699fd..00000000 --- a/test/llvm/type_analyzer/variabletypeafterbranch_test.cpp +++ /dev/null @@ -1,2515 +0,0 @@ -#include -#include -#include -#include -#include -#include - -using namespace libscratchcpp; - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NullParams) -{ - LLVMTypeAnalyzer analyzer; - ASSERT_EQ(analyzer.variableTypeAfterBranch(nullptr, nullptr, Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NullVariable) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(nullptr, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NullStartInstruction) -{ - LLVMTypeAnalyzer analyzer; - Variable var("", ""); - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, nullptr, Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, EmptyLoop) -{ - LLVMTypeAnalyzer analyzer; - Variable var("", ""); - LLVMInstructionList list; - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, EmptyIfStatement) -{ - LLVMTypeAnalyzer analyzer; - Variable var("", ""); - LLVMInstructionList list; - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, EmptyIfElseStatement_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - Variable var("", ""); - LLVMInstructionList list; - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, EmptyIfElseStatement_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - Variable var("", ""); - LLVMInstructionList list; - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, elseStart.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, EmptyLoopUnknownType) -{ - LLVMTypeAnalyzer analyzer; - Variable var("", ""); - LLVMInstructionList list; - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NoWriteOperations) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteSameTypeNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteSameTypeBool) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteSameTypeString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteStringOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "1.25"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Although string is assigned, it's constant and represents a number - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteMultipleVariables) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - Variable var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Set var1 - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 6); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Set var2 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteFromUnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value(Compiler::StaticType::Unknown); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteToUnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteUnknownToUnknownType) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value(Compiler::StaticType::Unknown); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Unknown), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteDifferentTypeNumberToString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "test"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteDifferentTypeStringToNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteDifferentTypeNumberToBool) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, true); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteDifferentTypeBoolToNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteDifferentTypeBoolToString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::String, "abc"); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteDifferentTypeStringToBool) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Bool, false); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::String), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteSameType_IfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SingleWriteDifferentType_IfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5.8); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesSameTypeNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: string "abc" (incompatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: number 5.25 (compatible with Number pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return false because final assignment is compatible with pre-loop Number type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesSameTypeString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: number 42 (incompatible with String) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (compatible with String pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return false because final assignment is compatible with pre-loop String type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesSameTypeUnknown) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: unknown type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMRegister value1(Compiler::StaticType::Unknown); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (compatible with String pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return false because final assignment is compatible with pre-loop String type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesWithStringOptimization) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: string "abc" (incompatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "3.14" (optimized to Number, compatible with pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "3.14"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return false because final assignment is optimized to Number type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesNumberToString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: number 42 (compatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (incompatible with Number pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return true because final assignment is incompatible with pre-loop Number type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesBoolToNumber) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: compatible with Bool - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: number 4.25 (incompatible with Bool pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 4.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesBoolToString) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: compatible with Bool - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (incompatible with Bool pre-loop type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesSameType_IfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: string "abc" (incompatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: number 5.25 (compatible with Number type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // Should return false because final assignment is compatible with Number type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesSameType_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: string "abc" (incompatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: number 5.25 (compatible with Number type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Else branch write: string "test" (incompatible with Number type) - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "test"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // Should return false because final assignment is compatible with Number type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesSameType_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: string "abc" (incompatible with Number) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "abc"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Else branch first write: string "abc" (incompatible with Number) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "abc"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Else branch second write: number 5.25 (compatible with Number type) - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 5.25); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // Should return false because final assignment is compatible with Number type - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, elseStart.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesDifferentTypes_IfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: compatible with Bool - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (incompatible with Bool type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesDifferentTypes_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: compatible with Bool - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Second write: string "hello" (incompatible with Bool type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Else branch write: compatible with Bool - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Bool, true); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleWritesDifferentTypes_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: compatible with Bool - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Bool, true); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // First else branch write: compatible with Bool - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Bool, true); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Second else branch write: string "hello" (incompatible with Bool type) - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "hello"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, elseStart.get(), Compiler::StaticType::Bool), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoop_TypeChangeInIfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - changes type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - does not change type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Returns unknown type because the type-changing branch might or might not run - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoop_TypeChangeInElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - does not change type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - changes type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Returns unknown type because the type-changing branch might or might not run - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoop_IfElseWithoutTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // First write - does not change type - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(elseStart); - - // Second write - does not change type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NestedLoopWithTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NestedLoopWithoutTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.5); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NestedLoopTypeChangesButResets) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Variable write after inner loop - resets type - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 2); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NestedLoopTypeChangesAndDoesNotReset) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Variable write after inner loop - keeps the different type - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, NestedLoopWithTypeChangeBeforeLoop) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop begin - auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop); - - // Variable write inside inner loop - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - // Returns unknown type because loops can be skipped - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoopWithTypeChangeBeforeLoop_TypeChangeInIfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner if statement begin - auto innerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf); - - // Variable write inside inner if statement if branch - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner if statement else branch begin - auto innerElse = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(innerElse); - - // Inner if statement end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - // Returns unknown type because if statements can be skipped - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoopWithTypeChangeBeforeLoop_TypeChangeInElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner if statement begin - auto innerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf); - - // Inner if statement else branch begin - auto innerElse = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(innerElse); - - // Variable write inside inner if statement else branch - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner if statement end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - // Returns unknown type because if statements can be skipped - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, IfStatementInLoopWithTypeChangeBeforeLoop_TypeChangeInBothBranches) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner if statement begin - auto innerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf); - - auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value(Compiler::StaticType::Number, 5); - setVar->workVariable = &var; - setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); - list.addInstruction(setVar); - - // Inner if statement else branch begin - auto innerElse = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(innerElse); - - // Variable write inside inner if statement else branch - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner if statement end - auto innerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - // Returns number type because the type always changes to number - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleNestedLoopsWithTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop1); - - // Variable write inside inner loop 1 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop2); - - // Variable write inside inner loop 2 - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd2); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd1); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleNestedLoopsWithTypeChangeButTypeReset) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer loop begin - auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(outerLoop); - - // Variable write inside outer loop - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner loop 1 begin - auto innerLoop1 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop1); - - // Inner loop 2 begin - auto innerLoop2 = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(innerLoop2); - - // Variable write inside inner loop 2 - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Inner loop 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd2); - - // Variable write inside inner loop 1 - resets type - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner loop 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(innerEnd1); - - // Outer loop end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerLoop.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleNestedIfStatementsWithTypeChange_IfBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer if statement begin - auto outerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(outerIf); - - // Variable write inside outer if statement - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner if statement 1 begin - auto innerIf1 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf1); - - // Variable write inside inner if statement 1 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner if statement 2 begin - auto innerIf2 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf2); - - // Variable write inside inner if statement 2 - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::String, "abc"); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Inner if statement 2 else branch - auto innerElse2 = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(innerElse2); - - // Variable write inside inner if statement 2 else branch - auto setVar4 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value4(Compiler::StaticType::Number, 4); - setVar4->workVariable = &var; - setVar4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); - list.addInstruction(setVar4); - - // Inner if statement 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd2); - - // Inner if statement 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd1); - - // Outer if statement end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerIf.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleNestedIfStatementsWithTypeChange_ElseBranch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer if statement begin - auto outerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(outerIf); - - // Variable write inside outer if statement - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner if statement 1 begin - auto innerIf1 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf1); - - // Variable write inside inner if statement 1 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner if statement 2 begin - auto innerIf2 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf2); - - // Variable write inside inner if statement 2 - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 4); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Inner if statement 2 else branch - auto innerElse2 = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(innerElse2); - - // Variable write inside inner if statement 2 else branch - auto setVar4 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value4(Compiler::StaticType::String, "abc"); - setVar4->workVariable = &var; - setVar4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); - list.addInstruction(setVar4); - - // Inner if statement 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd2); - - // Inner if statement 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd1); - - // Outer if statement end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerIf.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, MultipleNestedIfStatementsWithoutTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var("", ""); - - // Outer if statement begin - auto outerIf = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(outerIf); - - // Variable write inside outer if statement - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 5); - setVar1->workVariable = &var; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Inner if statement 1 begin - auto innerIf1 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf1); - - // Variable write inside inner if statement 1 - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 2.75); - setVar2->workVariable = &var; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // Inner if statement 2 begin - auto innerIf2 = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(innerIf2); - - // Variable write inside inner if statement 2 - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value3(Compiler::StaticType::Number, 4); - setVar3->workVariable = &var; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); - list.addInstruction(setVar3); - - // Inner if statement 2 else branch - auto innerElse2 = std::make_shared(LLVMInstruction::Type::BeginElse, false); - list.addInstruction(innerElse2); - - // Variable write inside inner if statement 2 else branch - auto setVar4 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value4(Compiler::StaticType::Number, 0); - setVar4->workVariable = &var; - setVar4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); - list.addInstruction(setVar4); - - // Inner if statement 2 end - auto innerEnd2 = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd2); - - // Inner if statement 1 end - auto innerEnd1 = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(innerEnd1); - - // Outer if statement end - auto outerEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(outerEnd); - - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var, outerIf.get(), Compiler::StaticType::Number), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_SimpleAssignment) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: var2 = "test" (String type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have String type due to cross-variable dependency - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_TypeMismatch) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: var2 = "test" (String type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::String, "test"); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency with type mismatch) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have String type (incompatible with Number pre-loop type) - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_SimpleAssignmentBeforeTypeChange) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // var1 = var2 - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // var2 = 5.2 (type resets to number) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 5.2); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Although the type of var2 is changed to number, it's unknown before the first iteration - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_ChainedAssignments) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""), var3("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: var3 = 42 (Number type) - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setVar3->workVariable = &var3; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar3); - - // Second write: var2 = var3 (cross-variable dependency) - auto readVar3 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar3); - readVar3->workVariable = &var3; - - LLVMRegister var3Value(Compiler::StaticType::Unknown); - var3Value.isRawValue = false; - var3Value.instruction = readVar3; - readVar3->functionReturnReg = &var3Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var3Value }); - list.addInstruction(setVar2); - - // Third write: var1 = var2 (chained cross-variable dependency) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have Number type through the chain var1 = var2 = var3 = 42 - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Bool), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_CircularReference) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: var1 = var2 (circular dependency setup) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Second write: var2 = var1 (completes circular dependency) - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return Unknown due to circular dependency - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_CircularReference_KnownTypes) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // var1 is a number - auto setupVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setupVar1->workVariable = &var1; - setupVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setupVar1); - - // var2 is a number - auto setupVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - setupVar2->workVariable = &var2; - setupVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setupVar2); - - // First write: var1 = var2 (circular dependency setup) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Second write: var2 = var1 (completes circular dependency) - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Although there is a circular dependency, the types of both variables are known - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_CircularReference_FirstAssignedTypeKnown) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // var1: unknown type - - // var2 is a number - auto setupVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 42); - setupVar2->workVariable = &var2; - setupVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setupVar2); - - // First write: var1 = var2 (circular dependency setup) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Second write: var2 = var1 (completes circular dependency) - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Although there is a circular dependency, the type of var2 is known and this variable is assigned to var1 - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_CircularReference_AssignedTypeUnknown) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // var1 is a number - auto setupVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 42); - setupVar1->workVariable = &var1; - setupVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setupVar1); - - // var2: unknown type - - // First write: var1 = var2 (circular dependency setup) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Second write: var2 = var1 (completes circular dependency) - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar2); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // The type of var2 is not known and this variable is assigned to var1 - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_ChainedCircularReference) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""), var3("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: var1 = var2 (circular dependency setup) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - // Second write: var2 = var3 - auto readVar3 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar3); - readVar3->workVariable = &var3; - - LLVMRegister var3Value(Compiler::StaticType::Unknown); - var3Value.isRawValue = false; - var3Value.instruction = readVar3; - readVar3->functionReturnReg = &var3Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var3Value }); - list.addInstruction(setVar2); - - // Third write: var3 = var1 (completes circular dependency) - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar3->workVariable = &var3; - setVar3->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar3); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return Unknown due to circular dependency - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Unknown); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, CrossVariableDependency_InIfStatement) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""), var2("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(start); - - // First write: var2 = 3.14 (Number type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); - setVar2->workVariable = &var2; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar2); - - // Second write: var1 = var2 (cross-variable dependency in if statement) - auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar2); - readVar2->workVariable = &var2; - - LLVMRegister var2Value(Compiler::StaticType::Unknown); - var2Value.isRawValue = false; - var2Value.instruction = readVar2; - readVar2->functionReturnReg = &var2Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(end); - - // var1 should have Number type from cross-variable dependency - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SelfAssignment) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Start if statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // No-op: var1 = var1 - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar1); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - // End loop - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return the previous type because the write is a no-op - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SelfAssignmentWithTypeChange_Before) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First write: var1 = 3.14 (Number type) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); - list.addInstruction(setVar1); - - // Start if statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // No-op: var1 = var1 - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar2->workVariable = &var1; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar2); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - // End loop - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return the previous type (number) because the write is a no-op - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, SelfAssignmentWithTypeChange_After) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Start if statement - auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); - list.addInstruction(ifStart); - - // No-op: var1 = var1 - auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); - list.addInstruction(readVar1); - readVar1->workVariable = &var1; - - LLVMRegister var1Value(Compiler::StaticType::Unknown); - var1Value.isRawValue = false; - var1Value.instruction = readVar1; - readVar1->functionReturnReg = &var1Value; - - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &var1Value }); - list.addInstruction(setVar1); - - // End if statement - auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); - list.addInstruction(ifEnd); - - // var1 = 3.14 (Number type) - auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - LLVMConstantRegister value2(Compiler::StaticType::Number, 3.14); - setVar2->workVariable = &var1; - setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); - list.addInstruction(setVar2); - - // End loop - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // Should return number because it's assigned later in the loop - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, InstructionReturnType_AddNumbers) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Create add instruction: 5 + 3 - auto addInstruction = std::make_shared(LLVMInstruction::Type::Add, false); - LLVMConstantRegister operand1(Compiler::StaticType::Number, 5); - LLVMConstantRegister operand2(Compiler::StaticType::Number, 3); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand1 }); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand2 }); - - // Set up return register for add instruction - LLVMRegister addResult(Compiler::StaticType::Number); - addResult.isRawValue = true; - addResult.instruction = addInstruction; - addInstruction->functionReturnReg = &addResult; - list.addInstruction(addInstruction); - - // Assign add result to variable: var1 = (5 + 3) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &addResult }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have Number type from add instruction return - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, InstructionReturnType_StringConcat) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Create string concatenation instruction: "hello" + "world" - auto concatInstruction = std::make_shared(LLVMInstruction::Type::StringConcat, false); - LLVMConstantRegister str1(Compiler::StaticType::String, "hello"); - LLVMConstantRegister str2(Compiler::StaticType::String, "world"); - concatInstruction->args.push_back({ Compiler::StaticType::String, &str1 }); - concatInstruction->args.push_back({ Compiler::StaticType::String, &str2 }); - - // Set up return register for concat instruction - LLVMRegister concatResult(Compiler::StaticType::String); - concatResult.isRawValue = true; - concatResult.instruction = concatInstruction; - concatInstruction->functionReturnReg = &concatResult; - list.addInstruction(concatInstruction); - - // Assign concat result to variable: var1 = ("hello" + "world") - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &concatResult }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have String type from concat instruction return - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, InstructionReturnType_MathFunction) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Create sqrt instruction: sqrt(16) - auto sqrtInstruction = std::make_shared(LLVMInstruction::Type::Sqrt, false); - LLVMConstantRegister operand(Compiler::StaticType::Number, 16); - sqrtInstruction->args.push_back({ Compiler::StaticType::Number, &operand }); - - // Set up return register for sqrt instruction - LLVMRegister sqrtResult(Compiler::StaticType::Number); - sqrtResult.isRawValue = true; - sqrtResult.instruction = sqrtInstruction; - sqrtInstruction->functionReturnReg = &sqrtResult; - list.addInstruction(sqrtInstruction); - - // Assign sqrt result to variable: var1 = sqrt(16) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &sqrtResult }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have Number type from sqrt instruction return - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, InstructionReturnType_Comparison) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Create comparison instruction: 5 > 3 - auto cmpInstruction = std::make_shared(LLVMInstruction::Type::CmpGT, false); - LLVMConstantRegister operand1(Compiler::StaticType::Number, 5); - LLVMConstantRegister operand2(Compiler::StaticType::Number, 3); - cmpInstruction->args.push_back({ Compiler::StaticType::Number, &operand1 }); - cmpInstruction->args.push_back({ Compiler::StaticType::Number, &operand2 }); - - // Set up return register for comparison instruction - LLVMRegister cmpResult(Compiler::StaticType::Bool); - cmpResult.isRawValue = true; - cmpResult.instruction = cmpInstruction; - cmpInstruction->functionReturnReg = &cmpResult; - list.addInstruction(cmpInstruction); - - // Assign comparison result to variable: var1 = (5 > 3) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &cmpResult }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have Bool type from comparison instruction return - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::Bool); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, InstructionReturnType_FunctionCall) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // Create function call instruction - auto funcInstruction = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - funcInstruction->functionName = "some_reporter_function"; - LLVMConstantRegister arg(Compiler::StaticType::Number, 42); - funcInstruction->args.push_back({ Compiler::StaticType::Number, &arg }); - - // Set up return register for function call - LLVMRegister funcResult(Compiler::StaticType::String); - funcResult.isRawValue = true; - funcResult.instruction = funcInstruction; - funcInstruction->functionReturnReg = &funcResult; - list.addInstruction(funcInstruction); - - // Assign function result to variable: var1 = some_reporter_function(42) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &funcResult }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have String type from function call return - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::Number), Compiler::StaticType::String); -} - -TEST(LLVMTypeAnalyzer_VariableTypeAfterBranch, InstructionReturnType_ChainedOperations) -{ - LLVMTypeAnalyzer analyzer; - LLVMInstructionList list; - Variable var1("", ""); - - auto start = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); - list.addInstruction(start); - - // First operation: 5 + 3 - auto addInstruction = std::make_shared(LLVMInstruction::Type::Add, false); - LLVMConstantRegister operand1(Compiler::StaticType::Number, 5); - LLVMConstantRegister operand2(Compiler::StaticType::Number, 3); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand1 }); - addInstruction->args.push_back({ Compiler::StaticType::Number, &operand2 }); - - LLVMRegister addResult(Compiler::StaticType::Number); - addResult.isRawValue = true; - addResult.instruction = addInstruction; - addInstruction->functionReturnReg = &addResult; - list.addInstruction(addInstruction); - - // Second operation: (5 + 3) * 2 - auto mulInstruction = std::make_shared(LLVMInstruction::Type::Mul, false); - LLVMConstantRegister multiplier(Compiler::StaticType::Number, 2); - mulInstruction->args.push_back({ Compiler::StaticType::Unknown, &addResult }); - mulInstruction->args.push_back({ Compiler::StaticType::Number, &multiplier }); - - LLVMRegister mulResult(Compiler::StaticType::Number); - mulResult.isRawValue = true; - mulResult.instruction = mulInstruction; - mulInstruction->functionReturnReg = &mulResult; - list.addInstruction(mulInstruction); - - // Assign final result to variable: var1 = ((5 + 3) * 2) - auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); - setVar1->workVariable = &var1; - setVar1->args.push_back({ Compiler::StaticType::Unknown, &mulResult }); - list.addInstruction(setVar1); - - auto end = std::make_shared(LLVMInstruction::Type::EndLoop, false); - list.addInstruction(end); - - // var1 should have Number type from chained operations - ASSERT_EQ(analyzer.variableTypeAfterBranch(&var1, start.get(), Compiler::StaticType::String), Compiler::StaticType::Number); -} From 1ed91fcec1a3442f06cb8d3b7d56827a37c288e5 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:20:36 +0200 Subject: [PATCH 02/27] Add support for Compiler::StaticType bitmasking --- CMakeLists.txt | 1 + include/scratchcpp/compiler.h | 15 +++++---- include/scratchcpp/enum_bitmask.h | 53 +++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 include/scratchcpp/enum_bitmask.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bf7c1354..31411131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ install(TARGETS scratchcpp DESTINATION lib) target_sources(scratchcpp PUBLIC include/scratchcpp/global.h + include/scratchcpp/enum_bitmask.h include/scratchcpp/project.h include/scratchcpp/scratchconfiguration.h include/scratchcpp/iengine.h diff --git a/include/scratchcpp/compiler.h b/include/scratchcpp/compiler.h index f718a780..cf534783 100644 --- a/include/scratchcpp/compiler.h +++ b/include/scratchcpp/compiler.h @@ -6,6 +6,7 @@ #include #include "global.h" +#include "enum_bitmask.h" #include "spimpl.h" namespace libscratchcpp @@ -31,12 +32,12 @@ class LIBSCRATCHCPP_EXPORT Compiler public: enum class StaticType { - Void, - Number, - Bool, - String, - Pointer, - Unknown + Void = 0, + Number = 1 << 0, + Bool = 1 << 1, + String = 1 << 2, + Pointer = 1 << 3, + Unknown = Number | Bool | String }; enum class CodeType @@ -161,4 +162,6 @@ class LIBSCRATCHCPP_EXPORT Compiler spimpl::unique_impl_ptr impl; }; +constexpr bool enable_enum_bitmask(Compiler::StaticType); + } // namespace libscratchcpp diff --git a/include/scratchcpp/enum_bitmask.h b/include/scratchcpp/enum_bitmask.h new file mode 100644 index 00000000..5f97959d --- /dev/null +++ b/include/scratchcpp/enum_bitmask.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include + +namespace libscratchcpp +{ + +// https://andreasfertig.com/blog/2024/01/cpp20-concepts-applied +// OR operator | +template +constexpr std::enable_if_t, std::is_same()))>>, T> operator|(const T lhs, const T rhs) +{ + using underlying = std::underlying_type_t; + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +// OR assignment operator |= +template +constexpr std::enable_if_t, std::is_same()))>>, T &> operator|=(T &lhs, const T rhs) +{ + using underlying = std::underlying_type_t; + lhs = static_cast(static_cast(lhs) | static_cast(rhs)); + return lhs; +} + +// AND operator & +template +constexpr std::enable_if_t, std::is_same()))>>, T> operator&(const T lhs, const T rhs) +{ + using underlying = std::underlying_type_t; + return static_cast(static_cast(lhs) & static_cast(rhs)); +} + +// AND assignment operator &= +template +constexpr std::enable_if_t, std::is_same()))>>, T &> operator&=(T &lhs, const T rhs) +{ + using underlying = std::underlying_type_t; + lhs = static_cast(static_cast(lhs) & static_cast(rhs)); + return lhs; +} + +// NOT operator ~ +template +constexpr std::enable_if_t, std::is_same()))>>, T> operator~(const T value) +{ + using underlying = std::underlying_type_t; + return static_cast(~static_cast(value)); +} + +} // namespace libscratchcpp From b2aeaa0bbd3fe351dae495053be5edf9dfa08455 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:21:37 +0200 Subject: [PATCH 03/27] LLVMInstruction: Add writeTargetType field --- src/engine/internal/llvm/llvminstruction.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/engine/internal/llvm/llvminstruction.h b/src/engine/internal/llvm/llvminstruction.h index e4fa059a..ad11b8a9 100644 --- a/src/engine/internal/llvm/llvminstruction.h +++ b/src/engine/internal/llvm/llvminstruction.h @@ -90,10 +90,11 @@ struct LLVMInstruction std::string functionName; std::vector> args; // target type, register LLVMRegister *functionReturnReg = nullptr; - bool functionTargetArg = false; // whether to add target ptr to function parameters - bool functionCtxArg = false; // whether to add execution context ptr to function parameters - Variable *workVariable = nullptr; // for variables - List *workList = nullptr; // for lists + bool functionTargetArg = false; // whether to add target ptr to function parameters + bool functionCtxArg = false; // whether to add execution context ptr to function parameters + Variable *workVariable = nullptr; // for variables + List *workList = nullptr; // for lists + Compiler::StaticType targetType = Compiler::StaticType::Unknown; // variable or list type (before read/write operation) BlockPrototype *procedurePrototype = nullptr; size_t procedureArgIndex = 0; bool loopCondition = false; // whether the instruction is part of a loop condition From b497e777fb114e4dd1f9c043eaa90b9ed07df794 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:36:10 +0200 Subject: [PATCH 04/27] Read variable/list type from LLVMInstruction --- .../internal/llvm/instructions/lists.cpp | 31 ++++--------------- .../internal/llvm/instructions/variables.cpp | 12 +------ 2 files changed, 7 insertions(+), 36 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index b8695409..ca9f0951 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -109,10 +109,7 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) Compiler::StaticType type = m_utils.optimizeRegisterType(arg.second); LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); - Compiler::StaticType listType = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ + Compiler::StaticType listType = ins->targetType; // Check if enough space is allocated llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); @@ -151,10 +148,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) Compiler::StaticType type = m_utils.optimizeRegisterType(valueArg.second); LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); - Compiler::StaticType listType = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ + Compiler::StaticType listType = ins->targetType; // Range check llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -189,10 +183,7 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) Compiler::StaticType type = m_utils.optimizeRegisterType(valueArg.second); LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); - Compiler::StaticType listType = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ + Compiler::StaticType listType = ins->targetType; // Range check llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); @@ -232,10 +223,7 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) const auto &arg = ins->args[0]; LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); - Compiler::StaticType listType = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ + Compiler::StaticType listType = ins->functionReturnReg->type(); llvm::Value *min = llvm::ConstantFP::get(m_utils.llvmCtx(), llvm::APFloat(0.0)); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -248,7 +236,6 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); ins->functionReturnReg->value = m_builder.CreateSelect(inRange, m_utils.getListItem(listPtr, index), null); - ins->functionReturnReg->setType(listType); return ins->next; } @@ -269,10 +256,7 @@ LLVMInstruction *Lists::buildGetListItemIndex(LLVMInstruction *ins) const auto &arg = ins->args[0]; LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); - Compiler::StaticType listType = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ + Compiler::StaticType listType = ins->targetType; ins->functionReturnReg->value = m_builder.CreateSIToFP(m_utils.getListItemIndex(listPtr, listType, arg.second), m_builder.getDoubleTy()); return ins->next; @@ -284,10 +268,7 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) const auto &arg = ins->args[0]; LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); - Compiler::StaticType listType = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - listType = m_utils.typeAnalyzer().listType(ins->workList, ins, Compiler::StaticType::Unknown, false);*/ + Compiler::StaticType listType = ins->targetType; llvm::Value *index = m_utils.getListItemIndex(listPtr, listType, arg.second); ins->functionReturnReg->value = m_builder.CreateICmpSGT(index, llvm::ConstantInt::get(m_builder.getInt64Ty(), -1, true)); diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 4d5fa1e1..4763d2c3 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -113,11 +113,6 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->workVariable); varPtr.changed = true; // TODO: Handle loops and if statements - Compiler::StaticType varType = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - varType = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown);*/ - // Initialize stack variable on first assignment // TODO: Use stack in the top level (outside loops and if statements) /*if (!varPtr.onStack) { @@ -139,7 +134,7 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typeField); }*/ - m_utils.createValueStore(arg.second, varPtr.stackPtr, argType, varType); + m_utils.createValueStore(arg.second, varPtr.stackPtr, argType, ins->targetType); return ins->next; } @@ -147,12 +142,7 @@ LLVMInstruction *Variables::buildReadVariable(LLVMInstruction *ins) { assert(ins->args.size() == 0); LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->workVariable); - Compiler::StaticType type = Compiler::StaticType::Unknown; - - /*if (m_utils.warp()) - type = m_utils.typeAnalyzer().variableType(ins->workVariable, ins, Compiler::StaticType::Unknown);*/ ins->functionReturnReg->value = varPtr.onStack && !(ins->loopCondition && !m_utils.warp()) ? varPtr.stackPtr : varPtr.heapPtr; - ins->functionReturnReg->setType(type); return ins->next; } From f0470f85ab50d1dc815cc144ff4e737f637ec096 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 13 Aug 2025 16:36:58 +0200 Subject: [PATCH 05/27] LLVMInstruction: Rename workVariable/workList fields --- .../internal/llvm/instructions/lists.cpp | 20 ++++++++-------- .../internal/llvm/instructions/variables.cpp | 4 ++-- src/engine/internal/llvm/llvmbuildutils.cpp | 2 +- src/engine/internal/llvm/llvmcodebuilder.cpp | 24 +++++++++---------- src/engine/internal/llvm/llvminstruction.h | 4 ++-- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index ca9f0951..3bf45a11 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -64,7 +64,7 @@ ProcessResult Lists::process(LLVMInstruction *ins) LLVMInstruction *Lists::buildClearList(LLVMInstruction *ins) { assert(ins->args.size() == 0); - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); m_builder.CreateCall(m_utils.functions().resolve_list_clear(), listPtr.ptr); return ins->next; @@ -77,7 +77,7 @@ LLVMInstruction *Lists::buildRemoveListItem(LLVMInstruction *ins) assert(ins->args.size() == 1); const auto &arg = ins->args[0]; - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); // Range check llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); @@ -107,7 +107,7 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) assert(ins->args.size() == 1); const auto &arg = ins->args[0]; Compiler::StaticType type = m_utils.optimizeRegisterType(arg.second); - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); Compiler::StaticType listType = ins->targetType; @@ -146,7 +146,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) const auto &indexArg = ins->args[0]; const auto &valueArg = ins->args[1]; Compiler::StaticType type = m_utils.optimizeRegisterType(valueArg.second); - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); Compiler::StaticType listType = ins->targetType; @@ -181,7 +181,7 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) const auto &indexArg = ins->args[0]; const auto &valueArg = ins->args[1]; Compiler::StaticType type = m_utils.optimizeRegisterType(valueArg.second); - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); Compiler::StaticType listType = ins->targetType; @@ -209,7 +209,7 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) LLVMInstruction *Lists::buildGetListContents(LLVMInstruction *ins) { assert(ins->args.size() == 0); - const LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + const LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); llvm::Value *ptr = m_builder.CreateCall(m_utils.functions().resolve_list_to_string(), listPtr.ptr); m_utils.freeStringLater(ptr); ins->functionReturnReg->value = ptr; @@ -221,7 +221,7 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); Compiler::StaticType listType = ins->functionReturnReg->type(); @@ -243,7 +243,7 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) LLVMInstruction *Lists::buildGetListSize(LLVMInstruction *ins) { assert(ins->args.size() == 0); - const LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + const LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); ins->functionReturnReg->value = m_builder.CreateUIToFP(size, m_builder.getDoubleTy()); @@ -254,7 +254,7 @@ LLVMInstruction *Lists::buildGetListItemIndex(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); Compiler::StaticType listType = ins->targetType; @@ -266,7 +266,7 @@ LLVMInstruction *Lists::buildListContainsItem(LLVMInstruction *ins) { assert(ins->args.size() == 1); const auto &arg = ins->args[0]; - LLVMListPtr &listPtr = m_utils.listPtr(ins->workList); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); Compiler::StaticType listType = ins->targetType; diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 4763d2c3..8e180c15 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -110,7 +110,7 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) assert(ins->args.size() == 1); const auto &arg = ins->args[0]; Compiler::StaticType argType = m_utils.optimizeRegisterType(arg.second); - LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->workVariable); + LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); varPtr.changed = true; // TODO: Handle loops and if statements // Initialize stack variable on first assignment @@ -141,7 +141,7 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) LLVMInstruction *Variables::buildReadVariable(LLVMInstruction *ins) { assert(ins->args.size() == 0); - LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->workVariable); + LLVMVariablePtr &varPtr = m_utils.variablePtr(ins->targetVariable); ins->functionReturnReg->value = varPtr.onStack && !(ins->loopCondition && !m_utils.warp()) ? varPtr.stackPtr : varPtr.heapPtr; return ins->next; diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index f0636b42..8b84f820 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -74,7 +74,7 @@ void LLVMBuildUtils::init(llvm::Function *function, BlockPrototype *procedurePro /*Variable *variable = var; // TODO: Loop scope was used here, replace it with some "inside loop" flag if needed auto it = std::find_if(m_variableInstructions.begin(), m_variableInstructions.end(), [variable](const LLVMInstruction *ins) { - return ins->type == LLVMInstruction::Type::WriteVariable && ins->workVariable == variable && !ins->loopScope; + return ins->type == LLVMInstruction::Type::WriteVariable && ins->targetVariable == variable && !ins->loopScope; }); if (it == m_variableInstructions.end()) { diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index b189905f..2dcace86 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -156,7 +156,7 @@ CompilerValue *LLVMCodeBuilder::addLocalVariableValue(CompilerLocalVariable *var CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) { auto ins = std::make_shared(LLVMInstruction::Type::ReadVariable, m_loopCondition); - ins->workVariable = variable; + ins->targetVariable = variable; m_utils.createVariablePtr(variable); auto ret = std::make_shared(Compiler::StaticType::Unknown); @@ -170,7 +170,7 @@ CompilerValue *LLVMCodeBuilder::addVariableValue(Variable *variable) CompilerValue *LLVMCodeBuilder::addListContents(List *list) { LLVMInstruction ins(LLVMInstruction::Type::GetListContents, m_loopCondition); - ins.workList = list; + ins.targetList = list; m_utils.createListPtr(list); return createOp(ins, Compiler::StaticType::String); @@ -179,7 +179,7 @@ CompilerValue *LLVMCodeBuilder::addListContents(List *list) CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) { auto ins = std::make_shared(LLVMInstruction::Type::GetListItem, m_loopCondition); - ins->workList = list; + ins->targetList = list; m_utils.createListPtr(list); ins->args.push_back({ Compiler::StaticType::Number, dynamic_cast(index) }); @@ -195,7 +195,7 @@ CompilerValue *LLVMCodeBuilder::addListItem(List *list, CompilerValue *index) CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::GetListItemIndex, m_loopCondition); - ins.workList = list; + ins.targetList = list; m_utils.createListPtr(list); auto ret = createOp(ins, Compiler::StaticType::Number, Compiler::StaticType::Unknown, { item }); @@ -205,7 +205,7 @@ CompilerValue *LLVMCodeBuilder::addListItemIndex(List *list, CompilerValue *item CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::ListContainsItem, m_loopCondition); - ins.workList = list; + ins.targetList = list; m_utils.createListPtr(list); auto ret = createOp(ins, Compiler::StaticType::Bool, Compiler::StaticType::Unknown, { item }); @@ -215,7 +215,7 @@ CompilerValue *LLVMCodeBuilder::addListContains(List *list, CompilerValue *item) CompilerValue *LLVMCodeBuilder::addListSize(List *list) { LLVMInstruction ins(LLVMInstruction::Type::GetListSize, m_loopCondition); - ins.workList = list; + ins.targetList = list; m_utils.createListPtr(list); return createOp(ins, Compiler::StaticType::Number); @@ -422,7 +422,7 @@ void LLVMCodeBuilder::createLocalVariableWrite(CompilerLocalVariable *variable, void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *value) { LLVMInstruction ins(LLVMInstruction::Type::WriteVariable, m_loopCondition); - ins.workVariable = variable; + ins.targetVariable = variable; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { value }); m_utils.createVariablePtr(variable); } @@ -430,7 +430,7 @@ void LLVMCodeBuilder::createVariableWrite(Variable *variable, CompilerValue *val void LLVMCodeBuilder::createListClear(List *list) { LLVMInstruction ins(LLVMInstruction::Type::ClearList, m_loopCondition); - ins.workList = list; + ins.targetList = list; createOp(ins, Compiler::StaticType::Void); m_utils.createListPtr(list); } @@ -438,7 +438,7 @@ void LLVMCodeBuilder::createListClear(List *list) void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) { LLVMInstruction ins(LLVMInstruction::Type::RemoveListItem, m_loopCondition); - ins.workList = list; + ins.targetList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Number, { index }); m_utils.createListPtr(list); } @@ -446,7 +446,7 @@ void LLVMCodeBuilder::createListRemove(List *list, CompilerValue *index) void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::AppendToList, m_loopCondition); - ins.workList = list; + ins.targetList = list; createOp(ins, Compiler::StaticType::Void, Compiler::StaticType::Unknown, { item }); m_utils.createListPtr(list); } @@ -454,7 +454,7 @@ void LLVMCodeBuilder::createListAppend(List *list, CompilerValue *item) void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::InsertToList, m_loopCondition); - ins.workList = list; + ins.targetList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); m_utils.createListPtr(list); } @@ -462,7 +462,7 @@ void LLVMCodeBuilder::createListInsert(List *list, CompilerValue *index, Compile void LLVMCodeBuilder::createListReplace(List *list, CompilerValue *index, CompilerValue *item) { LLVMInstruction ins(LLVMInstruction::Type::ListReplace, m_loopCondition); - ins.workList = list; + ins.targetList = list; createOp(ins, Compiler::StaticType::Void, { Compiler::StaticType::Number, Compiler::StaticType::Unknown }, { index, item }); m_utils.createListPtr(list); } diff --git a/src/engine/internal/llvm/llvminstruction.h b/src/engine/internal/llvm/llvminstruction.h index ad11b8a9..e6a5212e 100644 --- a/src/engine/internal/llvm/llvminstruction.h +++ b/src/engine/internal/llvm/llvminstruction.h @@ -92,8 +92,8 @@ struct LLVMInstruction LLVMRegister *functionReturnReg = nullptr; bool functionTargetArg = false; // whether to add target ptr to function parameters bool functionCtxArg = false; // whether to add execution context ptr to function parameters - Variable *workVariable = nullptr; // for variables - List *workList = nullptr; // for lists + Variable *targetVariable = nullptr; // for variables + List *targetList = nullptr; // for lists Compiler::StaticType targetType = Compiler::StaticType::Unknown; // variable or list type (before read/write operation) BlockPrototype *procedurePrototype = nullptr; size_t procedureArgIndex = 0; From d46d9e0cc098cb9c9e3956b63c528fcc46ae2a00 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:30:23 +0200 Subject: [PATCH 06/27] LLVMBuildUtils: Add const to optimizeRegisterType() parameter --- src/engine/internal/llvm/llvmbuildutils.cpp | 2 +- src/engine/internal/llvm/llvmbuildutils.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 8b84f820..30f232d0 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -366,7 +366,7 @@ std::vector &LLVMBuildUtils::loops() return m_loops; } -Compiler::StaticType LLVMBuildUtils::optimizeRegisterType(LLVMRegister *reg) +Compiler::StaticType LLVMBuildUtils::optimizeRegisterType(const LLVMRegister *reg) { Compiler::StaticType ret = reg->type(); diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 14c99d4a..2474225d 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -73,7 +73,7 @@ class LLVMBuildUtils std::vector &ifStatements(); std::vector &loops(); - static Compiler::StaticType optimizeRegisterType(LLVMRegister *reg); + static Compiler::StaticType optimizeRegisterType(const LLVMRegister *reg); static Compiler::StaticType mapType(ValueType type); llvm::Value *addAlloca(llvm::Type *type); From 31593f33910fe43e54f1eb1d9fab4a2efa262ee8 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 14 Aug 2025 15:00:15 +0200 Subject: [PATCH 07/27] Add new variable type analysis algorithm --- src/engine/internal/llvm/CMakeLists.txt | 2 + src/engine/internal/llvm/llvmcodeanalyzer.cpp | 184 +++ src/engine/internal/llvm/llvmcodeanalyzer.h | 45 + test/llvm/CMakeLists.txt | 1 + .../code_analyzer/variable_type_analysis.cpp | 1053 +++++++++++++++++ 5 files changed, 1285 insertions(+) create mode 100644 src/engine/internal/llvm/llvmcodeanalyzer.cpp create mode 100644 src/engine/internal/llvm/llvmcodeanalyzer.h create mode 100644 test/llvm/code_analyzer/variable_type_analysis.cpp diff --git a/src/engine/internal/llvm/CMakeLists.txt b/src/engine/internal/llvm/CMakeLists.txt index eb37a95a..54b79e88 100644 --- a/src/engine/internal/llvm/CMakeLists.txt +++ b/src/engine/internal/llvm/CMakeLists.txt @@ -2,6 +2,8 @@ target_sources(scratchcpp PRIVATE llvmbuildutils.cpp llvmbuildutils.h + llvmcodeanalyzer.cpp + llvmcodeanalyzer.h llvmcodebuilder.cpp llvmcodebuilder.h llvmregister.h diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp new file mode 100644 index 00000000..8af64a6a --- /dev/null +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: Apache-2.0 + +#include "llvmcodeanalyzer.h" +#include "llvminstructionlist.h" +#include "llvminstruction.h" +#include "llvmbuildutils.h" + +using namespace libscratchcpp; + +static const std::unordered_set + BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginWhileLoop, LLVMInstruction::Type::BeginRepeatUntilLoop }; + +void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const +{ + std::unordered_set typeAssignedInstructions; + std::vector> branches; + LLVMInstruction *ins = script.first(); + + auto topBranch = std::make_unique(); + topBranch->start = ins; + Branch *topBranchPtr = topBranch.get(); + branches.push_back(std::move(topBranch)); + + Branch *currentBranch = branches.back().get(); + + while (ins) { + if (isIfStart(ins) || isLoopStart(ins)) { + auto branch = std::make_unique(); + branch->start = ins; + branch->variableTypes = currentBranch->variableTypes; + currentBranch = branch.get(); + branches.push_back(std::move(branch)); + } else if (isElse(ins)) { + assert(!currentBranch->elseBranch); + + // Enter else branch with type information from the previous branch + Branch *previousBranch = branches[branches.size() - 2].get(); + currentBranch->elseBranch = std::make_unique(); + currentBranch = currentBranch->elseBranch.get(); + currentBranch->start = ins; + currentBranch->variableTypes = previousBranch->variableTypes; + } else if (isIfEnd(ins) || isLoopEnd(ins)) { + if (isLoopEnd(ins) && currentBranch->typeChanges) { + // Next iteration + ins = currentBranch->start; + currentBranch->typeChanges = false; + } else { + // Merge/override types + Branch *previousBranch = branches[branches.size() - 2].get(); + Branch *primaryBranch = branches.back().get(); + + assert(primaryBranch); + + if (primaryBranch && primaryBranch->elseBranch) { + // The previous types can be ignored in if/else statements + overrideBranchTypes(primaryBranch, previousBranch); + mergeBranchTypes(primaryBranch->elseBranch.get(), previousBranch); + } else + mergeBranchTypes(primaryBranch, previousBranch); + + // Remove the branch + branches.pop_back(); + currentBranch = previousBranch; + } + } else if (isVariableWrite(ins)) { + // Type before the write + updateVariableType(currentBranch, ins, typeAssignedInstructions, true); + + // Type after the write + currentBranch->variableTypes[ins->targetVariable] = writeType(ins); + } else if (isVariableRead(ins)) { + // Type before the read + updateVariableType(currentBranch, ins, typeAssignedInstructions, false); + + // Store the type in the return register + ins->functionReturnReg->setType(ins->targetType); + } + + ins = ins->next; + } + + assert(branches.size() == 1); + assert(branches.back().get() == topBranchPtr); +} + +void LLVMCodeAnalyzer::updateVariableType(Branch *branch, LLVMInstruction *ins, std::unordered_set &typeAssignedInstructions, bool isWrite) const +{ + auto it = branch->variableTypes.find(ins->targetVariable); + + if (it == branch->variableTypes.cend()) { + if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) { + if (isWrite) + branch->typeChanges = true; + + typeAssignedInstructions.insert(ins); + } + } else { + if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) { + if (isWrite) + branch->typeChanges = true; + + ins->targetType = it->second; + typeAssignedInstructions.insert(ins); + } else { + if (isWrite && ((ins->targetType | it->second) != ins->targetType)) + branch->typeChanges = true; + + ins->targetType |= it->second; + } + } +} + +void LLVMCodeAnalyzer::mergeBranchTypes(Branch *branch, Branch *previousBranch) const +{ + // Variables + for (const auto &[var, type] : branch->variableTypes) { + auto it = previousBranch->variableTypes.find(var); + + if (it == previousBranch->variableTypes.cend()) + previousBranch->variableTypes[var] = type; + else + it->second |= type; + } +} + +void LLVMCodeAnalyzer::overrideBranchTypes(Branch *branch, Branch *previousBranch) const +{ + // Variables + for (const auto &[var, type] : branch->variableTypes) + previousBranch->variableTypes[var] = type; +} + +bool LLVMCodeAnalyzer::isLoopStart(const LLVMInstruction *ins) const +{ + return (BEGIN_LOOP_INSTRUCTIONS.find(ins->type) != BEGIN_LOOP_INSTRUCTIONS.cend()); +} + +bool LLVMCodeAnalyzer::isLoopEnd(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::EndLoop); +} + +bool LLVMCodeAnalyzer::isIfStart(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::BeginIf); +} + +bool LLVMCodeAnalyzer::isElse(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::BeginElse); +} + +bool LLVMCodeAnalyzer::isIfEnd(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::EndIf); +} + +bool LLVMCodeAnalyzer::isVariableRead(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::ReadVariable); +} + +bool LLVMCodeAnalyzer::isVariableWrite(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::WriteVariable); +} + +Compiler::StaticType LLVMCodeAnalyzer::writeType(LLVMInstruction *ins) const +{ + assert(ins); + assert(!ins->args.empty()); + auto &arg = ins->args.back(); // value is always the last argument in variable/list write instructions + const LLVMRegister *argReg = arg.second; + + if (argReg->instruction) { + // TODO: Handle list item + if (isVariableRead(argReg->instruction.get())) { + // Store the variable type in the value argument + arg.first = argReg->instruction->functionReturnReg->type(); + } + } + + return LLVMBuildUtils::optimizeRegisterType(argReg); +} diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.h b/src/engine/internal/llvm/llvmcodeanalyzer.h new file mode 100644 index 00000000..899227f9 --- /dev/null +++ b/src/engine/internal/llvm/llvmcodeanalyzer.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace libscratchcpp +{ + +class LLVMInstructionList; +class LLVMInstruction; + +class LLVMCodeAnalyzer +{ + public: + void analyzeScript(const LLVMInstructionList &script) const; + + private: + struct Branch + { + LLVMInstruction *start = nullptr; + bool typeChanges = false; + std::unordered_map variableTypes; + + std::unique_ptr elseBranch; + }; + + void updateVariableType(Branch *branch, LLVMInstruction *ins, std::unordered_set &typeAssignedInstructions, bool isWrite) const; + void mergeBranchTypes(Branch *branch, Branch *previousBranch) const; + void overrideBranchTypes(Branch *branch, Branch *previousBranch) const; + + bool isLoopStart(const LLVMInstruction *ins) const; + bool isLoopEnd(const LLVMInstruction *ins) const; + bool isIfStart(const LLVMInstruction *ins) const; + bool isElse(const LLVMInstruction *ins) const; + bool isIfEnd(const LLVMInstruction *ins) const; + + bool isVariableRead(const LLVMInstruction *ins) const; + bool isVariableWrite(const LLVMInstruction *ins) const; + + Compiler::StaticType writeType(LLVMInstruction *ins) const; +}; + +} // namespace libscratchcpp diff --git a/test/llvm/CMakeLists.txt b/test/llvm/CMakeLists.txt index 62167f0d..3f9e02c3 100644 --- a/test/llvm/CMakeLists.txt +++ b/test/llvm/CMakeLists.txt @@ -22,6 +22,7 @@ add_executable( llvmexecutablecode_test.cpp llvmcodebuilder_test.cpp llvminstructionlist_test.cpp + code_analyzer/variable_type_analysis.cpp operators/equal_comparison_test.cpp operators/greater_than_test.cpp operators/less_than_test.cpp diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp new file mode 100644 index 00000000..58b5588d --- /dev/null +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -0,0 +1,1053 @@ +#include +#include +#include +#include +#include +#include + +using namespace libscratchcpp; + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, EmptyScript) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + + analyzer.analyzeScript(list); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, NoVariableOperations) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + + auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); + list.addInstruction(funcCall); + + analyzer.analyzeScript(list); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, FirstVariableWrite) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Number, 42.0); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + analyzer.analyzeScript(list); + + // First write should have Unknown targetType (no previous type) + ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SecondVariableWrite) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + analyzer.analyzeScript(list); + + // First write has no previous type + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + // Second write has Number type from first write + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleWritesSameType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "first"); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "second"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::String, "third"); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::String); + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, StringOptimization) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "3.14"); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + // String "3.14" optimized to Number, so second write sees Number type + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopSingleWrite) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // Loop convergence: first iteration Unknown, subsequent iterations Number + ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WhileLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + // Establish bool type before loop + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, true); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); + list.addInstruction(loopStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, RepeatUntilLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + // Establish bool type before loop + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, true); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); + list.addInstruction(loopStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_UnknownType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // First write: Unknown (unknown before loop) + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + + // Second write: Number + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, LoopMultipleWrites_KnownType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + // Establish bool type before loop + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, true); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // First write: Bool | String (from loop iterations) + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Bool | Compiler::StaticType::String); + + // Second write: Number + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementSameTypes) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Number, 1.25); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::String, "test"); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + // Both writes see Unknown before the if-else + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); + + // The type is always Number after the if statement + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentTypes) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + // Both writes see Unknown before the if-else + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); + + // The type is Number or String after the if statement + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_IfBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); + + // The type is Number or String after the if statement + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, IfElseStatementDifferentType_ElseBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); + + // The type is Number or String after the if statement + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVarBefore = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::String, "initial"); + setVarBefore->targetVariable = &var; + setVarBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(setVarBefore); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto setVarInLoop = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueInLoop(Compiler::StaticType::Number, 42); + setVarInLoop->targetVariable = &var; + setVarInLoop->args.push_back({ Compiler::StaticType::Unknown, &valueInLoop }); + list.addInstruction(setVarInLoop); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + analyzer.analyzeScript(list); + + // The type might be String or Number because the loop might or might not run + ASSERT_EQ(setVar->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_IfBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVarBefore = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::String, "initial"); + setVarBefore->targetVariable = &var; + setVarBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(setVarBefore); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVarInIfStatement = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42); + setVarInIfStatement->targetVariable = &var; + setVarInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement }); + list.addInstruction(setVarInIfStatement); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + analyzer.analyzeScript(list); + + // The type might be String or Number because the if statement might or might not run + ASSERT_EQ(setVar->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfStatement_ElseBranch) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVarBefore = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::String, "initial"); + setVarBefore->targetVariable = &var; + setVarBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(setVarBefore); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVarInIfStatement = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42); + setVarInIfStatement->targetVariable = &var; + setVarInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement }); + list.addInstruction(setVarInIfStatement); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(setVar); + + analyzer.analyzeScript(list); + + // The type might be String or Number because the else branch might or might not run + ASSERT_EQ(setVar->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, WriteBeforeIfElse) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto setVarBefore = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::Bool, true); + setVarBefore->targetVariable = &var; + setVarBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(setVarBefore); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + // Write before if-else establishes Bool type + ASSERT_EQ(setVarBefore->targetType, Compiler::StaticType::Unknown); + + // Both writes in branches see Bool type from before + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Bool); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Bool); + + // The type is Number or String after the if statement + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ComplexNestedControlFlow) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(outerLoop); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 1); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(innerLoop); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "nested"); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto innerLoopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(innerLoopEnd); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto outerLoopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(outerLoopEnd); + + auto setVar4 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value4(Compiler::StaticType::Bool, true); + setVar4->targetVariable = &var; + setVar4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); + list.addInstruction(setVar4); + + analyzer.analyzeScript(list); + + // Complex analysis with multiple execution paths and loop convergence + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number); + ASSERT_EQ(setVar4->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, MultipleVariablesSeparateAnalysis) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var1("", ""), var2("", ""); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var1; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar2->targetVariable = &var2; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + analyzer.analyzeScript(list); + + // Both are first writes for their respective variables + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_SingleType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var1("", ""), var2("", ""); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); + setVar2->targetVariable = &var2; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar2); + + auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar2->targetVariable = &var2; + list.addInstruction(readVar2); + + LLVMRegister var2Value(Compiler::StaticType::Unknown); + var2Value.isRawValue = false; + var2Value.instruction = readVar2; + readVar2->functionReturnReg = &var2Value; + + auto setVar1_1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setVar1_1->targetVariable = &var1; + setVar1_1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); + list.addInstruction(setVar1_1); + + auto setVar1_2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + setVar1_2->targetVariable = &var1; + setVar1_2->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); + list.addInstruction(setVar1_2); + + analyzer.analyzeScript(list); + + // var2 first write has no previous type + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Unknown); + + // var2 has Number type at read operation + ASSERT_EQ(readVar2->targetType, Compiler::StaticType::Number); + + // var1 first write has no previous type + ASSERT_EQ(setVar1_1->targetType, Compiler::StaticType::Unknown); + + // var1 second write has Number type + ASSERT_EQ(setVar1_2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableDependency_MultipleTypes) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var1("", ""), var2("", ""); + + // Establish Number | Bool types + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var2; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + setVar2->targetVariable = &var2; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(setVar2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar2->targetVariable = &var2; + list.addInstruction(readVar2); + + LLVMRegister var2Value(Compiler::StaticType::Unknown); + var2Value.isRawValue = false; + var2Value.instruction = readVar2; + readVar2->functionReturnReg = &var2Value; + + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setVar3->targetVariable = &var1; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); + list.addInstruction(setVar3); + + auto setVar4 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value4(Compiler::StaticType::String, "test"); + setVar4->targetVariable = &var1; + setVar4->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); + list.addInstruction(setVar4); + + analyzer.analyzeScript(list); + + // var2 has Number or Bool type at read operation + ASSERT_EQ(readVar2->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); + + // var1 first write has no previous type + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Unknown); + + // var1 second write has Number or Bool type + ASSERT_EQ(setVar4->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, ChainedAssignmentsInLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable varA("a", ""), varB("b", ""), varC("c", ""); + + // a = 5 + auto setA1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueA1(Compiler::StaticType::Number, 5); + setA1->targetVariable = &varA; + setA1->args.push_back({ Compiler::StaticType::Unknown, &valueA1 }); + list.addInstruction(setA1); + + // b = 3 + auto setB1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueB1(Compiler::StaticType::Number, 3); + setB1->targetVariable = &varB; + setB1->args.push_back({ Compiler::StaticType::Unknown, &valueB1 }); + list.addInstruction(setB1); + + // c = 8 + auto setC1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueC1(Compiler::StaticType::Number, 8); + setC1->targetVariable = &varC; + setC1->args.push_back({ Compiler::StaticType::Unknown, &valueC1 }); + list.addInstruction(setC1); + + // loop start + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + // a = b + auto readB = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readB->targetVariable = &varB; + list.addInstruction(readB); + + LLVMRegister bValue(Compiler::StaticType::Unknown); + bValue.isRawValue = false; + bValue.instruction = readB; + readB->functionReturnReg = &bValue; + + auto setA2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setA2->targetVariable = &varA; + setA2->args.push_back({ Compiler::StaticType::Unknown, &bValue }); + list.addInstruction(setA2); + + // b = c + auto readC1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readC1->targetVariable = &varC; + list.addInstruction(readC1); + + LLVMRegister cValue1(Compiler::StaticType::Unknown); + cValue1.isRawValue = false; + cValue1.instruction = readC1; + readC1->functionReturnReg = &cValue1; + + auto setB2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setB2->targetVariable = &varB; + setB2->args.push_back({ Compiler::StaticType::Unknown, &cValue1 }); + list.addInstruction(setB2); + + // c = "test" + auto setC2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister valueC2(Compiler::StaticType::String, "test"); + setC2->targetVariable = &varC; + setC2->args.push_back({ Compiler::StaticType::Unknown, &valueC2 }); + list.addInstruction(setC2); + + // loop end + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + // a = c (final assignment we want to test) + auto readC2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readC2->targetVariable = &varC; + list.addInstruction(readC2); + + LLVMRegister cValue2(Compiler::StaticType::Unknown); + cValue2.isRawValue = false; + cValue2.instruction = readC2; + readC2->functionReturnReg = &cValue2; + + auto setA3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setA3->targetVariable = &varA; + setA3->args.push_back({ Compiler::StaticType::Unknown, &cValue2 }); + list.addInstruction(setA3); + + analyzer.analyzeScript(list); + + // Check the type of 'a' before the final a = c assignment + // 'a' could be Number (from initial assignment) or String (from loop iterations where a=b, b=c, c="test") + auto expectedAType = Compiler::StaticType::Number | Compiler::StaticType::String; + ASSERT_EQ(setA3->targetType, expectedAType); + + // Check the type of 'c' before the final a = c assignment + // 'c' could be Number (from initial assignment) or String (from loop assignment c="test") + auto expectedCType = Compiler::StaticType::Number | Compiler::StaticType::String; + // We need to check what type 'c' has at the point of the final read + // This would be determined by the loop convergence analysis + ASSERT_EQ(readC2->targetType, expectedCType); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignment) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + // First establish a type for the variable + auto setVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + setVar1->targetVariable = &var; + setVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar1); + + // Self-assignment: var = var + auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar->targetVariable = &var; + list.addInstruction(readVar); + + LLVMRegister varValue(Compiler::StaticType::Unknown); + varValue.isRawValue = false; + varValue.instruction = readVar; + readVar->functionReturnReg = &varValue; + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setVar2->targetVariable = &var; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &varValue }); + list.addInstruction(setVar2); + + // Should still be Number + auto setVar3 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value3(Compiler::StaticType::String, "test"); + setVar3->targetVariable = &var; + setVar3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(setVar3); + + analyzer.analyzeScript(list); + + // First write should have Unknown targetType (no previous type) + ASSERT_EQ(setVar1->targetType, Compiler::StaticType::Unknown); + + // Self-assignment should see the Number type from the previous write + ASSERT_EQ(setVar2->targetType, Compiler::StaticType::Number); + + // The variable should still have the Number type from the first write + ASSERT_EQ(setVar3->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, SelfAssignmentInLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("", ""); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + // Self-assignment inside loop: var = var + auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar->targetVariable = &var; + list.addInstruction(readVar); + + LLVMRegister varValue(Compiler::StaticType::Unknown); + varValue.isRawValue = false; + varValue.instruction = readVar; + readVar->functionReturnReg = &varValue; + + auto setVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setVar->targetVariable = &var; + setVar->args.push_back({ Compiler::StaticType::Unknown, &varValue }); + list.addInstruction(setVar); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // Self-assignment in loop should maintain Unknown type since it's a no-op + // and doesn't change the variable's type across iterations + ASSERT_EQ(setVar->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, VariableReadReturnRegType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var1("", ""), var2("", ""); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); + setVar2->targetVariable = &var2; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar2); + + auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar2->targetVariable = &var2; + list.addInstruction(readVar2); + + LLVMRegister var2Value(Compiler::StaticType::Unknown); + var2Value.isRawValue = false; + var2Value.instruction = readVar2; + readVar2->functionReturnReg = &var2Value; + + auto setVar1_1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setVar1_1->targetVariable = &var1; + setVar1_1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); + list.addInstruction(setVar1_1); + + analyzer.analyzeScript(list); + + // var2 read return register has Number type + ASSERT_EQ(var2Value.type(), Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, CrossVariableWriteArgType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var1("", ""), var2("", ""); + + auto setVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); + setVar2->targetVariable = &var2; + setVar2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(setVar2); + + auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar2->targetVariable = &var2; + list.addInstruction(readVar2); + + LLVMRegister var2Value(Compiler::StaticType::Unknown); + var2Value.isRawValue = false; + var2Value.instruction = readVar2; + readVar2->functionReturnReg = &var2Value; + + auto setVar1_1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + setVar1_1->targetVariable = &var1; + setVar1_1->args.push_back({ Compiler::StaticType::Unknown, &var2Value }); + list.addInstruction(setVar1_1); + + analyzer.analyzeScript(list); + + // last write argument has Number type + ASSERT_EQ(setVar1_1->args.front().first, Compiler::StaticType::Number); +} From 507ce25eeca2d090d3f8cc92bb56c6afce8da4b9 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:07:47 +0200 Subject: [PATCH 08/27] LLVMCodeAnalyzer: Move common test cases to mixed tests --- test/llvm/CMakeLists.txt | 1 + .../code_analyzer/mixed_type_analysis.cpp | 28 +++++++++++++++++++ .../code_analyzer/variable_type_analysis.cpp | 19 ------------- 3 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 test/llvm/code_analyzer/mixed_type_analysis.cpp diff --git a/test/llvm/CMakeLists.txt b/test/llvm/CMakeLists.txt index 3f9e02c3..191dea5e 100644 --- a/test/llvm/CMakeLists.txt +++ b/test/llvm/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable( llvmcodebuilder_test.cpp llvminstructionlist_test.cpp code_analyzer/variable_type_analysis.cpp + code_analyzer/mixed_type_analysis.cpp operators/equal_comparison_test.cpp operators/greater_than_test.cpp operators/less_than_test.cpp diff --git a/test/llvm/code_analyzer/mixed_type_analysis.cpp b/test/llvm/code_analyzer/mixed_type_analysis.cpp new file mode 100644 index 00000000..cf99d404 --- /dev/null +++ b/test/llvm/code_analyzer/mixed_type_analysis.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace libscratchcpp; + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, EmptyScript) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + + analyzer.analyzeScript(list); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, NoOperations) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + + auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); + list.addInstruction(funcCall); + + analyzer.analyzeScript(list); +} diff --git a/test/llvm/code_analyzer/variable_type_analysis.cpp b/test/llvm/code_analyzer/variable_type_analysis.cpp index 58b5588d..7e7e1124 100644 --- a/test/llvm/code_analyzer/variable_type_analysis.cpp +++ b/test/llvm/code_analyzer/variable_type_analysis.cpp @@ -7,25 +7,6 @@ using namespace libscratchcpp; -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, EmptyScript) -{ - LLVMCodeAnalyzer analyzer; - LLVMInstructionList list; - - analyzer.analyzeScript(list); -} - -TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, NoVariableOperations) -{ - LLVMCodeAnalyzer analyzer; - LLVMInstructionList list; - - auto funcCall = std::make_shared(LLVMInstruction::Type::FunctionCall, false); - list.addInstruction(funcCall); - - analyzer.analyzeScript(list); -} - TEST(LLVMCodeAnalyzer_VariableTypeAnalysis, FirstVariableWrite) { LLVMCodeAnalyzer analyzer; From 0a14d894da81846ca8e2b7e693e134d2a26ca6eb Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 10:15:58 +0200 Subject: [PATCH 09/27] LLVMCodeAnalyzer: Implement list type analysis --- src/engine/internal/llvm/llvmcodeanalyzer.cpp | 104 +- src/engine/internal/llvm/llvmcodeanalyzer.h | 12 +- test/llvm/CMakeLists.txt | 1 + .../llvm/code_analyzer/list_type_analysis.cpp | 1290 +++++++++++++++++ 4 files changed, 1393 insertions(+), 14 deletions(-) create mode 100644 test/llvm/code_analyzer/list_type_analysis.cpp diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp index 8af64a6a..b8a89de0 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.cpp +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -10,6 +10,8 @@ using namespace libscratchcpp; static const std::unordered_set BEGIN_LOOP_INSTRUCTIONS = { LLVMInstruction::Type::BeginRepeatLoop, LLVMInstruction::Type::BeginWhileLoop, LLVMInstruction::Type::BeginRepeatUntilLoop }; +static const std::unordered_set LIST_WRITE_INSTRUCTIONS = { LLVMInstruction::Type::AppendToList, LLVMInstruction::Type::InsertToList, LLVMInstruction::Type::ListReplace }; + void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const { std::unordered_set typeAssignedInstructions; @@ -28,6 +30,7 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const auto branch = std::make_unique(); branch->start = ins; branch->variableTypes = currentBranch->variableTypes; + branch->listTypes = currentBranch->listTypes; currentBranch = branch.get(); branches.push_back(std::move(branch)); } else if (isElse(ins)) { @@ -39,6 +42,7 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const currentBranch = currentBranch->elseBranch.get(); currentBranch->start = ins; currentBranch->variableTypes = previousBranch->variableTypes; + currentBranch->listTypes = previousBranch->listTypes; } else if (isIfEnd(ins) || isLoopEnd(ins)) { if (isLoopEnd(ins) && currentBranch->typeChanges) { // Next iteration @@ -52,11 +56,16 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const assert(primaryBranch); if (primaryBranch && primaryBranch->elseBranch) { - // The previous types can be ignored in if/else statements - overrideBranchTypes(primaryBranch, previousBranch); - mergeBranchTypes(primaryBranch->elseBranch.get(), previousBranch); - } else - mergeBranchTypes(primaryBranch, previousBranch); + // The previous variable types can be ignored in if/else statements + overrideVariableTypes(primaryBranch, previousBranch); + mergeListTypes(primaryBranch, previousBranch); + + mergeVariableTypes(primaryBranch->elseBranch.get(), previousBranch); + mergeListTypes(primaryBranch->elseBranch.get(), previousBranch); + } else { + mergeVariableTypes(primaryBranch, previousBranch); + mergeListTypes(primaryBranch, previousBranch); + } // Remove the branch branches.pop_back(); @@ -72,6 +81,24 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const // Type before the read updateVariableType(currentBranch, ins, typeAssignedInstructions, false); + // Store the type in the return register + ins->functionReturnReg->setType(ins->targetType); + } else if (isListClear(ins)) { + // Type before the read + updateListType(currentBranch, ins, typeAssignedInstructions, true); + + // Clear the list type + currentBranch->listTypes[ins->targetList] = Compiler::StaticType::Void; + } else if (isListWrite(ins)) { + // Type before the write + updateListType(currentBranch, ins, typeAssignedInstructions, true); + + // Type after the write + currentBranch->listTypes[ins->targetList] |= writeType(ins); + } else if (isListRead(ins)) { + // Type before the read + updateListType(currentBranch, ins, typeAssignedInstructions, false); + // Store the type in the return register ins->functionReturnReg->setType(ins->targetType); } @@ -110,9 +137,37 @@ void LLVMCodeAnalyzer::updateVariableType(Branch *branch, LLVMInstruction *ins, } } -void LLVMCodeAnalyzer::mergeBranchTypes(Branch *branch, Branch *previousBranch) const +void LLVMCodeAnalyzer::updateListType(Branch *branch, LLVMInstruction *ins, std::unordered_set &typeAssignedInstructions, bool isWrite) const +{ + auto it = branch->listTypes.find(ins->targetList); + + if (it == branch->listTypes.cend()) { + if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) { + if (isWrite) + branch->typeChanges = true; + + typeAssignedInstructions.insert(ins); + ins->targetType = Compiler::StaticType::Unknown; + branch->listTypes[ins->targetList] = ins->targetType; + } + } else { + if (typeAssignedInstructions.find(ins) == typeAssignedInstructions.cend()) { + if (isWrite) + branch->typeChanges = true; + + ins->targetType = it->second; + typeAssignedInstructions.insert(ins); + } else { + if (isWrite && ((ins->targetType | it->second) != ins->targetType)) + branch->typeChanges = true; + + ins->targetType |= it->second; + } + } +} + +void LLVMCodeAnalyzer::mergeVariableTypes(Branch *branch, Branch *previousBranch) const { - // Variables for (const auto &[var, type] : branch->variableTypes) { auto it = previousBranch->variableTypes.find(var); @@ -123,13 +178,24 @@ void LLVMCodeAnalyzer::mergeBranchTypes(Branch *branch, Branch *previousBranch) } } -void LLVMCodeAnalyzer::overrideBranchTypes(Branch *branch, Branch *previousBranch) const +void LLVMCodeAnalyzer::overrideVariableTypes(Branch *branch, Branch *previousBranch) const { - // Variables for (const auto &[var, type] : branch->variableTypes) previousBranch->variableTypes[var] = type; } +void LLVMCodeAnalyzer::mergeListTypes(Branch *branch, Branch *previousBranch) const +{ + for (const auto &[list, type] : branch->listTypes) { + auto it = previousBranch->listTypes.find(list); + + if (it == previousBranch->listTypes.cend()) + previousBranch->listTypes[list] = type; + else + it->second |= type; + } +} + bool LLVMCodeAnalyzer::isLoopStart(const LLVMInstruction *ins) const { return (BEGIN_LOOP_INSTRUCTIONS.find(ins->type) != BEGIN_LOOP_INSTRUCTIONS.cend()); @@ -165,6 +231,21 @@ bool LLVMCodeAnalyzer::isVariableWrite(const LLVMInstruction *ins) const return (ins->type == LLVMInstruction::Type::WriteVariable); } +bool LLVMCodeAnalyzer::isListRead(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::GetListItem); +} + +bool LLVMCodeAnalyzer::isListWrite(const LLVMInstruction *ins) const +{ + return (LIST_WRITE_INSTRUCTIONS.find(ins->type) != LIST_WRITE_INSTRUCTIONS.cend()); +} + +bool LLVMCodeAnalyzer::isListClear(const LLVMInstruction *ins) const +{ + return (ins->type == LLVMInstruction::Type::ClearList); +} + Compiler::StaticType LLVMCodeAnalyzer::writeType(LLVMInstruction *ins) const { assert(ins); @@ -173,9 +254,8 @@ Compiler::StaticType LLVMCodeAnalyzer::writeType(LLVMInstruction *ins) const const LLVMRegister *argReg = arg.second; if (argReg->instruction) { - // TODO: Handle list item - if (isVariableRead(argReg->instruction.get())) { - // Store the variable type in the value argument + if (isVariableRead(argReg->instruction.get()) || isListRead(argReg->instruction.get())) { + // Store the variable/list type in the value argument arg.first = argReg->instruction->functionReturnReg->type(); } } diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.h b/src/engine/internal/llvm/llvmcodeanalyzer.h index 899227f9..65c73c5d 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.h +++ b/src/engine/internal/llvm/llvmcodeanalyzer.h @@ -22,13 +22,17 @@ class LLVMCodeAnalyzer LLVMInstruction *start = nullptr; bool typeChanges = false; std::unordered_map variableTypes; + std::unordered_map listTypes; std::unique_ptr elseBranch; }; void updateVariableType(Branch *branch, LLVMInstruction *ins, std::unordered_set &typeAssignedInstructions, bool isWrite) const; - void mergeBranchTypes(Branch *branch, Branch *previousBranch) const; - void overrideBranchTypes(Branch *branch, Branch *previousBranch) const; + void updateListType(Branch *branch, LLVMInstruction *ins, std::unordered_set &typeAssignedInstructions, bool isWrite) const; + + void mergeVariableTypes(Branch *branch, Branch *previousBranch) const; + void overrideVariableTypes(Branch *branch, Branch *previousBranch) const; + void mergeListTypes(Branch *branch, Branch *previousBranch) const; bool isLoopStart(const LLVMInstruction *ins) const; bool isLoopEnd(const LLVMInstruction *ins) const; @@ -39,6 +43,10 @@ class LLVMCodeAnalyzer bool isVariableRead(const LLVMInstruction *ins) const; bool isVariableWrite(const LLVMInstruction *ins) const; + bool isListRead(const LLVMInstruction *ins) const; + bool isListWrite(const LLVMInstruction *ins) const; + bool isListClear(const LLVMInstruction *ins) const; + Compiler::StaticType writeType(LLVMInstruction *ins) const; }; diff --git a/test/llvm/CMakeLists.txt b/test/llvm/CMakeLists.txt index 191dea5e..4a51365c 100644 --- a/test/llvm/CMakeLists.txt +++ b/test/llvm/CMakeLists.txt @@ -23,6 +23,7 @@ add_executable( llvmcodebuilder_test.cpp llvminstructionlist_test.cpp code_analyzer/variable_type_analysis.cpp + code_analyzer/list_type_analysis.cpp code_analyzer/mixed_type_analysis.cpp operators/equal_comparison_test.cpp operators/greater_than_test.cpp diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp new file mode 100644 index 00000000..8a436a32 --- /dev/null +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -0,0 +1,1290 @@ +#include +#include +#include +#include +#include +#include + +using namespace libscratchcpp; + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, FirstListWrite) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Number, 42.0); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + analyzer.analyzeScript(list); + + // First write should have Unknown targetType (no previous type) + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, SecondListWrite) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + // First write has no previous type + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); + + // Second write has Unknown type because the list wasn't cleared + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesSameType_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "first"); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "second"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::String, "third"); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::String); + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleWritesDifferentTypes_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, StringOptimization_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "3.14"); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + + // String "3.14" optimized to Number, so second write sees Number type + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearListOperation) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); + + // After clear, list is empty so new type can be established + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsSameType_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "hello"); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList); + + auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + LLVMConstantRegister value2(Compiler::StaticType::String, "world"); + insertList->targetList = &targetList; + insertList->args.push_back({ Compiler::StaticType::Number, &index }); + insertList->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(insertList); + + auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); + LLVMConstantRegister index2(Compiler::StaticType::Number, 1); + LLVMConstantRegister value3(Compiler::StaticType::String, "test"); + replaceList->targetList = &targetList; + replaceList->args.push_back({ Compiler::StaticType::Number, &index2 }); + replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(replaceList); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); + ASSERT_EQ(insertList->targetType, Compiler::StaticType::String); + ASSERT_EQ(replaceList->targetType, Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MixedWriteOperationsDifferentTypes_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "hello"); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList); + + auto insertList = std::make_shared(LLVMInstruction::Type::InsertToList, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + LLVMConstantRegister value2(Compiler::StaticType::Bool, false); + insertList->targetList = &targetList; + insertList->args.push_back({ Compiler::StaticType::Number, &index }); + insertList->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(insertList); + + auto replaceList = std::make_shared(LLVMInstruction::Type::ListReplace, false); + LLVMConstantRegister index2(Compiler::StaticType::Number, 1); + LLVMConstantRegister value3(Compiler::StaticType::Number, 5.2); + replaceList->targetList = &targetList; + replaceList->args.push_back({ Compiler::StaticType::Number, &index2 }); + replaceList->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(replaceList); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value4(Compiler::StaticType::String, "world"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value4 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); + ASSERT_EQ(insertList->targetType, Compiler::StaticType::String); + ASSERT_EQ(replaceList->targetType, Compiler::StaticType::Bool | Compiler::StaticType::String); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // Loop convergence: first iteration Unknown, subsequent iterations Number + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopSingleWrite_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Number, 1.25); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ClearAfterWriteInLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + // Establish bool type before loop + auto clearList1 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList1->targetList = &targetList; + list.addInstruction(clearList1); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, true); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); + list.addInstruction(loopStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList2->targetList = &targetList; + list.addInstruction(clearList2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WhileLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + // Establish bool type before loop + auto clearList1 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList1->targetList = &targetList; + list.addInstruction(clearList1); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, true); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginWhileLoop, false); + list.addInstruction(loopStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList2->targetList = &targetList; + list.addInstruction(clearList2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, RepeatUntilLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + // Establish bool type before loop + auto clearList1 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList1->targetList = &targetList; + list.addInstruction(clearList1); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, true); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatUntilLoop, false); + list.addInstruction(loopStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto clearList2 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList2->targetList = &targetList; + list.addInstruction(clearList2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_UnknownType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // First write: Unknown (unknown before loop) + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); + + // Second write: still Unknown + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, LoopMultipleWrites_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + // Establish bool type before loop + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 5); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // Number | String (from loop iterations) + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + // Both writes see Unknown before the if-else + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); + + // The type is still Unknown even after the if statement + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentTypes_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + // Both writes see Void (empty list) before the if-else + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); + + // The type is Number or String after the if statement + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_IfBranch_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); + + // The type is Number or String after the if statement + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, IfElseStatementDifferentType_ElseBranch_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Number); + + // The type is Number or String after the if statement + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeLoop_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendListBefore = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::String, "initial"); + appendListBefore->targetList = &targetList; + appendListBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(appendListBefore); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + auto appendListInLoop = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueInLoop(Compiler::StaticType::Number, 42); + appendListInLoop->targetList = &targetList; + appendListInLoop->args.push_back({ Compiler::StaticType::Unknown, &valueInLoop }); + list.addInstruction(appendListInLoop); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_IfBranch_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendListBefore = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::String, "initial"); + appendListBefore->targetList = &targetList; + appendListBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(appendListBefore); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto appendListInIfStatement = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42); + appendListInIfStatement->targetList = &targetList; + appendListInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement }); + list.addInstruction(appendListInIfStatement); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfStatement_ElseBranch_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendListBefore = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::String, "initial"); + appendListBefore->targetList = &targetList; + appendListBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(appendListBefore); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto appendListInIfStatement = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueInIfStatement(Compiler::StaticType::Number, 42); + appendListInIfStatement->targetList = &targetList; + appendListInIfStatement->args.push_back({ Compiler::StaticType::Unknown, &valueInIfStatement }); + list.addInstruction(appendListInIfStatement); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value(Compiler::StaticType::Bool, false); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &value }); + list.addInstruction(appendList); + + analyzer.analyzeScript(list); + + ASSERT_EQ(appendList->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, WriteBeforeIfElse_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto appendListBefore = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueBefore(Compiler::StaticType::Bool, true); + appendListBefore->targetList = &targetList; + appendListBefore->args.push_back({ Compiler::StaticType::Unknown, &valueBefore }); + list.addInstruction(appendListBefore); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + analyzer.analyzeScript(list); + + // Write before if-else establishes Bool type + ASSERT_EQ(appendListBefore->targetType, Compiler::StaticType::Void); + + // Both writes in branches see Bool type from before + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Bool); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Bool); + + // The type is Unknown after the if statement because the first type is used as well + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ComplexNestedControlFlow_AfterClear) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList("", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto outerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(outerLoop); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 1); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + auto innerLoop = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(innerLoop); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "nested"); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto innerLoopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(innerLoopEnd); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value3(Compiler::StaticType::Bool, true); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &value3 }); + list.addInstruction(appendList3); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + auto outerLoopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(outerLoopEnd); + + auto appendList4 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value4(Compiler::StaticType::Bool, true); + appendList4->targetList = &targetList; + appendList4->args.push_back({ Compiler::StaticType::Unknown, &value4 }); + list.addInstruction(appendList4); + + analyzer.analyzeScript(list); + + // Complex analysis with multiple execution paths and loop convergence + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(appendList4->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, MultipleListsSeparateAnalysis) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List targetList1("", ""), targetList2("", ""); + + auto clearList1 = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList1->targetList = &targetList1; + list.addInstruction(clearList1); + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &targetList1; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList2->targetList = &targetList2; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + // Both are first writes for their respective lists + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Unknown); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_SingleType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List sourceList("", ""), targetList("", ""); + + auto clearSource = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearSource->targetList = &sourceList; + list.addInstruction(clearSource); + + auto clearTarget = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearTarget->targetList = &targetList; + list.addInstruction(clearTarget); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); + appendList2->targetList = &sourceList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList2); + + auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getItem->targetList = &sourceList; + getItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getItem); + + LLVMRegister sourceValue(Compiler::StaticType::Unknown); + sourceValue.isRawValue = false; + sourceValue.instruction = getItem; + getItem->functionReturnReg = &sourceValue; + + auto appendList1_1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList1_1->targetList = &targetList; + appendList1_1->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); + list.addInstruction(appendList1_1); + + auto appendList1_2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "test"); + appendList1_2->targetList = &targetList; + appendList1_2->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); + list.addInstruction(appendList1_2); + + analyzer.analyzeScript(list); + + // sourceList first write has no previous type + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); + + // sourceList has Number type at read operation + ASSERT_EQ(getItem->targetType, Compiler::StaticType::Number); + + // targetList first write has no previous type + ASSERT_EQ(appendList1_1->targetType, Compiler::StaticType::Void); + + // targetList second write has Number type + ASSERT_EQ(appendList1_2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_MultipleTypes) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List sourceList("", ""), targetList("", ""); + + auto clearSource = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearSource->targetList = &sourceList; + list.addInstruction(clearSource); + + auto clearTarget = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearTarget->targetList = &targetList; + list.addInstruction(clearTarget); + + // Establish Number | Bool types + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + appendList1->targetList = &sourceList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, true); + appendList2->targetList = &sourceList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(appendList2); + + auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getItem->targetList = &sourceList; + getItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getItem); + + LLVMRegister sourceValue(Compiler::StaticType::Unknown); + sourceValue.isRawValue = false; + sourceValue.instruction = getItem; + getItem->functionReturnReg = &sourceValue; + + auto appendList3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList3->targetList = &targetList; + appendList3->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); + list.addInstruction(appendList3); + + auto appendList4 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value4(Compiler::StaticType::String, "test"); + appendList4->targetList = &targetList; + appendList4->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); + list.addInstruction(appendList4); + + analyzer.analyzeScript(list); + + // sourceList has Number or Bool type at read operation + ASSERT_EQ(getItem->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); + + // targetList first write has no previous type + ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Void); + + // targetList second write has Number or Bool type + ASSERT_EQ(appendList4->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ChainedAssignmentsInLoop) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List listA("a", ""), listB("b", ""), listC("c", ""); + + auto clearA = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearA->targetList = &listA; + list.addInstruction(clearA); + + auto clearB = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearB->targetList = &listB; + list.addInstruction(clearB); + + auto clearC = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearC->targetList = &listC; + list.addInstruction(clearC); + + // a += 5 + auto appendA1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueA1(Compiler::StaticType::Number, 5); + appendA1->targetList = &listA; + appendA1->args.push_back({ Compiler::StaticType::Unknown, &valueA1 }); + list.addInstruction(appendA1); + + // b += 3 + auto appendB1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueB1(Compiler::StaticType::Number, 3); + appendB1->targetList = &listB; + appendB1->args.push_back({ Compiler::StaticType::Unknown, &valueB1 }); + list.addInstruction(appendB1); + + // c += 8 + auto appendC1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueC1(Compiler::StaticType::Number, 8); + appendC1->targetList = &listC; + appendC1->args.push_back({ Compiler::StaticType::Unknown, &valueC1 }); + list.addInstruction(appendC1); + + // loop start + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + // clear a + clearA = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearA->targetList = &listA; + list.addInstruction(clearA); + + // a += b[0] + auto getB = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister indexB(Compiler::StaticType::Number, 0); + getB->targetList = &listB; + getB->args.push_back({ Compiler::StaticType::Number, &indexB }); + list.addInstruction(getB); + + LLVMRegister bValue(Compiler::StaticType::Unknown); + bValue.isRawValue = false; + bValue.instruction = getB; + getB->functionReturnReg = &bValue; + + auto appendA2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendA2->targetList = &listA; + appendA2->args.push_back({ Compiler::StaticType::Unknown, &bValue }); + list.addInstruction(appendA2); + + // clear b + clearB = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearB->targetList = &listB; + list.addInstruction(clearB); + + // b += c[0] + auto getC1 = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister indexC1(Compiler::StaticType::Number, 0); + getC1->targetList = &listC; + getC1->args.push_back({ Compiler::StaticType::Number, &indexC1 }); + list.addInstruction(getC1); + + LLVMRegister cValue1(Compiler::StaticType::Unknown); + cValue1.isRawValue = false; + cValue1.instruction = getC1; + getC1->functionReturnReg = &cValue1; + + auto appendB2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendB2->targetList = &listB; + appendB2->args.push_back({ Compiler::StaticType::Unknown, &cValue1 }); + list.addInstruction(appendB2); + + // clear c + clearC = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearC->targetList = &listC; + list.addInstruction(clearC); + + // c += "test" + auto appendC2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister valueC2(Compiler::StaticType::String, "test"); + appendC2->targetList = &listC; + appendC2->args.push_back({ Compiler::StaticType::Unknown, &valueC2 }); + list.addInstruction(appendC2); + + // loop end + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + // clear a (final assignment we want to test) + auto clearAFinal = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearAFinal->targetList = &listA; + list.addInstruction(clearAFinal); + + // a += c[0] + auto getC2 = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister indexC2(Compiler::StaticType::Number, 0); + getC2->targetList = &listC; + getC2->args.push_back({ Compiler::StaticType::Number, &indexC2 }); + list.addInstruction(getC2); + + LLVMRegister cValue2(Compiler::StaticType::Unknown); + cValue2.isRawValue = false; + cValue2.instruction = getC2; + getC2->functionReturnReg = &cValue2; + + auto appendA3 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendA3->targetList = &listA; + appendA3->args.push_back({ Compiler::StaticType::Unknown, &cValue2 }); + list.addInstruction(appendA3); + + analyzer.analyzeScript(list); + + // Check the type of 'a' before the final clear operation + // 'a' could be Number (from initial assignment) or String (from loop iterations) + auto expectedAType = Compiler::StaticType::Number | Compiler::StaticType::String; + ASSERT_EQ(clearAFinal->targetType, expectedAType); + + // Check the type of 'c' before the final read + // 'c' could be Number (from initial assignment) or String (from loop assignment) + auto expectedCType = Compiler::StaticType::Number | Compiler::StaticType::String; + ASSERT_EQ(getC2->targetType, expectedCType); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ListReadReturnRegType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List sourceList("", ""), targetList("", ""); + + auto clearSource = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearSource->targetList = &sourceList; + list.addInstruction(clearSource); + + auto clearTarget = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearTarget->targetList = &targetList; + list.addInstruction(clearTarget); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); + appendList2->targetList = &sourceList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList2); + + auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getItem->targetList = &sourceList; + getItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getItem); + + LLVMRegister sourceValue(Compiler::StaticType::Unknown); + sourceValue.isRawValue = false; + sourceValue.instruction = getItem; + getItem->functionReturnReg = &sourceValue; + + auto appendList1_1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList1_1->targetList = &targetList; + appendList1_1->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); + list.addInstruction(appendList1_1); + + analyzer.analyzeScript(list); + + // sourceList read return register has Number type + ASSERT_EQ(sourceValue.type(), Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListWriteArgType) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List sourceList("", ""), targetList("", ""); + + auto clearSource = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearSource->targetList = &sourceList; + list.addInstruction(clearSource); + + auto clearTarget = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearTarget->targetList = &targetList; + list.addInstruction(clearTarget); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 3.14); + appendList2->targetList = &sourceList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(appendList2); + + auto getItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getItem->targetList = &sourceList; + getItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getItem); + + LLVMRegister sourceValue(Compiler::StaticType::Unknown); + sourceValue.isRawValue = false; + sourceValue.instruction = getItem; + getItem->functionReturnReg = &sourceValue; + + auto appendList1_1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList1_1->targetList = &targetList; + appendList1_1->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); + list.addInstruction(appendList1_1); + + analyzer.analyzeScript(list); + + // last write argument has Number type + ASSERT_EQ(appendList1_1->args.back().first, Compiler::StaticType::Number); +} From 1d59bbe6fe5eadba11a5e35953ecc9fe326a1210 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 17:05:27 +0200 Subject: [PATCH 10/27] LLVMCodeAnalyzer: Add test cases for mixed type analysis --- .../code_analyzer/mixed_type_analysis.cpp | 478 ++++++++++++++++++ 1 file changed, 478 insertions(+) diff --git a/test/llvm/code_analyzer/mixed_type_analysis.cpp b/test/llvm/code_analyzer/mixed_type_analysis.cpp index cf99d404..94eb5ebd 100644 --- a/test/llvm/code_analyzer/mixed_type_analysis.cpp +++ b/test/llvm/code_analyzer/mixed_type_analysis.cpp @@ -26,3 +26,481 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, NoOperations) analyzer.analyzeScript(list); } + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, VariableToList_SimpleTransfer) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable sourceVar("sourceVar", ""); + List targetList("targetList", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + // Write to variable + auto writeVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister varValue(Compiler::StaticType::String, "hello"); + writeVar->targetVariable = &sourceVar; + writeVar->args.push_back({ Compiler::StaticType::Unknown, &varValue }); + list.addInstruction(writeVar); + + // Read from variable and write to list + auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar->targetVariable = &sourceVar; + list.addInstruction(readVar); + + LLVMRegister readVarReg(Compiler::StaticType::Unknown); + readVarReg.isRawValue = false; + readVarReg.instruction = readVar; + readVar->functionReturnReg = &readVarReg; + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &readVarReg }); + list.addInstruction(appendList); + + analyzer.analyzeScript(list); + + // Variable write should see Unknown (first write) + ASSERT_EQ(writeVar->targetType, Compiler::StaticType::Unknown); + + // Variable read should see String type + ASSERT_EQ(readVar->targetType, Compiler::StaticType::String); + + // List append should see Void (empty list after clear) + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); + + // Return register should have String type + ASSERT_EQ(readVarReg.type(), Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ListToVariable_SimpleTransfer) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List sourceList("sourceList", ""); + Variable targetVar("targetVar", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &sourceList; + list.addInstruction(clearList); + + // Write to list + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister listValue(Compiler::StaticType::Bool, true); + appendList->targetList = &sourceList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &listValue }); + list.addInstruction(appendList); + + // Read from list and write to variable + auto getListItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getListItem->targetList = &sourceList; + getListItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getListItem); + + LLVMRegister readListReg(Compiler::StaticType::Unknown); + readListReg.isRawValue = false; + readListReg.instruction = getListItem; + getListItem->functionReturnReg = &readListReg; + + auto writeVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + writeVar->targetVariable = &targetVar; + writeVar->args.push_back({ Compiler::StaticType::Unknown, &readListReg }); + list.addInstruction(writeVar); + + analyzer.analyzeScript(list); + + // List append should see Void (empty list after clear) + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); + + // List read should see Bool type + ASSERT_EQ(getListItem->targetType, Compiler::StaticType::Bool); + + // Variable write should see Unknown (first write) + ASSERT_EQ(writeVar->targetType, Compiler::StaticType::Unknown); + + // Return register should have Bool type + ASSERT_EQ(readListReg.type(), Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, CircularVarListDependency) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("var", ""); + List targetList("list", ""); + + // Initialize variable with number + auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 42); + writeVar1->targetVariable = &var; + writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(writeVar1); + + // Clear list + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + // Read from variable and write to list + auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar->targetVariable = &var; + list.addInstruction(readVar); + + LLVMRegister readVarReg(Compiler::StaticType::Unknown); + readVarReg.isRawValue = false; + readVarReg.instruction = readVar; + readVar->functionReturnReg = &readVarReg; + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &readVarReg }); + list.addInstruction(appendList); + + // Read from list and write back to variable (circular dependency) + auto getListItem = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + getListItem->targetList = &targetList; + getListItem->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(getListItem); + + LLVMRegister readListReg(Compiler::StaticType::Unknown); + readListReg.isRawValue = false; + readListReg.instruction = getListItem; + getListItem->functionReturnReg = &readListReg; + + auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + writeVar2->targetVariable = &var; + writeVar2->args.push_back({ Compiler::StaticType::Unknown, &readListReg }); + list.addInstruction(writeVar2); + + analyzer.analyzeScript(list); + + // Should handle circular dependency gracefully + ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(readVar->targetType, Compiler::StaticType::Number); + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); + ASSERT_EQ(getListItem->targetType, Compiler::StaticType::Number); + ASSERT_EQ(writeVar2->targetType, Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, LoopWithVarListInteraction_TypeConflict) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("var", ""); + List targetList("list", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + // First iteration: write string to variable + auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::String, "text"); + writeVar1->targetVariable = &var; + writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(writeVar1); + + auto loopStart = std::make_shared(LLVMInstruction::Type::BeginRepeatLoop, false); + list.addInstruction(loopStart); + + // Read from variable and write to list + auto readVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar->targetVariable = &var; + list.addInstruction(readVar); + + LLVMRegister readVarReg(Compiler::StaticType::Unknown); + readVarReg.isRawValue = false; + readVarReg.instruction = readVar; + readVar->functionReturnReg = &readVarReg; + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList->targetList = &targetList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &readVarReg }); + list.addInstruction(appendList); + + // Second iteration: write number to variable (creates type conflict) + auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Number, 42); + writeVar2->targetVariable = &var; + writeVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(writeVar2); + + auto loopEnd = std::make_shared(LLVMInstruction::Type::EndLoop, false); + list.addInstruction(loopEnd); + + analyzer.analyzeScript(list); + + // Should show type accumulation through loop iterations + ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(readVar->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); + ASSERT_EQ(appendList->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); + ASSERT_EQ(writeVar2->targetType, Compiler::StaticType::String | Compiler::StaticType::Number); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ConditionalVarListTransfer_TypeConflict) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var("var", ""); + List targetList("list", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + auto ifStart = std::make_shared(LLVMInstruction::Type::BeginIf, false); + list.addInstruction(ifStart); + + // If branch: write number to variable, transfer to list + auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Number, 123); + writeVar1->targetVariable = &var; + writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(writeVar1); + + auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar1->targetVariable = &var; + list.addInstruction(readVar1); + + LLVMRegister readVar1Reg(Compiler::StaticType::Unknown); + readVar1Reg.isRawValue = false; + readVar1Reg.instruction = readVar1; + readVar1->functionReturnReg = &readVar1Reg; + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &readVar1Reg }); + list.addInstruction(appendList1); + + auto elseStart = std::make_shared(LLVMInstruction::Type::BeginElse, false); + list.addInstruction(elseStart); + + // Else branch: write string to variable, transfer to list + auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::String, "hello"); + writeVar2->targetVariable = &var; + writeVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(writeVar2); + + auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar2->targetVariable = &var; + list.addInstruction(readVar2); + + LLVMRegister readVar2Reg(Compiler::StaticType::Unknown); + readVar2Reg.isRawValue = false; + readVar2Reg.instruction = readVar2; + readVar2->functionReturnReg = &readVar2Reg; + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &readVar2Reg }); + list.addInstruction(appendList2); + + auto ifEnd = std::make_shared(LLVMInstruction::Type::EndIf, false); + list.addInstruction(ifEnd); + + analyzer.analyzeScript(list); + + // Should show type conflicts from conditional branches + ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(writeVar2->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Void); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, MultipleVarsToSingleList_TypePropagation) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable var1("var1", ""); + Variable var2("var2", ""); + List targetList("list", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &targetList; + list.addInstruction(clearList); + + // Initialize first variable with boolean + auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value1(Compiler::StaticType::Bool, true); + writeVar1->targetVariable = &var1; + writeVar1->args.push_back({ Compiler::StaticType::Unknown, &value1 }); + list.addInstruction(writeVar1); + + // Initialize second variable with same type + auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister value2(Compiler::StaticType::Bool, false); + writeVar2->targetVariable = &var2; + writeVar2->args.push_back({ Compiler::StaticType::Unknown, &value2 }); + list.addInstruction(writeVar2); + + // Read from first variable and write to list + auto readVar1 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar1->targetVariable = &var1; + list.addInstruction(readVar1); + + LLVMRegister readVar1Reg(Compiler::StaticType::Unknown); + readVar1Reg.isRawValue = false; + readVar1Reg.instruction = readVar1; + readVar1->functionReturnReg = &readVar1Reg; + + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList1->targetList = &targetList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &readVar1Reg }); + list.addInstruction(appendList1); + + // Read from second variable and write to list + auto readVar2 = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readVar2->targetVariable = &var2; + list.addInstruction(readVar2); + + LLVMRegister readVar2Reg(Compiler::StaticType::Unknown); + readVar2Reg.isRawValue = false; + readVar2Reg.instruction = readVar2; + readVar2->functionReturnReg = &readVar2Reg; + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList2->targetList = &targetList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &readVar2Reg }); + list.addInstruction(appendList2); + + analyzer.analyzeScript(list); + + // List should have Bool type from both variables + ASSERT_EQ(appendList1->targetType, Compiler::StaticType::Void); + ASSERT_EQ(appendList2->targetType, Compiler::StaticType::Bool); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, SingleListToMultipleVars_TypePropagation) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + List sourceList("sourceList", ""); + Variable var1("var1", ""); + Variable var2("var2", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &sourceList; + list.addInstruction(clearList); + + // Initialize list with string values + auto appendList1 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister listValue1(Compiler::StaticType::String, "first"); + appendList1->targetList = &sourceList; + appendList1->args.push_back({ Compiler::StaticType::Unknown, &listValue1 }); + list.addInstruction(appendList1); + + auto appendList2 = std::make_shared(LLVMInstruction::Type::AppendToList, false); + LLVMConstantRegister listValue2(Compiler::StaticType::String, "second"); + appendList2->targetList = &sourceList; + appendList2->args.push_back({ Compiler::StaticType::Unknown, &listValue2 }); + list.addInstruction(appendList2); + + // Read first item and write to first variable + auto readList1 = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index1(Compiler::StaticType::Number, 0); + readList1->targetList = &sourceList; + readList1->args.push_back({ Compiler::StaticType::Number, &index1 }); + list.addInstruction(readList1); + + LLVMRegister readList1Reg(Compiler::StaticType::Unknown); + readList1Reg.isRawValue = false; + readList1Reg.instruction = readList1; + readList1->functionReturnReg = &readList1Reg; + + auto writeVar1 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + writeVar1->targetVariable = &var1; + writeVar1->args.push_back({ Compiler::StaticType::Unknown, &readList1Reg }); + list.addInstruction(writeVar1); + + // Read second item and write to second variable + auto readList2 = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index2(Compiler::StaticType::Number, 1); + readList2->targetList = &sourceList; + readList2->args.push_back({ Compiler::StaticType::Number, &index2 }); + list.addInstruction(readList2); + + LLVMRegister readList2Reg(Compiler::StaticType::Unknown); + readList2Reg.isRawValue = false; + readList2Reg.instruction = readList2; + readList2->functionReturnReg = &readList2Reg; + + auto writeVar2 = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + writeVar2->targetVariable = &var2; + writeVar2->args.push_back({ Compiler::StaticType::Unknown, &readList2Reg }); + list.addInstruction(writeVar2); + + analyzer.analyzeScript(list); + + // Both variables should have String type from the list + ASSERT_EQ(writeVar1->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(writeVar2->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(readList1Reg.type(), Compiler::StaticType::String); + ASSERT_EQ(readList2Reg.type(), Compiler::StaticType::String); +} + +TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ComplexChain_VarToListToVar_TypePropagation) +{ + LLVMCodeAnalyzer analyzer; + LLVMInstructionList list; + Variable sourceVar("sourceVar", ""); + List intermediateList("intermediateList", ""); + Variable targetVar("targetVar", ""); + + auto clearList = std::make_shared(LLVMInstruction::Type::ClearList, false); + clearList->targetList = &intermediateList; + list.addInstruction(clearList); + + // Initialize source variable + auto writeSourceVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + LLVMConstantRegister sourceValue(Compiler::StaticType::Number, 3.14159); + writeSourceVar->targetVariable = &sourceVar; + writeSourceVar->args.push_back({ Compiler::StaticType::Unknown, &sourceValue }); + list.addInstruction(writeSourceVar); + + // Read from source variable and write to intermediate list + auto readSourceVar = std::make_shared(LLVMInstruction::Type::ReadVariable, false); + readSourceVar->targetVariable = &sourceVar; + list.addInstruction(readSourceVar); + + LLVMRegister readSourceVarReg(Compiler::StaticType::Unknown); + readSourceVarReg.isRawValue = false; + readSourceVarReg.instruction = readSourceVar; + readSourceVar->functionReturnReg = &readSourceVarReg; + + auto appendList = std::make_shared(LLVMInstruction::Type::AppendToList, false); + appendList->targetList = &intermediateList; + appendList->args.push_back({ Compiler::StaticType::Unknown, &readSourceVarReg }); + list.addInstruction(appendList); + + // Read from intermediate list and write to target variable + auto readList = std::make_shared(LLVMInstruction::Type::GetListItem, false); + LLVMConstantRegister index(Compiler::StaticType::Number, 0); + readList->targetList = &intermediateList; + readList->args.push_back({ Compiler::StaticType::Number, &index }); + list.addInstruction(readList); + + LLVMRegister readListReg(Compiler::StaticType::Unknown); + readListReg.isRawValue = false; + readListReg.instruction = readList; + readList->functionReturnReg = &readListReg; + + auto writeTargetVar = std::make_shared(LLVMInstruction::Type::WriteVariable, false); + writeTargetVar->targetVariable = &targetVar; + writeTargetVar->args.push_back({ Compiler::StaticType::Unknown, &readListReg }); + list.addInstruction(writeTargetVar); + + analyzer.analyzeScript(list); + + // Type should propagate through the chain: sourceVar -> intermediateList -> targetVar + ASSERT_EQ(writeSourceVar->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(readSourceVar->targetType, Compiler::StaticType::Number); + ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); + ASSERT_EQ(readList->targetType, Compiler::StaticType::Number); + ASSERT_EQ(writeTargetVar->targetType, Compiler::StaticType::Unknown); + ASSERT_EQ(readListReg.type(), Compiler::StaticType::Number); +} From 8db2ec39c8672e62dbdb80917d3512fee044b146 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:15:51 +0200 Subject: [PATCH 11/27] Add support for mixed types in function return values --- src/engine/internal/llvm/instructions/functions.cpp | 7 ++++--- src/engine/internal/llvm/llvmbuildutils.cpp | 13 ++++++++----- src/engine/internal/llvm/llvmbuildutils.h | 1 + src/engine/internal/llvm/llvmcodebuilder.cpp | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/engine/internal/llvm/instructions/functions.cpp b/src/engine/internal/llvm/instructions/functions.cpp index 1bdb379b..94cc4bc0 100644 --- a/src/engine/internal/llvm/instructions/functions.cpp +++ b/src/engine/internal/llvm/instructions/functions.cpp @@ -54,11 +54,12 @@ LLVMInstruction *Functions::buildFunctionCall(LLVMInstruction *ins) llvm::Value *ret = m_builder.CreateCall(m_utils.functions().resolveFunction(ins->functionName, llvm::FunctionType::get(retType, types, false)), args); if (ins->functionReturnReg) { - if (ins->functionReturnReg->type() == Compiler::StaticType::Unknown) { + if (m_utils.isSingleType(ins->functionReturnReg->type())) + ins->functionReturnReg->value = ret; + else { ins->functionReturnReg->value = m_utils.addAlloca(retType); m_builder.CreateStore(ret, ins->functionReturnReg->value); - } else - ins->functionReturnReg->value = ret; + } if (ins->functionReturnReg->type() == Compiler::StaticType::String) m_utils.freeStringLater(ins->functionReturnReg->value); diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 30f232d0..7ec7d02c 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -383,6 +383,12 @@ Compiler::StaticType LLVMBuildUtils::mapType(ValueType type) return TYPE_MAP[type]; } +bool LLVMBuildUtils::isSingleType(Compiler::StaticType type) +{ + // Check if the type is a power of 2 (only one bit set) + return (type & (static_cast(static_cast(type) - 1))) == static_cast(0); +} + llvm::Value *LLVMBuildUtils::addAlloca(llvm::Type *type) { // Add an alloca to the entry block because allocas must be there (to avoid stack overflow) @@ -509,15 +515,12 @@ llvm::Type *LLVMBuildUtils::getType(Compiler::StaticType type, bool isReturnType case Compiler::StaticType::Pointer: return m_builder.getVoidTy()->getPointerTo(); - case Compiler::StaticType::Unknown: + default: + // Multiple types if (isReturnType) return m_valueDataType; else return m_valueDataType->getPointerTo(); - - default: - assert(false); - return nullptr; } } diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 2474225d..2a16e7c4 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -75,6 +75,7 @@ class LLVMBuildUtils static Compiler::StaticType optimizeRegisterType(const LLVMRegister *reg); static Compiler::StaticType mapType(ValueType type); + static bool isSingleType(Compiler::StaticType type); llvm::Value *addAlloca(llvm::Type *type); llvm::Value *castValue(LLVMRegister *reg, Compiler::StaticType targetType); diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index 2dcace86..b93c2e64 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -101,7 +101,7 @@ CompilerValue *LLVMCodeBuilder::addFunctionCall(const std::string &functionName, if (returnType != Compiler::StaticType::Void) { auto reg = std::make_shared(returnType); - if (returnType != Compiler::StaticType::Unknown) + if (m_utils.isSingleType(returnType)) reg->isRawValue = true; ins->functionReturnReg = reg.get(); From 31dbd173071603cdead4a399f27214f916aa0a2e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:17:02 +0200 Subject: [PATCH 12/27] Implement casting from mixed type values --- src/engine/internal/llvm/llvmbuildutils.cpp | 309 ++++++++++++++++---- test/llvm/llvmcodebuilder_test.cpp | 288 ++++++++++++++++++ test/llvm/llvmtestutils.cpp | 19 ++ test/llvm/llvmtestutils.h | 1 + test/llvm/testfunctions.cpp | 24 ++ test/llvm/testfunctions.h | 6 + 6 files changed, 596 insertions(+), 51 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 7ec7d02c..8c05502e 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -414,86 +414,293 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t assert(reg->type() != Compiler::StaticType::Void && targetType != Compiler::StaticType::Void); switch (targetType) { - case Compiler::StaticType::Number: - switch (reg->type()) { - case Compiler::StaticType::Number: { - // Read number directly + case Compiler::StaticType::Number: { + Compiler::StaticType type = reg->type(); + bool singleType = isSingleType(type); + + if (singleType) { + // Handle single type cases directly + switch (type) { + case Compiler::StaticType::Number: { + // Read number directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + return m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + } + + case Compiler::StaticType::Bool: { + // Read boolean and cast to double + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + return m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); + } + + case Compiler::StaticType::String: + // Convert to double + // TODO: Use value_stringToDouble() + return m_builder.CreateCall(m_functions.resolve_value_toDouble(), reg->value); + + default: + assert(false); + return nullptr; + } + } else { + // Handle multiple type cases with runtime switch + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + + llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + + std::vector> results; + + // Number case + if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + + m_builder.SetInsertPoint(numberBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *numberResult = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + m_builder.CreateBr(mergeBlock); + results.push_back({ numberBlock, numberResult }); } - case Compiler::StaticType::Bool: { - // Read boolean and cast to double + // Bool case + if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + + m_builder.SetInsertPoint(boolBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - return m_builder.CreateSIToFP(boolValue, m_builder.getDoubleTy()); + llvm::Value *boolResult = m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); + m_builder.CreateBr(mergeBlock); + results.push_back({ boolBlock, boolResult }); } - case Compiler::StaticType::String: - case Compiler::StaticType::Unknown: { - // Convert to double - return m_builder.CreateCall(m_functions.resolve_value_toDouble(), reg->value); + // String case + if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + + m_builder.SetInsertPoint(stringBlock); + // TODO: Use value_stringToDouble() + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_toDouble(), reg->value); + m_builder.CreateBr(mergeBlock); + results.push_back({ stringBlock, stringResult }); } - default: - assert(false); - return nullptr; + // Default case + m_builder.SetInsertPoint(defaultBlock); + + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); + + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(m_builder.getDoubleTy(), results.size()); + + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); + + return result; } + } - case Compiler::StaticType::Bool: - switch (reg->type()) { - case Compiler::StaticType::Number: { - // True if != 0 + case Compiler::StaticType::Bool: { + Compiler::StaticType type = reg->type(); + bool singleType = isSingleType(type); + + if (singleType) { + // Handle single type cases directly + switch (type) { + case Compiler::StaticType::Number: { + // True if != 0 + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + return m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + } + + case Compiler::StaticType::Bool: { + // Read boolean directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + return m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + } + + case Compiler::StaticType::String: + // Convert to bool + // TODO: Use value_stringToBool() + return m_builder.CreateCall(m_functions.resolve_value_toBool(), reg->value); + + default: + assert(false); + return nullptr; + } + } else { + // Handle multiple type cases with runtime switch + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + + llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + + std::vector> results; + + // Number case + if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + + m_builder.SetInsertPoint(numberBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - return m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + llvm::Value *numberResult = m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + m_builder.CreateBr(mergeBlock); + results.push_back({ numberBlock, numberResult }); } - case Compiler::StaticType::Bool: { - // Read boolean directly + // Bool case + if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + + m_builder.SetInsertPoint(boolBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + llvm::Value *boolResult = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + m_builder.CreateBr(mergeBlock); + results.push_back({ boolBlock, boolResult }); } - case Compiler::StaticType::String: - case Compiler::StaticType::Unknown: - // Convert to bool - return m_builder.CreateCall(m_functions.resolve_value_toBool(), reg->value); + // String case + if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); - default: - assert(false); - return nullptr; + m_builder.SetInsertPoint(stringBlock); + // TODO: Use value_stringToBool() + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_toBool(), reg->value); + m_builder.CreateBr(mergeBlock); + results.push_back({ stringBlock, stringResult }); + } + + // Default case + m_builder.SetInsertPoint(defaultBlock); + + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); + + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(m_builder.getInt1Ty(), results.size()); + + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); + + return result; } + } - case Compiler::StaticType::String: - switch (reg->type()) { - case Compiler::StaticType::Number: - case Compiler::StaticType::Bool: - case Compiler::StaticType::Unknown: { - // Cast to string - // TODO: Use value_stringToDouble() and value_stringToBool() - llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); - freeStringLater(ptr); - return ptr; + case Compiler::StaticType::String: { + Compiler::StaticType type = reg->type(); + bool singleType = isSingleType(type); + + if (singleType) { + // Handle single type cases directly + switch (type) { + case Compiler::StaticType::Number: + case Compiler::StaticType::Bool: { + // Cast to string + // TODO: Use value_doubleToStringPtr() and value_boolToStringPtr() + llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); + freeStringLater(ptr); + return ptr; + } + + case Compiler::StaticType::String: { + // Read string pointer directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + return m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + } + + default: + assert(false); + return nullptr; } + } else { + // Handle multiple type cases with runtime switch + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); - case Compiler::StaticType::String: { - // Read string pointer directly + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + + llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + + std::vector> results; + + // Number case + if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + + m_builder.SetInsertPoint(numberBlock); + // TODO: Use value_doubleToStringPtr() + llvm::Value *numberResult = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); + m_builder.CreateBr(mergeBlock); + results.push_back({ numberBlock, numberResult }); + } + + // Bool case + if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + + m_builder.SetInsertPoint(boolBlock); + // TODO: Use value_boolToStringPtr() + llvm::Value *boolResult = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); + m_builder.CreateBr(mergeBlock); + results.push_back({ boolBlock, boolResult }); + } + + // String case + if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + + m_builder.SetInsertPoint(stringBlock); llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); + m_builder.CreateCall(m_functions.resolve_string_assign(), { stringResult, stringPtr }); + m_builder.CreateBr(mergeBlock); + results.push_back({ stringBlock, stringResult }); } - default: - assert(false); - return nullptr; - } + // Default case + m_builder.SetInsertPoint(defaultBlock); - case Compiler::StaticType::Unknown: - return createValue(reg); + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); + + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(m_stringPtrType->getPointerTo(), results.size()); + + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); + + freeStringLater(result); + return result; + } + } default: - assert(false); - return nullptr; + // Multiple types + return createValue(reg); } } diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index 91e0c475..d79328f4 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -292,6 +292,294 @@ TEST_F(LLVMCodeBuilderTest, RawValueCasting) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, ValueCasting_FromNumberBool) +{ + LLVMCodeBuilder *builder = m_utils.createBuilder(true); + + // Number | Bool -> number + CompilerValue *v = builder->addConstValue(5.2); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Number | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + // Number | Bool -> bool + v = builder->addConstValue(-24.156); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue(0); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Number | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + // Number | Bool -> string + v = builder->addConstValue(4.5); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Number | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); + script.setCode(code); + Thread thread(&m_utils.target(), nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + + static const std::string expected = + "5.2\n" + "1\n" + "1\n" + "0\n" + "1\n" + "4.5\n" + "true\n"; + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ValueCasting_FromNumberString) +{ + LLVMCodeBuilder *builder = m_utils.createBuilder(true); + + // Number | String -> number + CompilerValue *v = builder->addConstValue(42.5); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue("123.45"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue("not_a_number"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + // Number | String -> bool + v = builder->addConstValue(0); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue(-15.7); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue("false"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue("hello"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + // Number | String -> string + v = builder->addConstValue(4.5); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + v = builder->addConstValue("world"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); + script.setCode(code); + Thread thread(&m_utils.target(), nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + + static const std::string expected = + "42.5\n" + "123.45\n" + "0\n" + "0\n" + "1\n" + "0\n" + "1\n" + "4.5\n" + "world\n"; + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ValueCasting_FromBoolString) +{ + LLVMCodeBuilder *builder = m_utils.createBuilder(true); + + // Bool | String -> number + CompilerValue *v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue("456.78"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue("invalid"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + // Bool | String -> bool + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue("false"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue("anything"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + // Bool | String -> string + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + v = builder->addConstValue("test_string"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Bool | Compiler::StaticType::String); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); + script.setCode(code); + Thread thread(&m_utils.target(), nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + + static const std::string expected = + "1\n" + "0\n" + "456.78\n" + "0\n" + "1\n" + "0\n" + "0\n" + "1\n" + "true\n" + "false\n" + "test_string\n"; + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + +TEST_F(LLVMCodeBuilderTest, ValueCasting_FromUnknown) +{ + LLVMCodeBuilder *builder = m_utils.createBuilder(true); + + // Unknown -> number + CompilerValue *v = builder->addConstValue(42.5); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue("123.45"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + v = builder->addConstValue("not_a_number"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + + // Unknown -> bool + v = builder->addConstValue(0); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue(-15.7); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue(true); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue("false"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + v = builder->addConstValue("hello"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_bool", Compiler::StaticType::Void, { Compiler::StaticType::Bool }, { v }); + + // Unknown -> string + v = builder->addConstValue(4.5); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + v = builder->addConstValue(false); + v = m_utils.callConstFuncForType(ValueType::Bool, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + v = builder->addConstValue("world"); + v = m_utils.callConstFuncForType(ValueType::String, v, Compiler::StaticType::Unknown); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + + // Unknown -> unknown (passthrough) + v = builder->addConstValue(321.5); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + + v = builder->addConstValue("test"); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + + v = builder->addConstValue(true); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Unknown }, { v }); + + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); + script.setCode(code); + Thread thread(&m_utils.target(), nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + + static const std::string expected = + "42.5\n" + "123.45\n" + "1\n" + "0\n" + "0\n" + "1\n" + "1\n" + "0\n" + "1\n" + "4.5\n" + "false\n" + "world\n" + "321.5\n" + "test\n" + "true\n"; + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, LocalVariables) { Stage stage; diff --git a/test/llvm/llvmtestutils.cpp b/test/llvm/llvmtestutils.cpp index 24c0d580..2089493f 100644 --- a/test/llvm/llvmtestutils.cpp +++ b/test/llvm/llvmtestutils.cpp @@ -72,6 +72,25 @@ CompilerValue *LLVMTestUtils::callConstFuncForType(ValueType type, CompilerValue } } +CompilerValue *LLVMTestUtils::callConstFuncForType(ValueType type, CompilerValue *arg, Compiler::StaticType returnType) +{ + switch (type) { + case ValueType::Number: + return m_builder->addFunctionCall("test_const_number_unknown", returnType, { Compiler::StaticType::Number }, { arg }); + + case ValueType::Bool: + return m_builder->addFunctionCall("test_const_bool_unknown", returnType, { Compiler::StaticType::Bool }, { arg }); + + case ValueType::String: + return m_builder->addFunctionCall("test_const_string_unknown", returnType, { Compiler::StaticType::String }, { arg }); + + default: + EXPECT_TRUE(false); + } + + return nullptr; +} + Value LLVMTestUtils::getOpResult(OpType type, bool rawArg, const Value &v) { createReporterBuilder(nullptr); diff --git a/test/llvm/llvmtestutils.h b/test/llvm/llvmtestutils.h index 3080fc29..afbc3c52 100644 --- a/test/llvm/llvmtestutils.h +++ b/test/llvm/llvmtestutils.h @@ -79,6 +79,7 @@ class LLVMTestUtils LLVMCodeBuilder *createPredicateBuilder(Target *target); CompilerValue *callConstFuncForType(ValueType type, CompilerValue *arg); + CompilerValue *callConstFuncForType(ValueType type, CompilerValue *arg, Compiler::StaticType returnType); Value getOpResult(OpType type, bool rawArg, const Value &v); Value getOpResult(OpType type, bool rawArgs, const Value &v1, const Value &v2); diff --git a/test/llvm/testfunctions.cpp b/test/llvm/testfunctions.cpp index b49e701a..36d3d88e 100644 --- a/test/llvm/testfunctions.cpp +++ b/test/llvm/testfunctions.cpp @@ -152,6 +152,30 @@ extern "C" return v; } + ValueData test_const_number_unknown(double v) + { + ValueData ret; + value_init(&ret); + value_assign_double(&ret, v); + return ret; + } + + ValueData test_const_bool_unknown(bool v) + { + ValueData ret; + value_init(&ret); + value_assign_bool(&ret, v); + return ret; + } + + ValueData test_const_string_unknown(const StringPtr *v) + { + ValueData ret; + value_init(&ret); + value_assign_stringPtr(&ret, v); + return ret; + } + bool test_not(bool arg) { return !arg; diff --git a/test/llvm/testfunctions.h b/test/llvm/testfunctions.h index eaf93214..0e83168f 100644 --- a/test/llvm/testfunctions.h +++ b/test/llvm/testfunctions.h @@ -32,11 +32,17 @@ extern "C" bool test_equals(const StringPtr *a, const StringPtr *b); bool test_lower_than(double a, double b); bool test_not(bool arg); + double test_const_number(double v); bool test_const_bool(bool v); StringPtr *test_const_string(const StringPtr *v); + ValueData test_const_unknown(const ValueData *v); const void *test_const_pointer(const void *v); + ValueData test_const_number_unknown(double v); + ValueData test_const_bool_unknown(bool v); + ValueData test_const_string_unknown(const StringPtr *v); + void test_unreachable(); void test_reset_counter(); From a06b40bfae1da207bec9eb1f6de208f8048b9773 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:17:56 +0200 Subject: [PATCH 13/27] Support mixed types in select instruction --- src/engine/internal/llvm/instructions/control.cpp | 8 ++++---- src/engine/internal/llvm/llvmcodebuilder.cpp | 5 +---- test/llvm/llvmcodebuilder_test.cpp | 7 ++++++- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/engine/internal/llvm/instructions/control.cpp b/src/engine/internal/llvm/instructions/control.cpp index d3b769af..75c4d9fc 100644 --- a/src/engine/internal/llvm/instructions/control.cpp +++ b/src/engine/internal/llvm/instructions/control.cpp @@ -79,12 +79,12 @@ LLVMInstruction *Control::buildSelect(LLVMInstruction *ins) llvm::Value *trueValue; llvm::Value *falseValue; - if (type == Compiler::StaticType::Unknown) { - trueValue = m_utils.createValue(arg2.second); - falseValue = m_utils.createValue(arg3.second); - } else { + if (m_utils.isSingleType(type)) { trueValue = m_utils.castValue(arg2.second, type); falseValue = m_utils.castValue(arg3.second, type); + } else { + trueValue = m_utils.createValue(arg2.second); + falseValue = m_utils.createValue(arg3.second); } ins->functionReturnReg->value = m_builder.CreateSelect(cond, trueValue, falseValue); diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index b93c2e64..ff25d26d 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -399,10 +399,7 @@ CompilerValue *LLVMCodeBuilder::createStringConcat(CompilerValue *string1, Compi CompilerValue *LLVMCodeBuilder::createSelect(CompilerValue *cond, CompilerValue *trueValue, CompilerValue *falseValue, Compiler::StaticType valueType) { LLVMRegister *ret = createOp(LLVMInstruction::Type::Select, valueType, { Compiler::StaticType::Bool, valueType, valueType }, { cond, trueValue, falseValue }); - - if (valueType == Compiler::StaticType::Unknown) - ret->isRawValue = false; - + ret->isRawValue = m_utils.isSingleType(valueType); return ret; } diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index d79328f4..34e346f1 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -761,6 +761,10 @@ TEST_F(LLVMCodeBuilderTest, Select) v = builder->createSelect(v, builder->addConstValue("abc"), builder->addConstValue(true), Compiler::StaticType::Unknown); builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + v = builder->addConstValue(true); + v = builder->createSelect(v, builder->addConstValue("abc"), builder->addConstValue(true), Compiler::StaticType::String | Compiler::StaticType::Bool); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + static const std::string expected = "5.8\n" "-17.42\n" @@ -773,7 +777,8 @@ TEST_F(LLVMCodeBuilderTest, Select) "1\n" "0\n" "test\n" - "true\n"; + "true\n" + "abc\n"; auto code = builder->build(); Script script(&sprite, nullptr, nullptr); From f10c765c624f75d31817005dad4bc731066be480 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:26:06 +0200 Subject: [PATCH 14/27] Add support for casting of constants to mixed type values --- src/engine/internal/llvm/llvmbuildutils.cpp | 2 +- test/llvm/llvmcodebuilder_test.cpp | 24 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 8c05502e..1e209d93 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -402,7 +402,7 @@ llvm::Value *LLVMBuildUtils::addAlloca(llvm::Type *type) llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType targetType) { if (reg->isConst()) { - if (targetType == Compiler::StaticType::Unknown) + if (!isSingleType(targetType)) return createValue(reg); else return castConstValue(reg->constValue(), targetType); diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index 34e346f1..efefd08e 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -196,6 +196,30 @@ TEST_F(LLVMCodeBuilderTest, ConstCasting) ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); } +TEST_F(LLVMCodeBuilderTest, ConstCasting_ToMixedTypes) +{ + LLVMCodeBuilder *builder = m_utils.createBuilder(true); + + CompilerValue *v = builder->addConstValue(true); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Number | Compiler::StaticType::Bool }, { v }); + v = builder->addConstValue(-24.156); + builder->addFunctionCall("test_print_unknown", Compiler::StaticType::Void, { Compiler::StaticType::Number | Compiler::StaticType::Bool }, { v }); + + auto code = builder->build(); + Script script(&m_utils.target(), nullptr, nullptr); + script.setCode(code); + Thread thread(&m_utils.target(), nullptr, &script); + auto ctx = code->createExecutionContext(&thread); + + static const std::string expected = + "true\n" + "-24.156\n"; + + testing::internal::CaptureStdout(); + code->run(ctx.get()); + ASSERT_EQ(testing::internal::GetCapturedStdout(), expected); +} + TEST_F(LLVMCodeBuilderTest, RawValueCasting) { LLVMCodeBuilder *builder = m_utils.createBuilder(true); From b5689429a343abf274103b7c0cc365bcde94c54f Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:27:57 +0200 Subject: [PATCH 15/27] Add support for mixed types in comparison --- src/engine/internal/llvm/llvmbuildutils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 1e209d93..5910f923 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -1014,7 +1014,7 @@ llvm::Value *LLVMBuildUtils::createComparison(LLVMRegister *arg1, LLVMRegister * return m_builder.getInt1(false); } - if (type1 != type2 || type1 == Compiler::StaticType::Unknown || type2 == Compiler::StaticType::Unknown) { + if (type1 != type2 || !isSingleType(type1) || !isSingleType(type2)) { // If the types are different or at least one of them // is unknown, we must use value functions llvm::Value *value1 = createValue(arg1); From d2b38f2f2b4b95fe4306026a8d1a67a0126e1693 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:29:21 +0200 Subject: [PATCH 16/27] Add support for mixed types in procedure arguments --- src/engine/internal/llvm/instructions/procedures.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/engine/internal/llvm/instructions/procedures.cpp b/src/engine/internal/llvm/instructions/procedures.cpp index a6de4350..39e3fc36 100644 --- a/src/engine/internal/llvm/instructions/procedures.cpp +++ b/src/engine/internal/llvm/instructions/procedures.cpp @@ -59,10 +59,10 @@ LLVMInstruction *Procedures::buildCallProcedure(LLVMInstruction *ins) // Add procedure args for (const auto &arg : ins->args) { - if (arg.first == Compiler::StaticType::Unknown) - args.push_back(m_utils.createValue(arg.second)); - else + if (m_utils.isSingleType(arg.first)) args.push_back(m_utils.castValue(arg.second, arg.first)); + else + args.push_back(m_utils.createValue(arg.second)); } llvm::Value *handle = m_builder.CreateCall(m_utils.functions().resolveFunction(name, type), args); From 81b1c578e2468e1956a70853733dd7e97fb6adfd Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:29:50 +0200 Subject: [PATCH 17/27] Remove type check from get list item null register --- src/engine/internal/llvm/instructions/lists.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 3bf45a11..9d06d8ec 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -231,7 +231,7 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) llvm::Value *index = m_utils.castValue(arg.second, arg.first); llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); - LLVMConstantRegister nullReg(listType == Compiler::StaticType::Unknown ? Compiler::StaticType::Number : listType, Value()); + LLVMConstantRegister nullReg(listType, Value()); llvm::Value *null = m_utils.createValue(static_cast(&nullReg)); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); From 7976cc06c22cdae7e2fc7e4e6357edddd69228a7 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:38:56 +0200 Subject: [PATCH 18/27] Enable code analysis Make it optional using the LIBSCRATCHCPP_ENABLE_CODE_ANALYZER option. --- CMakeLists.txt | 5 +++++ src/engine/internal/llvm/llvmcodebuilder.cpp | 8 ++++++++ src/engine/internal/llvm/llvmcodebuilder.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 31411131..e986c93e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) option(LIBSCRATCHCPP_BUILD_UNIT_TESTS "Build unit tests" ON) option(LIBSCRATCHCPP_NETWORK_SUPPORT "Support for downloading projects" ON) option(LIBSCRATCHCPP_PRINT_LLVM_IR "Print LLVM IR of compiled Scratch scripts (for debugging)" OFF) +option(LIBSCRATCHCPP_ENABLE_CODE_ANALYZER "Analyze Scratch scripts to enable various optimizations" ON) option(LIBSCRATCHCPP_ENABLE_SANITIZER "Enable sanitizer to detect memory issues" OFF) if (LIBSCRATCHCPP_ENABLE_SANITIZER) @@ -121,6 +122,10 @@ if(LIBSCRATCHCPP_PRINT_LLVM_IR) target_compile_definitions(scratchcpp PRIVATE PRINT_LLVM_IR) endif() +if(LIBSCRATCHCPP_ENABLE_CODE_ANALYZER) + target_compile_definitions(scratchcpp PRIVATE ENABLE_CODE_ANALYZER) +endif() + # Macros target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_LIBRARY) target_compile_definitions(scratchcpp PRIVATE LIBSCRATCHCPP_VERSION="${PROJECT_VERSION}") diff --git a/src/engine/internal/llvm/llvmcodebuilder.cpp b/src/engine/internal/llvm/llvmcodebuilder.cpp index ff25d26d..3cba9270 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.cpp +++ b/src/engine/internal/llvm/llvmcodebuilder.cpp @@ -52,6 +52,14 @@ std::shared_ptr LLVMCodeBuilder::build() m_warp = true; } + if (m_warp) { +#ifdef ENABLE_CODE_ANALYZER + // Analyze the script (type analysis, optimizations, etc.) + // NOTE: Do this only for non-warp scripts + m_codeAnalyzer.analyzeScript(m_instructions); +#endif + } + // Set fast math flags llvm::FastMathFlags fmf; fmf.setFast(true); diff --git a/src/engine/internal/llvm/llvmcodebuilder.h b/src/engine/internal/llvm/llvmcodebuilder.h index ecb1f4bf..37c060c3 100644 --- a/src/engine/internal/llvm/llvmcodebuilder.h +++ b/src/engine/internal/llvm/llvmcodebuilder.h @@ -11,6 +11,7 @@ #include "../icodebuilder.h" #include "llvmbuildutils.h" +#include "llvmcodeanalyzer.h" #include "llvminstruction.h" #include "llvminstructionlist.h" #include "llvmcoroutine.h" @@ -137,6 +138,7 @@ class LLVMCodeBuilder : public ICodeBuilder llvm::IRBuilder<> m_builder; llvm::Function *m_function = nullptr; LLVMBuildUtils m_utils; + LLVMCodeAnalyzer m_codeAnalyzer; llvm::StructType *m_valueDataType = nullptr; llvm::StructType *m_stringPtrType = nullptr; From 87a025a85c71d978db96a91e929fc332bbbf9e0b Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 15 Aug 2025 23:51:07 +0200 Subject: [PATCH 19/27] LLVMBuildUtils: Use type-specific functions for string casting --- src/engine/internal/llvm/llvmbuildutils.cpp | 60 ++++++++++++++------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 5910f923..8af9e33d 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -434,10 +434,12 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t return m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); } - case Compiler::StaticType::String: + case Compiler::StaticType::String: { // Convert to double - // TODO: Use value_stringToDouble() - return m_builder.CreateCall(m_functions.resolve_value_toDouble(), reg->value); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + return m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), stringPtr); + } default: assert(false); @@ -486,8 +488,9 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); m_builder.SetInsertPoint(stringBlock); - // TODO: Use value_stringToDouble() - llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_toDouble(), reg->value); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), stringPtr); m_builder.CreateBr(mergeBlock); results.push_back({ stringBlock, stringResult }); } @@ -529,10 +532,12 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t return m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); } - case Compiler::StaticType::String: + case Compiler::StaticType::String: { // Convert to bool - // TODO: Use value_stringToBool() - return m_builder.CreateCall(m_functions.resolve_value_toBool(), reg->value); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + return m_builder.CreateCall(m_functions.resolve_value_stringToBool(), stringPtr); + } default: assert(false); @@ -581,8 +586,9 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); m_builder.SetInsertPoint(stringBlock); - // TODO: Use value_stringToBool() - llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_toBool(), reg->value); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_stringToBool(), stringPtr); m_builder.CreateBr(mergeBlock); results.push_back({ stringBlock, stringResult }); } @@ -611,13 +617,22 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t if (singleType) { // Handle single type cases directly switch (type) { - case Compiler::StaticType::Number: + case Compiler::StaticType::Number: { + // Cast double to string + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *value = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), value); + freeStringLater(stringPtr); + return stringPtr; + } + case Compiler::StaticType::Bool: { - // Cast to string - // TODO: Use value_doubleToStringPtr() and value_boolToStringPtr() - llvm::Value *ptr = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); - freeStringLater(ptr); - return ptr; + // Cast bool to string + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *value = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), value); + // NOTE: Dot not deallocate later + return stringPtr; } case Compiler::StaticType::String: { @@ -648,8 +663,9 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); m_builder.SetInsertPoint(numberBlock); - // TODO: Use value_doubleToStringPtr() - llvm::Value *numberResult = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *value = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *numberResult = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), value); m_builder.CreateBr(mergeBlock); results.push_back({ numberBlock, numberResult }); } @@ -659,9 +675,13 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + // Since the value is deallocated later, we need to create a copy m_builder.SetInsertPoint(boolBlock); - // TODO: Use value_boolToStringPtr() - llvm::Value *boolResult = m_builder.CreateCall(m_functions.resolve_value_toStringPtr(), reg->value); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *value = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), value); + llvm::Value *boolResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); + m_builder.CreateCall(m_functions.resolve_string_assign(), { boolResult, stringPtr }); m_builder.CreateBr(mergeBlock); results.push_back({ boolBlock, boolResult }); } From 2e1a537eb8ab58771728e77b8afcfad03a9b8118 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 09:46:54 +0200 Subject: [PATCH 20/27] LLVMBuildUtils: Use runtime switch statement for single type casting LLVM will optimize it out anyway. --- src/engine/internal/llvm/llvmbuildutils.cpp | 410 ++++++++------------ 1 file changed, 158 insertions(+), 252 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 8af9e33d..8686be20 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -416,306 +416,212 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t switch (targetType) { case Compiler::StaticType::Number: { Compiler::StaticType type = reg->type(); - bool singleType = isSingleType(type); - - if (singleType) { - // Handle single type cases directly - switch (type) { - case Compiler::StaticType::Number: { - // Read number directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - } - - case Compiler::StaticType::Bool: { - // Read boolean and cast to double - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - return m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); - } - - case Compiler::StaticType::String: { - // Convert to double - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - return m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), stringPtr); - } - default: - assert(false); - return nullptr; - } - } else { - // Handle multiple type cases with runtime switch - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); - llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + // Handle multiple type cases with runtime switch + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); - llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); - llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); - llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); - std::vector> results; + std::vector> results; - // Number case - if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { - llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + // Number case + if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); - m_builder.SetInsertPoint(numberBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *numberResult = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - m_builder.CreateBr(mergeBlock); - results.push_back({ numberBlock, numberResult }); - } + m_builder.SetInsertPoint(numberBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *numberResult = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + m_builder.CreateBr(mergeBlock); + results.push_back({ numberBlock, numberResult }); + } - // Bool case - if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { - llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); - - m_builder.SetInsertPoint(boolBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - llvm::Value *boolResult = m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); - m_builder.CreateBr(mergeBlock); - results.push_back({ boolBlock, boolResult }); - } + // Bool case + if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + + m_builder.SetInsertPoint(boolBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *boolValue = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + llvm::Value *boolResult = m_builder.CreateUIToFP(boolValue, m_builder.getDoubleTy()); + m_builder.CreateBr(mergeBlock); + results.push_back({ boolBlock, boolResult }); + } - // String case - if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { - llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); - - m_builder.SetInsertPoint(stringBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), stringPtr); - m_builder.CreateBr(mergeBlock); - results.push_back({ stringBlock, stringResult }); - } + // String case + if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + + m_builder.SetInsertPoint(stringBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_stringToDouble(), stringPtr); + m_builder.CreateBr(mergeBlock); + results.push_back({ stringBlock, stringResult }); + } - // Default case - m_builder.SetInsertPoint(defaultBlock); + // Default case + m_builder.SetInsertPoint(defaultBlock); - // All possible types are covered, mark as unreachable - m_builder.CreateUnreachable(); + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); - // Create phi node to merge results - m_builder.SetInsertPoint(mergeBlock); - llvm::PHINode *result = m_builder.CreatePHI(m_builder.getDoubleTy(), results.size()); + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(m_builder.getDoubleTy(), results.size()); - for (auto &pair : results) - result->addIncoming(pair.second, pair.first); + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); - return result; - } + return result; } case Compiler::StaticType::Bool: { Compiler::StaticType type = reg->type(); - bool singleType = isSingleType(type); - - if (singleType) { - // Handle single type cases directly - switch (type) { - case Compiler::StaticType::Number: { - // True if != 0 - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - return m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); - } - - case Compiler::StaticType::Bool: { - // Read boolean directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - } - case Compiler::StaticType::String: { - // Convert to bool - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - return m_builder.CreateCall(m_functions.resolve_value_stringToBool(), stringPtr); - } + // Handle multiple type cases with runtime switch + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); - default: - assert(false); - return nullptr; - } - } else { - // Handle multiple type cases with runtime switch - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); - llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); - llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); - llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); - llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + std::vector> results; - std::vector> results; + // Number case + if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); - // Number case - if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { - llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); - - m_builder.SetInsertPoint(numberBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - llvm::Value *numberResult = m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); - m_builder.CreateBr(mergeBlock); - results.push_back({ numberBlock, numberResult }); - } + m_builder.SetInsertPoint(numberBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *numberValue = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *numberResult = m_builder.CreateFCmpONE(numberValue, llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0))); + m_builder.CreateBr(mergeBlock); + results.push_back({ numberBlock, numberResult }); + } - // Bool case - if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { - llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + // Bool case + if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); - m_builder.SetInsertPoint(boolBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *boolResult = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - m_builder.CreateBr(mergeBlock); - results.push_back({ boolBlock, boolResult }); - } + m_builder.SetInsertPoint(boolBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *boolResult = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + m_builder.CreateBr(mergeBlock); + results.push_back({ boolBlock, boolResult }); + } - // String case - if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { - llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); - - m_builder.SetInsertPoint(stringBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_stringToBool(), stringPtr); - m_builder.CreateBr(mergeBlock); - results.push_back({ stringBlock, stringResult }); - } + // String case + if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + + m_builder.SetInsertPoint(stringBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_value_stringToBool(), stringPtr); + m_builder.CreateBr(mergeBlock); + results.push_back({ stringBlock, stringResult }); + } - // Default case - m_builder.SetInsertPoint(defaultBlock); + // Default case + m_builder.SetInsertPoint(defaultBlock); - // All possible types are covered, mark as unreachable - m_builder.CreateUnreachable(); + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); - // Create phi node to merge results - m_builder.SetInsertPoint(mergeBlock); - llvm::PHINode *result = m_builder.CreatePHI(m_builder.getInt1Ty(), results.size()); + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(m_builder.getInt1Ty(), results.size()); - for (auto &pair : results) - result->addIncoming(pair.second, pair.first); + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); - return result; - } + return result; } case Compiler::StaticType::String: { Compiler::StaticType type = reg->type(); - bool singleType = isSingleType(type); - - if (singleType) { - // Handle single type cases directly - switch (type) { - case Compiler::StaticType::Number: { - // Cast double to string - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *value = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), value); - freeStringLater(stringPtr); - return stringPtr; - } - case Compiler::StaticType::Bool: { - // Cast bool to string - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *value = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), value); - // NOTE: Dot not deallocate later - return stringPtr; - } + // Handle multiple type cases with runtime switch + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); - case Compiler::StaticType::String: { - // Read string pointer directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - return m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - } + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); - default: - assert(false); - return nullptr; - } - } else { - // Handle multiple type cases with runtime switch - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); - llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); - llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); - llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + std::vector> results; - llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + // Number case + if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); - std::vector> results; - - // Number case - if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { - llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); - - m_builder.SetInsertPoint(numberBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *value = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); - llvm::Value *numberResult = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), value); - m_builder.CreateBr(mergeBlock); - results.push_back({ numberBlock, numberResult }); - } + m_builder.SetInsertPoint(numberBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *value = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + llvm::Value *numberResult = m_builder.CreateCall(m_functions.resolve_value_doubleToStringPtr(), value); + m_builder.CreateBr(mergeBlock); + results.push_back({ numberBlock, numberResult }); + } - // Bool case - if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { - llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); - - // Since the value is deallocated later, we need to create a copy - m_builder.SetInsertPoint(boolBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *value = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); - llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), value); - llvm::Value *boolResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); - m_builder.CreateCall(m_functions.resolve_string_assign(), { boolResult, stringPtr }); - m_builder.CreateBr(mergeBlock); - results.push_back({ boolBlock, boolResult }); - } + // Bool case + if ((type & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + + // Since the value is deallocated later, we need to create a copy + m_builder.SetInsertPoint(boolBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *value = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + llvm::Value *stringPtr = m_builder.CreateCall(m_functions.resolve_value_boolToStringPtr(), value); + llvm::Value *boolResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); + m_builder.CreateCall(m_functions.resolve_string_assign(), { boolResult, stringPtr }); + m_builder.CreateBr(mergeBlock); + results.push_back({ boolBlock, boolResult }); + } - // String case - if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { - llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); - sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); - - m_builder.SetInsertPoint(stringBlock); - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); - llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); - llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); - m_builder.CreateCall(m_functions.resolve_string_assign(), { stringResult, stringPtr }); - m_builder.CreateBr(mergeBlock); - results.push_back({ stringBlock, stringResult }); - } + // String case + if ((type & Compiler::StaticType::String) == Compiler::StaticType::String) { + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + + m_builder.SetInsertPoint(stringBlock); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + llvm::Value *stringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + llvm::Value *stringResult = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(true)); + m_builder.CreateCall(m_functions.resolve_string_assign(), { stringResult, stringPtr }); + m_builder.CreateBr(mergeBlock); + results.push_back({ stringBlock, stringResult }); + } - // Default case - m_builder.SetInsertPoint(defaultBlock); + // Default case + m_builder.SetInsertPoint(defaultBlock); - // All possible types are covered, mark as unreachable - m_builder.CreateUnreachable(); + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); - // Create phi node to merge results - m_builder.SetInsertPoint(mergeBlock); - llvm::PHINode *result = m_builder.CreatePHI(m_stringPtrType->getPointerTo(), results.size()); + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(m_stringPtrType->getPointerTo(), results.size()); - for (auto &pair : results) - result->addIncoming(pair.second, pair.first); + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); - freeStringLater(result); - return result; - } + freeStringLater(result); + return result; } default: From f29970a498df435b0b77a2d6861d8c2f6bfdfeec Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 09:52:57 +0200 Subject: [PATCH 21/27] LLVMBuildUtils: Simplify castValue() --- src/engine/internal/llvm/llvmbuildutils.cpp | 120 +++++++------------- 1 file changed, 38 insertions(+), 82 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 8686be20..4508a485 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -413,21 +413,28 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t assert(reg->type() != Compiler::StaticType::Void && targetType != Compiler::StaticType::Void); - switch (targetType) { - case Compiler::StaticType::Number: { - Compiler::StaticType type = reg->type(); - - // Handle multiple type cases with runtime switch - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); - llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); - - llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); - llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); - - llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + llvm::Value *typePtr = nullptr; + llvm::Value *loadedType = nullptr; + llvm::BasicBlock *mergeBlock = nullptr; + llvm::BasicBlock *defaultBlock = nullptr; + llvm::SwitchInst *sw = nullptr; + std::vector> results; + + if (isSingleType(targetType)) { + // Handle multiple source type cases with runtime switch + typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); + + mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + + sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); + } - std::vector> results; + Compiler::StaticType type = reg->type(); + switch (targetType) { + case Compiler::StaticType::Number: { // Number case if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); @@ -466,36 +473,10 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t results.push_back({ stringBlock, stringResult }); } - // Default case - m_builder.SetInsertPoint(defaultBlock); - - // All possible types are covered, mark as unreachable - m_builder.CreateUnreachable(); - - // Create phi node to merge results - m_builder.SetInsertPoint(mergeBlock); - llvm::PHINode *result = m_builder.CreatePHI(m_builder.getDoubleTy(), results.size()); - - for (auto &pair : results) - result->addIncoming(pair.second, pair.first); - - return result; + break; } case Compiler::StaticType::Bool: { - Compiler::StaticType type = reg->type(); - - // Handle multiple type cases with runtime switch - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); - llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); - - llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); - llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); - - llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); - - std::vector> results; - // Number case if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); @@ -534,36 +515,10 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t results.push_back({ stringBlock, stringResult }); } - // Default case - m_builder.SetInsertPoint(defaultBlock); - - // All possible types are covered, mark as unreachable - m_builder.CreateUnreachable(); - - // Create phi node to merge results - m_builder.SetInsertPoint(mergeBlock); - llvm::PHINode *result = m_builder.CreatePHI(m_builder.getInt1Ty(), results.size()); - - for (auto &pair : results) - result->addIncoming(pair.second, pair.first); - - return result; + break; } case Compiler::StaticType::String: { - Compiler::StaticType type = reg->type(); - - // Handle multiple type cases with runtime switch - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); - llvm::Value *loadedType = m_builder.CreateLoad(m_builder.getInt32Ty(), typePtr); - - llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); - llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); - - llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedType, defaultBlock, 4); - - std::vector> results; - // Number case if ((type & Compiler::StaticType::Number) == Compiler::StaticType::Number) { llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); @@ -607,27 +562,28 @@ llvm::Value *LLVMBuildUtils::castValue(LLVMRegister *reg, Compiler::StaticType t results.push_back({ stringBlock, stringResult }); } - // Default case - m_builder.SetInsertPoint(defaultBlock); - - // All possible types are covered, mark as unreachable - m_builder.CreateUnreachable(); - - // Create phi node to merge results - m_builder.SetInsertPoint(mergeBlock); - llvm::PHINode *result = m_builder.CreatePHI(m_stringPtrType->getPointerTo(), results.size()); - - for (auto &pair : results) - result->addIncoming(pair.second, pair.first); - - freeStringLater(result); - return result; + break; } default: // Multiple types return createValue(reg); } + + // Default case + m_builder.SetInsertPoint(defaultBlock); + + // All possible types are covered, mark as unreachable + m_builder.CreateUnreachable(); + + // Create phi node to merge results + m_builder.SetInsertPoint(mergeBlock); + llvm::PHINode *result = m_builder.CreatePHI(getType(targetType, false), results.size()); + + for (auto &pair : results) + result->addIncoming(pair.second, pair.first); + + return result; } llvm::Type *LLVMBuildUtils::getType(Compiler::StaticType type, bool isReturnType) From c1fe63b9cd7f5002427601d963a5248352872d90 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 10:07:35 +0200 Subject: [PATCH 22/27] Swap source and target type parameters in create store methods --- src/engine/internal/llvm/instructions/lists.cpp | 8 ++++---- .../internal/llvm/instructions/variables.cpp | 2 +- src/engine/internal/llvm/llvmbuildutils.cpp | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 9d06d8ec..1dc460d6 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -123,14 +123,14 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) // If there's enough space, use the allocated memory m_builder.SetInsertPoint(ifBlock); llvm::Value *itemPtr = m_utils.getListItem(listPtr, size); - m_utils.createReusedValueStore(arg.second, itemPtr, type, listType); + m_utils.createReusedValueStore(arg.second, itemPtr, listType, type); m_builder.CreateStore(m_builder.CreateAdd(size, m_builder.getInt64(1)), listPtr.sizePtr); m_builder.CreateBr(nextBlock); // Otherwise call appendEmpty() m_builder.SetInsertPoint(elseBlock); itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_append_empty(), listPtr.ptr); - m_utils.createReusedValueStore(arg.second, itemPtr, type, listType); + m_utils.createReusedValueStore(arg.second, itemPtr, listType, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -164,7 +164,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) m_builder.SetInsertPoint(insertBlock); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); llvm::Value *itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_insert_empty(), { listPtr.ptr, index }); - m_utils.createReusedValueStore(valueArg.second, itemPtr, type, listType); + m_utils.createReusedValueStore(valueArg.second, itemPtr, listType, type); m_builder.CreateBr(nextBlock); @@ -199,7 +199,7 @@ LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) m_builder.SetInsertPoint(replaceBlock); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); llvm::Value *itemPtr = m_utils.getListItem(listPtr, index); - m_utils.createValueStore(valueArg.second, itemPtr, type, listType); + m_utils.createValueStore(valueArg.second, itemPtr, listType, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); diff --git a/src/engine/internal/llvm/instructions/variables.cpp b/src/engine/internal/llvm/instructions/variables.cpp index 8e180c15..9298c3d5 100644 --- a/src/engine/internal/llvm/instructions/variables.cpp +++ b/src/engine/internal/llvm/instructions/variables.cpp @@ -134,7 +134,7 @@ LLVMInstruction *Variables::buildWriteVariable(LLVMInstruction *ins) m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typeField); }*/ - m_utils.createValueStore(arg.second, varPtr.stackPtr, argType, ins->targetType); + m_utils.createValueStore(arg.second, varPtr.stackPtr, ins->targetType, argType); return ins->next; } diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 4508a485..aac478a1 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -628,15 +628,15 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, { llvm::Value *converted = nullptr; - if (sourceType != Compiler::StaticType::Unknown) - converted = castValue(reg, sourceType); + if (targetType != Compiler::StaticType::Unknown) + converted = castValue(reg, targetType); - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [sourceType](const std::pair &pair) { return pair.second == sourceType; }); + auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [targetType](const std::pair &pair) { return pair.second == targetType; }); const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored - switch (sourceType) { + switch (targetType) { case Compiler::StaticType::Number: - switch (targetType) { + switch (sourceType) { case Compiler::StaticType::Number: { // Write number to number directly llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); @@ -661,7 +661,7 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, break; case Compiler::StaticType::Bool: - switch (targetType) { + switch (sourceType) { case Compiler::StaticType::Number: { // Write bool to number value directly and change type llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); @@ -705,10 +705,10 @@ void LLVMBuildUtils::createReusedValueStore(LLVMRegister *reg, llvm::Value *targ // Same as createValueStore(), but ensures that type is updated createValueStore(reg, targetPtr, sourceType, targetType); - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [sourceType](const std::pair &pair) { return pair.second == sourceType; }); + auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [targetType](const std::pair &pair) { return pair.second == targetType; }); const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored - if ((sourceType == Compiler::StaticType::Number || sourceType == Compiler::StaticType::Bool) && sourceType == targetType) { + if ((targetType == Compiler::StaticType::Number || targetType == Compiler::StaticType::Bool) && targetType == sourceType) { // Update type when writing number to number and bool to bool llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); From 586529b19a1bff56cf722880e5ecceec7ad94130 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 10:10:51 +0200 Subject: [PATCH 23/27] LLVMBuildUtils: Rename parameters in create store methods --- src/engine/internal/llvm/llvmbuildutils.cpp | 34 ++++++++++----------- src/engine/internal/llvm/llvmbuildutils.h | 4 +-- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index aac478a1..9c7f8655 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -624,7 +624,7 @@ llvm::Value *LLVMBuildUtils::removeNaN(llvm::Value *num) return m_builder.CreateSelect(isNaN(num), llvm::ConstantFP::get(m_llvmCtx, llvm::APFloat(0.0)), num); } -void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType) +void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType) { llvm::Value *converted = nullptr; @@ -636,37 +636,37 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, switch (targetType) { case Compiler::StaticType::Number: - switch (sourceType) { + switch (destType) { case Compiler::StaticType::Number: { // Write number to number directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); m_builder.CreateStore(converted, ptr); break; } case Compiler::StaticType::Bool: { // Write number to bool value directly and change type - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); m_builder.CreateStore(converted, ptr); m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); break; } default: - m_builder.CreateCall(m_functions.resolve_value_assign_double(), { targetPtr, converted }); + m_builder.CreateCall(m_functions.resolve_value_assign_double(), { destPtr, converted }); break; } break; case Compiler::StaticType::Bool: - switch (sourceType) { + switch (destType) { case Compiler::StaticType::Number: { // Write bool to number value directly and change type - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); m_builder.CreateStore(converted, ptr); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); m_builder.CreateStore(converted, ptr); m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); break; @@ -674,24 +674,24 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, case Compiler::StaticType::Bool: { // Write bool to bool directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 0); + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); m_builder.CreateStore(converted, ptr); break; } default: - m_builder.CreateCall(m_functions.resolve_value_assign_bool(), { targetPtr, converted }); + m_builder.CreateCall(m_functions.resolve_value_assign_bool(), { destPtr, converted }); break; } break; case Compiler::StaticType::String: - m_builder.CreateCall(m_functions.resolve_value_assign_stringPtr(), { targetPtr, converted }); + m_builder.CreateCall(m_functions.resolve_value_assign_stringPtr(), { destPtr, converted }); break; case Compiler::StaticType::Unknown: - m_builder.CreateCall(m_functions.resolve_value_assign_copy(), { targetPtr, reg->value }); + m_builder.CreateCall(m_functions.resolve_value_assign_copy(), { destPtr, reg->value }); break; default: @@ -700,17 +700,17 @@ void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, } } -void LLVMBuildUtils::createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType) +void LLVMBuildUtils::createReusedValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType) { // Same as createValueStore(), but ensures that type is updated - createValueStore(reg, targetPtr, sourceType, targetType); + createValueStore(reg, destPtr, destType, targetType); auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [targetType](const std::pair &pair) { return pair.second == targetType; }); const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored - if ((targetType == Compiler::StaticType::Number || targetType == Compiler::StaticType::Bool) && targetType == sourceType) { + if ((targetType == Compiler::StaticType::Number || targetType == Compiler::StaticType::Bool) && targetType == destType) { // Update type when writing number to number and bool to bool - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, targetPtr, 1); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); } } diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index 2a16e7c4..b9a85eb0 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -83,8 +83,8 @@ class LLVMBuildUtils llvm::Value *isNaN(llvm::Value *num); llvm::Value *removeNaN(llvm::Value *num); - void createValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); - void createReusedValueStore(LLVMRegister *reg, llvm::Value *targetPtr, Compiler::StaticType sourceType, Compiler::StaticType targetType); + void createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType); + void createReusedValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType); llvm::Value *getListItem(const LLVMListPtr &listPtr, llvm::Value *index); llvm::Value *getListItemIndex(const LLVMListPtr &listPtr, Compiler::StaticType listType, LLVMRegister *item); From a51bfad50b27fe4f5c66187fc1e736c0bea972a7 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 15:25:40 +0200 Subject: [PATCH 24/27] LLVMConstantRegister: Mark as raw by default --- src/engine/internal/llvm/llvmconstantregister.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/engine/internal/llvm/llvmconstantregister.h b/src/engine/internal/llvm/llvmconstantregister.h index 4d7beacc..d4474de0 100644 --- a/src/engine/internal/llvm/llvmconstantregister.h +++ b/src/engine/internal/llvm/llvmconstantregister.h @@ -18,6 +18,7 @@ struct LLVMConstantRegister CompilerConstant(type, value), LLVMRegister(type) { + isRawValue = true; // all constants can be read directly } const Value &constValue() const override final { return CompilerConstant::value(); } From e471eb5e0de01f43916a9be97c1321a6ab15e91d Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 15:25:56 +0200 Subject: [PATCH 25/27] Implement mixed type stores --- .../internal/llvm/instructions/lists.cpp | 11 +- src/engine/internal/llvm/llvmbuildutils.cpp | 297 ++++++++++++++---- src/engine/internal/llvm/llvmbuildutils.h | 2 +- test/llvm/llvmcodebuilder_test.cpp | 7 + 4 files changed, 244 insertions(+), 73 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 1dc460d6..617bb641 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -109,8 +109,6 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) Compiler::StaticType type = m_utils.optimizeRegisterType(arg.second); LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); - Compiler::StaticType listType = ins->targetType; - // Check if enough space is allocated llvm::Value *allocatedSize = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.allocatedSizePtr); llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); @@ -123,14 +121,15 @@ LLVMInstruction *Lists::buildAppendToList(LLVMInstruction *ins) // If there's enough space, use the allocated memory m_builder.SetInsertPoint(ifBlock); llvm::Value *itemPtr = m_utils.getListItem(listPtr, size); - m_utils.createReusedValueStore(arg.second, itemPtr, listType, type); + m_utils.createValueStore(arg.second, itemPtr, type); m_builder.CreateStore(m_builder.CreateAdd(size, m_builder.getInt64(1)), listPtr.sizePtr); m_builder.CreateBr(nextBlock); // Otherwise call appendEmpty() m_builder.SetInsertPoint(elseBlock); itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_append_empty(), listPtr.ptr); - m_utils.createReusedValueStore(arg.second, itemPtr, listType, type); + // NOTE: Items created using appendEmpty() are always numbers + m_utils.createValueStore(arg.second, itemPtr, Compiler::StaticType::Number, type); m_builder.CreateBr(nextBlock); m_builder.SetInsertPoint(nextBlock); @@ -148,8 +147,6 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) Compiler::StaticType type = m_utils.optimizeRegisterType(valueArg.second); LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); - Compiler::StaticType listType = ins->targetType; - // Range check llvm::Value *size = m_builder.CreateLoad(m_builder.getInt64Ty(), listPtr.sizePtr); llvm::Value *min = llvm::ConstantFP::get(llvmCtx, llvm::APFloat(0.0)); @@ -164,7 +161,7 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) m_builder.SetInsertPoint(insertBlock); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); llvm::Value *itemPtr = m_builder.CreateCall(m_utils.functions().resolve_list_insert_empty(), { listPtr.ptr, index }); - m_utils.createReusedValueStore(valueArg.second, itemPtr, listType, type); + m_utils.createValueStore(valueArg.second, itemPtr, type); m_builder.CreateBr(nextBlock); diff --git a/src/engine/internal/llvm/llvmbuildutils.cpp b/src/engine/internal/llvm/llvmbuildutils.cpp index 9c7f8655..cd363288 100644 --- a/src/engine/internal/llvm/llvmbuildutils.cpp +++ b/src/engine/internal/llvm/llvmbuildutils.cpp @@ -626,93 +626,260 @@ llvm::Value *LLVMBuildUtils::removeNaN(llvm::Value *num) void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType) { - llvm::Value *converted = nullptr; + llvm::Value *targetPtr = nullptr; + const bool targetTypeIsSingle = isSingleType(targetType); - if (targetType != Compiler::StaticType::Unknown) - converted = castValue(reg, targetType); + if (targetTypeIsSingle) + targetPtr = castValue(reg, targetType); auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [targetType](const std::pair &pair) { return pair.second == targetType; }); const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored + assert(!(reg->isRawValue && it == TYPE_MAP.cend())); - switch (targetType) { - case Compiler::StaticType::Number: - switch (destType) { - case Compiler::StaticType::Number: { - // Write number to number directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - m_builder.CreateStore(converted, ptr); - break; - } + // Handle multiple type cases with runtime switch + llvm::Value *loadedTargetType = nullptr; - case Compiler::StaticType::Bool: { - // Write number to bool value directly and change type - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(converted, ptr); - m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); - break; - } + if (reg->isRawValue) + loadedTargetType = m_builder.getInt32(static_cast(mappedType)); + else { + assert(!reg->isConst()); + llvm::Value *targetTypePtr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 1); + loadedTargetType = m_builder.CreateLoad(m_builder.getInt32Ty(), targetTypePtr); + } - default: - m_builder.CreateCall(m_functions.resolve_value_assign_double(), { destPtr, converted }); - break; + llvm::Value *destTypePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); + llvm::Value *loadedDestType = m_builder.CreateLoad(m_builder.getInt32Ty(), destTypePtr); + + llvm::BasicBlock *mergeBlock = llvm::BasicBlock::Create(m_llvmCtx, "merge", m_function); + llvm::BasicBlock *defaultBlock = llvm::BasicBlock::Create(m_llvmCtx, "default", m_function); + + llvm::SwitchInst *sw = m_builder.CreateSwitch(loadedDestType, defaultBlock, 4); + + if ((destType & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + // Writing to number + llvm::BasicBlock *numberDestBlock = llvm::BasicBlock::Create(m_llvmCtx, "numberDest", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberDestBlock); + m_builder.SetInsertPoint(numberDestBlock); + + llvm::SwitchInst *targetSw = m_builder.CreateSwitch(loadedTargetType, defaultBlock, 4); + + if ((targetType & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + // Writing number to number + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + m_builder.SetInsertPoint(numberBlock); + + // Load number + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); } - break; + // Write number to number directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + m_builder.CreateStore(targetPtr, ptr); + m_builder.CreateBr(mergeBlock); + } - case Compiler::StaticType::Bool: - switch (destType) { - case Compiler::StaticType::Number: { - // Write bool to number value directly and change type - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - m_builder.CreateStore(converted, ptr); - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(converted, ptr); - m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); - break; - } + if ((targetType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + // Writing bool to number + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + m_builder.SetInsertPoint(boolBlock); - case Compiler::StaticType::Bool: { - // Write bool to bool directly - llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); - m_builder.CreateStore(converted, ptr); - break; - } + // Load bool + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + } - default: - m_builder.CreateCall(m_functions.resolve_value_assign_bool(), { destPtr, converted }); - break; + // Write bool to number value directly and change type + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + m_builder.CreateStore(targetPtr, ptr); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Bool)), typePtr); + m_builder.CreateBr(mergeBlock); + } + + if ((targetType & Compiler::StaticType::String) == Compiler::StaticType::String) { + // Writing string to number + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + m_builder.SetInsertPoint(stringBlock); + + // Load string pointer + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); } - break; + // Create a new string, change type and assign + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(false)); - case Compiler::StaticType::String: - m_builder.CreateCall(m_functions.resolve_value_assign_stringPtr(), { destPtr, converted }); - break; + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), typePtr); + m_builder.CreateStore(destStringPtr, ptr); + m_builder.CreateCall(m_functions.resolve_string_assign(), { destStringPtr, targetPtr }); - case Compiler::StaticType::Unknown: - m_builder.CreateCall(m_functions.resolve_value_assign_copy(), { destPtr, reg->value }); - break; + m_builder.CreateBr(mergeBlock); + } + } - default: - assert(false); - break; + if ((destType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + // Writing to bool + llvm::BasicBlock *boolDestBlock = llvm::BasicBlock::Create(m_llvmCtx, "boolDest", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolDestBlock); + m_builder.SetInsertPoint(boolDestBlock); + + llvm::SwitchInst *targetSw = m_builder.CreateSwitch(loadedTargetType, defaultBlock, 4); + + if ((targetType & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + // Writing number to bool + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + m_builder.SetInsertPoint(numberBlock); + + // Load number + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + } + + // Write number to bool value directly and change type + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); + m_builder.CreateStore(targetPtr, ptr); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), typePtr); + m_builder.CreateBr(mergeBlock); + } + + if ((targetType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + // Writing bool to bool + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + m_builder.SetInsertPoint(boolBlock); + + // Load bool + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + } + + // Write bool to bool directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + m_builder.CreateStore(targetPtr, ptr); + m_builder.CreateBr(mergeBlock); + } + + if ((targetType & Compiler::StaticType::String) == Compiler::StaticType::String) { + // Writing string to bool + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + m_builder.SetInsertPoint(stringBlock); + + // Load string pointer + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + } + + // Create a new string, change type and assign + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + llvm::Value *destStringPtr = m_builder.CreateCall(m_functions.resolve_string_pool_new(), m_builder.getInt1(false)); + + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::String)), typePtr); + m_builder.CreateStore(destStringPtr, ptr); + m_builder.CreateCall(m_functions.resolve_string_assign(), { destStringPtr, targetPtr }); + + m_builder.CreateBr(mergeBlock); + } } -} -void LLVMBuildUtils::createReusedValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType) -{ - // Same as createValueStore(), but ensures that type is updated - createValueStore(reg, destPtr, destType, targetType); + if ((destType & Compiler::StaticType::String) == Compiler::StaticType::String) { + // Writing to string + llvm::BasicBlock *stringDestBlock = llvm::BasicBlock::Create(m_llvmCtx, "stringDest", m_function); + sw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringDestBlock); + m_builder.SetInsertPoint(stringDestBlock); - auto it = std::find_if(TYPE_MAP.begin(), TYPE_MAP.end(), [targetType](const std::pair &pair) { return pair.second == targetType; }); - const ValueType mappedType = it == TYPE_MAP.cend() ? ValueType::Number : it->first; // unknown type can be ignored + llvm::SwitchInst *targetSw = m_builder.CreateSwitch(loadedTargetType, defaultBlock, 4); + + if ((targetType & Compiler::StaticType::Number) == Compiler::StaticType::Number) { + // Writing number to string + llvm::BasicBlock *numberBlock = llvm::BasicBlock::Create(m_llvmCtx, "number", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::Number)), numberBlock); + m_builder.SetInsertPoint(numberBlock); - if ((targetType == Compiler::StaticType::Number || targetType == Compiler::StaticType::Bool) && targetType == destType) { - // Update type when writing number to number and bool to bool - llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); - m_builder.CreateStore(m_builder.getInt32(static_cast(mappedType)), typePtr); + // Load number + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_builder.getDoubleTy(), ptr); + } + + // Free the string, write the number and change type + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + llvm::Value *destStringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + m_builder.CreateCall(m_functions.resolve_string_pool_free(), destStringPtr); + m_builder.CreateStore(targetPtr, ptr); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Number)), typePtr); + m_builder.CreateBr(mergeBlock); + } + + if ((targetType & Compiler::StaticType::Bool) == Compiler::StaticType::Bool) { + // Writing bool to string + llvm::BasicBlock *boolBlock = llvm::BasicBlock::Create(m_llvmCtx, "bool", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::Bool)), boolBlock); + m_builder.SetInsertPoint(boolBlock); + + // Load bool + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_builder.getInt1Ty(), ptr); + } + + // Free the string, write the bool and change type + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + llvm::Value *destStringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + m_builder.CreateCall(m_functions.resolve_string_pool_free(), destStringPtr); + m_builder.CreateStore(targetPtr, ptr); + llvm::Value *typePtr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 1); + m_builder.CreateStore(m_builder.getInt32(static_cast(ValueType::Bool)), typePtr); + m_builder.CreateBr(mergeBlock); + } + + if ((targetType & Compiler::StaticType::String) == Compiler::StaticType::String) { + // Writing string to string + llvm::BasicBlock *stringBlock = llvm::BasicBlock::Create(m_llvmCtx, "string", m_function); + targetSw->addCase(m_builder.getInt32(static_cast(ValueType::String)), stringBlock); + m_builder.SetInsertPoint(stringBlock); + + // Load string pointer + if (!targetTypeIsSingle) { + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, reg->value, 0); + targetPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + } + + // Assign string directly + llvm::Value *ptr = m_builder.CreateStructGEP(m_valueDataType, destPtr, 0); + llvm::Value *destStringPtr = m_builder.CreateLoad(m_stringPtrType->getPointerTo(), ptr); + m_builder.CreateCall(m_functions.resolve_string_assign(), { destStringPtr, targetPtr }); + m_builder.CreateBr(mergeBlock); + } } + + // Default case - unreachable + m_builder.SetInsertPoint(defaultBlock); + m_builder.CreateUnreachable(); + + m_builder.SetInsertPoint(mergeBlock); +} + +void LLVMBuildUtils::createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType targetType) +{ + // Same as createValueStore(), but the destination type is unknown at compile time + createValueStore(reg, destPtr, Compiler::StaticType::Unknown, targetType); } llvm::Value *LLVMBuildUtils::getListItem(const LLVMListPtr &listPtr, llvm::Value *index) diff --git a/src/engine/internal/llvm/llvmbuildutils.h b/src/engine/internal/llvm/llvmbuildutils.h index b9a85eb0..f39c4d00 100644 --- a/src/engine/internal/llvm/llvmbuildutils.h +++ b/src/engine/internal/llvm/llvmbuildutils.h @@ -84,7 +84,7 @@ class LLVMBuildUtils llvm::Value *removeNaN(llvm::Value *num); void createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType); - void createReusedValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType destType, Compiler::StaticType targetType); + void createValueStore(LLVMRegister *reg, llvm::Value *destPtr, Compiler::StaticType targetType); llvm::Value *getListItem(const LLVMListPtr &listPtr, llvm::Value *index); llvm::Value *getListItemIndex(const LLVMListPtr &listPtr, Compiler::StaticType listType, LLVMRegister *item); diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index efefd08e..b36b7e91 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -677,9 +677,11 @@ TEST_F(LLVMCodeBuilderTest, WriteVariable) auto localVar1 = std::make_shared("", ""); auto localVar2 = std::make_shared("", ""); auto localVar3 = std::make_shared("", ""); + auto localVar4 = std::make_shared("", ""); sprite.addVariable(localVar1); sprite.addVariable(localVar2); sprite.addVariable(localVar3); + sprite.addVariable(localVar4); LLVMCodeBuilder *builder = m_utils.createBuilder(&sprite, true); @@ -707,6 +709,10 @@ TEST_F(LLVMCodeBuilderTest, WriteVariable) v = m_utils.callConstFuncForType(ValueType::Bool, v); builder->createVariableWrite(localVar3.get(), v); + v = builder->addConstValue(45.23); + v = m_utils.callConstFuncForType(ValueType::Number, v, Compiler::StaticType::Number | Compiler::StaticType::String); + builder->createVariableWrite(localVar4.get(), v); + auto code = builder->build(); Script script(&sprite, nullptr, nullptr); script.setCode(code); @@ -721,6 +727,7 @@ TEST_F(LLVMCodeBuilderTest, WriteVariable) ASSERT_EQ(localVar1->value(), "hello world"); ASSERT_EQ(localVar2->value(), false); ASSERT_EQ(localVar3->value(), true); + ASSERT_EQ(localVar4->value(), 45.23); } TEST_F(LLVMCodeBuilderTest, Select) From 59bce8061b84ed858eca36b90dec26bf7a8b988e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:11:31 +0200 Subject: [PATCH 26/27] Optimize empty lists --- .../internal/llvm/instructions/lists.cpp | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index 617bb641..c41da094 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -63,20 +63,27 @@ ProcessResult Lists::process(LLVMInstruction *ins) LLVMInstruction *Lists::buildClearList(LLVMInstruction *ins) { - assert(ins->args.size() == 0); - LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); - m_builder.CreateCall(m_utils.functions().resolve_list_clear(), listPtr.ptr); + if (ins->targetType != Compiler::StaticType::Void) { // do not clear a list that is already empty + assert(ins->args.size() == 0); + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); + m_builder.CreateCall(m_utils.functions().resolve_list_clear(), listPtr.ptr); + } return ins->next; } LLVMInstruction *Lists::buildRemoveListItem(LLVMInstruction *ins) { + // No-op in empty lists + if (ins->targetType == Compiler::StaticType::Void) + return ins->next; + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); llvm::Function *function = m_utils.function(); assert(ins->args.size() == 1); const auto &arg = ins->args[0]; + LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); // Range check @@ -171,6 +178,10 @@ LLVMInstruction *Lists::buildInsertToList(LLVMInstruction *ins) LLVMInstruction *Lists::buildListReplace(LLVMInstruction *ins) { + // No-op in empty lists + if (ins->targetType == Compiler::StaticType::Void) + return ins->next; + llvm::LLVMContext &llvmCtx = m_utils.llvmCtx(); llvm::Function *function = m_utils.function(); @@ -216,6 +227,13 @@ LLVMInstruction *Lists::buildGetListContents(LLVMInstruction *ins) LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) { + // Return empty string for empty lists + if (ins->targetType == Compiler::StaticType::Void) { + LLVMConstantRegister nullReg(Compiler::StaticType::String, ""); + ins->functionReturnReg->value = m_utils.createValue(static_cast(&nullReg)); + return ins->next; + } + assert(ins->args.size() == 1); const auto &arg = ins->args[0]; LLVMListPtr &listPtr = m_utils.listPtr(ins->targetList); From f2de9fb22909631b3418e14294f9cafa978a0068 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:37:35 +0200 Subject: [PATCH 27/27] Fix out of range return value of get list item instruction --- .../internal/llvm/instructions/lists.cpp | 2 +- src/engine/internal/llvm/llvmcodeanalyzer.cpp | 3 ++- test/blocks/list_blocks_test.cpp | 10 +++++----- .../llvm/code_analyzer/list_type_analysis.cpp | 16 +++++++-------- .../code_analyzer/mixed_type_analysis.cpp | 6 +++--- test/llvm/llvmcodebuilder_test.cpp | 20 ++++++++++++++----- 6 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/engine/internal/llvm/instructions/lists.cpp b/src/engine/internal/llvm/instructions/lists.cpp index c41da094..c8ab9df1 100644 --- a/src/engine/internal/llvm/instructions/lists.cpp +++ b/src/engine/internal/llvm/instructions/lists.cpp @@ -246,7 +246,7 @@ LLVMInstruction *Lists::buildGetListItem(LLVMInstruction *ins) llvm::Value *index = m_utils.castValue(arg.second, arg.first); llvm::Value *inRange = m_builder.CreateAnd(m_builder.CreateFCmpOGE(index, min), m_builder.CreateFCmpOLT(index, size)); - LLVMConstantRegister nullReg(listType, Value()); + LLVMConstantRegister nullReg(Compiler::StaticType::String, ""); llvm::Value *null = m_utils.createValue(static_cast(&nullReg)); index = m_builder.CreateFPToUI(index, m_builder.getInt64Ty()); diff --git a/src/engine/internal/llvm/llvmcodeanalyzer.cpp b/src/engine/internal/llvm/llvmcodeanalyzer.cpp index b8a89de0..dfc3ccc3 100644 --- a/src/engine/internal/llvm/llvmcodeanalyzer.cpp +++ b/src/engine/internal/llvm/llvmcodeanalyzer.cpp @@ -100,7 +100,8 @@ void LLVMCodeAnalyzer::analyzeScript(const LLVMInstructionList &script) const updateListType(currentBranch, ins, typeAssignedInstructions, false); // Store the type in the return register - ins->functionReturnReg->setType(ins->targetType); + // NOTE: Get list item returns empty string if index is out of range + ins->functionReturnReg->setType(ins->targetType | Compiler::StaticType::String); } ins = ins->next; diff --git a/test/blocks/list_blocks_test.cpp b/test/blocks/list_blocks_test.cpp index 3fc928a0..1eb1e44b 100644 --- a/test/blocks/list_blocks_test.cpp +++ b/test/blocks/list_blocks_test.cpp @@ -422,14 +422,14 @@ TEST_F(ListBlocksTest, ItemOfList) static const std::string expected = "dolor\n" "true\n" - "0\n" - "0\n" + "\n" + "\n" "true\n" "123\n" "Lorem\n" - "0\n" - "0\n" - "0\n"; + "\n" + "\n" + "\n"; EXPECT_CALL(m_rng, randint(1, 5)).WillOnce(Return(4)).WillOnce(Return(1)); testing::internal::CaptureStdout(); diff --git a/test/llvm/code_analyzer/list_type_analysis.cpp b/test/llvm/code_analyzer/list_type_analysis.cpp index 8a436a32..021f0a0e 100644 --- a/test/llvm/code_analyzer/list_type_analysis.cpp +++ b/test/llvm/code_analyzer/list_type_analysis.cpp @@ -1002,8 +1002,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_SingleType) // targetList first write has no previous type ASSERT_EQ(appendList1_1->targetType, Compiler::StaticType::Void); - // targetList second write has Number type - ASSERT_EQ(appendList1_2->targetType, Compiler::StaticType::Number); + // targetList second write has Number | String type (get list item can always return string) + ASSERT_EQ(appendList1_2->targetType, Compiler::StaticType::Number | Compiler::StaticType::String); } TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_MultipleTypes) @@ -1063,8 +1063,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListDependency_MultipleTypes) // targetList first write has no previous type ASSERT_EQ(appendList3->targetType, Compiler::StaticType::Void); - // targetList second write has Number or Bool type - ASSERT_EQ(appendList4->targetType, Compiler::StaticType::Number | Compiler::StaticType::Bool); + // targetList second write has Number or Bool type + String (get list item can always return string) + ASSERT_EQ(appendList4->targetType, Compiler::StaticType::Unknown); } TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ChainedAssignmentsInLoop) @@ -1243,8 +1243,8 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, ListReadReturnRegType) analyzer.analyzeScript(list); - // sourceList read return register has Number type - ASSERT_EQ(sourceValue.type(), Compiler::StaticType::Number); + // sourceList read return register has Number | String type (get list item can always return string) + ASSERT_EQ(sourceValue.type(), Compiler::StaticType::Number | Compiler::StaticType::String); } TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListWriteArgType) @@ -1285,6 +1285,6 @@ TEST(LLVMCodeAnalyzer_ListTypeAnalysis, CrossListWriteArgType) analyzer.analyzeScript(list); - // last write argument has Number type - ASSERT_EQ(appendList1_1->args.back().first, Compiler::StaticType::Number); + // last write argument has Number | String type (get list item can always return string) + ASSERT_EQ(appendList1_1->args.back().first, Compiler::StaticType::Number | Compiler::StaticType::String); } diff --git a/test/llvm/code_analyzer/mixed_type_analysis.cpp b/test/llvm/code_analyzer/mixed_type_analysis.cpp index 94eb5ebd..b1dbde86 100644 --- a/test/llvm/code_analyzer/mixed_type_analysis.cpp +++ b/test/llvm/code_analyzer/mixed_type_analysis.cpp @@ -121,8 +121,8 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ListToVariable_SimpleTransfer) // Variable write should see Unknown (first write) ASSERT_EQ(writeVar->targetType, Compiler::StaticType::Unknown); - // Return register should have Bool type - ASSERT_EQ(readListReg.type(), Compiler::StaticType::Bool); + // Return register should have Bool | String type (get list item can always return string) + ASSERT_EQ(readListReg.type(), Compiler::StaticType::Bool | Compiler::StaticType::String); } TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, CircularVarListDependency) @@ -502,5 +502,5 @@ TEST(LLVMCodeAnalyzer_MixedTypeAnalysis, ComplexChain_VarToListToVar_TypePropaga ASSERT_EQ(appendList->targetType, Compiler::StaticType::Void); ASSERT_EQ(readList->targetType, Compiler::StaticType::Number); ASSERT_EQ(writeTargetVar->targetType, Compiler::StaticType::Unknown); - ASSERT_EQ(readListReg.type(), Compiler::StaticType::Number); + ASSERT_EQ(readListReg.type(), Compiler::StaticType::Number | Compiler::StaticType::String); // get list item can always return string } diff --git a/test/llvm/llvmcodebuilder_test.cpp b/test/llvm/llvmcodebuilder_test.cpp index b36b7e91..e33ef788 100644 --- a/test/llvm/llvmcodebuilder_test.cpp +++ b/test/llvm/llvmcodebuilder_test.cpp @@ -1449,20 +1449,30 @@ TEST_F(LLVMCodeBuilderTest, GetListItem) v = builder->addListItem(localList3.get(), v); builder->addFunctionCall("test_print_number", Compiler::StaticType::Void, { Compiler::StaticType::Number }, { v }); + // Establish number type + builder->createListClear(localList3.get()); + builder->createListAppend(localList3.get(), builder->addConstValue(1.5)); + + // Out-of-range index should still return empty string + v = builder->addConstValue(2); + v = builder->addListItem(localList3.get(), v); + builder->addFunctionCall("test_print_string", Compiler::StaticType::Void, { Compiler::StaticType::String }, { v }); + static const std::string expected = "3\n" "1\n" - "0\n" - "0\n" + "\n" + "\n" "Lorem\n" "dolor\n" "sit\n" + "\n" + "\n" "0\n" "0\n" "0\n" "0\n" - "0\n" - "0\n"; + "\n"; auto code = builder->build(); Script script(&sprite, nullptr, nullptr); @@ -1477,7 +1487,7 @@ TEST_F(LLVMCodeBuilderTest, GetListItem) ASSERT_EQ(globalList->toString(), "1 test 3"); ASSERT_EQ(localList1->toString(), "Lorem ipsum dolor sit"); ASSERT_EQ(localList2->toString(), "-564.121 4257.4"); - ASSERT_EQ(localList3->toString(), "true false"); + ASSERT_EQ(localList3->toString(), "1.5"); } TEST_F(LLVMCodeBuilderTest, GetListSize)