Skip to main content

Crate bynk_ir

Crate bynk_ir 

Source
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_ir is a standalone per-route reader, wired directly into emitter/workers_entry.rs’s own route construction — the same live, standalone-consumer shape lower_protocol_ir/lower_handler_given_ir/ lower_actor_seam_ir already established. (A service-level policy struct existed beside it until Slice D2 of #1542; it had no consumer.)
CapRefIr
P6.14’s real CapRefIr ([DECISION A], #1174, review of #1186) — one bynk_syntax::ast::CapRef entry of a provider’s own given clause, under this module’s usual “no arena, bare name” substitution: context: Option<QualifiedName> flattens to Option<String> via QualifiedName::joined() (the same .-joined form resolve_consume_prefix — bynk-emit/src/project.rs — already resolves against consumes/aliases), and name: Ident flattens to String, mirroring every other bare-name identity field in this module. Deliberately not resolved further here: which context a Some prefix actually names is whole-project consumes/alias data, a phase boundary this crate sits below — this type only preserves what CapRef itself carries, unresolved.
EventPatternIr
The payload of ProtocolIr::Events’s own pattern — a from Events(E { field: value, .. }) structural filter, [DECISION C] (#1171). Not bynk_syntax::ast::EventPattern reused verbatim, unlike SchemaVersionPattern: that type carries rest_span (a parse artefact for the grammar-required trailing .., giving a later reader nothing to act on) and its own EventPatternValue::Variant is an unresolved, optionally-qualified name pair — exactly the shape this module’s whole posture rejects everywhere else.
EventSubscriberShape
#1226/#1187 slice 6: the two facts a service’s own event-subscriber shape needs, captured at that unit’s own check time (its CheckedProgram does not survive past check_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 raw UnitTable. Pure syntax, zero TyId dependency. Produced by bynk_lower::lower_event_subscriber_shapes_ir, sized like #1187’s own unit_callees (#1202) accumulator.
FnSig
A fn‘s own resolved signature, with no body and no receiver — the OpSig-shaped value a foreign unit’s attached method needs when only its signature will ever be rendered (emit_attached_methods’ delegating forward at bynk_emit::emitter::emit::emit_forwarded_methods), never its body (P6.18). bynk_lower::lower_attached_fn_sig_ir_from_types resolves params/return_ty in the scope the method’s own [T, …] list names (mirroring OpSig‘s identical type_params treatment) — a genuinely unresolvable name degrades to Ty::Unit, deliberately, the same non-panicking posture OpSig already established: nothing checker-side actually validates an attached method’s own params/return_type against 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 by emit_capability (bynk-emit/src/emitter/emit.rs). The reference’s own sketch named ops: Vec<OpSig> (bynk-greenfield-compiler.md:1134) without defining the type; this is that shape, adapted from bynk_syntax::ast::CapabilityOp — a signature only, no body — under this crate’s “no arena” substitutions: params: Vec<(String, TyId)>, and type_params: Vec<String> mirroring the checker’s own already-resolved CapabilityOpInfo::type_params (bynk-check/src/checker.rs) — a bare rigid-variable name, not a TypeParam AST 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_ir resolves params/return_ty in the scope type_params names, mirroring context_checks::build_capability_op_info’s own vars treatment (bynk-check/src/context_checks.rs) so a generic op’s own T survives as Ty::Var("T") rather than collapsing to Ty::Unit. On a genuinely unresolvable name a params/return_ty entry is Ty::Unit, deliberately — see bynk_lower::lower_op_sig_ir’s own doc comment for why that mirrors the checker’s own fallback rather than panicking.
StoreFieldIr
An agent store field’s storage shape (design/bynk-greenfield-compiler.md §6.6, R6.14, #1163) — bynk_lower::lower_store_field_shape_ir’s return value, read by emit_agent’s state section. Mirrors checker::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: a Cell field’s initialiser expression is rendered by the emitter from the AST (the init slot that once carried it as an IrExpr went with the expression IR in Slice D2 of #1542).

Enums§

ActorSeamIr
#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 any bynk_syntax::ast/TypeRef/Expr: every field is String/bool/ i64/Option/Vec (or, for BearerSeam::authorization, ClaimPredicate, itself a plain recursive String/Box enum). Built by bynk_lower::lower_actor_seam_ir, which tries the five resolvers in the one priority order that actually matters — sum_members_for first, since it’s the only resolver whose result can otherwise collide with bearer_seam_for’s (a sum’s own first peer can itself be Bearer-schemed; bearer_seam_for has no by.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 own auth scheme to match one specific Scheme variant, a closed set — so their relative order here is a no-op, not a second load-bearing decision.
ConstVal
A literal value an event-subscription pattern can match on (EventPatternValueIr::Const). Adapted from the reference’s own ConstVal (Int Float Str Bool Unit Bytes, Part 6.2’s comment): DurationMillis replaces Bytes because Bynk has a real <int>.<unit> duration literal (ExprKind::DurationLit) Const must cover, while Bytes has no literal AST form at all in this language — every Bytes value comes from a static-constructor call (Bytes.fromUtf8/fromBase64/empty(), Callee::Intrinsic territory, not a literal).
EventPatternValueIr
One EventPatternIr field’s own matched value.
IrHandlerKind
P6.24a: an IR-native mirror of bynk_syntax::ast::HandlerKind — a field-for-field copy, not a re-export. Every field (HttpMethod, a route path: String, a cron expr: String) is already fully resolved at parse time; nothing here ever needed TyId/CheckedProgram, so bynk_lower::lower_handler_kind_ir is 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.
IrHttpMethod
IrHandlerKind::Http’s own method field — a field-for-field mirror of bynk_syntax::ast::HttpMethod, same reasoning as IrHandlerKind itself.
ProtocolIr
P6.11’s real ProtocolIr ([DECISION A], #1171) — one variant per bynk_syntax::ast::ServiceProtocol variant, 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) and WebSocket { in_ty, out_ty } (:1959) — field names taken verbatim from those two rows. Call/ Http/Cron carry 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 through IrHandlerKind — 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 closed ServiceProtocol already satisfies that exactly, so this type is total over what a certified program’s own service can declare, the same claim StoreKindIr’s own doc makes about Queue being gated pre-certify.
StoreKindIr
P6.7’s real StoreKindIr (Part 6.6, R6.14, #1163) — five variants, one per functional storage kind (Cell/Map/Set/Cache/Log). Queue is not a variant here: bynk.store.kind_unsupported gates it before certify (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]: Duration substitutes to i64 milliseconds throughout — the same substitution ConstVal::DurationMillis and checker::StoreField::Cache’s own already-resolved TTL already made.
TypeShape
A declared type’s own resolved structure (Part 6.6, #1161) — bynk_lower::lower_type_shape_ir’s return value. Covers the AST’s four TypeBody variants (Refined/Record/Sum/Opaque) with the reference’s own three ([DECISION A]): Opaque unifies into Refined via its own opaque: bool field, mirroring emitter/emit.rs’s own RefinedShape { 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 (a Paren, an Ok/Err wrapper, a Call/RecordConstruction argument, a BinOp operand, …)? Gates release-at-commit buffer threading (deps.__events) so a handler that never emits keeps byte-identical output, mirroring block_uses_send’s gate on deps.__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 switch on .tag can express neither. Flat, unguarded matches keep the switch (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 by ast::expr_children, the exhaustive total child iterator, rather than a hand-matched recursion duplicating it — a new ExprKind variant fails to compile in expr_children until it is taught to visit it, instead of silently under-visiting here.