Expand description
bynk-ir: the resolved declaration-level vocabulary bynk-emit reads
through bynk-lower’s AST-analysis helpers, instead of re-deriving the
same facts from bynk_syntax::ast a second time.
Every type here is a value some bynk-lower helper returns from a
syntax-tree node plus the checker’s own TypedCommons, and some
bynk-emit site consumes — a service’s protocol (ProtocolIr), a
handler’s kind (IrHandlerKind) and route cache (CacheIr), a
type declaration’s structure (TypeShape), an agent store field’s
storage shape (StoreFieldIr/StoreKindIr), a
capability op’s or attached method’s signature (OpSig/FnSig), a
given capability reference (CapRefIr), an actor’s authentication
seam (ActorSeamIr), an events subscription’s pattern and shape
(EventPatternIr/EventPatternValueIr/EventSubscriberShape),
and the literal values a pattern can carry (ConstVal). Alongside
them: four AST-walk helpers bynk-emit reads (block_uses_emit,
walk_block_exprs, walk_exprs, match_needs_if_chain — they
live here rather than in the emitter because they were written to be
shared with bynk-lower, and bynk-emit is the consumer that remains).
Every pub item here has a reader outside both bynk-ir and
bynk-lower, and the gated unconsumed_ir_items probe
(cargo xtask greenfield-status) fails CI if one ever does not — the
two crates do not get to vouch for each other, since a bynk-ir type
constructed only by a bynk-lower helper nobody calls is exactly the
shape phase 6 shipped.
What this crate is not, and was. Phase 6 of
design/bynk-compiler-trajectory.md (the-ir.md, #1137) built here a
full typed expression IR — IrExpr/IrExprKind/IrStmt, patterns and
match compilation, IrItem with every declaration variant, IrHandler,
CommitShape — and bynk-lower built the &CheckedProgram → Ir pass
that constructed it. The emitter never consumed any of it: its own
string-emitting lowerer kept reading the AST, and the one production
route into the IR constructors lowered every events-service handler body
and discarded the result. The follow-on track (the IR cutover,
#1542) priced finishing that cutover, found it was a second code
generator rather than a retype, and re-settled on deletion: Slices D0–D2
removed the detour, the constructors and those 23 types. The refusal is
recorded with a trigger in design/bynk-greenfield-compiler.md Part 15.1
(Slice D3). design/archive/retired-tracks.md has both tracks’ closing
summaries.
Identity fields are adapted, not literal (Decision B of P6.1,
extending ADR 0333’s own precedent): the reference’s
DefId/FieldId/VariantId arena does not exist in this codebase, so
every such slot is whatever cheap resolved handle the checker already
has — a TyId for a type, a String for a name with no arena of its
own.
Two AST types are embedded on purpose (ADR 0366, P6.41):
TypeShape::Refined carries bynk_syntax::ast::BaseType and
Refinement directly, because the refinement predicate is rendered from
its syntax and an IR mirror would be a field-for-field copy with no
consumer of its own. The ast_importers probe’s own doc comment
(xtask/src/greenfield_status.rs) records that this embedding is
invisible to it by construction.
Structs§
- CacheIr
- A GET handler’s own
@cache(maxAge:, scope:)freshness policy (#1228), interpreted — raw{name, value: Expr}annotation pairs mean nothing on their own, so the seconds and the scope are resolved here once. Handler-scoped, not service-scoped:bynk_lower::lower_route_cache_iris a standalone per-route reader, wired directly intoemitter/workers_entry.rs’s own route construction — the same live, standalone-consumer shapelower_protocol_ir/lower_handler_given_ir/lower_actor_seam_iralready established. (A service-level policy struct existed beside it until Slice D2 of #1542; it had no consumer.) - CapRef
Ir - P6.14’s real
CapRefIr([DECISION A], #1174, review of #1186) — onebynk_syntax::ast::CapRefentry of a provider’s owngivenclause, under this module’s usual “no arena, bare name” substitution:context: Option<QualifiedName>flattens toOption<String>viaQualifiedName::joined()(the same.-joined formresolve_consume_prefix—bynk-emit/src/project.rs— already resolves againstconsumes/aliases), andname: Identflattens toString, mirroring every other bare-name identity field in this module. Deliberately not resolved further here: which context aSomeprefix actually names is whole-projectconsumes/alias data, a phase boundary this crate sits below — this type only preserves whatCapRefitself carries, unresolved. - Event
Pattern Ir - The payload of
ProtocolIr::Events’s ownpattern— afrom Events(E { field: value, .. })structural filter, [DECISION C] (#1171). Notbynk_syntax::ast::EventPatternreused verbatim, unlikeSchemaVersionPattern: that type carriesrest_span(a parse artefact for the grammar-required trailing.., giving a later reader nothing to act on) and its ownEventPatternValue::Variantis an unresolved, optionally-qualified name pair — exactly the shape this module’s whole posture rejects everywhere else. - Event
Subscriber Shape - #1226/#1187 slice 6: the two facts a service’s own event-subscriber
shape needs, captured at that unit’s own check time (its
CheckedProgramdoes not survive pastcheck_unit_files’s per-file loop) so a different unit’s own composition root can later decide whether its subscriber to this service wants the event envelope forwarded, without re-walking this unit’s rawUnitTable. Pure syntax, zeroTyIddependency. Produced bybynk_lower::lower_event_subscriber_shapes_ir, sized like #1187’s ownunit_callees(#1202) accumulator. - FnSig
- A
fn‘s own resolved signature, with nobodyand noreceiver— theOpSig-shaped value a foreign unit’s attached method needs when only its signature will ever be rendered (emit_attached_methods’ delegating forward atbynk_emit::emitter::emit::emit_forwarded_methods), never its body (P6.18).bynk_lower::lower_attached_fn_sig_ir_from_typesresolvesparams/return_tyin the scope the method’s own[T, …]list names (mirroringOpSig‘s identicaltype_paramstreatment) — a genuinely unresolvable name degrades toTy::Unit, deliberately, the same non-panicking postureOpSigalready established: nothing checker-side actually validates an attached method’s ownparams/return_typeagainst the importing context’s own visible types (only the declaring commons’ own checking does), so a resolve miss here is an expected, not exceptional, state. - OpSig
- A capability op’s resolved signature (P6.12, [DECISION A], #1173) —
bynk_lower::lower_capability_ops_ir’s per-op return value, read byemit_capability(bynk-emit/src/emitter/emit.rs). The reference’s own sketch namedops: Vec<OpSig>(bynk-greenfield-compiler.md:1134) without defining the type; this is that shape, adapted frombynk_syntax::ast::CapabilityOp— a signature only, no body — under this crate’s “no arena” substitutions:params: Vec<(String, TyId)>, andtype_params: Vec<String>mirroring the checker’s own already-resolvedCapabilityOpInfo::type_params(bynk-check/src/checker.rs) — a bare rigid-variable name, not aTypeParamAST node, since nothing here re-derives bounds a capability op’s own[T, …]list never carries in the first place (#926).bynk_lower::lower_op_sig_irresolvesparams/return_tyin the scopetype_paramsnames, mirroringcontext_checks::build_capability_op_info’s ownvarstreatment (bynk-check/src/context_checks.rs) so a generic op’s ownTsurvives asTy::Var("T")rather than collapsing toTy::Unit. On a genuinely unresolvable name aparams/return_tyentry isTy::Unit, deliberately — seebynk_lower::lower_op_sig_ir’s own doc comment for why that mirrors the checker’s own fallback rather than panicking. - Store
Field Ir - An agent
storefield’s storage shape (design/bynk-greenfield-compiler.md§6.6, R6.14, #1163) —bynk_lower::lower_store_field_shape_ir’s return value, read byemit_agent’s state section. Mirrorschecker::StoreField’s own five-kind dispatch (bynk-check/src/checker.rs) in shape, but is persistent IR data, not that checking pass’s own ephemeral, per-agent scratch value — the two are deliberately not unified. Shape only: aCellfield’s initialiser expression is rendered by the emitter from the AST (theinitslot that once carried it as anIrExprwent with the expression IR in Slice D2 of #1542).
Enums§
- Actor
Seam Ir - #1187’s slice 3: a handler’s resolved actor-verification seam, wrapping
bynk-check’s own five already-resolved seam structs (bynk-check/src/actors.rs) by value — confirmed none carry anybynk_syntax::ast/TypeRef/Expr: every field isString/bool/i64/Option/Vec(or, forBearerSeam::authorization,ClaimPredicate, itself a plain recursiveString/Boxenum). Built bybynk_lower::lower_actor_seam_ir, which tries the five resolvers in the one priority order that actually matters —sum_members_forfirst, since it’s the only resolver whose result can otherwise collide withbearer_seam_for’s (a sum’s own first peer can itself be Bearer-schemed;bearer_seam_forhas noby.is_sum()guard of its own to prevent that). The other three pairs are mutually exclusive by construction — each single-actor resolver requires the primary actor’s ownauthscheme to match one specificSchemevariant, a closed set — so their relative order here is a no-op, not a second load-bearing decision. - Const
Val - A literal value an event-subscription pattern can match on
(
EventPatternValueIr::Const). Adapted from the reference’s ownConstVal(Int Float Str Bool Unit Bytes, Part 6.2’s comment):DurationMillisreplacesBytesbecause Bynk has a real<int>.<unit>duration literal (ExprKind::DurationLit)Constmust cover, whileByteshas no literal AST form at all in this language — everyBytesvalue comes from a static-constructor call (Bytes.fromUtf8/fromBase64/empty(),Callee::Intrinsicterritory, not a literal). - Event
Pattern Value Ir - One
EventPatternIrfield’s own matched value. - IrHandler
Kind - P6.24a: an IR-native mirror of
bynk_syntax::ast::HandlerKind— a field-for-field copy, not a re-export. Every field (HttpMethod, a routepath: String, a cronexpr: String) is already fully resolved at parse time; nothing here ever neededTyId/CheckedProgram, sobynk_lower::lower_handler_kind_iris a pure, unconditional conversion — unlike almost everything else in this module, it carries no ADR 0334 totality story because it can never fail to resolve. - IrHttp
Method IrHandlerKind::Http’s own method field — a field-for-field mirror ofbynk_syntax::ast::HttpMethod, same reasoning asIrHandlerKinditself.- Protocol
Ir - P6.11’s real
ProtocolIr([DECISION A], #1171) — one variant perbynk_syntax::ast::ServiceProtocolvariant,bynk_lower::lower_protocol_ir’s own return value. The reference’s own sketch specifies only two of the six:Events { event, pattern, schema_dispatch }(bynk-greenfield-compiler.md:1881) andWebSocket { in_ty, out_ty }(:1959) — field names taken verbatim from those two rows.Call/Http/Croncarry no payload, not because one was dropped: the actual per-trigger binding (a route, a schedule) lives on each handler (HandlerKind::Http { method, path }/Cron { expr }), already reachable throughIrHandlerKind—ServiceProtocol’s own doc comment says this in as many words (“the endpoint lives on each handler”), which is why the reference never spells these three out either. E2 (:1737) constrains the set, not the shape: “a closed nominal set … grows one variant per real trigger” — the AST’s own closedServiceProtocolalready satisfies that exactly, so this type is total over what a certified program’s own service can declare, the same claimStoreKindIr’s own doc makes aboutQueuebeing gated pre-certify. - Store
Kind Ir - P6.7’s real
StoreKindIr(Part 6.6, R6.14, #1163) — five variants, one per functional storage kind (Cell/Map/Set/Cache/Log).Queueis not a variant here:bynk.store.kind_unsupportedgates it beforecertify(R3.10), so this type is total over what a certified program’s own store fields can actually contain, not a subset some later slice needs to extend. [DECISION B]:Durationsubstitutes toi64milliseconds throughout — the same substitutionConstVal::DurationMillisandchecker::StoreField::Cache’s own already-resolved TTL already made. - Type
Shape - A declared type’s own resolved structure (Part 6.6, #1161) —
bynk_lower::lower_type_shape_ir’s return value. Covers the AST’s fourTypeBodyvariants (Refined/Record/Sum/Opaque) with the reference’s own three ([DECISION A]):Opaqueunifies intoRefinedvia its ownopaque: boolfield, mirroringemitter/emit.rs’s ownRefinedShape { base, refinement, is_opaque }— the shipped emitter’s own precedent for exactly this unification (emit_type,emitter/emit.rs:19).
Functions§
- block_
uses_ emit - Events track, slice 0 (spine #936): does this block contain a real
Events.emit[...]call anywhere — including nested branches, match arms, lambdas, and any other expression position (aParen, anOk/Errwrapper, aCall/RecordConstructionargument, aBinOpoperand, …)? Gates release-at-commit buffer threading (deps.__events) so a handler that never emits keeps byte-identical output, mirroringblock_uses_send’s gate ondeps.__exec. - match_
needs_ if_ chain - A match needs the if/else-if lowering (ADR 0169) when any arm carries a guard
or a refutable nested payload pattern — a JS
switchon.tagcan express neither. Flat, unguarded matches keep theswitch(zero churn to existing output). - walk_
block_ exprs - walk_
exprs - v0.22b: pre-order expression visitor — visits
e, then every sub-expression, including statements and tails of nested blocks. Driven byast::expr_children, the exhaustive total child iterator, rather than a hand-matched recursion duplicating it — a newExprKindvariant fails to compile inexpr_childrenuntil it is taught to visit it, instead of silently under-visiting here.