Improve support for type classes and simplify algorithm
This commit is contained in:
parent
b467f9e644
commit
693301bac6
6 changed files with 138 additions and 210 deletions
|
@ -1765,6 +1765,10 @@ namespace bolt {
|
||||||
return Parent->getKind() == NodeKind::ClassDeclaration;
|
return Parent->getKind() == NodeKind::ClassDeclaration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isSignature() const noexcept {
|
||||||
|
return !Body;
|
||||||
|
}
|
||||||
|
|
||||||
Token* getFirstToken() const override;
|
Token* getFirstToken() const override;
|
||||||
Token* getLastToken() const override;
|
Token* getLastToken() const override;
|
||||||
|
|
||||||
|
|
|
@ -109,9 +109,9 @@ namespace bolt {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TypeclassSignature Sig;
|
TypeclassSignature Sig;
|
||||||
FunctionDeclaration* Decl;
|
Node* Decl;
|
||||||
|
|
||||||
inline TypeclassMissingDiagnostic(TypeclassSignature Sig, FunctionDeclaration* Decl):
|
inline TypeclassMissingDiagnostic(TypeclassSignature Sig, Node* Decl):
|
||||||
Diagnostic(DiagnosticKind::TypeclassMissing), Sig(Sig), Decl(Decl) {}
|
Diagnostic(DiagnosticKind::TypeclassMissing), Sig(Sig), Decl(Decl) {}
|
||||||
|
|
||||||
inline Node* getNode() const override {
|
inline Node* getNode() const override {
|
||||||
|
@ -124,10 +124,10 @@ namespace bolt {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ByteString TypeclassName;
|
ByteString TypeclassName;
|
||||||
TCon* Ty;
|
Type* Ty;
|
||||||
Node* Source;
|
Node* Source;
|
||||||
|
|
||||||
inline InstanceNotFoundDiagnostic(ByteString TypeclassName, TCon* Ty, Node* Source):
|
inline InstanceNotFoundDiagnostic(ByteString TypeclassName, Type* Ty, Node* Source):
|
||||||
Diagnostic(DiagnosticKind::InstanceNotFound), TypeclassName(TypeclassName), Ty(Ty), Source(Source) {}
|
Diagnostic(DiagnosticKind::InstanceNotFound), TypeclassName(TypeclassName), Ty(Ty), Source(Source) {}
|
||||||
|
|
||||||
inline Node* getNode() const override {
|
inline Node* getNode() const override {
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace bolt {
|
||||||
|
|
||||||
class Type;
|
class Type;
|
||||||
class TVar;
|
class TVar;
|
||||||
|
class TCon;
|
||||||
|
|
||||||
using TypeclassId = ByteString;
|
using TypeclassId = ByteString;
|
||||||
|
|
||||||
|
@ -29,6 +30,12 @@ namespace bolt {
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TypeSig {
|
||||||
|
Type* Orig;
|
||||||
|
Type* Op;
|
||||||
|
std::vector<Type*> Args;
|
||||||
|
};
|
||||||
|
|
||||||
enum class TypeIndexKind {
|
enum class TypeIndexKind {
|
||||||
AppOpType,
|
AppOpType,
|
||||||
AppArgType,
|
AppArgType,
|
||||||
|
@ -267,6 +274,10 @@ namespace bolt {
|
||||||
return VK;
|
return VK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool isRigid() const noexcept {
|
||||||
|
return VK == VarKind::Rigid;
|
||||||
|
}
|
||||||
|
|
||||||
Type* find();
|
Type* find();
|
||||||
|
|
||||||
void set(Type* Ty);
|
void set(Type* Ty);
|
||||||
|
@ -282,6 +293,8 @@ namespace bolt {
|
||||||
|
|
||||||
ByteString Name;
|
ByteString Name;
|
||||||
|
|
||||||
|
TypeclassContext Provided;
|
||||||
|
|
||||||
inline TVarRigid(size_t Id, ByteString Name):
|
inline TVarRigid(size_t Id, ByteString Name):
|
||||||
TVar(Id, VarKind::Rigid), Name(Name) {}
|
TVar(Id, VarKind::Rigid), Name(Name) {}
|
||||||
|
|
||||||
|
@ -405,44 +418,48 @@ namespace bolt {
|
||||||
virtual void enterType(C<Type>* Ty) {}
|
virtual void enterType(C<Type>* Ty) {}
|
||||||
virtual void exitType(C<Type>* Ty) {}
|
virtual void exitType(C<Type>* Ty) {}
|
||||||
|
|
||||||
virtual void visitVarType(C<TVar>* Ty) {
|
virtual void visitType(C<Type>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitEachChild(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void visitVarType(C<TVar>* Ty) {
|
||||||
|
visitType(Ty);
|
||||||
|
}
|
||||||
|
|
||||||
virtual void visitAppType(C<TApp>* Ty) {
|
virtual void visitAppType(C<TApp>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitPresentType(C<TPresent>* Ty) {
|
virtual void visitPresentType(C<TPresent>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitConType(C<TCon>* Ty) {
|
virtual void visitConType(C<TCon>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitArrowType(C<TArrow>* Ty) {
|
virtual void visitArrowType(C<TArrow>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitTupleType(C<TTuple>* Ty) {
|
virtual void visitTupleType(C<TTuple>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitTupleIndexType(C<TTupleIndex>* Ty) {
|
virtual void visitTupleIndexType(C<TTupleIndex>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitAbsentType(C<TAbsent>* Ty) {
|
virtual void visitAbsentType(C<TAbsent>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitFieldType(C<TField>* Ty) {
|
virtual void visitFieldType(C<TField>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visitNilType(C<TNil>* Ty) {
|
virtual void visitNilType(C<TNil>* Ty) {
|
||||||
visitEachChild(Ty);
|
visitType(Ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
285
src/Checker.cc
285
src/Checker.cc
|
@ -20,6 +20,8 @@
|
||||||
|
|
||||||
// TODO create the constraint in addConstraint, not the other way round
|
// TODO create the constraint in addConstraint, not the other way round
|
||||||
|
|
||||||
|
// TODO Find a way to create a match expression in between ( and )
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
@ -271,11 +273,11 @@ namespace bolt {
|
||||||
case NodeKind::ClassDeclaration:
|
case NodeKind::ClassDeclaration:
|
||||||
{
|
{
|
||||||
auto Class = static_cast<ClassDeclaration*>(X);
|
auto Class = static_cast<ClassDeclaration*>(X);
|
||||||
for (auto TE: Class->TypeVars) {
|
// for (auto TE: Class->TypeVars) {
|
||||||
auto TV = new TVarRigid(NextTypeVarId++, TE->Name->getCanonicalText());
|
// auto TV = new TVarRigid(NextTypeVarId++, TE->Name->getCanonicalText());
|
||||||
TV->Contexts.emplace(Class->Name->getCanonicalText());
|
// // TV->Contexts.emplace(Class->Name->getCanonicalText());
|
||||||
TE->setType(TV);
|
// TE->setType(TV);
|
||||||
}
|
// }
|
||||||
for (auto Element: Class->Elements) {
|
for (auto Element: Class->Elements) {
|
||||||
forwardDeclare(Element);
|
forwardDeclare(Element);
|
||||||
}
|
}
|
||||||
|
@ -459,17 +461,26 @@ namespace bolt {
|
||||||
|
|
||||||
setContext(Let->Ctx);
|
setContext(Let->Ctx);
|
||||||
|
|
||||||
|
auto addClassVars = [&](ClassDeclaration* Class, bool IsRigid) {
|
||||||
|
auto Id = Class->Name->getCanonicalText();
|
||||||
|
auto Ctx = &getContext();
|
||||||
|
std::vector<TVar*> Out;
|
||||||
|
for (auto TE: Class->TypeVars) {
|
||||||
|
auto Name = TE->Name->getCanonicalText();
|
||||||
|
auto TV = IsRigid ? createRigidVar(Name) : createTypeVar();
|
||||||
|
TV->Contexts.emplace(Id);
|
||||||
|
Ctx->Env.emplace(Name, new Forall(TV));
|
||||||
|
Out.push_back(TV);
|
||||||
|
}
|
||||||
|
return Out;
|
||||||
|
};
|
||||||
|
|
||||||
// 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 (Let->isClass()) {
|
if (Let->isClass()) {
|
||||||
auto Class = static_cast<ClassDeclaration*>(Let->Parent);
|
addClassVars(static_cast<ClassDeclaration*>(Let->Parent), true);
|
||||||
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
|
// Here we infer the primary type of the let declaration. If there's a
|
||||||
|
@ -492,27 +503,22 @@ namespace bolt {
|
||||||
|
|
||||||
auto Instance = static_cast<InstanceDeclaration*>(Let->Parent);
|
auto Instance = static_cast<InstanceDeclaration*>(Let->Parent);
|
||||||
auto Class = llvm::cast<ClassDeclaration>(Instance->getScope()->lookup({ {}, Instance->Name->getCanonicalText() }, SymbolKind::Class));
|
auto Class = llvm::cast<ClassDeclaration>(Instance->getScope()->lookup({ {}, Instance->Name->getCanonicalText() }, SymbolKind::Class));
|
||||||
|
auto SigLet = llvm::cast<FunctionDeclaration>(Class->getScope()->lookupDirect({ {}, Let->Name->getCanonicalText() }, SymbolKind::Var));
|
||||||
|
|
||||||
|
auto Params = addClassVars(Class, false);
|
||||||
|
|
||||||
// The type asserts in the type class declaration might make use of
|
// The type asserts in the type class declaration might make use of
|
||||||
// the type parameters of the type class declaration, so it is
|
// the type parameters of the type class declaration, so it is
|
||||||
// important to make them available in the type environment. Moreover,
|
// important to make them available in the type environment. Moreover,
|
||||||
// we will be unifying them with the actual types declared in the
|
// we will be unifying them with the actual types declared in the
|
||||||
// instance declaration, so we keep track of them.
|
// instance declaration, so we keep track of them.
|
||||||
std::vector<TVar *> Params;
|
// std::vector<TVar *> Params;
|
||||||
TVSub Sub;
|
// TVSub Sub;
|
||||||
for (auto TE: Class->TypeVars) {
|
// for (auto TE: Class->TypeVars) {
|
||||||
auto TV = createTypeVar();
|
// auto TV = createTypeVar();
|
||||||
Sub.emplace(llvm::cast<TVar>(TE->getType()), TV);
|
// Sub.emplace(llvm::cast<TVar>(TE->getType()), TV);
|
||||||
Params.push_back(TV);
|
// Params.push_back(TV);
|
||||||
}
|
// }
|
||||||
|
|
||||||
auto SigLet = llvm::cast<FunctionDeclaration>(Class->getScope()->lookupDirect({ {}, Let->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
|
// Here we do the actual unification of e.g. Eq a with Eq Bool. The
|
||||||
// unification variables we created previously will be unified with
|
// unification variables we created previously will be unified with
|
||||||
|
@ -522,6 +528,16 @@ namespace bolt {
|
||||||
addConstraint(new CEqual(Param, TE->getType(), TE));
|
addConstraint(new CEqual(Param, TE->getType(), TE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// Note that we can't do SigLet->TypeAssert->TypeExpression->getType()
|
||||||
|
// because we need to re-generate the type within the local context of
|
||||||
|
// this let-declaration.
|
||||||
|
// TODO make CEqual accept multiple nodes
|
||||||
|
addConstraint(new CEqual(Ty, inferTypeExpression(SigLet->TypeAssert->TypeExpression), Let));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Let->Body) {
|
if (Let->Body) {
|
||||||
|
@ -542,12 +558,18 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Let->isInstance()) {
|
||||||
Let->Ctx->Parent->Env.emplace(Let->Name->getCanonicalText(), new Forall(Let->Ctx->TVs, Let->Ctx->Constraints, Ty));
|
Let->Ctx->Parent->Env.emplace(Let->Name->getCanonicalText(), new Forall(Let->Ctx->TVs, Let->Ctx->Constraints, Ty));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Checker::inferFunctionDeclaration(FunctionDeclaration* Decl) {
|
void Checker::inferFunctionDeclaration(FunctionDeclaration* Decl) {
|
||||||
|
|
||||||
|
if (Decl->isSignature()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setContext(Decl->Ctx);
|
setContext(Decl->Ctx);
|
||||||
|
|
||||||
std::vector<Type*> ParamTypes;
|
std::vector<Type*> ParamTypes;
|
||||||
|
@ -770,7 +792,9 @@ namespace bolt {
|
||||||
auto D = static_cast<TypeclassConstraintExpression*>(C);
|
auto D = static_cast<TypeclassConstraintExpression*>(C);
|
||||||
std::vector<Type*> Types;
|
std::vector<Type*> Types;
|
||||||
for (auto TE: D->TEs) {
|
for (auto TE: D->TEs) {
|
||||||
Types.push_back(inferTypeExpression(TE));
|
auto TV = static_cast<TVarRigid*>(inferTypeExpression(TE));
|
||||||
|
TV->Provided.emplace(D->Name->getCanonicalText());
|
||||||
|
Types.push_back(TV);
|
||||||
}
|
}
|
||||||
return new CClass(D->Name->getCanonicalText(), Types);
|
return new CClass(D->Name->getCanonicalText(), Types);
|
||||||
}
|
}
|
||||||
|
@ -1175,136 +1199,6 @@ namespace bolt {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Checker::checkTypeclassSigs(Node* N) {
|
|
||||||
|
|
||||||
struct LetVisitor : CSTVisitor<LetVisitor> {
|
|
||||||
|
|
||||||
Checker& C;
|
|
||||||
|
|
||||||
void visitLetDeclaration(FunctionDeclaration* 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.
|
|
||||||
// There might be some other signatures as well, but those are an implementation detail.
|
|
||||||
std::vector<TypeclassSignature> Expected;
|
|
||||||
|
|
||||||
// We must add the type class itself to Expected because in order for
|
|
||||||
// propagation to work the rigid type variables expect this class to be
|
|
||||||
// present even inside the current class. By adding it to Expected, we
|
|
||||||
// are effectively cancelling out the default behavior of requiring the
|
|
||||||
// presence of this type classes.
|
|
||||||
if (llvm::isa<ClassDeclaration>(Decl->Parent)) {
|
|
||||||
auto Class = llvm::cast<ClassDeclaration>(Decl->Parent);
|
|
||||||
std::vector<TVar *> Tys;
|
|
||||||
for (auto TE : Class->TypeVars) {
|
|
||||||
Tys.push_back(llvm::cast<TVar>(TE->getType()));
|
|
||||||
}
|
|
||||||
Expected.push_back(
|
|
||||||
TypeclassSignature{Class->Name->getCanonicalText(), Tys});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here we scan the type signature for type classes that user expects to be there.
|
|
||||||
if (Decl->TypeAssert != nullptr) {
|
|
||||||
if (llvm::isa<QualifiedTypeExpression>(Decl->TypeAssert->TypeExpression)) {
|
|
||||||
auto QTE = static_cast<QualifiedTypeExpression*>(Decl->TypeAssert->TypeExpression);
|
|
||||||
for (auto [C, Comma]: QTE->Constraints) {
|
|
||||||
if (llvm::isa<TypeclassConstraintExpression>(C)) {
|
|
||||||
auto TCE = static_cast<TypeclassConstraintExpression*>(C);
|
|
||||||
std::vector<TVar*> Tys;
|
|
||||||
for (auto TE: TCE->TEs) {
|
|
||||||
auto TV = TE->getType();
|
|
||||||
ZEN_ASSERT(llvm::isa<TVar>(TV));
|
|
||||||
Tys.push_back(static_cast<TVar*>(TV));
|
|
||||||
}
|
|
||||||
Expected.push_back(TypeclassSignature { TCE->Name->getCanonicalText(), Tys });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort them lexically and remove any duplicates
|
|
||||||
std::sort(Expected.begin(), Expected.end());
|
|
||||||
Expected.erase(std::unique(Expected.begin(), Expected.end()), Expected.end());
|
|
||||||
|
|
||||||
// Will contain the type class signatures that our program inferred that
|
|
||||||
// at the very least should be present to make the body work.
|
|
||||||
std::vector<TypeclassSignature> Actual;
|
|
||||||
|
|
||||||
// This is ugly but it works. Scan all type variables local to this
|
|
||||||
// declaration and add the classes that they require to Actual.
|
|
||||||
for (auto Ty: *Decl->Ctx->TVs) {
|
|
||||||
auto S = Ty->solve();
|
|
||||||
if (llvm::isa<TVar>(S)) {
|
|
||||||
auto TV = static_cast<TVar*>(S);
|
|
||||||
for (auto Class: TV->Contexts) {
|
|
||||||
Actual.push_back(TypeclassSignature { Class, { TV } });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort them lexically and remove any duplicates
|
|
||||||
std::sort(Actual.begin(), Actual.end());
|
|
||||||
Actual.erase(std::unique(Actual.begin(), Actual.end()), Actual.end());
|
|
||||||
|
|
||||||
auto ActualIter = Actual.begin();
|
|
||||||
auto ExpectedIter = Expected.begin();
|
|
||||||
|
|
||||||
for (; ActualIter != Actual.end() || ExpectedIter != Expected.end() ;) {
|
|
||||||
|
|
||||||
// Our program inferred no more type classes that should be present,
|
|
||||||
// yet Expected still did find a few that the user declared in a
|
|
||||||
// signature. No errors should be reported, and we can quit this loop.
|
|
||||||
if (ActualIter == Actual.end()) {
|
|
||||||
// TODO Maybe issue a warning that a type class went unused
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There are no more type classes that were expected, so any remaining
|
|
||||||
// type classes in Actual will not have a corresponding signature.
|
|
||||||
// This should be reported as an error.
|
|
||||||
if (ExpectedIter == Expected.end()) {
|
|
||||||
for (; ActualIter != Actual.end(); ActualIter++) {
|
|
||||||
C.DE.add<TypeclassMissingDiagnostic>(*ActualIter, Decl);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If ExpectedIter is already at Show, but ActualIter is still at Eq,
|
|
||||||
// then we clearly missed the Eq in ExpectedIter. This clearly is an
|
|
||||||
// error, since the user missed something in a type signature.
|
|
||||||
if (*ActualIter < *ExpectedIter) {
|
|
||||||
C.DE.add<TypeclassMissingDiagnostic>(*ActualIter, Decl);
|
|
||||||
ActualIter++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If ActualIter is Show but ExpectedIter is still Eq, then the user
|
|
||||||
// specified too much type classes in a type signature. This is no error,
|
|
||||||
// but it might be worthwhile to issue a warning.
|
|
||||||
if (*ExpectedIter < *ActualIter) {
|
|
||||||
// DE.add<TypeclassMissingDiagnostic>(It2->Name, Decl);
|
|
||||||
ExpectedIter++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both type class signatures are equal, cancelling each other out.
|
|
||||||
ActualIter++;
|
|
||||||
ExpectedIter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
LetVisitor V { {}, *this };
|
|
||||||
V.visit(N);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Type* Checker::getType(TypedNode *Node) {
|
Type* Checker::getType(TypedNode *Node) {
|
||||||
return Node->getType()->solve();
|
return Node->getType()->solve();
|
||||||
}
|
}
|
||||||
|
@ -1368,7 +1262,6 @@ namespace bolt {
|
||||||
ActiveContext = nullptr;
|
ActiveContext = nullptr;
|
||||||
|
|
||||||
solve(new CMany(*SF->Ctx->Constraints));
|
solve(new CMany(*SF->Ctx->Constraints));
|
||||||
checkTypeclassSigs(SF);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Checker::solve(Constraint* Constraint) {
|
void Checker::solve(Constraint* Constraint) {
|
||||||
|
@ -1419,15 +1312,9 @@ namespace bolt {
|
||||||
if (Con1->Id != Con2-> Id) {
|
if (Con1->Id != Con2-> Id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// TODO must handle a TApp
|
|
||||||
// ZEN_ASSERT(Con1->Args.size() == Con2->Args.size());
|
|
||||||
// for (auto [T1, T2]: zen::zip(Con1->Args, Con2->Args)) {
|
|
||||||
// if (!assignableTo(T1, T2)) {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// TODO must handle a TApp
|
||||||
ZEN_UNREACHABLE
|
ZEN_UNREACHABLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1504,52 +1391,78 @@ namespace bolt {
|
||||||
return unify(Constraint->Left, Constraint->Right, false);
|
return unify(Constraint->Left, Constraint->Right, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<TypeclassContext> findInstanceContext(TCon* Ty, TypeclassId& Class) {
|
std::vector<TypeclassContext> findInstanceContext(const TypeSig& Ty, TypeclassId& Class) {
|
||||||
auto Match = C.InstanceMap.find(Class);
|
auto Match = C.InstanceMap.find(Class);
|
||||||
std::vector<TypeclassContext> S;
|
std::vector<TypeclassContext> S;
|
||||||
if (Match != C.InstanceMap.end()) {
|
if (Match != C.InstanceMap.end()) {
|
||||||
for (auto Instance: Match->second) {
|
for (auto Instance: Match->second) {
|
||||||
if (assignableTo(Ty, Instance->TypeExps[0]->getType())) {
|
if (assignableTo(Ty.Orig, Instance->TypeExps[0]->getType())) {
|
||||||
std::vector<TypeclassContext> S;
|
std::vector<TypeclassContext> S;
|
||||||
// TODO handle TApp
|
for (auto Arg: Ty.Args) {
|
||||||
// for (auto Arg: Ty->Args) {
|
TypeclassContext Classes;
|
||||||
// TypeclassContext Classes;
|
// TODO
|
||||||
// // TODO
|
S.push_back(Classes);
|
||||||
// S.push_back(Classes);
|
}
|
||||||
// }
|
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
C.DE.add<InstanceNotFoundDiagnostic>(Class, Ty, getSource());
|
C.DE.add<InstanceNotFoundDiagnostic>(Class, Ty.Orig, getSource());
|
||||||
// TODO handle TApp
|
for (auto Arg: Ty.Args) {
|
||||||
// for (auto Arg: Ty->Args) {
|
S.push_back({});
|
||||||
// S.push_back({});
|
}
|
||||||
// }
|
|
||||||
return S;
|
return S;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeSig getTypeSig(Type* Ty) {
|
||||||
|
struct Visitor : TypeVisitor {
|
||||||
|
Type* Op = nullptr;
|
||||||
|
std::vector<Type*> Args;
|
||||||
|
void visitType(Type* Ty) override {
|
||||||
|
if (!Op) {
|
||||||
|
Op = Ty;
|
||||||
|
} else {
|
||||||
|
Args.push_back(Ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void visitAppType(TApp* Ty) override {
|
||||||
|
visitEachChild(Ty);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Visitor V;
|
||||||
|
V.visit(Ty);
|
||||||
|
return TypeSig { Ty, V.Op, V.Args };
|
||||||
|
}
|
||||||
|
|
||||||
void propagateClasses(std::unordered_set<TypeclassId>& Classes, Type* Ty) {
|
void propagateClasses(std::unordered_set<TypeclassId>& Classes, Type* Ty) {
|
||||||
if (llvm::isa<TVar>(Ty)) {
|
if (llvm::isa<TVar>(Ty)) {
|
||||||
auto TV = llvm::cast<TVar>(Ty);
|
auto TV = llvm::cast<TVar>(Ty);
|
||||||
for (auto Class: Classes) {
|
for (auto Class: Classes) {
|
||||||
TV->Contexts.emplace(Class);
|
TV->Contexts.emplace(Class);
|
||||||
}
|
}
|
||||||
} else if (llvm::isa<TCon>(Ty)) {
|
if (TV->isRigid()) {
|
||||||
|
auto RV = static_cast<TVarRigid*>(Ty);
|
||||||
|
for (auto Id: RV->Contexts) {
|
||||||
|
if (!RV->Provided.count(Id)) {
|
||||||
|
C.DE.add<TypeclassMissingDiagnostic>(TypeclassSignature { Id, { RV } }, getSource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (llvm::isa<TCon>(Ty) || llvm::isa<TApp>(Ty)) {
|
||||||
|
auto Sig = getTypeSig(Ty);
|
||||||
for (auto Class: Classes) {
|
for (auto Class: Classes) {
|
||||||
propagateClassTycon(Class, llvm::cast<TCon>(Ty));
|
propagateClassTycon(Class, Sig);
|
||||||
}
|
}
|
||||||
} else if (!Classes.empty()) {
|
} else if (!Classes.empty()) {
|
||||||
C.DE.add<InvalidTypeToTypeclassDiagnostic>(Ty, std::vector(Classes.begin(), Classes.end()), getSource());
|
C.DE.add<InvalidTypeToTypeclassDiagnostic>(Ty, std::vector(Classes.begin(), Classes.end()), getSource());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void propagateClassTycon(TypeclassId& Class, TCon* Ty) {
|
void propagateClassTycon(TypeclassId& Class, const TypeSig& Sig) {
|
||||||
auto S = findInstanceContext(Ty, Class);
|
auto S = findInstanceContext(Sig, Class);
|
||||||
// TODO handle TApp
|
for (auto [Classes, Arg]: zen::zip(S, Sig.Args)) {
|
||||||
// for (auto [Classes, Arg]: zen::zip(S, Ty->Args)) {
|
propagateClasses(Classes, Arg);
|
||||||
// propagateClasses(Classes, Arg);
|
}
|
||||||
// }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -110,6 +110,8 @@ namespace bolt {
|
||||||
return "'return'";
|
return "'return'";
|
||||||
case NodeKind::TypeKeyword:
|
case NodeKind::TypeKeyword:
|
||||||
return "'type'";
|
return "'type'";
|
||||||
|
case NodeKind::ReferenceTypeExpression:
|
||||||
|
return "a type reference";
|
||||||
case NodeKind::FunctionDeclaration:
|
case NodeKind::FunctionDeclaration:
|
||||||
return "a function declaration";
|
return "a function declaration";
|
||||||
case NodeKind::VariableDeclaration:
|
case NodeKind::VariableDeclaration:
|
||||||
|
|
|
@ -1029,7 +1029,6 @@ finish:
|
||||||
|
|
||||||
PubKeyword* Pub = nullptr;
|
PubKeyword* Pub = nullptr;
|
||||||
FnKeyword* Fn;
|
FnKeyword* Fn;
|
||||||
MutKeyword* Mut = nullptr;
|
|
||||||
TypeAssert* TA = nullptr;
|
TypeAssert* TA = nullptr;
|
||||||
LetBody* Body = nullptr;
|
LetBody* Body = nullptr;
|
||||||
|
|
||||||
|
@ -1047,11 +1046,6 @@ finish:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
Fn = static_cast<FnKeyword*>(T0);
|
Fn = static_cast<FnKeyword*>(T0);
|
||||||
auto T1 = Tokens.peek();
|
|
||||||
if (T1->getKind() == NodeKind::MutKeyword) {
|
|
||||||
Mut = static_cast<MutKeyword*>(T1);
|
|
||||||
Tokens.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Name = expectToken<Identifier>();
|
auto Name = expectToken<Identifier>();
|
||||||
if (!Name) {
|
if (!Name) {
|
||||||
|
@ -1059,9 +1053,6 @@ finish:
|
||||||
Pub->unref();
|
Pub->unref();
|
||||||
}
|
}
|
||||||
Fn->unref();
|
Fn->unref();
|
||||||
if (Mut) {
|
|
||||||
Mut->unref();
|
|
||||||
}
|
|
||||||
skipToLineFoldEnd();
|
skipToLineFoldEnd();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1146,6 +1137,7 @@ after_params:
|
||||||
checkLineFoldEnd();
|
checkLineFoldEnd();
|
||||||
|
|
||||||
finish:
|
finish:
|
||||||
|
|
||||||
return new FunctionDeclaration(
|
return new FunctionDeclaration(
|
||||||
Pub,
|
Pub,
|
||||||
Fn,
|
Fn,
|
||||||
|
|
Loading…
Reference in a new issue