Prevent infinite loops when checking something like "foo".0.1.2
This commit is contained in:
parent
f2550e9430
commit
a2701257dd
2 changed files with 26 additions and 6 deletions
|
@ -265,7 +265,12 @@ namespace bolt {
|
||||||
*/
|
*/
|
||||||
std::deque<class Constraint*> Queue;
|
std::deque<class Constraint*> Queue;
|
||||||
|
|
||||||
void unify(Type* Left, Type* Right, Node* Source);
|
/**
|
||||||
|
* Unify two types, using `Source` as source location.
|
||||||
|
*
|
||||||
|
* \returns Whether a type variable was assigned a type or not.
|
||||||
|
*/
|
||||||
|
bool unify(Type* Left, Type* Right, Node* Source);
|
||||||
|
|
||||||
void solve(Constraint* Constraint);
|
void solve(Constraint* Constraint);
|
||||||
|
|
||||||
|
|
|
@ -1294,8 +1294,18 @@ namespace bolt {
|
||||||
void Checker::solve(Constraint* Constraint) {
|
void Checker::solve(Constraint* Constraint) {
|
||||||
|
|
||||||
Queue.push_back(Constraint);
|
Queue.push_back(Constraint);
|
||||||
|
bool DidJoin = false;
|
||||||
|
std::deque<class Constraint*> NextQueue;
|
||||||
|
|
||||||
while (!Queue.empty()) {
|
while (true) {
|
||||||
|
|
||||||
|
if (Queue.empty()) {
|
||||||
|
if (NextQueue.empty() || !DidJoin) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DidJoin = false;
|
||||||
|
std::swap(Queue, NextQueue);
|
||||||
|
}
|
||||||
|
|
||||||
auto Constraint = Queue.front();
|
auto Constraint = Queue.front();
|
||||||
Queue.pop_front();
|
Queue.pop_front();
|
||||||
|
@ -1318,7 +1328,7 @@ namespace bolt {
|
||||||
unify(ElementTy, Field->FieldTy, Field->Source);
|
unify(ElementTy, Field->FieldTy, Field->Source);
|
||||||
}
|
}
|
||||||
} else if (MaybeTuple->isVar()) {
|
} else if (MaybeTuple->isVar()) {
|
||||||
// TODO Add logic for when tuple is a var
|
NextQueue.push_back(Constraint);
|
||||||
} else {
|
} else {
|
||||||
DE.add<NotATupleDiagnostic>(MaybeTuple, Field->Source);
|
DE.add<NotATupleDiagnostic>(MaybeTuple, Field->Source);
|
||||||
}
|
}
|
||||||
|
@ -1337,7 +1347,9 @@ namespace bolt {
|
||||||
case ConstraintKind::Equal:
|
case ConstraintKind::Equal:
|
||||||
{
|
{
|
||||||
auto Equal = static_cast<CEqual*>(Constraint);
|
auto Equal = static_cast<CEqual*>(Constraint);
|
||||||
unify(Equal->Left, Equal->Right, Equal->Source);
|
if (unify(Equal->Left, Equal->Right, Equal->Source)) {
|
||||||
|
DidJoin = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1413,11 +1425,11 @@ namespace bolt {
|
||||||
Type* Right;
|
Type* Right;
|
||||||
Node* Source;
|
Node* Source;
|
||||||
|
|
||||||
|
|
||||||
// Internal state used by the unifier
|
// Internal state used by the unifier
|
||||||
ByteString CurrentFieldName;
|
ByteString CurrentFieldName;
|
||||||
TypePath LeftPath;
|
TypePath LeftPath;
|
||||||
TypePath RightPath;
|
TypePath RightPath;
|
||||||
|
bool DidJoin = false;
|
||||||
|
|
||||||
Type* getLeft() const {
|
Type* getLeft() const {
|
||||||
return Left;
|
return Left;
|
||||||
|
@ -1525,6 +1537,8 @@ namespace bolt {
|
||||||
|
|
||||||
TV->set(Ty);
|
TV->set(Ty);
|
||||||
|
|
||||||
|
DidJoin = true;
|
||||||
|
|
||||||
propagateClasses(TV->asVar().Context, Ty);
|
propagateClasses(TV->asVar().Context, Ty);
|
||||||
|
|
||||||
// This is a very specific adjustment that is critical to the
|
// This is a very specific adjustment that is critical to the
|
||||||
|
@ -1826,10 +1840,11 @@ namespace bolt {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Checker::unify(Type* Left, Type* Right, Node* Source) {
|
bool Checker::unify(Type* Left, Type* Right, Node* Source) {
|
||||||
// std::cerr << describe(C->Left) << " ~ " << describe(C->Right) << std::endl;
|
// std::cerr << describe(C->Left) << " ~ " << describe(C->Right) << std::endl;
|
||||||
Unifier A { *this, Left, Right, Source };
|
Unifier A { *this, Left, Right, Source };
|
||||||
A.unify();
|
A.unify();
|
||||||
|
return A.DidJoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue