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);
}
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<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) {
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;
*/
}
}

View File

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