Fix variable declarations, fix regression due to eager solving, fix unification
This commit is contained in:
parent
302823ac9b
commit
b23dc84f72
3 changed files with 105 additions and 64 deletions
|
@ -11,6 +11,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
namespace bolt {
|
namespace bolt {
|
||||||
|
|
||||||
|
@ -207,6 +208,16 @@ namespace bolt {
|
||||||
|
|
||||||
TVSub Solution;
|
TVSub Solution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The queue that is used during solving to store any unsolved constraints.
|
||||||
|
*/
|
||||||
|
std::deque<class Constraint*> Queue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointer to the current constraint being unified.
|
||||||
|
*/
|
||||||
|
CEqual* C;
|
||||||
|
|
||||||
std::vector<InferContext*> Contexts;
|
std::vector<InferContext*> Contexts;
|
||||||
|
|
||||||
InferContext& getContext();
|
InferContext& getContext();
|
||||||
|
@ -265,6 +276,8 @@ namespace bolt {
|
||||||
|
|
||||||
Type* simplify(Type* Ty);
|
Type* simplify(Type* Ty);
|
||||||
|
|
||||||
|
Type* find(Type* Ty);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign a type to a unification variable.
|
* Assign a type to a unification variable.
|
||||||
*
|
*
|
||||||
|
|
154
src/Checker.cc
154
src/Checker.cc
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
// TODO Fix TVSub to use TVar.Id instead of the pointer address
|
// TODO Fix TVSub to use TVar.Id instead of the pointer address
|
||||||
|
|
||||||
|
// Optimise constraint solving by solving some constraints during inference
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
@ -124,32 +126,33 @@ namespace bolt {
|
||||||
case ConstraintKind::Equal:
|
case ConstraintKind::Equal:
|
||||||
{
|
{
|
||||||
auto Y = static_cast<CEqual*>(C);
|
auto Y = static_cast<CEqual*>(C);
|
||||||
std::size_t MaxLevel = 0;
|
|
||||||
for (std::size_t I = Contexts.size(); I-- > 0; ) {
|
// std::size_t MaxLevel = 0;
|
||||||
auto Ctx = Contexts[I];
|
// for (std::size_t I = Contexts.size(); I-- > 0; ) {
|
||||||
if (hasTypeVar(*Ctx->TVs, Y->Left) || hasTypeVar(*Ctx->TVs, Y->Right)) {
|
// auto Ctx = Contexts[I];
|
||||||
MaxLevel = I;
|
// if (hasTypeVar(*Ctx->TVs, Y->Left) || hasTypeVar(*Ctx->TVs, Y->Right)) {
|
||||||
break;
|
// MaxLevel = I;
|
||||||
}
|
// break;
|
||||||
}
|
// }
|
||||||
std::size_t MinLevel = MaxLevel;
|
// }
|
||||||
for (std::size_t I = 0; I < Contexts.size(); I++) {
|
|
||||||
auto Ctx = Contexts[I];
|
// std::size_t MinLevel = MaxLevel;
|
||||||
if (hasTypeVar(*Ctx->TVs, Y->Left) || hasTypeVar(*Ctx->TVs, Y->Right)) {
|
// for (std::size_t I = 0; I < Contexts.size(); I++) {
|
||||||
MinLevel = I;
|
// auto Ctx = Contexts[I];
|
||||||
break;
|
// if (hasTypeVar(*Ctx->TVs, Y->Left) || hasTypeVar(*Ctx->TVs, Y->Right)) {
|
||||||
}
|
// MinLevel = I;
|
||||||
}
|
// break;
|
||||||
if (MaxLevel == MinLevel) {
|
// }
|
||||||
solveCEqual(Y);
|
// }
|
||||||
} else {
|
|
||||||
Contexts[MaxLevel]->Constraints->push_back(C);
|
// // TODO detect if MaxLevelLeft == 0 or MaxLevelRight == 0
|
||||||
}
|
// if (MaxLevel == MinLevel) {
|
||||||
// Contexts.front()->Constraints->push_back(C);
|
// solveCEqual(Y);
|
||||||
//auto I = std::max(Y->Left->MaxDepth, Y->Right->MaxDepth);
|
// } else {
|
||||||
//ZEN_ASSERT(I < Contexts.size());
|
// Contexts[MaxLevel]->Constraints->push_back(C);
|
||||||
//auto Ctx = Contexts[I];
|
// }
|
||||||
//Ctx->Constraints.push_back(Constraint);
|
|
||||||
|
Contexts.back()->Constraints->push_back(C);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ConstraintKind::Many:
|
case ConstraintKind::Many:
|
||||||
|
@ -232,29 +235,31 @@ namespace bolt {
|
||||||
{
|
{
|
||||||
auto Let = static_cast<LetDeclaration*>(X);
|
auto Let = static_cast<LetDeclaration*>(X);
|
||||||
|
|
||||||
auto NewCtx = createInferContext();
|
if (!Let->Params.empty()) {
|
||||||
Let->Ctx = NewCtx;
|
|
||||||
|
|
||||||
Contexts.push_back(NewCtx);
|
Let->Ctx = createInferContext();
|
||||||
|
Contexts.push_back(Let->Ctx);
|
||||||
|
|
||||||
// If declaring a let-declaration inside a type class declaration,
|
// If declaring a let-declaration inside a type class declaration,
|
||||||
// we need to mark that the let-declaration requires this class.
|
// we need to mark that the let-declaration requires this class.
|
||||||
// This marking is set on the rigid type variables of the class, which
|
// This marking is set on the rigid type variables of the class, which
|
||||||
// are then added to this local type environment.
|
// are then added to this local type environment.
|
||||||
if (llvm::isa<ClassDeclaration>(Let->Parent)) {
|
if (llvm::isa<ClassDeclaration>(Let->Parent)) {
|
||||||
auto Decl = static_cast<ClassDeclaration*>(Let->Parent);
|
auto Decl = static_cast<ClassDeclaration*>(Let->Parent);
|
||||||
for (auto TE: Decl->TypeVars) {
|
for (auto TE: Decl->TypeVars) {
|
||||||
auto TV = llvm::cast<TVar>(TE->getType());
|
auto TV = llvm::cast<TVar>(TE->getType());
|
||||||
NewCtx->Env.emplace(TE->Name->getCanonicalText(), new Forall(TV));
|
Let->Ctx->Env.emplace(TE->Name->getCanonicalText(), new Forall(TV));
|
||||||
NewCtx->TVs->emplace(TV);
|
Let->Ctx->TVs->emplace(TV);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we infer the primary type of the let declaration. If there's a
|
// 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.
|
// type assert, that assert should be authoritative so we use that.
|
||||||
// Otherwise, the type is not further specified and we create a new
|
// Otherwise, the type is not further specified and we create a new
|
||||||
// unification variable.
|
// unification variable.
|
||||||
Type *Ty;
|
Type* Ty;
|
||||||
if (Let->TypeAssert) {
|
if (Let->TypeAssert) {
|
||||||
Ty = inferTypeExpression(Let->TypeAssert->TypeExpression);
|
Ty = inferTypeExpression(Let->TypeAssert->TypeExpression);
|
||||||
} else {
|
} else {
|
||||||
|
@ -276,7 +281,7 @@ namespace bolt {
|
||||||
std::vector<TVar *> Params;
|
std::vector<TVar *> Params;
|
||||||
for (auto TE: Class->TypeVars) {
|
for (auto TE: Class->TypeVars) {
|
||||||
auto TV = createTypeVar();
|
auto TV = createTypeVar();
|
||||||
NewCtx->Env.emplace(TE->Name->getCanonicalText(), new Forall(TV));
|
Let->Ctx->Env.emplace(TE->Name->getCanonicalText(), new Forall(TV));
|
||||||
Params.push_back(TV);
|
Params.push_back(TV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,7 +309,9 @@ namespace bolt {
|
||||||
case NodeKind::LetBlockBody:
|
case NodeKind::LetBlockBody:
|
||||||
{
|
{
|
||||||
auto Block = static_cast<LetBlockBody*>(Let->Body);
|
auto Block = static_cast<LetBlockBody*>(Let->Body);
|
||||||
NewCtx->ReturnType = createTypeVar();
|
if (!Let->Params.empty()) {
|
||||||
|
Let->Ctx->ReturnType = createTypeVar();
|
||||||
|
}
|
||||||
for (auto Element: Block->Elements) {
|
for (auto Element: Block->Elements) {
|
||||||
forwardDeclare(Element);
|
forwardDeclare(Element);
|
||||||
}
|
}
|
||||||
|
@ -315,9 +322,15 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Contexts.pop_back();
|
if (!Let->Params.empty()) {
|
||||||
|
Contexts.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
inferBindings(Let->Pattern, Ty, NewCtx->Constraints, NewCtx->TVs);
|
if (Let->Params.empty()) {
|
||||||
|
inferBindings(Let->Pattern, Ty);
|
||||||
|
} else {
|
||||||
|
inferBindings(Let->Pattern, Ty, Let->Ctx->Constraints, Let->Ctx->TVs);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -380,8 +393,9 @@ namespace bolt {
|
||||||
{
|
{
|
||||||
auto Decl = static_cast<LetDeclaration*>(N);
|
auto Decl = static_cast<LetDeclaration*>(N);
|
||||||
|
|
||||||
auto NewCtx = Decl->Ctx;
|
if (!Decl->Params.empty()) {
|
||||||
Contexts.push_back(NewCtx);
|
Contexts.push_back(Decl->Ctx);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Type*> ParamTypes;
|
std::vector<Type*> ParamTypes;
|
||||||
Type* RetType;
|
Type* RetType;
|
||||||
|
@ -425,7 +439,9 @@ namespace bolt {
|
||||||
addConstraint(new CEqual { Decl->Ty, new TArrow(ParamTypes, RetType), N });
|
addConstraint(new CEqual { Decl->Ty, new TArrow(ParamTypes, RetType), N });
|
||||||
}
|
}
|
||||||
|
|
||||||
Contexts.pop_back();
|
if (!Decl->Params.empty()) {
|
||||||
|
Contexts.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -783,6 +799,11 @@ namespace bolt {
|
||||||
|
|
||||||
void visitLetDeclaration(LetDeclaration* Decl) {
|
void visitLetDeclaration(LetDeclaration* Decl) {
|
||||||
|
|
||||||
|
// Only inspect those let-declarations that look like a function
|
||||||
|
if (Decl->Params.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Will contain the type classes that were specified in the type assertion by the user.
|
// Will contain the type classes that were specified in the type assertion by the user.
|
||||||
// There might be some other signatures as well, but those are an implementation detail.
|
// There might be some other signatures as well, but those are an implementation detail.
|
||||||
std::vector<TypeclassSignature> Expected;
|
std::vector<TypeclassSignature> Expected;
|
||||||
|
@ -923,14 +944,12 @@ namespace bolt {
|
||||||
|
|
||||||
void Checker::solve(Constraint* Constraint, TVSub& Solution) {
|
void Checker::solve(Constraint* Constraint, TVSub& Solution) {
|
||||||
|
|
||||||
std::stack<class Constraint*> Queue;
|
Queue.push_back(Constraint);
|
||||||
Queue.push(Constraint);
|
|
||||||
|
|
||||||
while (!Queue.empty()) {
|
while (!Queue.empty()) {
|
||||||
|
|
||||||
auto Constraint = Queue.top();
|
auto Constraint = Queue.front();
|
||||||
|
Queue.pop_front();
|
||||||
Queue.pop();
|
|
||||||
|
|
||||||
switch (Constraint->getKind()) {
|
switch (Constraint->getKind()) {
|
||||||
|
|
||||||
|
@ -947,7 +966,7 @@ namespace bolt {
|
||||||
{
|
{
|
||||||
auto Many = static_cast<CMany*>(Constraint);
|
auto Many = static_cast<CMany*>(Constraint);
|
||||||
for (auto Constraint: Many->Elements) {
|
for (auto Constraint: Many->Elements) {
|
||||||
Queue.push(Constraint);
|
Queue.push_back(Constraint);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1028,18 +1047,16 @@ namespace bolt {
|
||||||
};
|
};
|
||||||
|
|
||||||
void Checker::solveCEqual(CEqual* C) {
|
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;
|
||||||
OrigLeft = C->Left;
|
OrigLeft = C->Left;
|
||||||
OrigRight = C->Right;
|
OrigRight = C->Right;
|
||||||
Source = C->Source;
|
Source = C->Source;
|
||||||
unify(C->Left, C->Right);
|
unify(C->Left, C->Right);
|
||||||
LeftPath = {};
|
LeftPath = {};
|
||||||
RightPath = {};
|
RightPath = {};
|
||||||
/* DE.add<UnificationErrorDiagnostic>(simplify(C->Left), simplify(C->Right), C->Source); */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Type* Checker::simplify(Type* Ty) {
|
Type* Checker::find(Type* Ty) {
|
||||||
|
|
||||||
while (Ty->getKind() == TypeKind::Var) {
|
while (Ty->getKind() == TypeKind::Var) {
|
||||||
auto Match = Solution.find(static_cast<TVar*>(Ty));
|
auto Match = Solution.find(static_cast<TVar*>(Ty));
|
||||||
if (Match == Solution.end()) {
|
if (Match == Solution.end()) {
|
||||||
|
@ -1047,6 +1064,12 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
Ty = Match->second;
|
Ty = Match->second;
|
||||||
}
|
}
|
||||||
|
return Ty;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type* Checker::simplify(Type* Ty) {
|
||||||
|
|
||||||
|
Ty = find(Ty);
|
||||||
|
|
||||||
switch (Ty->getKind()) {
|
switch (Ty->getKind()) {
|
||||||
|
|
||||||
|
@ -1136,11 +1159,11 @@ namespace bolt {
|
||||||
// type such as Int. We therefore never want the variable to be polymorphic
|
// type such as Int. We therefore never want the variable to be polymorphic
|
||||||
// and be instantiated with a fresh variable, as it has already been solved.
|
// and be instantiated with a fresh variable, as it has already been solved.
|
||||||
// Should it get assigned another unification variable, that's OK too
|
// Should it get assigned another unification variable, that's OK too
|
||||||
// because then the context of that variable is what matters and not anymore
|
// because then the inference context of that variable is what matters and
|
||||||
// the context of this one.
|
// not anymore the context of this one.
|
||||||
if (!Contexts.empty()) {
|
// if (!Contexts.empty()) {
|
||||||
Contexts.back()->TVs->erase(TV);
|
// Contexts.back()->TVs->erase(TV);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1197,8 +1220,6 @@ namespace bolt {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool Checker::unify(Type* A, Type* B) {
|
bool Checker::unify(Type* A, Type* B) {
|
||||||
|
|
||||||
A = simplify(A);
|
A = simplify(A);
|
||||||
|
@ -1322,6 +1343,11 @@ namespace bolt {
|
||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (llvm::isa<TTupleIndex>(A) || llvm::isa<TTupleIndex>(B)) {
|
||||||
|
Queue.push_back(C);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// if (llvm::isa<TTupleIndex>(A) && llvm::isa<TTupleIndex>(B)) {
|
// if (llvm::isa<TTupleIndex>(A) && llvm::isa<TTupleIndex>(B)) {
|
||||||
// auto Index1 = static_cast<TTupleIndex*>(A);
|
// auto Index1 = static_cast<TTupleIndex*>(A);
|
||||||
// auto Index2 = static_cast<TTupleIndex*>(B);
|
// auto Index2 = static_cast<TTupleIndex*>(B);
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
|
||||||
|
// FIXME writeExcerpt does not work well with the last line in a file
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue