Add again generalization in Checker.cc
Constraint instantiation is still missing.
This commit is contained in:
parent
fc24bb9091
commit
b1d685bdaf
7 changed files with 111 additions and 25 deletions
|
@ -49,6 +49,7 @@ add_library(
|
||||||
src/Scanner.cc
|
src/Scanner.cc
|
||||||
src/Parser.cc
|
src/Parser.cc
|
||||||
src/Type.cc
|
src/Type.cc
|
||||||
|
src/Constraint.cc
|
||||||
src/Checker.cc
|
src/Checker.cc
|
||||||
src/Evaluator.cc
|
src/Evaluator.cc
|
||||||
src/Scope.cc
|
src/Scope.cc
|
||||||
|
|
|
@ -31,6 +31,8 @@ public:
|
||||||
|
|
||||||
bool hasVar(TVar* TV) const;
|
bool hasVar(TVar* TV) const;
|
||||||
|
|
||||||
|
void dump() const;
|
||||||
|
|
||||||
TypeScheme* lookup(ByteString Name, SymbolKind Kind);
|
TypeScheme* lookup(ByteString Name, SymbolKind Kind);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -46,6 +48,8 @@ class Checker {
|
||||||
Type* StringType;
|
Type* StringType;
|
||||||
Type* UnitType;
|
Type* UnitType;
|
||||||
|
|
||||||
|
unsigned NextVarId = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Checker(DiagnosticEngine& DE);
|
Checker(DiagnosticEngine& DE);
|
||||||
|
@ -67,7 +71,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
TVar* createTVar() {
|
TVar* createTVar() {
|
||||||
return new TVar();
|
return new TVar("a" + std::to_string(NextVarId++));
|
||||||
}
|
}
|
||||||
|
|
||||||
Type* instantiate(TypeScheme* Scm);
|
Type* instantiate(TypeScheme* Scm);
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
namespace bolt {
|
namespace bolt {
|
||||||
|
|
||||||
|
class Node;
|
||||||
|
|
||||||
enum class ConstraintKind {
|
enum class ConstraintKind {
|
||||||
TypesEqual,
|
TypesEqual,
|
||||||
};
|
};
|
||||||
|
@ -24,6 +26,8 @@ public:
|
||||||
return Kind;
|
return Kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CTypesEqual : public Constraint {
|
class CTypesEqual : public Constraint {
|
||||||
|
@ -49,6 +53,8 @@ public:
|
||||||
return Origin;
|
return Origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
namespace bolt {
|
namespace bolt {
|
||||||
|
|
||||||
|
class Constraint;
|
||||||
|
|
||||||
enum class TypeIndexKind {
|
enum class TypeIndexKind {
|
||||||
AppOp,
|
AppOp,
|
||||||
AppArg,
|
AppArg,
|
||||||
|
@ -123,8 +125,10 @@ class TVar : public Type {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TVar():
|
std::string Name;
|
||||||
Type(TypeKind::Var) {}
|
|
||||||
|
TVar(std::string Name):
|
||||||
|
Type(TypeKind::Var), Name(Name) {}
|
||||||
|
|
||||||
void set(Type* Ty) {
|
void set(Type* Ty) {
|
||||||
auto Root = find();
|
auto Root = find();
|
||||||
|
@ -136,11 +140,11 @@ public:
|
||||||
Type* find() const override {
|
Type* find() const override {
|
||||||
TVar* Curr = const_cast<TVar*>(this);
|
TVar* Curr = const_cast<TVar*>(this);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto Keep = Curr->Parent;
|
auto Parent = Curr->Parent;
|
||||||
if (Keep == Curr || !Keep->isVar()) {
|
if (Parent == Curr || !Parent->isVar()) {
|
||||||
return Keep;
|
return Parent;
|
||||||
}
|
}
|
||||||
auto Keep2 = static_cast<TVar*>(Keep);
|
auto Keep2 = static_cast<TVar*>(Parent);
|
||||||
Curr->Parent = Keep2->Parent;
|
Curr->Parent = Keep2->Parent;
|
||||||
Curr = Keep2;
|
Curr = Keep2;
|
||||||
}
|
}
|
||||||
|
@ -214,12 +218,15 @@ public:
|
||||||
struct TypeScheme {
|
struct TypeScheme {
|
||||||
|
|
||||||
std::unordered_set<TVar*> Unbound;
|
std::unordered_set<TVar*> Unbound;
|
||||||
|
std::vector<Constraint*> Constraints;
|
||||||
Type* Ty;
|
Type* Ty;
|
||||||
|
|
||||||
Type* getType() const {
|
Type* getType() const {
|
||||||
return Ty;
|
return Ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TypeVisitor {
|
class TypeVisitor {
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <cwchar>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#include "bolt/CSTVisitor.hpp"
|
|
||||||
#include "zen/graph.hpp"
|
#include "zen/graph.hpp"
|
||||||
|
|
||||||
#include "bolt/ByteString.hpp"
|
#include "bolt/ByteString.hpp"
|
||||||
#include "bolt/CST.hpp"
|
#include "bolt/CST.hpp"
|
||||||
|
#include "bolt/CSTVisitor.hpp"
|
||||||
#include "bolt/Type.hpp"
|
#include "bolt/Type.hpp"
|
||||||
#include "bolt/Diagnostics.hpp"
|
#include "bolt/Diagnostics.hpp"
|
||||||
#include <algorithm>
|
|
||||||
#include <cwchar>
|
|
||||||
#include <functional>
|
|
||||||
#include <variant>
|
|
||||||
#include "bolt/Checker.hpp"
|
#include "bolt/Checker.hpp"
|
||||||
|
|
||||||
namespace bolt {
|
namespace bolt {
|
||||||
|
@ -33,11 +32,30 @@ TypeScheme* TypeEnv::lookup(ByteString Name, SymbolKind Kind) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeEnv::add(ByteString Name, TypeScheme* Scm, SymbolKind Kind) {
|
void TypeEnv::add(ByteString Name, TypeScheme* Scm, SymbolKind Kind) {
|
||||||
Mapping.emplace(std::make_tuple(Name, Kind), Scm);
|
Mapping[std::make_tuple(Name, Kind)] = Scm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TypeEnv::add(ByteString Name, Type* Ty, SymbolKind Kind) {
|
void TypeEnv::add(ByteString Name, Type* Ty, SymbolKind Kind) {
|
||||||
add(Name, new TypeScheme { {}, Ty }, Kind);
|
add(Name, new TypeScheme { {}, {}, Ty }, Kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TypeEnv::dump() const {
|
||||||
|
for (auto [Tuple, Scm]: Mapping) {
|
||||||
|
auto Name = std::get<0>(Tuple);
|
||||||
|
auto Kind = std::get<1>(Tuple);
|
||||||
|
switch (Kind) {
|
||||||
|
case SymbolKind::Var:
|
||||||
|
std::cerr << "let " << Name << " : " << Scm->toString() << "\n";
|
||||||
|
break;
|
||||||
|
case SymbolKind::Type:
|
||||||
|
std::cerr << "type " << Name << " = " << Scm->toString() << "\n";
|
||||||
|
break;
|
||||||
|
case SymbolKind::Class:
|
||||||
|
ZEN_UNREACHABLE // TODO
|
||||||
|
case SymbolKind::Constructor:
|
||||||
|
ZEN_UNREACHABLE // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
using TVSub = std::unordered_map<TVar*, Type*>;
|
using TVSub = std::unordered_map<TVar*, Type*>;
|
||||||
|
@ -94,6 +112,7 @@ Type* Checker::instantiate(TypeScheme* Scm) {
|
||||||
auto Fresh = createTVar();
|
auto Fresh = createTVar();
|
||||||
Sub[TV] = Fresh;
|
Sub[TV] = Fresh;
|
||||||
}
|
}
|
||||||
|
// TODO instantiate constraints
|
||||||
return substituteType(Scm->getType(), Sub);
|
return substituteType(Scm->getType(), Sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,26 +486,45 @@ bool TypeEnv::hasVar(TVar* TV) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getUnbound(const TypeEnv& Env, Type* Ty) {
|
static void addUnbound(Type* Ty, const TypeEnv& Env, std::unordered_set<TVar*>& Vars) {
|
||||||
struct Visitor : public TypeVisitor {
|
struct Visitor : public TypeVisitor {
|
||||||
const TypeEnv& Env;
|
const TypeEnv& Env;
|
||||||
Visitor(const TypeEnv& Env):
|
std::unordered_set<TVar*>& Vars;
|
||||||
Env(Env) {}
|
Visitor(const TypeEnv& Env, std::unordered_set<TVar*>& Vars):
|
||||||
std::vector<TVar*> Out;
|
Env(Env), Vars(Vars) {}
|
||||||
void visitVar(TVar* TV) {
|
void visitVar(TVar* TV) {
|
||||||
auto Solved = TV->find();
|
auto Solved = TV->find();
|
||||||
if (isa<TVar>(Solved)) {
|
if (isa<TVar>(Solved)) {
|
||||||
auto Var = static_cast<TVar*>(Solved);
|
auto Var = static_cast<TVar*>(Solved);
|
||||||
if (!Env.hasVar(Var)) {
|
if (!Env.hasVar(Var)) {
|
||||||
Out.push_back(Var);
|
Vars.emplace(Var);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
visit(Solved);
|
visit(Solved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} V { Env };
|
} V { Env, Vars };
|
||||||
V.visit(Ty);
|
V.visit(Ty);
|
||||||
return V.Out;
|
}
|
||||||
|
|
||||||
|
static void addUnbound(const Constraint& C, const TypeEnv& Env, std::unordered_set<TVar*>& Vars) {
|
||||||
|
switch (C.getKind()) {
|
||||||
|
case ConstraintKind::TypesEqual:
|
||||||
|
{
|
||||||
|
auto TE = static_cast<const CTypesEqual&>(C);
|
||||||
|
addUnbound(TE.getLeft(), Env, Vars);
|
||||||
|
addUnbound(TE.getRight(), Env, Vars);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeScheme* generalize(const TypeEnv& Env, const ConstraintSet& Constraints, Type* Ty) {
|
||||||
|
std::unordered_set<TVar*> Vars;
|
||||||
|
for (const auto C: Constraints) {
|
||||||
|
addUnbound(*C, Env, Vars);
|
||||||
|
}
|
||||||
|
return new TypeScheme { Vars, Constraints, Ty };
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstraintSet Checker::inferMany(TypeEnv& Env, std::vector<Node*>& Elements, Type* RetTy) {
|
ConstraintSet Checker::inferMany(TypeEnv& Env, std::vector<Node*>& Elements, Type* RetTy) {
|
||||||
|
@ -564,10 +602,9 @@ ConstraintSet Checker::inferMany(TypeEnv& Env, std::vector<Node*>& Elements, Typ
|
||||||
for (auto N: Mutual) {
|
for (auto N: Mutual) {
|
||||||
if (isa<FunctionDeclaration>(N)) {
|
if (isa<FunctionDeclaration>(N)) {
|
||||||
auto Func = static_cast<FunctionDeclaration*>(N);
|
auto Func = static_cast<FunctionDeclaration*>(N);
|
||||||
auto Unbound = getUnbound(Env, Func->getType());
|
|
||||||
Env.add(
|
Env.add(
|
||||||
Func->getNameAsString(),
|
Func->getNameAsString(),
|
||||||
new TypeScheme { { Unbound.begin(), Unbound.end() }, Func->getType()->find() },
|
generalize(Env, Out, Func->getType()->find()),
|
||||||
SymbolKind::Var
|
SymbolKind::Var
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -737,7 +774,7 @@ void Checker::run(SourceFile* SF) {
|
||||||
Env.add("not", new TFun(Bool, Bool), SymbolKind::Var);
|
Env.add("not", new TFun(Bool, Bool), SymbolKind::Var);
|
||||||
Env.add("+", new TFun(Int, new TFun(Int, Int)), SymbolKind::Var);
|
Env.add("+", new TFun(Int, new TFun(Int, Int)), SymbolKind::Var);
|
||||||
Env.add("-", new TFun(Int, new TFun(Int, Int)), SymbolKind::Var);
|
Env.add("-", new TFun(Int, new TFun(Int, Int)), SymbolKind::Var);
|
||||||
Env.add("$", new TypeScheme({ A, B }, new TFun(new TFun(A, B), new TFun(A, B))), SymbolKind::Var);
|
Env.add("$", new TypeScheme({ A, B }, {}, new TFun(new TFun(A, B), new TFun(A, B))), SymbolKind::Var);
|
||||||
auto Out = inferSourceFile(Env, SF);
|
auto Out = inferSourceFile(Env, SF);
|
||||||
solve(Out);
|
solve(Out);
|
||||||
}
|
}
|
||||||
|
|
17
src/Constraint.cc
Normal file
17
src/Constraint.cc
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
#include "bolt/Constraint.hpp"
|
||||||
|
|
||||||
|
namespace bolt {
|
||||||
|
|
||||||
|
std::string Constraint::toString() const {
|
||||||
|
switch (Kind) {
|
||||||
|
case ConstraintKind::TypesEqual:
|
||||||
|
return static_cast<const CTypesEqual*>(this)->toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CTypesEqual::toString() const {
|
||||||
|
return A->toString() + " ~ " + B->toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/Type.cc
16
src/Type.cc
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
#include "zen/config.hpp"
|
#include "zen/config.hpp"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "bolt/Type.hpp"
|
#include "bolt/Type.hpp"
|
||||||
|
|
||||||
|
@ -74,7 +75,7 @@ std::string Type::toString() const {
|
||||||
return F->getLeft()->toString() + " -> " + F->getRight()->toString();
|
return F->getLeft()->toString() + " -> " + F->getRight()->toString();
|
||||||
}
|
}
|
||||||
case TypeKind::Var:
|
case TypeKind::Var:
|
||||||
return "α";
|
return static_cast<const TVar*>(this)->Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,4 +100,17 @@ void TypeVisitor::visit(Type* Ty) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TypeScheme::toString() const {
|
||||||
|
std::ostringstream Out;
|
||||||
|
if (!Unbound.empty()) {
|
||||||
|
Out << "forall";
|
||||||
|
for (auto TV: Unbound) {
|
||||||
|
Out << " " << TV->toString();
|
||||||
|
}
|
||||||
|
Out << ". ";
|
||||||
|
}
|
||||||
|
Out << Ty->toString();
|
||||||
|
return Out.str();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue