Tidy up Checker.cc/Checker.hpp a bit
This commit is contained in:
parent
75e0ebc14b
commit
fd462f6d05
2 changed files with 380 additions and 386 deletions
|
@ -9,6 +9,7 @@
|
||||||
#include "bolt/Type.hpp"
|
#include "bolt/Type.hpp"
|
||||||
#include "bolt/Support/Graph.hpp"
|
#include "bolt/Support/Graph.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -181,21 +182,24 @@ namespace bolt {
|
||||||
|
|
||||||
std::unordered_map<ByteString, std::vector<InstanceDeclaration*>> InstanceMap;
|
std::unordered_map<ByteString, std::vector<InstanceDeclaration*>> InstanceMap;
|
||||||
|
|
||||||
// std::vector<InferContext*> Contexts;
|
/// Inference context management
|
||||||
|
|
||||||
InferContext* ActiveContext;
|
InferContext* ActiveContext;
|
||||||
|
|
||||||
InferContext& getContext();
|
InferContext& getContext();
|
||||||
void setContext(InferContext* Ctx);
|
void setContext(InferContext* Ctx);
|
||||||
void popContext();
|
void popContext();
|
||||||
|
|
||||||
/**
|
|
||||||
* The queue that is used during solving to store any unsolved constraints.
|
|
||||||
*/
|
|
||||||
std::deque<class Constraint*> Queue;
|
|
||||||
|
|
||||||
void addConstraint(Constraint* Constraint);
|
void addConstraint(Constraint* Constraint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the return type for the current context. If none could be found, the
|
||||||
|
* program will abort.
|
||||||
|
*/
|
||||||
|
Type* getReturnType();
|
||||||
|
|
||||||
|
/// Type inference
|
||||||
|
|
||||||
void forwardDeclare(Node* Node);
|
void forwardDeclare(Node* Node);
|
||||||
void forwardDeclareFunctionDeclaration(FunctionDeclaration* N, TVSet* TVs, ConstraintSet* Constraints);
|
void forwardDeclareFunctionDeclaration(FunctionDeclaration* N, TVSet* TVs, ConstraintSet* Constraints);
|
||||||
|
|
||||||
|
@ -210,6 +214,8 @@ namespace bolt {
|
||||||
|
|
||||||
Constraint* convertToConstraint(ConstraintExpression* C);
|
Constraint* convertToConstraint(ConstraintExpression* C);
|
||||||
|
|
||||||
|
/// Factory methods
|
||||||
|
|
||||||
TCon* createConType(ByteString Name);
|
TCon* createConType(ByteString Name);
|
||||||
TVar* createTypeVar();
|
TVar* createTypeVar();
|
||||||
TVarRigid* createRigidVar(ByteString Name);
|
TVarRigid* createRigidVar(ByteString Name);
|
||||||
|
@ -219,12 +225,10 @@ namespace bolt {
|
||||||
ConstraintSet* Constraints = new ConstraintSet
|
ConstraintSet* Constraints = new ConstraintSet
|
||||||
);
|
);
|
||||||
|
|
||||||
void addBinding(ByteString Name, Scheme* Scm);
|
/// Environment manipulation
|
||||||
|
|
||||||
Scheme* lookup(ByteString Name);
|
Scheme* lookup(ByteString Name);
|
||||||
|
|
||||||
void initialize(Node* N);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up a type/variable and ensures that it is a monomorphic type.
|
* Looks up a type/variable and ensures that it is a monomorphic type.
|
||||||
*
|
*
|
||||||
|
@ -239,40 +243,21 @@ namespace bolt {
|
||||||
*/
|
*/
|
||||||
Type* lookupMono(ByteString Name);
|
Type* lookupMono(ByteString Name);
|
||||||
|
|
||||||
/**
|
void addBinding(ByteString Name, Scheme* Scm);
|
||||||
* Get the return type for the current context. If none could be found, the program will abort.
|
|
||||||
*/
|
|
||||||
Type* getReturnType();
|
|
||||||
|
|
||||||
Type* instantiate(Scheme* S, Node* Source);
|
/// Constraint solving
|
||||||
|
|
||||||
std::vector<TypeclassContext> findInstanceContext(TCon* Ty, TypeclassId& Class);
|
|
||||||
void propagateClasses(TypeclassContext& Classes, Type* Ty);
|
|
||||||
void propagateClassTycon(TypeclassId& Class, TCon* Ty);
|
|
||||||
|
|
||||||
// TODO Remove this
|
|
||||||
Node* Source;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign a type to a unification variable.
|
* The queue that is used during solving to store any unsolved constraints.
|
||||||
*
|
|
||||||
* If there are class constraints, those are propagated.
|
|
||||||
*
|
|
||||||
* If this type variable is solved during inference, it will be removed from
|
|
||||||
* the inference context.
|
|
||||||
*
|
|
||||||
* Other side effects may occur.
|
|
||||||
*/
|
*/
|
||||||
void join(TVar* A, Type* B);
|
std::deque<class Constraint*> Queue;
|
||||||
|
|
||||||
bool unify(Type* A, Type* B);
|
void solveEqual(CEqual* C);
|
||||||
|
|
||||||
void unifyError();
|
|
||||||
|
|
||||||
void solveCEqual(CEqual* C);
|
|
||||||
|
|
||||||
void solve(Constraint* Constraint);
|
void solve(Constraint* Constraint);
|
||||||
|
|
||||||
|
/// Helpers
|
||||||
|
|
||||||
void populate(SourceFile* SF);
|
void populate(SourceFile* SF);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -281,6 +266,10 @@ namespace bolt {
|
||||||
*/
|
*/
|
||||||
void checkTypeclassSigs(Node* N);
|
void checkTypeclassSigs(Node* N);
|
||||||
|
|
||||||
|
Type* instantiate(Scheme* S, Node* Source);
|
||||||
|
|
||||||
|
void initialize(Node* N);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Checker(const LanguageConfig& Config, DiagnosticEngine& DE);
|
Checker(const LanguageConfig& Config, DiagnosticEngine& DE);
|
||||||
|
|
705
src/Checker.cc
705
src/Checker.cc
|
@ -227,7 +227,7 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MaxLevel == MinLevel || MaxLevelLeft == 0 || MaxLevelRight == 0) {
|
if (MaxLevel == MinLevel || MaxLevelLeft == 0 || MaxLevelRight == 0) {
|
||||||
solveCEqual(Y);
|
solveEqual(Y);
|
||||||
} else {
|
} else {
|
||||||
Contexts[Contexts.size() - MaxLevel - 1]->Constraints->push_back(C);
|
Contexts[Contexts.size() - MaxLevel - 1]->Constraints->push_back(C);
|
||||||
}
|
}
|
||||||
|
@ -1307,26 +1307,42 @@ namespace bolt {
|
||||||
auto TVs = new TVSet;
|
auto TVs = new TVSet;
|
||||||
auto Constraints = new ConstraintSet;
|
auto Constraints = new ConstraintSet;
|
||||||
for (auto N: Nodes) {
|
for (auto N: Nodes) {
|
||||||
|
if (N->getKind() != NodeKind::FunctionDeclaration) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto Decl = static_cast<FunctionDeclaration*>(N);
|
auto Decl = static_cast<FunctionDeclaration*>(N);
|
||||||
forwardDeclareFunctionDeclaration(Decl, TVs, Constraints);
|
forwardDeclareFunctionDeclaration(Decl, TVs, Constraints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto Nodes: SCCs) {
|
for (auto Nodes: SCCs) {
|
||||||
for (auto N: Nodes) {
|
for (auto N: Nodes) {
|
||||||
|
if (N->getKind() != NodeKind::FunctionDeclaration) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto Decl = static_cast<FunctionDeclaration*>(N);
|
auto Decl = static_cast<FunctionDeclaration*>(N);
|
||||||
Decl->IsCycleActive = true;
|
Decl->IsCycleActive = true;
|
||||||
}
|
}
|
||||||
for (auto N: Nodes) {
|
for (auto N: Nodes) {
|
||||||
|
if (N->getKind() != NodeKind::FunctionDeclaration) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto Decl = static_cast<FunctionDeclaration*>(N);
|
auto Decl = static_cast<FunctionDeclaration*>(N);
|
||||||
inferFunctionDeclaration(Decl);
|
inferFunctionDeclaration(Decl);
|
||||||
}
|
}
|
||||||
for (auto N: Nodes) {
|
for (auto N: Nodes) {
|
||||||
|
if (N->getKind() != NodeKind::FunctionDeclaration) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
auto Decl = static_cast<FunctionDeclaration*>(N);
|
auto Decl = static_cast<FunctionDeclaration*>(N);
|
||||||
Decl->IsCycleActive = false;
|
Decl->IsCycleActive = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setContext(SF->Ctx);
|
setContext(SF->Ctx);
|
||||||
infer(SF);
|
infer(SF);
|
||||||
|
|
||||||
|
// Important because otherwise some logic for some optimisations will kick in that are no longer active.
|
||||||
|
ActiveContext = nullptr;
|
||||||
|
|
||||||
solve(new CMany(*SF->Ctx->Constraints));
|
solve(new CMany(*SF->Ctx->Constraints));
|
||||||
checkTypeclassSigs(SF);
|
checkTypeclassSigs(SF);
|
||||||
}
|
}
|
||||||
|
@ -1362,7 +1378,7 @@ namespace bolt {
|
||||||
|
|
||||||
case ConstraintKind::Equal:
|
case ConstraintKind::Equal:
|
||||||
{
|
{
|
||||||
solveCEqual(static_cast<CEqual*>(Constraint));
|
solveEqual(static_cast<CEqual*>(Constraint));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1391,81 +1407,6 @@ namespace bolt {
|
||||||
ZEN_UNREACHABLE
|
ZEN_UNREACHABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TypeclassContext> Checker::findInstanceContext(TCon* Ty, TypeclassId& Class) {
|
|
||||||
auto Match = InstanceMap.find(Class);
|
|
||||||
std::vector<TypeclassContext> S;
|
|
||||||
if (Match != InstanceMap.end()) {
|
|
||||||
for (auto Instance: Match->second) {
|
|
||||||
if (assignableTo(Ty, Instance->TypeExps[0]->getType())) {
|
|
||||||
std::vector<TypeclassContext> S;
|
|
||||||
// TODO handle TApp
|
|
||||||
// for (auto Arg: Ty->Args) {
|
|
||||||
// TypeclassContext Classes;
|
|
||||||
// // TODO
|
|
||||||
// S.push_back(Classes);
|
|
||||||
// }
|
|
||||||
return S;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DE.add<InstanceNotFoundDiagnostic>(Class, Ty, Source);
|
|
||||||
// TODO handle TApp
|
|
||||||
// for (auto Arg: Ty->Args) {
|
|
||||||
// S.push_back({});
|
|
||||||
// }
|
|
||||||
return S;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Checker::propagateClasses(std::unordered_set<TypeclassId>& Classes, Type* Ty) {
|
|
||||||
if (llvm::isa<TVar>(Ty)) {
|
|
||||||
auto TV = llvm::cast<TVar>(Ty);
|
|
||||||
for (auto Class: Classes) {
|
|
||||||
TV->Contexts.emplace(Class);
|
|
||||||
}
|
|
||||||
} else if (llvm::isa<TCon>(Ty)) {
|
|
||||||
for (auto Class: Classes) {
|
|
||||||
propagateClassTycon(Class, llvm::cast<TCon>(Ty));
|
|
||||||
}
|
|
||||||
} else if (!Classes.empty()) {
|
|
||||||
DE.add<InvalidTypeToTypeclassDiagnostic>(Ty, std::vector(Classes.begin(), Classes.end()), Source);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void Checker::propagateClassTycon(TypeclassId& Class, TCon* Ty) {
|
|
||||||
auto S = findInstanceContext(Ty, Class);
|
|
||||||
// TODO handle TApp
|
|
||||||
// for (auto [Classes, Arg]: zen::zip(S, Ty->Args)) {
|
|
||||||
// propagateClasses(Classes, Arg);
|
|
||||||
// }
|
|
||||||
};
|
|
||||||
|
|
||||||
void Checker::join(TVar* TV, Type* Ty) {
|
|
||||||
|
|
||||||
// std::cerr << describe(TV) << " => " << describe(Ty) << std::endl;
|
|
||||||
|
|
||||||
TV->set(Ty);
|
|
||||||
|
|
||||||
propagateClasses(TV->Contexts, Ty);
|
|
||||||
|
|
||||||
// This is a very specific adjustment that is critical to the
|
|
||||||
// well-functioning of the infer/unify algorithm. When addConstraint() is
|
|
||||||
// called, it may decide to solve the constraint immediately during
|
|
||||||
// inference. If this happens, a type variable might get assigned a concrete
|
|
||||||
// type such as Int. We therefore never want the variable to be polymorphic
|
|
||||||
// and be instantiated with a fresh variable, as that would allow Bool to
|
|
||||||
// collide with Int.
|
|
||||||
//
|
|
||||||
// Should it get assigned another unification variable, that's OK too
|
|
||||||
// because then that variable is what matters and it will become the new
|
|
||||||
// (possibly polymorphic) variable.
|
|
||||||
if (ActiveContext) {
|
|
||||||
// std::cerr << "erase " << describe(TV) << std::endl;
|
|
||||||
auto TVs = ActiveContext->TVs;
|
|
||||||
TVs->erase(TV);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ArrowCursor {
|
class ArrowCursor {
|
||||||
|
|
||||||
std::stack<std::tuple<TArrow*, bool>> Stack;
|
std::stack<std::tuple<TArrow*, bool>> Stack;
|
||||||
|
@ -1531,76 +1472,154 @@ namespace bolt {
|
||||||
return Constraint->Source;
|
return Constraint->Source;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unify(Type* A, Type* B);
|
bool unify(Type* A, Type* B, bool DidSwap);
|
||||||
|
|
||||||
bool unifyField(Type* A, Type* B);
|
bool unifyField(Type* A, Type* B);
|
||||||
|
|
||||||
bool unify() {
|
bool unify() {
|
||||||
return unify(Constraint->Left, Constraint->Right);
|
return unify(Constraint->Left, Constraint->Right, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<TypeclassContext> findInstanceContext(TCon* Ty, TypeclassId& Class) {
|
||||||
|
auto Match = C.InstanceMap.find(Class);
|
||||||
|
std::vector<TypeclassContext> S;
|
||||||
|
if (Match != C.InstanceMap.end()) {
|
||||||
|
for (auto Instance: Match->second) {
|
||||||
|
if (assignableTo(Ty, Instance->TypeExps[0]->getType())) {
|
||||||
|
std::vector<TypeclassContext> S;
|
||||||
|
// TODO handle TApp
|
||||||
|
// for (auto Arg: Ty->Args) {
|
||||||
|
// TypeclassContext Classes;
|
||||||
|
// // TODO
|
||||||
|
// S.push_back(Classes);
|
||||||
|
// }
|
||||||
|
return S;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
C.DE.add<InstanceNotFoundDiagnostic>(Class, Ty, getSource());
|
||||||
|
// TODO handle TApp
|
||||||
|
// for (auto Arg: Ty->Args) {
|
||||||
|
// S.push_back({});
|
||||||
|
// }
|
||||||
|
return S;
|
||||||
|
}
|
||||||
|
|
||||||
|
void propagateClasses(std::unordered_set<TypeclassId>& Classes, Type* Ty) {
|
||||||
|
if (llvm::isa<TVar>(Ty)) {
|
||||||
|
auto TV = llvm::cast<TVar>(Ty);
|
||||||
|
for (auto Class: Classes) {
|
||||||
|
TV->Contexts.emplace(Class);
|
||||||
|
}
|
||||||
|
} else if (llvm::isa<TCon>(Ty)) {
|
||||||
|
for (auto Class: Classes) {
|
||||||
|
propagateClassTycon(Class, llvm::cast<TCon>(Ty));
|
||||||
|
}
|
||||||
|
} else if (!Classes.empty()) {
|
||||||
|
C.DE.add<InvalidTypeToTypeclassDiagnostic>(Ty, std::vector(Classes.begin(), Classes.end()), getSource());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void propagateClassTycon(TypeclassId& Class, TCon* Ty) {
|
||||||
|
auto S = findInstanceContext(Ty, Class);
|
||||||
|
// TODO handle TApp
|
||||||
|
// for (auto [Classes, Arg]: zen::zip(S, Ty->Args)) {
|
||||||
|
// propagateClasses(Classes, Arg);
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign a type to a unification variable.
|
||||||
|
*
|
||||||
|
* If there are class constraints, those are propagated.
|
||||||
|
*
|
||||||
|
* If this type variable is solved during inference, it will be removed from
|
||||||
|
* the inference context.
|
||||||
|
*
|
||||||
|
* Other side effects may occur.
|
||||||
|
*/
|
||||||
|
void join(TVar* TV, Type* Ty) {
|
||||||
|
|
||||||
|
// std::cerr << describe(TV) << " => " << describe(Ty) << std::endl;
|
||||||
|
|
||||||
|
TV->set(Ty);
|
||||||
|
|
||||||
|
propagateClasses(TV->Contexts, Ty);
|
||||||
|
|
||||||
|
// This is a very specific adjustment that is critical to the
|
||||||
|
// well-functioning of the infer/unify algorithm. When addConstraint() is
|
||||||
|
// called, it may decide to solve the constraint immediately during
|
||||||
|
// inference. If this happens, a type variable might get assigned a concrete
|
||||||
|
// type such as Int. We therefore never want the variable to be polymorphic
|
||||||
|
// and be instantiated with a fresh variable, as that would allow Bool to
|
||||||
|
// collide with Int.
|
||||||
|
//
|
||||||
|
// Should it get assigned another unification variable, that's OK too
|
||||||
|
// because then that variable is what matters and it will become the new
|
||||||
|
// (possibly polymorphic) variable.
|
||||||
|
if (C.ActiveContext) {
|
||||||
|
// std::cerr << "erase " << describe(TV) << std::endl;
|
||||||
|
auto TVs = C.ActiveContext->TVs;
|
||||||
|
TVs->erase(TV);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class UnificationFrame {
|
bool Unifier::unify(Type* A, Type* B, bool DidSwap) {
|
||||||
|
|
||||||
Unifier& U;
|
A = C.simplifyType(A);
|
||||||
Type* A;
|
B = C.simplifyType(B);
|
||||||
Type* B;
|
|
||||||
bool DidSwap = false;
|
|
||||||
|
|
||||||
public:
|
auto unifyError = [&]() {
|
||||||
|
C.DE.add<UnificationErrorDiagnostic>(
|
||||||
UnificationFrame(Unifier& U, Type* A, Type* B):
|
C.simplifyType(Constraint->Left),
|
||||||
U(U), A(U.C.simplifyType(A)), B(U.C.simplifyType(B)) {}
|
C.simplifyType(Constraint->Right),
|
||||||
|
LeftPath,
|
||||||
void unifyError() {
|
RightPath,
|
||||||
U.C.DE.add<UnificationErrorDiagnostic>(
|
Constraint->Source
|
||||||
U.C.simplifyType(U.Constraint->Left),
|
|
||||||
U.C.simplifyType(U.Constraint->Right),
|
|
||||||
U.LeftPath,
|
|
||||||
U.RightPath,
|
|
||||||
U.Constraint->Source
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
void pushLeft(TypeIndex I) {
|
auto pushLeft = [&](TypeIndex I) {
|
||||||
if (DidSwap) {
|
if (DidSwap) {
|
||||||
U.RightPath.push_back(I);
|
RightPath.push_back(I);
|
||||||
} else {
|
} else {
|
||||||
U.LeftPath.push_back(I);
|
LeftPath.push_back(I);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
void popLeft() {
|
auto popLeft = [&]() {
|
||||||
if (DidSwap) {
|
if (DidSwap) {
|
||||||
U.RightPath.pop_back();
|
RightPath.pop_back();
|
||||||
} else {
|
} else {
|
||||||
U.LeftPath.pop_back();
|
LeftPath.pop_back();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
void pushRight(TypeIndex I) {
|
auto pushRight = [&](TypeIndex I) {
|
||||||
if (DidSwap) {
|
if (DidSwap) {
|
||||||
U.LeftPath.push_back(I);
|
LeftPath.push_back(I);
|
||||||
} else {
|
} else {
|
||||||
U.RightPath.push_back(I);
|
RightPath.push_back(I);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
void popRight() {
|
auto popRight = [&]() {
|
||||||
if (DidSwap) {
|
if (DidSwap) {
|
||||||
U.LeftPath.pop_back();
|
LeftPath.pop_back();
|
||||||
} else {
|
} else {
|
||||||
U.RightPath.pop_back();
|
RightPath.pop_back();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
void swap() {
|
auto swap = [&]() {
|
||||||
std::swap(A, B);
|
std::swap(A, B);
|
||||||
DidSwap = !DidSwap;
|
DidSwap = !DidSwap;
|
||||||
}
|
};
|
||||||
|
|
||||||
bool unifyField() {
|
auto unifyField = [&](Type* A, Type* B) {
|
||||||
if (llvm::isa<TAbsent>(A) && llvm::isa<TAbsent>(B)) {
|
if (llvm::isa<TAbsent>(A) && llvm::isa<TAbsent>(B)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1609,251 +1628,237 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
if (llvm::isa<TAbsent>(A)) {
|
if (llvm::isa<TAbsent>(A)) {
|
||||||
auto Present = static_cast<TPresent*>(B);
|
auto Present = static_cast<TPresent*>(B);
|
||||||
U.C.DE.add<FieldNotFoundDiagnostic>(U.CurrentFieldName, U.C.simplifyType(U.getLeft()), U.LeftPath, U.getSource());
|
C.DE.add<FieldNotFoundDiagnostic>(CurrentFieldName, C.simplifyType(getLeft()), LeftPath, getSource());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto Present1 = static_cast<TPresent*>(A);
|
auto Present1 = static_cast<TPresent*>(A);
|
||||||
auto Present2 = static_cast<TPresent*>(B);
|
auto Present2 = static_cast<TPresent*>(B);
|
||||||
return U.unify(Present1->Ty, Present2->Ty);
|
return unify(Present1->Ty, Present2->Ty, DidSwap);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (llvm::isa<TVar>(A) && llvm::isa<TVar>(B)) {
|
||||||
|
auto Var1 = static_cast<TVar*>(A);
|
||||||
|
auto Var2 = static_cast<TVar*>(B);
|
||||||
|
if (Var1->getVarKind() == VarKind::Rigid && Var2->getVarKind() == VarKind::Rigid) {
|
||||||
|
if (Var1->Id != Var2->Id) {
|
||||||
|
unifyError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
TVar* To;
|
||||||
|
TVar* From;
|
||||||
|
if (Var1->getVarKind() == VarKind::Rigid && Var2->getVarKind() == VarKind::Unification) {
|
||||||
|
To = Var1;
|
||||||
|
From = Var2;
|
||||||
|
} else {
|
||||||
|
// Only cases left are Var1 = Unification, Var2 = Rigid and Var1 = Unification, Var2 = Unification
|
||||||
|
// Either way, Var1, being Unification, is a good candidate for being unified away
|
||||||
|
To = Var2;
|
||||||
|
From = Var1;
|
||||||
|
}
|
||||||
|
if (From->Id != To->Id) {
|
||||||
|
join(From, To);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unify() {
|
if (llvm::isa<TVar>(B)) {
|
||||||
|
swap();
|
||||||
if (llvm::isa<TVar>(A) && llvm::isa<TVar>(B)) {
|
|
||||||
auto Var1 = static_cast<TVar*>(A);
|
|
||||||
auto Var2 = static_cast<TVar*>(B);
|
|
||||||
if (Var1->getVarKind() == VarKind::Rigid && Var2->getVarKind() == VarKind::Rigid) {
|
|
||||||
if (Var1->Id != Var2->Id) {
|
|
||||||
unifyError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
TVar* To;
|
|
||||||
TVar* From;
|
|
||||||
if (Var1->getVarKind() == VarKind::Rigid && Var2->getVarKind() == VarKind::Unification) {
|
|
||||||
To = Var1;
|
|
||||||
From = Var2;
|
|
||||||
} else {
|
|
||||||
// Only cases left are Var1 = Unification, Var2 = Rigid and Var1 = Unification, Var2 = Unification
|
|
||||||
// Either way, Var1, being Unification, is a good candidate for being unified away
|
|
||||||
To = Var2;
|
|
||||||
From = Var1;
|
|
||||||
}
|
|
||||||
if (From->Id != To->Id) {
|
|
||||||
U.C.join(From, To);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TVar>(B)) {
|
|
||||||
swap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TVar>(A)) {
|
|
||||||
|
|
||||||
auto TV = static_cast<TVar*>(A);
|
|
||||||
|
|
||||||
// Rigid type variables can never unify with antything else than what we
|
|
||||||
// have already handled in the previous if-statement, so issue an error.
|
|
||||||
if (TV->getVarKind() == VarKind::Rigid) {
|
|
||||||
unifyError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Occurs check
|
|
||||||
if (B->hasTypeVar(TV)) {
|
|
||||||
// NOTE Just like GHC, we just display an error message indicating that
|
|
||||||
// A cannot match B, e.g. a cannot match [a]. It looks much better
|
|
||||||
// than obsure references to an occurs check
|
|
||||||
unifyError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
U.C.join(TV, B);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TArrow>(A) && llvm::isa<TArrow>(B)) {
|
|
||||||
auto C1 = ArrowCursor(static_cast<TArrow*>(A), DidSwap ? U.RightPath : U.LeftPath);
|
|
||||||
auto C2 = ArrowCursor(static_cast<TArrow*>(B), DidSwap ? U.LeftPath : U.RightPath);
|
|
||||||
bool Success = true;
|
|
||||||
for (;;) {
|
|
||||||
auto T1 = C1.next();
|
|
||||||
auto T2 = C2.next();
|
|
||||||
if (T1 == nullptr && T2 == nullptr) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (T1 == nullptr || T2 == nullptr) {
|
|
||||||
unifyError();
|
|
||||||
Success = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!U.unify(T1, T2)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Success;
|
|
||||||
/* if (Arr1->ParamTypes.size() != Arr2->ParamTypes.size()) { */
|
|
||||||
/* return false; */
|
|
||||||
/* } */
|
|
||||||
/* auto Count = Arr1->ParamTypes.size(); */
|
|
||||||
/* for (std::size_t I = 0; I < Count; I++) { */
|
|
||||||
/* if (!unify(Arr1->ParamTypes[I], Arr2->ParamTypes[I], Solution)) { */
|
|
||||||
/* return false; */
|
|
||||||
/* } */
|
|
||||||
/* } */
|
|
||||||
/* return unify(Arr1->ReturnType, Arr2->ReturnType, Solution); */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TApp>(A) && llvm::isa<TApp>(B)) {
|
|
||||||
auto App1 = static_cast<TApp*>(A);
|
|
||||||
auto App2 = static_cast<TApp*>(B);
|
|
||||||
bool Success = true;
|
|
||||||
if (!U.unify(App1->Op, App2->Op)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
if (!U.unify(App1->Arg, App2->Arg)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
return Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TArrow>(B)) {
|
|
||||||
swap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TArrow>(A)) {
|
|
||||||
auto Arr = static_cast<TArrow*>(A);
|
|
||||||
if (Arr->ParamTypes.empty()) {
|
|
||||||
auto Success = U.unify(Arr->ReturnType, B);
|
|
||||||
return Success;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TTuple>(A) && llvm::isa<TTuple>(B)) {
|
|
||||||
auto Tuple1 = static_cast<TTuple*>(A);
|
|
||||||
auto Tuple2 = static_cast<TTuple*>(B);
|
|
||||||
if (Tuple1->ElementTypes.size() != Tuple2->ElementTypes.size()) {
|
|
||||||
unifyError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto Count = Tuple1->ElementTypes.size();
|
|
||||||
bool Success = true;
|
|
||||||
for (size_t I = 0; I < Count; I++) {
|
|
||||||
U.LeftPath.push_back(TypeIndex::forTupleElement(I));
|
|
||||||
U.RightPath.push_back(TypeIndex::forTupleElement(I));
|
|
||||||
if (!U.unify(Tuple1->ElementTypes[I], Tuple2->ElementTypes[I])) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
U.LeftPath.pop_back();
|
|
||||||
U.RightPath.pop_back();
|
|
||||||
}
|
|
||||||
return Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TTupleIndex>(A) || llvm::isa<TTupleIndex>(B)) {
|
|
||||||
// Type(s) could not be simplified at the beginning of this function,
|
|
||||||
// so we have to re-visit the constraint when there is more information.
|
|
||||||
U.C.Queue.push_back(U.Constraint);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (llvm::isa<TTupleIndex>(A) && llvm::isa<TTupleIndex>(B)) {
|
|
||||||
// auto Index1 = static_cast<TTupleIndex*>(A);
|
|
||||||
// auto Index2 = static_cast<TTupleIndex*>(B);
|
|
||||||
// return unify(Index1->Ty, Index2->Ty, Source);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (llvm::isa<TCon>(A) && llvm::isa<TCon>(B)) {
|
|
||||||
auto Con1 = static_cast<TCon*>(A);
|
|
||||||
auto Con2 = static_cast<TCon*>(B);
|
|
||||||
if (Con1->Id != Con2->Id) {
|
|
||||||
unifyError();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TNil>(A) && llvm::isa<TNil>(B)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TField>(A) && llvm::isa<TField>(B)) {
|
|
||||||
auto Field1 = static_cast<TField*>(A);
|
|
||||||
auto Field2 = static_cast<TField*>(B);
|
|
||||||
bool Success = true;
|
|
||||||
if (Field1->Name == Field2->Name) {
|
|
||||||
U.LeftPath.push_back(TypeIndex::forFieldType());
|
|
||||||
U.RightPath.push_back(TypeIndex::forFieldType());
|
|
||||||
U.CurrentFieldName = Field1->Name;
|
|
||||||
if (!U.unifyField(Field1->Ty, Field2->Ty)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
U.LeftPath.pop_back();
|
|
||||||
U.RightPath.pop_back();
|
|
||||||
U.LeftPath.push_back(TypeIndex::forFieldRest());
|
|
||||||
U.RightPath.push_back(TypeIndex::forFieldRest());
|
|
||||||
if (!U.unify(Field1->RestTy, Field2->RestTy)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
U.LeftPath.pop_back();
|
|
||||||
U.RightPath.pop_back();
|
|
||||||
return Success;
|
|
||||||
}
|
|
||||||
auto NewRestTy = new TVar(U.C.NextTypeVarId++, VarKind::Unification);
|
|
||||||
pushLeft(TypeIndex::forFieldRest());
|
|
||||||
if (!U.unify(Field1->RestTy, new TField(Field2->Name, Field2->Ty, NewRestTy))) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
popLeft();
|
|
||||||
pushRight(TypeIndex::forFieldRest());
|
|
||||||
if (!U.unify(new TField(Field1->Name, Field1->Ty, NewRestTy), Field2->RestTy)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
popRight();
|
|
||||||
return Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TNil>(A) && llvm::isa<TField>(B)) {
|
|
||||||
swap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (llvm::isa<TField>(A) && llvm::isa<TNil>(B)) {
|
|
||||||
auto Field = static_cast<TField*>(A);
|
|
||||||
bool Success = true;
|
|
||||||
pushLeft(TypeIndex::forFieldType());
|
|
||||||
U.CurrentFieldName = Field->Name;
|
|
||||||
if (!U.unifyField(Field->Ty, new TAbsent)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
popLeft();
|
|
||||||
pushLeft(TypeIndex::forFieldRest());
|
|
||||||
if (!U.unify(Field->RestTy, B)) {
|
|
||||||
Success = false;
|
|
||||||
}
|
|
||||||
popLeft();
|
|
||||||
return Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
unifyError();
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
if (llvm::isa<TVar>(A)) {
|
||||||
|
|
||||||
bool Unifier::unify(Type* A, Type* B) {
|
auto TV = static_cast<TVar*>(A);
|
||||||
UnificationFrame Frame { *this, A, B };
|
|
||||||
return Frame.unify();
|
// Rigid type variables can never unify with antything else than what we
|
||||||
|
// have already handled in the previous if-statement, so issue an error.
|
||||||
|
if (TV->getVarKind() == VarKind::Rigid) {
|
||||||
|
unifyError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Occurs check
|
||||||
|
if (B->hasTypeVar(TV)) {
|
||||||
|
// NOTE Just like GHC, we just display an error message indicating that
|
||||||
|
// A cannot match B, e.g. a cannot match [a]. It looks much better
|
||||||
|
// than obsure references to an occurs check
|
||||||
|
unifyError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
join(TV, B);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TArrow>(A) && llvm::isa<TArrow>(B)) {
|
||||||
|
auto C1 = ArrowCursor(static_cast<TArrow*>(A), DidSwap ? RightPath : LeftPath);
|
||||||
|
auto C2 = ArrowCursor(static_cast<TArrow*>(B), DidSwap ? LeftPath : RightPath);
|
||||||
|
bool Success = true;
|
||||||
|
for (;;) {
|
||||||
|
auto T1 = C1.next();
|
||||||
|
auto T2 = C2.next();
|
||||||
|
if (T1 == nullptr && T2 == nullptr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (T1 == nullptr || T2 == nullptr) {
|
||||||
|
unifyError();
|
||||||
|
Success = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!unify(T1, T2, DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Success;
|
||||||
|
/* if (Arr1->ParamTypes.size() != Arr2->ParamTypes.size()) { */
|
||||||
|
/* return false; */
|
||||||
|
/* } */
|
||||||
|
/* auto Count = Arr1->ParamTypes.size(); */
|
||||||
|
/* for (std::size_t I = 0; I < Count; I++) { */
|
||||||
|
/* if (!unify(Arr1->ParamTypes[I], Arr2->ParamTypes[I], Solution)) { */
|
||||||
|
/* return false; */
|
||||||
|
/* } */
|
||||||
|
/* } */
|
||||||
|
/* return unify(Arr1->ReturnType, Arr2->ReturnType, Solution); */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TApp>(A) && llvm::isa<TApp>(B)) {
|
||||||
|
auto App1 = static_cast<TApp*>(A);
|
||||||
|
auto App2 = static_cast<TApp*>(B);
|
||||||
|
bool Success = true;
|
||||||
|
if (!unify(App1->Op, App2->Op, DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
if (!unify(App1->Arg, App2->Arg, DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TArrow>(B)) {
|
||||||
|
swap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TArrow>(A)) {
|
||||||
|
auto Arr = static_cast<TArrow*>(A);
|
||||||
|
if (Arr->ParamTypes.empty()) {
|
||||||
|
auto Success = unify(Arr->ReturnType, B, DidSwap);
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TTuple>(A) && llvm::isa<TTuple>(B)) {
|
||||||
|
auto Tuple1 = static_cast<TTuple*>(A);
|
||||||
|
auto Tuple2 = static_cast<TTuple*>(B);
|
||||||
|
if (Tuple1->ElementTypes.size() != Tuple2->ElementTypes.size()) {
|
||||||
|
unifyError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto Count = Tuple1->ElementTypes.size();
|
||||||
|
bool Success = true;
|
||||||
|
for (size_t I = 0; I < Count; I++) {
|
||||||
|
LeftPath.push_back(TypeIndex::forTupleElement(I));
|
||||||
|
RightPath.push_back(TypeIndex::forTupleElement(I));
|
||||||
|
if (!unify(Tuple1->ElementTypes[I], Tuple2->ElementTypes[I], DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
LeftPath.pop_back();
|
||||||
|
RightPath.pop_back();
|
||||||
|
}
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TTupleIndex>(A) || llvm::isa<TTupleIndex>(B)) {
|
||||||
|
// Type(s) could not be simplified at the beginning of this function,
|
||||||
|
// so we have to re-visit the constraint when there is more information.
|
||||||
|
C.Queue.push_back(Constraint);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (llvm::isa<TTupleIndex>(A) && llvm::isa<TTupleIndex>(B)) {
|
||||||
|
// auto Index1 = static_cast<TTupleIndex*>(A);
|
||||||
|
// auto Index2 = static_cast<TTupleIndex*>(B);
|
||||||
|
// return unify(Index1->Ty, Index2->Ty, Source);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (llvm::isa<TCon>(A) && llvm::isa<TCon>(B)) {
|
||||||
|
auto Con1 = static_cast<TCon*>(A);
|
||||||
|
auto Con2 = static_cast<TCon*>(B);
|
||||||
|
if (Con1->Id != Con2->Id) {
|
||||||
|
unifyError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TNil>(A) && llvm::isa<TNil>(B)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TField>(A) && llvm::isa<TField>(B)) {
|
||||||
|
auto Field1 = static_cast<TField*>(A);
|
||||||
|
auto Field2 = static_cast<TField*>(B);
|
||||||
|
bool Success = true;
|
||||||
|
if (Field1->Name == Field2->Name) {
|
||||||
|
LeftPath.push_back(TypeIndex::forFieldType());
|
||||||
|
RightPath.push_back(TypeIndex::forFieldType());
|
||||||
|
CurrentFieldName = Field1->Name;
|
||||||
|
if (!unifyField(Field1->Ty, Field2->Ty)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
LeftPath.pop_back();
|
||||||
|
RightPath.pop_back();
|
||||||
|
LeftPath.push_back(TypeIndex::forFieldRest());
|
||||||
|
RightPath.push_back(TypeIndex::forFieldRest());
|
||||||
|
if (!unify(Field1->RestTy, Field2->RestTy, DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
LeftPath.pop_back();
|
||||||
|
RightPath.pop_back();
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
auto NewRestTy = new TVar(C.NextTypeVarId++, VarKind::Unification);
|
||||||
|
pushLeft(TypeIndex::forFieldRest());
|
||||||
|
if (!unify(Field1->RestTy, new TField(Field2->Name, Field2->Ty, NewRestTy), DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
popLeft();
|
||||||
|
pushRight(TypeIndex::forFieldRest());
|
||||||
|
if (!unify(new TField(Field1->Name, Field1->Ty, NewRestTy), Field2->RestTy, DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
popRight();
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TNil>(A) && llvm::isa<TField>(B)) {
|
||||||
|
swap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TField>(A) && llvm::isa<TNil>(B)) {
|
||||||
|
auto Field = static_cast<TField*>(A);
|
||||||
|
bool Success = true;
|
||||||
|
pushLeft(TypeIndex::forFieldType());
|
||||||
|
CurrentFieldName = Field->Name;
|
||||||
|
if (!unifyField(Field->Ty, new TAbsent)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
popLeft();
|
||||||
|
pushLeft(TypeIndex::forFieldRest());
|
||||||
|
if (!unify(Field->RestTy, B, DidSwap)) {
|
||||||
|
Success = false;
|
||||||
|
}
|
||||||
|
popLeft();
|
||||||
|
return Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
unifyError();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Unifier::unifyField(Type* A, Type* B) {
|
void Checker::solveEqual(CEqual* C) {
|
||||||
UnificationFrame Frame { *this, A, B };
|
|
||||||
return Frame.unifyField();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Checker::solveCEqual(CEqual* C) {
|
|
||||||
// std::cerr << describe(C->Left) << " ~ " << describe(C->Right) << std::endl;
|
// std::cerr << describe(C->Left) << " ~ " << describe(C->Right) << std::endl;
|
||||||
Unifier A { *this, C };
|
Unifier A { *this, C };
|
||||||
A.unify();
|
A.unify();
|
||||||
|
|
Loading…
Reference in a new issue