Skip to content

Instantly share code, notes, and snippets.

@sezna

sezna/README.md Secret

Created March 13, 2024 01:53
Show Gist options
  • Save sezna/35925cdfcbf3a5f80c3d64f085907c9b to your computer and use it in GitHub Desktop.
Save sezna/35925cdfcbf3a5f80c3d64f085907c9b to your computer and use it in GitHub Desktop.
Namespaces Notes

The Lifecycle of a Namespace

  1. It gets parsed and becomes a qsc_ast::Namespace.
    • A namespace is an AST node, so it has a Node ID, as well as a span, the namespace name, and the contained items within.
  2. qsc_frontend::compile::resolve_all produces an IndexMap of globals called names, which contains all known NodeIds and their resolutions (Ress), including Namespaces. (compile.rs line 357). Namespaces are now contained in the globals.
    1. During resolution, both the AST and the HIR exist simultaneously, and AST NodeIds are mapped to HIR LocalItemIds.
  3. The namespace gets lowered (frontend/lower.rs fn lower_namespace). The namespace's HIR ItemId already exists, as it was mapped in step 2.
    1. All items within the namespace are lowered, and their parent is set to the current namespace being lowered.
    2. The ident for the namespace name itself is lowered into an hir::Ident.
    3. A new hir::Item is created for the namespace and inserted into With.lowerer.items, and all of its lowered items are contained in the hir::ItemKind. Notably, this is the second copy of the namespace, as its unlowered variant is already contained in With.names.
  4. Passes don't do anything to namespaces.
  5. In FIR (lowering happens in qsc_eval, not qsc_fir), namespaces are lowered in fn lower_item. They are not flattened, rather, their items are just converted into FIR items and the namespace is preserved.

How Items Within Namespaces Are Resolved

HIR has a PackageIter, which iterates over the public API of a package. Any top-level namespaces without parents are included in this list. FIR has a copy/pasted implementation of this.

In HIR and FIR, types and terms are stored with respect to their top-level namespace. If we are able to flatten namespaces before we get to HIR and FIR, the impact of this change on the compiler pipeline will be significantly reduced.

Opens are handled in resolve.rs, in fn resolve(), where symbols are looked up within open statements according to shadowing rules.

How I Think We Should Proceed

I think we can leverage existing compiler infra to accomplish this. In HIR, Namespace items (in HIR's ItemKind) contain a vector of LocalItemIds. LocalItemIds are mapped to Items in qsc_hir/hir.rs -- the struct Package contains it in its field items. Items contain an ItemKind, which can be another namespace. So, we can already support nested namespaces in some ways. However, these nested namespaces are not accessible via any sort of open mechanism, because the only two ways to access content from namespaces today is to open a global namespace or reference an item as fully-qualified. So, we need to add resolution logic to make these namespaces accessible in resolve.rs (probably in fn resolve(), where symbols are looked up according to shadowing logic), add appropriate parsing logic to understand nested names, and construct them as such in the HIR. Typeck et al operate on HIR, so this should "Just Work".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment