start type narrowing for binary exprs

This commit is contained in:
2026-08-02 18:03:31 +10:00
parent cf79fd00b9
commit 9bee018c09
2 changed files with 145 additions and 2 deletions

View File

@@ -122,7 +122,7 @@ namespace Solstice {
throw std::runtime_error("unknown variable " + *identifier); throw std::runtime_error("unknown variable " + *identifier);
} }
node.ptype = {{variables[*identifier]}}; node.ptype = variables[*identifier];
} }
void TypeChecker::checkTupleNodeType(Node& node) { void TypeChecker::checkTupleNodeType(Node& node) {
@@ -200,6 +200,135 @@ namespace Solstice {
variables[*id].isConstant = true; 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<TypePair, Type>& 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<Type> 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<Type> 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<Type> left;
std::unordered_set<Type> 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<Type> 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<Type> 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) { void TypeChecker::checkSetNodeType(Node& node) {
auto id = node.children[0].getIdentifier(); auto id = node.children[0].getIdentifier();
if (!id.has_value()) { if (!id.has_value()) {
@@ -232,6 +361,8 @@ namespace Solstice {
checkNodeType(node.children[0]); checkNodeType(node.children[0]);
checkNodeType(node.children[1]); checkNodeType(node.children[1]);
// narrow if required
narrowBinaryNode(node, addOverloads);
} }
@@ -280,6 +411,7 @@ namespace Solstice {
case NodeType::Add: case NodeType::Add:
checkAddType(node); checkAddType(node);
break; break;
/*
case NodeType::Subtract: case NodeType::Subtract:
checkSubtractType(node); checkSubtractType(node);
break; break;
@@ -301,6 +433,7 @@ namespace Solstice {
case NodeType::LesserThan: case NodeType::LesserThan:
checkLesserThanType(node); checkLesserThanType(node);
break; break;
*/
} }
} }

View File

@@ -8,6 +8,13 @@
namespace Solstice { namespace Solstice {
enum class Narrowing {
None,
Left,
Right,
Both
};
class TypeChecker { class TypeChecker {
std::unordered_map<std::string, PossibleType> variables; std::unordered_map<std::string, PossibleType> variables;
std::unordered_map<std::string, Type> types; std::unordered_map<std::string, Type> types;
@@ -37,6 +44,9 @@ namespace Solstice {
void checkCodeBlockType(Node& node); void checkCodeBlockType(Node& node);
Narrowing doesNodeChildrenNeedNarrowing(Node& node);
void narrowBinaryNode(Node& node, std::unordered_map<TypePair, Type>& overloads);
void checkAddType(Node& node); void checkAddType(Node& node);
void checkSubtractType(Node& node); void checkSubtractType(Node& node);
void checkMultiplyType(Node& node); void checkMultiplyType(Node& node);