Improve checking recursive functions and some minor fixes

This commit is contained in:
Sam Vervaeck 2023-05-26 14:30:50 +02:00
parent 2448a70c76
commit 06127ff624
Signed by: samvv
SSH key fingerprint: SHA256:dIg0ywU1OP+ZYifrYxy8c5esO72cIKB+4/9wkZj1VaY
9 changed files with 413 additions and 317 deletions

2
.vscode/tasks.json vendored
View file

@ -6,7 +6,7 @@
"label": "CMake: build", "label": "CMake: build",
"command": "build", "command": "build",
"targets": [ "targets": [
"all" "bolt"
], ],
"group": { "group": {
"kind": "build", "kind": "build",

View file

@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.10)
project(Bolt CXX) project(Bolt CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
add_subdirectory(deps/zen EXCLUDE_FROM_ALL) add_subdirectory(deps/zen EXCLUDE_FROM_ALL)
@ -22,7 +22,6 @@ add_library(
src/Parser.cc src/Parser.cc
src/Types.cc src/Types.cc
src/Checker.cc src/Checker.cc
src/IPRGraph.cc
) )
target_link_directories( target_link_directories(
BoltCore BoltCore

View file

@ -1530,6 +1530,7 @@ namespace bolt {
public: public:
bool IsCycleActive = false;
InferContext* Ctx; InferContext* Ctx;
class Type* Ty; class Type* Ty;
@ -1718,6 +1719,10 @@ namespace bolt {
return TheScope; return TheScope;
} }
static bool classof(const Node* N) {
return N->getKind() == NodeKind::SourceFile;
}
}; };
template<> inline NodeKind getNodeType<Equals>() { return NodeKind::Equals; } template<> inline NodeKind getNodeType<Equals>() { return NodeKind::Equals; }

View file

@ -7,6 +7,7 @@
#include "bolt/Common.hpp" #include "bolt/Common.hpp"
#include "bolt/CST.hpp" #include "bolt/CST.hpp"
#include "bolt/Type.hpp" #include "bolt/Type.hpp"
#include "bolt/Support/Graph.hpp"
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
@ -171,13 +172,17 @@ namespace bolt {
size_t NextConTypeId = 0; size_t NextConTypeId = 0;
size_t NextTypeVarId = 0; size_t NextTypeVarId = 0;
Type* BoolType;
Type* IntType;
Type* StringType;
Graph<Node*> RefGraph;
std::unordered_map<Node*, InferContext*> CallGraph; std::unordered_map<Node*, InferContext*> CallGraph;
std::unordered_map<ByteString, std::vector<InstanceDeclaration*>> InstanceMap; std::unordered_map<ByteString, std::vector<InstanceDeclaration*>> InstanceMap;
Type* BoolType; std::vector<InferContext*> Contexts;
Type* IntType;
Type* StringType;
TVSub Solution; TVSub Solution;
@ -191,14 +196,13 @@ namespace bolt {
*/ */
CEqual* C; CEqual* C;
std::vector<InferContext*> Contexts;
InferContext& getContext(); InferContext& getContext();
void addConstraint(Constraint* Constraint); void addConstraint(Constraint* Constraint);
void addClass(TypeclassSignature Sig); void addClass(TypeclassSignature Sig);
void forwardDeclare(Node* Node); void forwardDeclare(Node* Node);
void forwardDeclareLetDeclaration(LetDeclaration* N, TVSet* TVs, ConstraintSet* Constraints);
Type* inferExpression(Expression* Expression); Type* inferExpression(Expression* Expression);
Type* inferTypeExpression(TypeExpression* TE); Type* inferTypeExpression(TypeExpression* TE);
@ -208,13 +212,14 @@ namespace bolt {
void inferBindings(Pattern* Pattern, Type* T); void inferBindings(Pattern* Pattern, Type* T);
void infer(Node* node); void infer(Node* node);
void inferLetDeclaration(LetDeclaration* N);
Constraint* convertToConstraint(ConstraintExpression* C); Constraint* convertToConstraint(ConstraintExpression* C);
TCon* createPrimConType(); TCon* createPrimConType();
TVar* createTypeVar(); TVar* createTypeVar();
TVarRigid* createRigidVar(ByteString Name); TVarRigid* createRigidVar(ByteString Name);
InferContext* createInferContext(); InferContext* createInferContext(TVSet* TVs = new TVSet, ConstraintSet* Constraints = new ConstraintSet);
void addBinding(ByteString Name, Scheme* Scm); void addBinding(ByteString Name, Scheme* Scm);
@ -276,6 +281,8 @@ namespace bolt {
void solve(Constraint* Constraint, TVSub& Solution); void solve(Constraint* Constraint, TVSub& Solution);
void populate(SourceFile* SF);
/** /**
* Verifies that type class signatures on type asserts in let-declarations * Verifies that type class signatures on type asserts in let-declarations
* correctly declare the right type classes. * correctly declare the right type classes.

View file

@ -1,29 +0,0 @@
#pragma once
#include <unordered_map>
namespace bolt {
class Node;
class ReferenceExpression;
/**
* An inter-procedural reference graph.
*
* This graph keeps track of the references made to other procedures in the
* same program.
*/
class IPRGraph {
std::unordered_map<Node*, Node*> Edges;
public:
void populate(Node* , Node* Decl = nullptr);
bool isRecursive(ReferenceExpression* Node);
};
}

View file

@ -0,0 +1,145 @@
#pragma once
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <stack>
#include <optional>
#include "zen/range.hpp"
namespace bolt {
template<typename V>
class Graph {
std::unordered_set<V> Vertices;
std::unordered_multimap<V, V> Edges;
public:
void addVertex(V Vert) {
Vertices.emplace(Vert);
}
void addEdge(V A, V B) {
Vertices.emplace(A);
Vertices.emplace(B);
Edges.emplace(A, B);
}
std::size_t countVertices() const {
return Vertices.size();
}
bool hasVertex(const V& Vert) const {
return Vertices.count(Vert);
}
bool hasEdge(const V& From) const {
return Edges.count(From);
}
bool hasEdge(const V& From, const V& To) const {
for (auto X: Edges.equal_range(From)) {
if (X == To) {
return true;
}
}
}
auto getTargetVertices(const V& From) const {
return zen::make_iterator_range(Edges.equal_range(From)).map_second();
}
auto getVertices() const {
return zen::make_iterator_range(Vertices);
}
private:
struct TarjanVertexData {
std::optional<std::size_t> Index;
std::size_t LowLink;
bool OnStack = false;
};
class TarjanSolver {
public:
std::vector<std::vector<V>> SCCs;
private:
const Graph& G;
std::unordered_map<V, TarjanVertexData> Map;
std::size_t Index = 0;
std::stack<V> Stack;
TarjanVertexData& getData(V From) {
return Map.emplace(From, TarjanVertexData {}).first->second;
}
void visitCycle(const V& From) {
auto& DataFrom = getData(From);
DataFrom.Index = Index;
DataFrom.LowLink = Index;
Index++;
Stack.push(From);
DataFrom.OnStack = true;
for (const auto& To: G.getTargetVertices(From)) {
auto& DataTo = getData(To);
if (!DataTo.Index) {
visitCycle(To);
DataFrom.LowLink = std::min(DataFrom.LowLink, DataTo.LowLink);
} else if (DataTo.OnStack) {
DataFrom.LowLink = std::min(DataFrom.LowLink, *DataTo.Index);
}
}
if (DataFrom.LowLink == DataFrom.Index) {
std::vector<V> SCC;
for (;;) {
auto& X = Stack.top();
Stack.pop();
auto& DataX = getData(X);
DataX.OnStack = false;
SCC.push_back(X);
if (X == From) {
break;
}
}
SCCs.push_back(SCC);
}
}
public:
TarjanSolver(const Graph& G):
G(G) {}
void solve() {
for (auto From: G.Vertices) {
if (!Map.count(From)) {
visitCycle(From);
}
}
}
};
public:
std::vector<std::vector<V>> strongconnect() const {
TarjanSolver S { *this };
S.solve();
return S.SCCs;
}
};
}

View file

@ -230,112 +230,8 @@ namespace bolt {
} }
case NodeKind::LetDeclaration: case NodeKind::LetDeclaration:
{ // These declarations will be handled separately in check()
auto Let = static_cast<LetDeclaration*>(X);
bool IsFunc = !Let->Params.empty();
bool IsInstance = llvm::isa<InstanceDeclaration>(Let->Parent);
bool IsClass = llvm::isa<ClassDeclaration>(Let->Parent);
bool HasContext = IsFunc || IsInstance || IsClass;
if (HasContext) {
Let->Ctx = createInferContext();
Contexts.push_back(Let->Ctx);
}
// If declaring a let-declaration inside a type class declaration,
// we need to mark that the let-declaration requires this class.
// This marking is set on the rigid type variables of the class, which
// are then added to this local type environment.
if (IsClass) {
auto Class = static_cast<ClassDeclaration*>(Let->Parent);
for (auto TE: Class->TypeVars) {
auto TV = llvm::cast<TVar>(TE->getType());
Let->Ctx->Env.emplace(TE->Name->getCanonicalText(), new Forall(TV));
Let->Ctx->TVs->emplace(TV);
}
}
// Here we infer the primary type of the let declaration. If there's a
// type assert, that assert should be authoritative so we use that.
// Otherwise, the type is not further specified and we create a new
// unification variable.
Type* Ty;
if (Let->TypeAssert) {
Ty = inferTypeExpression(Let->TypeAssert->TypeExpression);
} else {
Ty = createTypeVar();
}
Let->Ty = Ty;
// If declaring a let-declaration inside a type instance declaration,
// we need to perform some work to make sure the type asserts of the
// corresponding let-declaration in the type class declaration are
// accounted for.
if (IsInstance) {
auto Instance = static_cast<InstanceDeclaration*>(Let->Parent);
auto Class = llvm::cast<ClassDeclaration>(Instance->getScope()->lookup({ {}, Instance->Name->getCanonicalText() }, SymbolKind::Class));
// The type asserts in the type class declaration might make use of
// the type parameters of the type class declaration, so it is
// important to make them available in the type environment. Moreover,
// we will be unifying them with the actual types declared in the
// instance declaration, so we keep track of them.
std::vector<TVar *> Params;
TVSub Sub;
for (auto TE: Class->TypeVars) {
auto TV = createTypeVar();
Sub.emplace(llvm::cast<TVar>(TE->getType()), TV);
Params.push_back(TV);
}
auto SigLet = llvm::cast<LetDeclaration>(Class->getScope()->lookupDirect({ {}, llvm::cast<BindPattern>(Let->Pattern)->Name->getCanonicalText() }, SymbolKind::Var));
// It would be very strange if there was no type assert in the type
// class let-declaration but we rather not let the compiler crash if that happens.
if (SigLet->TypeAssert) {
addConstraint(new CEqual(Ty, inferTypeExpression(SigLet->TypeAssert->TypeExpression)->substitute(Sub), Let));
}
// Here we do the actual unification of e.g. Eq a with Eq Bool. The
// unification variables we created previously will be unified with
// e.g. Bool, which causes the type assert to also collapse to e.g.
// Bool -> Bool -> Bool.
for (auto [Param, TE] : zen::zip(Params, Instance->TypeExps)) {
addConstraint(new CEqual(Param, TE->getType()));
}
}
if (Let->Body) {
switch (Let->Body->getKind()) {
case NodeKind::LetExprBody:
break;
case NodeKind::LetBlockBody:
{
auto Block = static_cast<LetBlockBody*>(Let->Body);
if (IsFunc) {
Let->Ctx->ReturnType = createTypeVar();
}
for (auto Element: Block->Elements) {
forwardDeclare(Element);
}
break;
}
default:
ZEN_UNREACHABLE
}
}
if (HasContext) {
Contexts.pop_back();
inferBindings(Let->Pattern, Ty, Let->Ctx->Constraints, Let->Ctx->TVs);
} else {
inferBindings(Let->Pattern, Ty);
}
break; break;
}
default: default:
ZEN_UNREACHABLE ZEN_UNREACHABLE
@ -344,6 +240,173 @@ namespace bolt {
} }
void Checker::forwardDeclareLetDeclaration(LetDeclaration* N, TVSet* TVs, ConstraintSet* Constraints) {
auto Let = static_cast<LetDeclaration*>(N);
bool IsFunc = !Let->Params.empty();
bool IsInstance = llvm::isa<InstanceDeclaration>(Let->Parent);
bool IsClass = llvm::isa<ClassDeclaration>(Let->Parent);
bool HasContext = IsFunc || IsInstance || IsClass;
if (HasContext) {
Let->Ctx = createInferContext(TVs, Constraints);
Contexts.push_back(Let->Ctx);
}
// If declaring a let-declaration inside a type class declaration,
// we need to mark that the let-declaration requires this class.
// This marking is set on the rigid type variables of the class, which
// are then added to this local type environment.
if (IsClass) {
auto Class = static_cast<ClassDeclaration*>(Let->Parent);
for (auto TE: Class->TypeVars) {
auto TV = llvm::cast<TVar>(TE->getType());
Let->Ctx->Env.emplace(TE->Name->getCanonicalText(), new Forall(TV));
Let->Ctx->TVs->emplace(TV);
}
}
// Here we infer the primary type of the let declaration. If there's a
// type assert, that assert should be authoritative so we use that.
// Otherwise, the type is not further specified and we create a new
// unification variable.
Type* Ty;
if (Let->TypeAssert) {
Ty = inferTypeExpression(Let->TypeAssert->TypeExpression);
} else {
Ty = createTypeVar();
}
Let->Ty = Ty;
// If declaring a let-declaration inside a type instance declaration,
// we need to perform some work to make sure the type asserts of the
// corresponding let-declaration in the type class declaration are
// accounted for.
if (IsInstance) {
auto Instance = static_cast<InstanceDeclaration*>(Let->Parent);
auto Class = llvm::cast<ClassDeclaration>(Instance->getScope()->lookup({ {}, Instance->Name->getCanonicalText() }, SymbolKind::Class));
// The type asserts in the type class declaration might make use of
// the type parameters of the type class declaration, so it is
// important to make them available in the type environment. Moreover,
// we will be unifying them with the actual types declared in the
// instance declaration, so we keep track of them.
std::vector<TVar *> Params;
TVSub Sub;
for (auto TE: Class->TypeVars) {
auto TV = createTypeVar();
Sub.emplace(llvm::cast<TVar>(TE->getType()), TV);
Params.push_back(TV);
}
auto SigLet = llvm::cast<LetDeclaration>(Class->getScope()->lookupDirect({ {}, llvm::cast<BindPattern>(Let->Pattern)->Name->getCanonicalText() }, SymbolKind::Var));
// It would be very strange if there was no type assert in the type
// class let-declaration but we rather not let the compiler crash if that happens.
if (SigLet->TypeAssert) {
addConstraint(new CEqual(Ty, inferTypeExpression(SigLet->TypeAssert->TypeExpression)->substitute(Sub), Let));
}
// Here we do the actual unification of e.g. Eq a with Eq Bool. The
// unification variables we created previously will be unified with
// e.g. Bool, which causes the type assert to also collapse to e.g.
// Bool -> Bool -> Bool.
for (auto [Param, TE] : zen::zip(Params, Instance->TypeExps)) {
addConstraint(new CEqual(Param, TE->getType()));
}
}
if (Let->Body) {
switch (Let->Body->getKind()) {
case NodeKind::LetExprBody:
break;
case NodeKind::LetBlockBody:
{
auto Block = static_cast<LetBlockBody*>(Let->Body);
if (IsFunc) {
Let->Ctx->ReturnType = createTypeVar();
}
for (auto Element: Block->Elements) {
forwardDeclare(Element);
}
break;
}
default:
ZEN_UNREACHABLE
}
}
if (HasContext) {
Contexts.pop_back();
inferBindings(Let->Pattern, Ty, Let->Ctx->Constraints, Let->Ctx->TVs);
} else {
inferBindings(Let->Pattern, Ty);
}
}
void Checker::inferLetDeclaration(LetDeclaration* N) {
auto Decl = static_cast<LetDeclaration*>(N);
bool IsFunc = !Decl->Params.empty();
bool IsInstance = llvm::isa<InstanceDeclaration>(Decl->Parent);
bool IsClass = llvm::isa<ClassDeclaration>(Decl->Parent);
bool HasContext = IsFunc || IsInstance || IsClass;
if (HasContext) {
Contexts.push_back(Decl->Ctx);
}
std::vector<Type*> ParamTypes;
Type* RetType;
for (auto Param: Decl->Params) {
// TODO incorporate Param->TypeAssert or make it a kind of pattern
TVar* TV = createTypeVar();
inferBindings(Param->Pattern, TV);
ParamTypes.push_back(TV);
}
if (Decl->Body) {
switch (Decl->Body->getKind()) {
case NodeKind::LetExprBody:
{
auto Expr = static_cast<LetExprBody*>(Decl->Body);
RetType = inferExpression(Expr->Expression);
break;
}
case NodeKind::LetBlockBody:
{
auto Block = static_cast<LetBlockBody*>(Decl->Body);
ZEN_ASSERT(HasContext);
RetType = Decl->Ctx->ReturnType;
for (auto Element: Block->Elements) {
infer(Element);
}
break;
}
default:
ZEN_UNREACHABLE
}
} else {
RetType = createTypeVar();
}
if (HasContext) {
Contexts.pop_back();
}
if (IsFunc) {
addConstraint(new CEqual { Decl->Ty, new TArrow(ParamTypes, RetType), N });
} else {
// Declaration is a plain (typed) variable
addConstraint(new CEqual { Decl->Ty, RetType, N });
}
}
void Checker::infer(Node* N) { void Checker::infer(Node* N) {
switch (N->getKind()) { switch (N->getKind()) {
@ -369,11 +432,9 @@ namespace bolt {
case NodeKind::InstanceDeclaration: case NodeKind::InstanceDeclaration:
{ {
auto Decl = static_cast<InstanceDeclaration*>(N); auto Decl = static_cast<InstanceDeclaration*>(N);
for (auto Element: Decl->Elements) { for (auto Element: Decl->Elements) {
infer(Element); infer(Element);
} }
break; break;
} }
@ -392,71 +453,18 @@ namespace bolt {
} }
case NodeKind::LetDeclaration: case NodeKind::LetDeclaration:
{
auto Decl = static_cast<LetDeclaration*>(N);
bool HasContext = !Decl->Params.empty();
if (HasContext) {
Contexts.push_back(Decl->Ctx);
}
std::vector<Type*> ParamTypes;
Type* RetType;
for (auto Param: Decl->Params) {
// TODO incorporate Param->TypeAssert or make it a kind of pattern
TVar* TV = createTypeVar();
inferBindings(Param->Pattern, TV);
ParamTypes.push_back(TV);
}
if (Decl->Body) {
switch (Decl->Body->getKind()) {
case NodeKind::LetExprBody:
{
auto Expr = static_cast<LetExprBody*>(Decl->Body);
RetType = inferExpression(Expr->Expression);
break;
}
case NodeKind::LetBlockBody:
{
auto Block = static_cast<LetBlockBody*>(Decl->Body);
ZEN_ASSERT(HasContext);
RetType = Decl->Ctx->ReturnType;
for (auto Element: Block->Elements) {
infer(Element);
}
break;
}
default:
ZEN_UNREACHABLE
}
} else {
RetType = createTypeVar();
}
if (HasContext) {
// Declaration is a function
addConstraint(new CEqual { Decl->Ty, new TArrow(ParamTypes, RetType), N });
Contexts.pop_back();
} else {
// Declaration is a plain (typed) variable
addConstraint(new CEqual { Decl->Ty, RetType, N });
}
break; break;
}
case NodeKind::ReturnStatement: case NodeKind::ReturnStatement:
{ {
auto RetStmt = static_cast<ReturnStatement*>(N); auto RetStmt = static_cast<ReturnStatement*>(N);
Type* ReturnType; Type* ReturnType;
if (RetStmt->Expression) { if (RetStmt->Expression) {
ReturnType = inferExpression(RetStmt->Expression); addConstraint(new CEqual { inferExpression(RetStmt->Expression), getReturnType(), RetStmt->Expression });
} else { } else {
ReturnType = new TTuple({}); ReturnType = new TTuple({});
addConstraint(new CEqual { new TTuple({}), getReturnType(), N });
} }
addConstraint(new CEqual { ReturnType, getReturnType(), N });
break; break;
} }
@ -486,7 +494,7 @@ namespace bolt {
return TV; return TV;
} }
InferContext* Checker::createInferContext() { InferContext* Checker::createInferContext(TVSet* TVs, ConstraintSet* Constraints) {
auto Ctx = new InferContext; auto Ctx = new InferContext;
Ctx->TVs = new TVSet; Ctx->TVs = new TVSet;
Ctx->Constraints = new ConstraintSet; Ctx->Constraints = new ConstraintSet;
@ -677,11 +685,12 @@ namespace bolt {
{ {
auto Ref = static_cast<ReferenceExpression*>(X); auto Ref = static_cast<ReferenceExpression*>(X);
ZEN_ASSERT(Ref->ModulePath.empty()); ZEN_ASSERT(Ref->ModulePath.empty());
auto Ctx = lookupCall(Ref, Ref->getSymbolPath()); auto Target = Ref->getScope()->lookup(Ref->getSymbolPath());
if (Ctx) { if (Target && llvm::isa<LetDeclaration>(Target)) {
/* std::cerr << "recursive call!\n"; */ auto Let = static_cast<LetDeclaration*>(Target);
ZEN_ASSERT(Ctx->ReturnType != nullptr); if (Let->IsCycleActive) {
return Ctx->ReturnType; return Let->Ty;
}
} }
auto Scm = lookup(Ref->Name->getCanonicalText()); auto Scm = lookup(Ref->Name->getCanonicalText());
if (Scm == nullptr) { if (Scm == nullptr) {
@ -821,6 +830,43 @@ namespace bolt {
return Ty; return Ty;
} }
void Checker::populate(SourceFile* SF) {
struct Visitor : public CSTVisitor<Visitor> {
Graph<Node*>& RefGraph;
std::stack<Node*> Stack;
void visitLetDeclaration(LetDeclaration* N) {
RefGraph.addVertex(N);
Stack.push(N);
visitEachChild(N);
Stack.pop();
}
void visitReferenceExpression(ReferenceExpression* N) {
auto Y = static_cast<ReferenceExpression*>(N);
auto Def = Y->getScope()->lookup(Y->getSymbolPath());
// Name lookup failures will be reported directly in inferExpression().
// Parameters are clearly no let-decarations. They never have their own
// inference context, so we have to skip them.
if (Def == nullptr || Def->getKind() == NodeKind::Parameter) {
return;
}
ZEN_ASSERT(Def->getKind() == NodeKind::LetDeclaration || Def->getKind() == NodeKind::SourceFile);
RefGraph.addEdge(Stack.top(), Def);
}
};
RefGraph.addVertex(SF);
Visitor V { {}, RefGraph };
V.Stack.push(SF);
V.visit(SF);
}
void Checker::checkTypeclassSigs(Node* N) { void Checker::checkTypeclassSigs(Node* N) {
struct LetVisitor : CSTVisitor<LetVisitor> { struct LetVisitor : CSTVisitor<LetVisitor> {
@ -965,7 +1011,37 @@ namespace bolt {
addBinding("-", new Forall(new TArrow({ IntType, IntType }, IntType))); addBinding("-", new Forall(new TArrow({ IntType, IntType }, IntType)));
addBinding("*", new Forall(new TArrow({ IntType, IntType }, IntType))); addBinding("*", new Forall(new TArrow({ IntType, IntType }, IntType)));
addBinding("/", new Forall(new TArrow({ IntType, IntType }, IntType))); addBinding("/", new Forall(new TArrow({ IntType, IntType }, IntType)));
populate(SF);
forwardDeclare(SF); forwardDeclare(SF);
auto SCCs = RefGraph.strongconnect();
for (auto Nodes: SCCs) {
if (Nodes.size() == 1 && llvm::isa<SourceFile>(Nodes[0])) {
continue;
}
auto TVs = new TVSet;
auto Constraints = new ConstraintSet;
for (auto N: Nodes) {
auto Decl = static_cast<LetDeclaration*>(N);
forwardDeclareLetDeclaration(Decl, TVs, Constraints);
}
}
for (auto Nodes: SCCs) {
if (Nodes.size() == 1 && llvm::isa<SourceFile>(Nodes[0])) {
continue;
}
for (auto N: Nodes) {
auto Decl = static_cast<LetDeclaration*>(N);
Decl->IsCycleActive = true;
}
for (auto N: Nodes) {
auto Decl = static_cast<LetDeclaration*>(N);
inferLetDeclaration(Decl);
}
for (auto N: Nodes) {
auto Decl = static_cast<LetDeclaration*>(N);
Decl->IsCycleActive = false;
}
}
infer(SF); infer(SF);
Contexts.pop_back(); Contexts.pop_back();
solve(new CMany(*RootContext->Constraints), Solution); solve(new CMany(*RootContext->Constraints), Solution);
@ -1280,7 +1356,9 @@ namespace bolt {
To = Var2; To = Var2;
From = Var1; From = Var1;
} }
join(From, To); if (From->Id != To->Id) {
join(From, To);
}
return true; return true;
} }

View file

@ -442,7 +442,7 @@ namespace bolt {
{ {
auto E = static_cast<const BindingNotFoundDiagnostic&>(D); auto E = static_cast<const BindingNotFoundDiagnostic&>(D);
writePrefix(E); writePrefix(E);
write("binding '"); write("binding ");
writeBinding(E.Name); writeBinding(E.Name);
write(" was not found\n\n"); write(" was not found\n\n");
if (E.Initiator != nullptr) { if (E.Initiator != nullptr) {

View file

@ -1,109 +0,0 @@
#include <stack>
#include <unordered_set>
#include "zen/config.hpp"
#include "bolt/CST.hpp"
#include "bolt/IPRGraph.hpp"
namespace bolt {
void IPRGraph::populate(Node* X, Node* Decl) {
switch (X->getKind()) {
case NodeKind::SourceFile:
{
auto Y = static_cast<SourceFile*>(X);
for (auto Element: Y->Elements) {
populate(Element, Decl);
}
break;
}
case NodeKind::IfStatement:
{
auto Y = static_cast<IfStatement*>(X);
for (auto Part: Y->Parts) {
for (auto Element: Part->Elements) {
populate(Element, Decl);
}
}
break;
}
case NodeKind::LetDeclaration:
{
auto Y = static_cast<LetDeclaration*>(X);
if (Y->Body) {
switch (Y->Body->getKind()) {
case NodeKind::LetBlockBody:
{
auto Z = static_cast<LetBlockBody*>(Y->Body);
for (auto Element: Z->Elements) {
populate(Element, Y);
}
break;
}
case NodeKind::LetExprBody:
{
auto Z = static_cast<LetExprBody*>(Y->Body);
populate(Z->Expression, Y);
break;
}
default:
ZEN_UNREACHABLE
}
}
break;
}
case NodeKind::ConstantExpression:
break;
case NodeKind::CallExpression:
{
auto Y = static_cast<CallExpression*>(X);
populate(Y->Function, Decl);
for (auto Arg: Y->Args) {
populate(Arg, Decl);
}
break;
}
case NodeKind::ReferenceExpression:
{
auto Y = static_cast<ReferenceExpression*>(X);
auto Def = Y->getScope()->lookup(Y->getSymbolPath());
ZEN_ASSERT(Def != nullptr);
if (Decl != nullptr) {
Edges.emplace(Decl, Y);
}
Edges.emplace(Y, Def);
break;
}
default:
ZEN_UNREACHABLE
}
}
/* bool IPRGraph::isRecursive(ReferenceExpression* From) { */
/* std::unordered_set<Node*> Visited; */
/* std::stack<Node*> Queue; */
/* while (Queue.size()) { */
/* auto A = Queue.top(); */
/* Queue.pop(); */
/* if (Visited.count(A)) { */
/* return true; */
/* } */
/* for (auto B: getOutEdges(A)) { */
/* } */
/* } */
/* return false; */
/* } */
}