From a2701257dde1ea3a60f53f7ef8ac025f78e0f001 Mon Sep 17 00:00:00 2001 From: Sam Vervaeck Date: Sun, 21 Jan 2024 01:56:01 +0100 Subject: [PATCH] Prevent infinite loops when checking something like `"foo".0.1.2` --- bootstrap/cxx/include/bolt/Checker.hpp | 7 ++++++- bootstrap/cxx/src/Checker.cc | 25 ++++++++++++++++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/bootstrap/cxx/include/bolt/Checker.hpp b/bootstrap/cxx/include/bolt/Checker.hpp index 71f596a11..729d546b9 100644 --- a/bootstrap/cxx/include/bolt/Checker.hpp +++ b/bootstrap/cxx/include/bolt/Checker.hpp @@ -265,7 +265,12 @@ namespace bolt { */ std::deque 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); diff --git a/bootstrap/cxx/src/Checker.cc b/bootstrap/cxx/src/Checker.cc index 7dcb71844..f5f7a0ee3 100644 --- a/bootstrap/cxx/src/Checker.cc +++ b/bootstrap/cxx/src/Checker.cc @@ -1294,8 +1294,18 @@ namespace bolt { void Checker::solve(Constraint* Constraint) { Queue.push_back(Constraint); + bool DidJoin = false; + std::deque NextQueue; - while (!Queue.empty()) { + while (true) { + + if (Queue.empty()) { + if (NextQueue.empty() || !DidJoin) { + break; + } + DidJoin = false; + std::swap(Queue, NextQueue); + } auto Constraint = Queue.front(); Queue.pop_front(); @@ -1318,7 +1328,7 @@ namespace bolt { unify(ElementTy, Field->FieldTy, Field->Source); } } else if (MaybeTuple->isVar()) { - // TODO Add logic for when tuple is a var + NextQueue.push_back(Constraint); } else { DE.add(MaybeTuple, Field->Source); } @@ -1337,7 +1347,9 @@ namespace bolt { case ConstraintKind::Equal: { auto Equal = static_cast(Constraint); - unify(Equal->Left, Equal->Right, Equal->Source); + if (unify(Equal->Left, Equal->Right, Equal->Source)) { + DidJoin = true; + } break; } @@ -1413,11 +1425,11 @@ namespace bolt { Type* Right; Node* Source; - // Internal state used by the unifier ByteString CurrentFieldName; TypePath LeftPath; TypePath RightPath; + bool DidJoin = false; Type* getLeft() const { return Left; @@ -1525,6 +1537,8 @@ namespace bolt { TV->set(Ty); + DidJoin = true; + propagateClasses(TV->asVar().Context, Ty); // This is a very specific adjustment that is critical to the @@ -1826,10 +1840,11 @@ namespace bolt { 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; Unifier A { *this, Left, Right, Source }; A.unify(); + return A.DidJoin; } }