Add some comments and lookup type class let declaration with Scope::lookupDirect()
This commit is contained in:
parent
6ea27d81f4
commit
4e27d778f0
3 changed files with 53 additions and 8 deletions
|
@ -187,6 +187,22 @@ namespace bolt {
|
||||||
|
|
||||||
Scope(Node* Source);
|
Scope(Node* Source);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs a direct lookup in this scope for the given symbol.
|
||||||
|
*
|
||||||
|
* This method will never traverse to parent scopes and will always return a
|
||||||
|
* symbol that belongs to this scope, if any is found.
|
||||||
|
*
|
||||||
|
* \returns nullptr when no such symbol could be found in this scope.
|
||||||
|
*/
|
||||||
|
Node* lookupDirect(SymbolPath Path, SymbolKind Kind = SymbolKind::Var);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the symbol with the given name, either in this scope or in any of
|
||||||
|
* the parent ones.
|
||||||
|
*
|
||||||
|
* \returns nullptr when no such symbol could be found in any of the scopes.
|
||||||
|
*/
|
||||||
Node* lookup(SymbolPath Path, SymbolKind Kind = SymbolKind::Var);
|
Node* lookup(SymbolPath Path, SymbolKind Kind = SymbolKind::Var);
|
||||||
|
|
||||||
Scope* getParentScope();
|
Scope* getParentScope();
|
||||||
|
|
15
src/CST.cc
15
src/CST.cc
|
@ -63,13 +63,22 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* Scope::lookupDirect(SymbolPath Path, SymbolKind Kind) {
|
||||||
|
ZEN_ASSERT(Path.Modules.empty());
|
||||||
|
auto Match = Mapping.find(Path.Name);
|
||||||
|
if (Match != Mapping.end() && std::get<1>(Match->second) == Kind) {
|
||||||
|
return std::get<0>(Match->second);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Node* Scope::lookup(SymbolPath Path, SymbolKind Kind) {
|
Node* Scope::lookup(SymbolPath Path, SymbolKind Kind) {
|
||||||
ZEN_ASSERT(Path.Modules.empty());
|
ZEN_ASSERT(Path.Modules.empty());
|
||||||
auto Curr = this;
|
auto Curr = this;
|
||||||
do {
|
do {
|
||||||
auto Match = Curr->Mapping.find(Path.Name);
|
auto Found= Curr->lookupDirect(Path, Kind);
|
||||||
if (Match != Curr->Mapping.end() && std::get<1>(Match->second) == Kind) {
|
if (Found) {
|
||||||
return std::get<0>(Match->second);
|
return Found;
|
||||||
}
|
}
|
||||||
Curr = Curr->getParentScope();
|
Curr = Curr->getParentScope();
|
||||||
} while (Curr != nullptr);
|
} while (Curr != nullptr);
|
||||||
|
|
|
@ -390,7 +390,11 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Type* Ty;
|
// 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) {
|
if (Let->TypeAssert) {
|
||||||
Ty = inferTypeExpression(Let->TypeAssert->TypeExpression);
|
Ty = inferTypeExpression(Let->TypeAssert->TypeExpression);
|
||||||
} else {
|
} else {
|
||||||
|
@ -398,21 +402,37 @@ namespace bolt {
|
||||||
}
|
}
|
||||||
Let->Ty = Ty;
|
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 (llvm::isa<InstanceDeclaration>(Let->Parent)) {
|
if (llvm::isa<InstanceDeclaration>(Let->Parent)) {
|
||||||
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));
|
||||||
std::vector<TVar*> Params;
|
|
||||||
|
// 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;
|
||||||
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));
|
NewCtx->Env.emplace(TE->Name->getCanonicalText(), new Forall(TV));
|
||||||
Params.push_back(TV);
|
Params.push_back(TV);
|
||||||
}
|
}
|
||||||
// FIXME lookup should not go over parent envs
|
|
||||||
auto Let2 = llvm::cast<LetDeclaration>(Class->getScope()->lookup({ {}, llvm::cast<BindPattern>(Let->Pattern)->Name->getCanonicalText() }, SymbolKind::Var));
|
auto Let2 = 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 (Let2->TypeAssert) {
|
if (Let2->TypeAssert) {
|
||||||
addConstraint(new CEqual(Ty, inferTypeExpression(Let2->TypeAssert->TypeExpression), Let));
|
addConstraint(new CEqual(Ty, inferTypeExpression(Let2->TypeAssert->TypeExpression), Let));
|
||||||
}
|
}
|
||||||
for (auto [Param, TE]: zen::zip(Params, Instance->TypeExps)) {
|
|
||||||
|
// 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()));
|
addConstraint(new CEqual(Param, TE->getType()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue