# Compiler Identity Model

Status: current implementation note with remaining migration tasks.

This note defines the identity model Muga uses across resolver/typechecker outputs, typed HIR, package identity, and in-memory package interface summaries.

## Goals

- avoid repeated string-based lookup in compiler hot paths
- give resolver, typechecker, typed HIR, and package interfaces a shared vocabulary
- keep local identity and package identity separate
- make package flattening removable without redesigning all IDs later

## Name Text vs Identity

Muga should keep these concepts separate:

- source text name: the spelling from the source file
- `Symbol`: interned spelling inside one compiler session
- `BindingId`: resolved binding inside a checked program body or scope tree
- `LocalId`: lowered local storage slot after name resolution and typing
- `PackageId`: package node in the package graph
- `ModuleId`: module/file node inside a package
- `PackageItemId`: top-level exported or private item inside a package

`Symbol` is not enough by itself because two different scopes can define the same spelling. A resolved identifier should eventually point to a `BindingId` or `PackageItemId`, not just to the interned text.

## Local Binding Identity

Resolver should assign a `BindingId` whenever it accepts a new binding:

- immutable local binding
- mutable local binding
- function binding
- function parameter
- prelude binding

Each scope maps `Symbol -> BindingId`.

Each `BindingId` records at least:

- symbol
- binding kind
- declaration span

Typed HIR stores resolved identifier uses with `BindingId` and package targets instead of rerunning name lookup.

The completed migration foundation includes:

- accepted binding tables from resolver/typechecker
- identifier-reference analysis records with `ExprId`, source spans, and `BindingId`
- expression type results from typechecking
- typed HIR nodes that preserve resolved local/package targets
- bytecode name references that carry semantic binding identity, lowered local identity, and display symbols
- runtime environments keyed by lowered `LocalId` slots instead of display-name lookup

Source spans are still kept for diagnostics, but analysis consumers should prefer explicit node identity. The current AST carries `ExprId` and `StmtId`, and resolver/typechecker outputs use `ExprId` for identifier references and expression types. Package flattening renumbers node IDs after combining files so IDs remain unique inside the final checked program.

## Package Identity

Package loading now introduces a package symbol graph before package flattening is removed.

Recommended model:

- `PackageId` identifies one loaded package
- `ModuleId` identifies one source module/file inside that package
- `PackageItemId` identifies one top-level item in that package
- imports map local alias symbols to `PackageId`
- qualified references resolve to `(PackageId, PackageItemId)`

Current implementation:

- `load_flattened_from_entry` returns both the flattened program and `PackageSymbolGraph`; its name is intentionally explicit because flattened ASTs are a compatibility representation
- `PackageSymbolGraph` stores package nodes, top-level item nodes, and import edges
- module records keep package membership and source file/module path
- item records keep source name, kind, visibility, declaring module, source span, and current mangled name
- package rewriting attaches `PackageItemId` to flattened top-level AST record/function declarations
- package execution now uses package-aware typed HIR lowered through MIR; the remaining flattened program path is a compatibility loader/API surface

Module-private visibility is now enforced for top-level package items during package rewriting. Unmodified top-level items are visible only inside their declaring source file, `pkg` items are visible to sibling files in the same package, and imports expose only `pub` items. Per-field record visibility is not a required v1 slice; if public hidden representations become necessary, opaque records or opaque types should be evaluated first.

This lets the compiler distinguish:

- local binding identity
- current-package top-level item identity
- imported package item identity

## Typed HIR Boundary

By the time code reaches typed HIR, these should be fixed:

- each identifier use has a resolved local binding or package item identity
- each expression has a resolved type
- each call has a resolved callee shape
- each qualified path has a resolved package identity
- visibility and import checks are complete

Typed HIR should not perform string-based name lookup.

Typed HIR should consume analysis outputs rather than rerunning resolver or typechecker logic. In particular, identifier expressions should already know their binding identity, and expressions should already have a resolved type.

