diff --git a/src/typechecker/typechecker.cpp b/src/typechecker/typechecker.cpp index 63b9a81..0bc65a5 100644 --- a/src/typechecker/typechecker.cpp +++ b/src/typechecker/typechecker.cpp @@ -122,7 +122,7 @@ namespace Solstice { throw std::runtime_error("unknown variable " + *identifier); } - node.ptype = {{variables[*identifier]}}; + node.ptype = variables[*identifier]; } void TypeChecker::checkTupleNodeType(Node& node) { @@ -200,6 +200,135 @@ namespace Solstice { variables[*id].isConstant = true; } + Narrowing TypeChecker::doesNodeChildrenNeedNarrowing(Node& node) { + Narrowing ret; + if ( + node.children[0].type == NodeType::Expression && + variables.find(*node.children[0].getIdentifier()) != variables.end() && + variables[*node.children[0].getIdentifier()].unknownType + ) { + ret = Narrowing::Left; + } + + if ( + node.children[1].type == NodeType::Expression && + variables.find(*node.children[1].getIdentifier()) != variables.end() && + variables[*node.children[1].getIdentifier()].unknownType + ) { + if (ret == Narrowing::Left) { + ret = Narrowing::Both; + } else { + ret = Narrowing::Right; + } + } + + return ret; + } + + void TypeChecker::narrowBinaryNode(Node& node, std::unordered_map& overloads) { + auto& left = node.children[0]; + auto& right = node.children[1]; + + // narrow types if we need to + switch (doesNodeChildrenNeedNarrowing(node)) { + case Narrowing::Both: { + auto& leftVar = variables[*left.getIdentifier()]; + auto& rightVar = variables[*right.getIdentifier()]; + + if (leftVar.possiblities.empty() && rightVar.possiblities.empty()) { + for (const auto& [key, value] : overloads) { + leftVar.possiblities.insert(key.left); + rightVar.possiblities.insert(key.right); + node.ptype.possiblities.insert(value); + } + } else if (leftVar.possiblities.empty()) { + std::unordered_set rightMatches; + for (const auto& [key, value] : overloads) { + if (rightVar.possiblities.find(key.right) != rightVar.possiblities.end()) { + rightMatches.insert(key.left); + node.ptype.possiblities.insert(value); + } + } + if (rightMatches.empty()) { + throw std::runtime_error("no valid overload compatible with previous statements"); + } + leftVar.possiblities = rightMatches; + } else if (rightVar.possiblities.empty()) { + std::unordered_set leftMatches; + for (const auto& [key, value] : overloads) { + if (leftVar.possiblities.find(key.right) != leftVar.possiblities.end()) { + leftMatches.insert(key.left); + node.ptype.possiblities.insert(value); + } + } + if (leftMatches.empty()) { + throw std::runtime_error("no valid overload compatible with previous statements"); + } + rightVar.possiblities = leftMatches; + } else { + std::unordered_set left; + std::unordered_set right; + for (const auto& [key, value] : overloads) { + if ( + leftVar.possiblities.find(key.right) != leftVar.possiblities.end() && + rightVar.possiblities.find(key.right) != rightVar.possiblities.end() + ) { + left.insert(key.left); + right.insert(key.right); + node.ptype.possiblities.insert(value); + } + } + if (left.empty() || right.empty()) { + throw std::runtime_error("no valid overload compatible with previous statements"); + } + } + + break; + } + case Narrowing::Left: { + auto& leftVar = variables[*left.getIdentifier()]; + std::unordered_set rightMatches; + for (const auto& [key, value] : overloads) { + if (right.ptype.possiblities.find(key.right) != right.ptype.possiblities.end()) { + rightMatches.insert(key.left); + node.ptype.possiblities.insert(value); + } + } + if (rightMatches.empty()) { + throw std::runtime_error("no valid overload compatible with previous statements"); + } + leftVar.possiblities = rightMatches; + break; + } + case Narrowing::Right: { + auto& rightVar = variables[*right.getIdentifier()]; + std::unordered_set rightMatches; + for (const auto& [key, value] : overloads) { + if (left.ptype.possiblities.find(key.left) != left.ptype.possiblities.end()) { + rightMatches.insert(key.left); + node.ptype.possiblities.insert(value); + } + } + if (rightMatches.empty()) { + throw std::runtime_error("no valid overload compatible with previous statements"); + } + rightVar.possiblities = rightMatches; + break; + } + case Narrowing::None: { + for (const auto& [key, value] : overloads) { + if ( + left.ptype.possiblities.find(key.right) != left.ptype.possiblities.end() && + right.ptype.possiblities.find(key.right) != right.ptype.possiblities.end() + ) { + node.ptype.possiblities.insert(value); + } + } + } + } + + } + void TypeChecker::checkSetNodeType(Node& node) { auto id = node.children[0].getIdentifier(); if (!id.has_value()) { @@ -232,7 +361,9 @@ namespace Solstice { checkNodeType(node.children[0]); checkNodeType(node.children[1]); - + // narrow if required + narrowBinaryNode(node, addOverloads); + } @@ -280,6 +411,7 @@ namespace Solstice { case NodeType::Add: checkAddType(node); break; + /* case NodeType::Subtract: checkSubtractType(node); break; @@ -301,6 +433,7 @@ namespace Solstice { case NodeType::LesserThan: checkLesserThanType(node); break; + */ } } diff --git a/src/typechecker/typechecker.hpp b/src/typechecker/typechecker.hpp index d6552a3..617336a 100644 --- a/src/typechecker/typechecker.hpp +++ b/src/typechecker/typechecker.hpp @@ -8,6 +8,13 @@ namespace Solstice { + enum class Narrowing { + None, + Left, + Right, + Both + }; + class TypeChecker { std::unordered_map variables; std::unordered_map types; @@ -37,6 +44,9 @@ namespace Solstice { void checkCodeBlockType(Node& node); + Narrowing doesNodeChildrenNeedNarrowing(Node& node); + void narrowBinaryNode(Node& node, std::unordered_map& overloads); + void checkAddType(Node& node); void checkSubtractType(Node& node); void checkMultiplyType(Node& node);