Expand description
Type checker and refinement validator (spec §§5–6, v0.1 §4.2, v0.2 §4.2).
Operates on a ResolvedCommons. Walks declarations, validates each
refinement against the spec’s predicate-base compatibility and combination
rules, then type-checks every function and method body.
v0.2 extensions:
- Record types (compatibility, field access, construction).
- Sum types and variant construction (qualified and unqualified).
- Methods (instance and static) with UFCS-style call resolution.
- Pattern matching with exhaustiveness checking.
- The
isoperator with binding flow into truthy contexts. - The built-in generic
Option[T].
Structs§
- Capability
Ctx - Mutable per-function context.
Capability bookkeeping for the checker — the
given-clause lifecycle and capability dispatch, grouped out of the checker’s working context (v0.29.10). Empty (Default) for pure functions / non-context code. - Capability
Info - Per-capability info for checker dispatch within a handler body.
- Capability
OpInfo - Check
Sinks - #522: the six output sinks a handler-body check writes into. One struct at
each call site instead of six positional
&mutarguments. - Checked
Program - T3.7 (R3.10): the gate between analysis and emission, as a type rather
than a control-flow decision — constructible only by
certify, so no unchecked or error-carryingTypedCommonscan reach the emitter by construction (previously enforced only by every caller happening to check aResultfirst).certifyrejects on any error-severity diagnostic; T3.3a’sTy::Erroris what a diagnosed checker failure records intoexpr_types, so in practice aTy::Errornever reaches aCheckedProgrameither — R4.3’s “rejected by certify” already holds today via the same diagnostic-severity gatecertifymakes structural. - Handler
Body Check - #522: everything
check_handler_bodyneeds to know about the handler — signature, capability scope, agent state, and held bindings. Replaces what was 17 positional parameters (of a 24-parameter signature);Self::newfills the agent/actor/store extras with empties, so a simple site (provider op, test body) sets only the fields it actually uses. - Record
Check - The outcome of
check_record: the typed model (Errif the file had any error) and, on the error path, the best-effort partialexpr_typesthe checker computed before bailing. Analyse mode surfaces that partial map for.-member completion and signature help even on a broken buffer (ADR 0094); on the Ok path the types live in theTypedCommons, so this is empty. - Test
Handler - One service handler, as a test body sees it (v0.178 / v0.182).
- Test
Service Sig - v0.178 (Slice 0, #662) / v0.182 (Slice A, #664): the shape a test body needs
to resolve a service invocation. Built by the project test pass from the
target unit’s service declarations, so the checker can resolve the addressed
handler (
svc.call(...)on anon callservice, or — Slice A —svc.GET("/x")/svc.schedule("…")/svc.message(m)on afrom http/cron/queueservice) and check its arity, argument types, and principal. - TyId
- T3.6b (R4.1/R4.2): a
Ty’s identity above the intern table —Copy,Hash,Ord, cheap to pass and compare. Resolved back to aTyonly via theTypestable it was interned into (see that type’s own doc). - Typed
Commons - Output of type checking.
- Typed
Expr - T3.4: an
expr_typesentry — the checked type, plus the span of the node it was computed for.Deref-free by design (.ty/.span, not.0/.1) so call sites read the same as they did against a bareTybefore this. - Types
- T3.6b (R4.1): the intern table
TyIdis minted from. Owned percheck_recordinvocation (design settled in the identity-and-totality track doc §9 before this slice started): created fresh atcheck_record’s entry, threaded throughCtx, carried out onTypedCommons/RecordCheckalongsideexpr_types, and forwarded across thebynk-check→bynk-emitboundary onCheckedProgram(T3.7a/T3.7b already built that seam). Confirmed safe by checking how cross-unit type references actually flow:compose_unit_symbolsmergesTypeDecl(immutable AST declarations) across units, never an already-internedTy/TyId— every unit re-interns its ownTygraph from shared declarations, soTyIds are never compared across two differentcheck_recordinvocations. - Variant
Info - A flattened view of a type’s variants (name + payload types).
Enums§
- Callee
- P6.0 (design/tracks/the-ir.md §6, #1139): a resolved classification of a
call-shaped expression, recorded once by the checker’s own dispatch
(
checker::calls) rather than re-derived by each later consumer — closing R6.10’s duplicated-classification gap betweenbynk-checkandbynk-emit’slower_method_call/lower_call. - Named
Kind - The shape of a named type — what its declaration looks like.
- Query
Role - Whether a
Callee::Querycall returns anotherQuery[T](chainable) or executes and returnsEffect[T]— R6.12: “the builder/terminal split is a field on the callee, not a name list.” Primarily read back fromcheck_query_kernel_method’s own return type at the recording site (query_role, below) — falling back tois_query_builder_nameonly when the call didn’t type at all (an arity mismatch, or a type error deeper inside the call —map’s own lambda body, say — both returnNonetoo, not just an arity failure), so a best-effort reader of an uncertified/erroring unit still gets the right role. - Store
Field - One agent
storefield’s kind and shape (finding #36) — the checker’s dispatch keys off this instead of five separate per-kind maps, so a new storage kind is one new variant rather than a sixth map threaded through every constructor and lookup site. - Ty
- A resolved type.
Functions§
- call_
record_ type_ name - The synthetic record type name for
trace(Cap.op)’s call records (v0.117): one record per capability operation, its fields the operation’s parameters. - certify
- The single place “may we emit?” is asked (R3.10). Rejects — returning
every diagnostic, not just the error-severity ones, matching
check_record’s own error-path convention — ifdiagnosticscontains an error-severity entry; otherwise wrapsprogramas certified. - check
- check_
body - Type-check a bare body against
return_tyinscope, withcapsavailable as both in-scope and declared capabilities and (if non-empty)test_services/test_actorsin scope for a test-case body’ssvc.call/by <Actor>(...)resolution (§32/#33: the one shape every hand-rolledCtxoutside this crate needed, lettingCtxitself staypub(crate)).where_pred, if present, is checked first againstBool(a property’s optionalfor all ... wherefilter —bynk.property.where_not_boolon mismatch), sharingctxwith the main body so both populate the sameexpr_types/errorssinks. Unlikecheck_handler_body, this skips the linearity pass, the return-type-mismatch diagnostic, and the unused-givendiagnostic — nothing outside this crate that built its ownCtxran those either, and adding them here would be a behaviour change, not a refactor. - check_
contracts - Check a function’s contract clauses (v0.115 §, testing track slice 3). A
contract is the invariant predicate attached to a function (ADR 0144 — one
predicate surface): each
requires/ensuresis a pureBool-typed expression,requiresover the parameters andensuresover the parameters plusresult(the return value; the awaited element for anEffect). The pass enforces, mirroringcheck_invariants: - check_
event_ field_ default - Events slice 3a (#972): type-check an event field’s default expression
(
field: T = init), reusingcheck_static_initialiser’s empty-pure-scope discipline. Pushesbynk.event.bad_field_default. - check_
handler_ body - Check a single handler body (used for service and agent handlers).
- check_
invariants - Check an agent’s invariant declarations (v0.80 §14). Each predicate is a pure
Bool-typed expression over the agent’s state fields (referenced by bare name), plusimplies/is. The pass enforces: - check_
record check, recording binding edges intorefsat the checker’s resolution sites (v0.25). A fresh sink records nothing.- check_
record_ in check_recordagainst a caller-supplied intern table (T3.6b, R4.1).- check_
state_ initialiser - v0.11: type-check an agent state-field initialiser (
field: T = init). Seecheck_static_initialiser. Pushesbynk.agents.bad_state_initialiser. - check_
transitions - Check an agent’s step invariants (v0.116 §, testing track slice 4). A
transitionis the invariant predicate widened to the step (ADR 0144 — one predicate surface): a pureBoolpredicate over theold/newstate pair, each bound to the agent’s synthetic state record (state_ty), soold.status/new.statusresolve like any record field. The pass enforces, mirroringcheck_invariants: - compatible
tis usable whereuis expected.- embedding_
for - v0.154 (ADR 0178): the declared error embedding that converts
source_errintotarget_err, if one exists. Whentarget_erris a sum declaringembeds E as VwithEcompatible withsource_err, returns(sum_type_name, variant_name)— the variant a value ofsource_errauto-wraps into. One level only: the source must match a declared embedding directly. Used by?in the checker (to accept the conversion) and the emitter (to lower theErr-wrap) from the same rule, so the two cannot diverge. - instantiate_
field_ ty - v0.157 (ADR 0183): the type of a generic record’s field at a concrete
instantiation. The field’s declared type is resolved with the declaration’s
type parameters in scope as rigid vars, then those vars are replaced by the
instantiation’s
args. For a non-generic record this is a plain resolve. - locale_
tag_ accepts - Whether
tagsatisfiesLocaleTag’s refinement — the check behindbynk.messages.invalid_locale_tag. Evaluated with the same regex-engine semantics (regress, anchored, no flags) the emittednew RegExp(...)runs under, so a tag accepted here is one the runtime cast is honest about. - locale_
tag_ pattern - The pattern
LocaleTag’s refinement matches against, for a diagnostic that names it.Noneif the type carries noMatchespredicate. - map_
entry_ ty - v0.158 (ADR 0184): the compiler-known
MapEntry[K, V]record — the element astore Map[K, V]’s.entriesquery yields. A nominal generic record ({ key: K, value: V }), so it flows throughunify/compatible/displayand the ADR 0183 non-boundary rule like any generic-record instantiation; its fields are resolved by name incheck_field_access(it has no user-visibleTypeDecl, likeJsonError). - named_
ty - Build a
Ty::Namedfor the given declaration (no applied type arguments). - named_
ty_ with_ args - Build a
Ty::Namedfor the given declaration with the given applied type arguments (empty for a non-generic reference). - record_
type_ refs - v0.25 (ADR 0053): record a binding edge for every
Namedreference inside a type-ref that resolved. Called alongside theresolve_type_ref*annotation sites;skipholds the enclosing fn’s type parameters (rigid vars are not type symbols). Handler signatures and body annotations never pass through the resolver’s reference walk, so these sites are their only recording point; where both passes run, assembly dedupes. - resolve_
type_ ref - resolve_
type_ ref_ in - v0.20a: like
resolve_type_ref, with a set of in-scope type parameters: aNamedreference matching one resolves toTy::Var(checked before the type-table lookup — a type parameter shadows a same-named declaration; the collision is diagnosed at the declaration). - type_
from_ decl - Build a
Tyfrom a TypeDecl name reference. - type_
param_ subst - v0.157 (ADR 0183): the substitution mapping a generic record’s declared
type parameters onto a concrete instantiation’s arguments. Empty when the
type is non-generic or
argsis empty (an under-applied reference — the resolver reports that separately). - variants_
of pubsince P6.4 (#1157, Decision A) — seeVariantInfo’s own doc comment for whybynk-emitneeds this exact function rather than a re-derived copy (R5.11, for the IR side).- zero_
value_ ts - The TypeScript zero-value expression for
type_ref(with an optional inline field refinement), orNoneif the type is not zeroable.