Current typed HIR status:

- checked AST can lower into `typed_hir::Program`
- typed HIR keeps language-shaped statements and expressions
- expressions carry `ExprId` and resolved `TypeInfo`
- identifier expressions carry `BindingId` plus `IdentTarget`, which can point at a package item
- assignment statements carry target `BindingId`
- package symbol graph is preserved on typed HIR programs
- call expressions carry explicit resolved callee shape and call origin
- package item statements, call targets, and package record types carry `PackageItemId`-backed identity
- typed HIR lowering reads package item identity from the AST instead of recovering it from flattened mangled names
- `Option` and `Result` match patterns are represented as enum variant patterns with enum name, variant name, and optional payload binding
- compiler-known enum metadata describes `Option` and `Result`, while user-defined enum declarations carry package item identity through typed HIR, package interfaces, MIR lowering, bytecode, and runtime values

## Current Migration Status

Done:

- resolver scopes use `Symbol -> BindingId`
- typechecker scopes use `Symbol -> BindingId`
- resolver and typechecker both keep internal binding tables
- shared ID wrapper types exist in `src/identity.rs`
- AST expressions and statements carry `ExprId` / `StmtId`
- resolver exposes accepted bindings and identifier references
- typechecker exposes accepted bindings, identifier references, and expression types
- package loading exposes `PackageSymbolGraph`
- initial typed HIR exists
- typed HIR calls carry resolved callee shape and call origin
- package loading exposes `ModuleId` data and enforces top-level module-private / `pkg` / `pub` visibility
- typed HIR package identifiers, public item statements, call targets, and record types point to package item identities
- shared public `TypeInfo` data lives in `types` instead of being owned by typechecker internals
- diagnostics support related notes and suggestions for selected resolver, typechecker, and package errors
- `interface` can generate in-memory package interface summaries for public records, enums, and functions from typed HIR item identities
- `interface` validates public package references against generated interface summaries
- `interface` validation uses package/name lookup and checks stale public item identity, function signatures, and record field shapes
- import/package-qualified lookup is routed through `interface::PackageExportGraph` before whole-program flattening
- `interface::PackageExportGraph` can be derived from either package identity data or typed package interface summaries
- local binding annotations, generic type expression syntax, `List[T]` / `Option[T]` / `Result[T, E]` / `Map[K, V]` `TypeInfo` cases, typed prelude list and map operations, direct list indexing, typed Option/Result construction, typed Option/Result `match`, user-defined enum declarations, enum-variant-shaped match patterns, and compiler-known enum metadata for Option/Result variants are in place
- resolver, typechecker output, runtime, and package builtin filtering share `prelude::BuiltinId` as the single builtin identity
- MIR is the backend boundary for default compile APIs, and bytecode/runtime name references now carry optional semantic `BindingId`, lowered `LocalId`, and display symbols for diagnostics; bytecode exposes local metadata for binding-backed and synthetic locals; runtime environments are slot-backed by `LocalId`

Remaining:

1. harden the explicit artifact workflow around MIR-lowered bytecode `.mgb` implementation artifacts, including diagnostics and sample coverage
2. keep `.mgi` as interface data, `.mgc` as check-cache proof, and `.mgb` as the execution body artifact while project-owned dependency metadata is still absent
3. continue expanding structured diagnostics as artifact-backed execution, cache, and interface errors are introduced
4. move serialized interface internals toward first-class `InterfaceTypeRef` / stable item-reference data after the v1 package workflow is closed

## Foundation Note

Typed HIR, package symbol graph, `interface::PackageExportGraph`, in-memory package summaries, MIR `LocalId`, and bytecode local metadata are now the foundation for the remaining v1 package work.

They add reusable compiler data while keeping the VM as the reference execution backend. The remaining tasks should build on that foundation instead of reintroducing string-based lookup, display-name runtime lookup, or package flattening as long-term architecture.
