Add some comments and lookup type class let declaration with Scope::lookupDirect()

This commit is contained in:
Sam Vervaeck 2023-05-21 20:33:06 +02:00
parent 6ea27d81f4
commit 4e27d778f0
Signed by: samvv
SSH key fingerprint: SHA256:dIg0ywU1OP+ZYifrYxy8c5esO72cIKB+4/9wkZj1VaY
3 changed files with 53 additions and 8 deletions

View file

@ -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();

View file

@ -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);

View file

@ -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()));
} }
} }