Skip to main content

Module resolver

Module resolver 

Source
Expand description

Name resolution (spec §5.1, v0.1 §4.1, v0.2 §4.1).

Builds symbol tables for the commons and validates that:

  • No two top-level items share a name (types, fns, methods are all named).
  • Every TypeRef::Named resolves to a declared type.
  • Every free function call resolves to a function declaration.
  • Every identifier in expression position resolves to a parameter, a let binding, or self (inside a method).
  • Constructor / static calls (TypeName.method(args)) resolve either to the built-in T.of of a refined type, a static method on T, or a variant constructor when T is a sum type.
  • Record construction targets a declared record type and uses only declared fields.
  • Method calls resolve via the receiver’s nominal type (the actual type check happens in the type checker).

On success returns a ResolvedCommons — the original AST plus symbol tables the type checker consumes.

Structs§

CrossContextCapability
Snapshot of one exported capability in a consumed context, as needed for v0.15 cross-context capability resolution. Operation signatures are expressed in the consumed context’s own namespace (resolved against consumed_types at the call site, mirroring CrossContextService).
CrossContextCapabilityOp
CrossContextInfo
Static information about the consuming context: the set of contexts it consumes, and any aliases introduced via as Alias clauses. Used by the resolver to recognise cross-context service calls and by the checker to type them (v0.6 §4.2).
CrossContextService
Snapshot of one service in a consumed context, as needed for v0.6 cross-context type checking. The params and return type are expressed in the consumed context’s own namespace.
MethodTable
Per-type method table built during resolution: keyed by method name, values are clones of the FnDecl for that method.
ResolvedCommons
Output of resolution: the AST plus the symbol tables the checker needs.
ShadowedImport
#1925: a uses-imported function or asset that the unit hides behind its own declaration of the same name and another kind. A local declaration wins, as it does in the emitted module, so the import is out of scope; a reference the local declaration cannot answer (a call to a type, a bare service name) is reported as this conflict, not as an unknown name.

Functions§

check_hidden_type_names
#1807: the naming gate for hidden types. A hidden type is one this unit’s composed table carries only because an imported declaration reaches it (crate::project_model::close_reachable_types): resolvable here, never nameable, since uses is one level (ADR 0278). Every place the unit’s source writes a type name is walked: type references in declarations, signatures, let annotations, lambda parameters and type arguments; a record, constructor or spread’s type; a type used as a receiver (Repo.of(…), Status.Pending); a method declared on a type; a pattern’s qualifier; and a refinement test (s is Repo). Bodies are walked, and so are an agent’s invariants, transitions and store initialisers, and an event field’s default. A hidden name in any of them is bynk.resolve.unknown_type, the error the name gave before the closure existed, with a note naming the commons to uses.
check_hidden_type_names_in_suite
#1814: the naming gate for a test suite’s view. A suite resolves names in its target’s privileged view (a unit suite) or its harness table (a system suite), each closed over the types its imported declarations reach (crate::project_model::close_view_types). A hidden type is resolvable there, so a case can build and read a reached field, but a suite may no more name it than its target may. Walks every stub clause’s argument patterns and values, every case body, and every property’s for all bindings, where filter and predicate, as check_hidden_type_names walks a unit’s items. target is the unit whose uses would admit a hidden name: the target of a unit suite, or the suite itself (None) for a system suite, which declares its own uses.
check_shadowed_type_names
#1927: the naming gate for a uses-imported type the unit hides behind its own declaration of another kind (a function, asset, service, agent or provider: crate::project_model::shadowed_imports). The type stays in the unit’s table, so an imported declaration that uses it still checks, and a value of it still flows through the unit’s code. But the emitted module binds the name to the local declaration, so the unit may not write it: every position check_hidden_type_names gates is bynk.uses.name_conflict here, and so is constructing one of the sum’s variants bare (Done), which emits the type’s name too. Matching a variant does not.
check_shadowed_type_names_in_suite
#1927: check_shadowed_type_names for a unit suite: a case may no more write a type its target hides than the target may.
compute_is_uses_commons_type
Is name a type imported via uses of a commons specifically — the exact predicate ResolvedCommons::is_uses_commons_type caches as uses_commons_type_names, and bynk-emit needs at two real call sites that must never disagree: emit_context_rebrands’s own two steps, “alias the import” and “rebrand the type” (its own doc comment, step 1 “Done in imports”, step 2 the rebrand itself, both in bynk-emit/src/emitter.rs) — an import narrower than the rebrand leaves an undefined name in the generated module; a rebrand narrower than the import leaves an alias imported and never used.
cross_context_service_for
Project one local on call handler into the CrossContextService shape both sides of a cross-context contract check need — a caller resolving a consumed service (crate::symbols::build_cross_context_info) and a callee stamping its own X-Bynk-Contract constant (crate::contract::own_contract_hashes). Sharing this one projection is the whole correctness argument for that symmetry: if the two sides ever diverged, a working deployment would 409 on every call instead of only on real skew. None when sdecl has no on call handler (e.g. an events-only or queue-only service).
resolve
Resolve names in a single-file (or already-merged) commons. Use this entry point only for self-contained Bynk programs. For multi-file projects and uses-resolving commons, use resolve_file against a pre-built combined symbol table.
resolve_file
Validate name references inside a single file’s items against an already-built symbol table (resolved.types, resolved.fns, resolved.methods). Used by the project-level driver after combining declarations from every file in a multi-file commons and from every commons brought in by uses.
resolve_file_record
resolve_file, recording binding edges into refs as the walk resolves them (v0.25). The project pass sets the sink’s per-file context; a fresh sink records nothing.
resolve_recovering
resolve, returning the symbol table with every resolve error rather than instead of it (#1663, Decision A). A resolve error is local to the declaration it is in, so the checker can still check every declaration — an unknown name in b no longer hides a type error in a. The table is complete apart from what the errors name (a duplicate keeps its first declaration). A caller that emits must still refuse on any error.
split_broken_decl_echoes
#1663 (Decision B): split resolve errors into those to report and those that only echo a declaration the parser had to skip.
without_resolve_echoes
#1663 (Decision A): the checker’s diagnostics after a resolve that reported errors, keeping only those in declarations the resolver found clean.