Expand description
P6.1 (design/tracks/the-ir.md §6, #1141): the IR’s core node types —
IrExpr/IrExprKind/IrStmt, Part 6.2 of design/bynk-greenfield-compiler.md.
R6.1 — “Every IrExpr carries its type. The constructor requires it;
there is no side table and no fallible lookup from the emitter to the
checker.” This module is the type only; bynk_lower is the
&CheckedProgram → Ir pass that constructs values of it.
Identity fields are adapted, not literal (Decision B, extending P6.0’s
own precedent, ADR 0333 the-ir-callee-in-bynk-check): the reference’s
DefId/FieldId/LocalId/VariantId arena does not
exist in this codebase (no Resolve phase mints them —
project-model.md §3.4 deferred that to phase 8), so every such slot
becomes whatever cheap resolved handle the checker already has —
Arc<TypeDecl>/Arc<FnDecl> for a declaration, String for a name with
no arena of its own. Call’s payload is Callee verbatim — P6.0
already did this exact substitution for call-dispatch identity, so Call
needs no adaptation of its own here.
The whole Part 6.2 shape lands in one piece (Decision D): every
variant below exists, including Match/Variant/Call/Lambda, so a
later slice (P6.2, P6.4, P6.5) widens only bynk_lower’s match, never this
type. Match’s own payload is four types: IrPat/IrArm/
Exhaustive are P6.4’s own commission (#1157, Part 5.1/5.2 of the
reference); MatchForm is P6.5’s own (#1159, R5.2/R5.3, scoped to
shape only — Decision A). All four are real, constructible types as of
P6.5, wired into a real IrExprKind::Match by bynk_lower’s
ExprKind::Match arm, which calls P6.4’s own standalone constructors
(bynk_lower::lower_pattern_ir/bynk_lower::lower_arm_ir/
bynk_lower::lower_exhaustive_ir) verbatim. Question/Is stay
desugars-to-Match in name only — neither gets real construction this
slice, each for a reason specific to it (see bynk_lower’s own todo!()
text for each).
Decision D’s own “never widens beyond the reference’s Part 6.2 shape”
is not absolute — it held only as long as the reference’s own node set
was complete (IrExprKind::BinOp/IrExprKind::Neg/
IrExprKind::InterpStr, #1189).
design/bynk-greenfield-compiler.md §6.2’s own listing (Const, Local, Global, Record, Variant, Field, List, Block, If, Match, And, Or, Not, Return, Call, Lambda, Await, Send, Pure) never names comparison,
arithmetic, unary negation, or string interpolation at all — confirmed a
true omission, not a deliberately-out-of-scope row this track chose to
defer (unlike, say, Question/Is above, each of which the reference
does name and this slice explicitly declines). P6.2 (#1143) and P6.3
(#1145) each independently confirmed the gap and left it a todo!()
rather than force-fitting it under Decision D‘s closed-shape framing;
#1189 settled it as buildable, three new variants, added here rather than
deferred indefinitely — real programs use +/-/</== routinely (a
certified .bynk invariant like balance >= 0 is ordinary, not an edge
case), and the checker already resolves every one of these expressions’
types (bynk-check/src/checker/expressions.rs’s check_binop/
check_unary, and checker.rs’s own InterpStr hole-checking) — nothing
about them was actually blocked on new checker work, only on this type
gaining a place to land them.
P6.6 (#1161) adds IrItem/TypeShape (Part 6.6) — a separate
top-level type from IrExpr, not a new IrExprKind variant:
declarations and expressions are different node families in the
reference sketch, and unlike Match’s own payload, IrItem carries no
constraint that its whole seven-variant design-sketch shape must exist as
of this slice — see IrItem’s own doc comment for exactly which are
real.
P6.7 (#1163) adds StoreFieldIr/StoreKindIr/IndexIr (Part
6.6’s own trailing two structs, R6.14) — an agent store field’s own
state shape and index-table keys, derived once here rather than
re-derived independently by both the checker’s own ephemeral
checker::StoreField dispatch and the shipped emitter’s own
store_map_fields/store_cache_fields/store_log_fields/…
(bynk-emit/src/emitter/emit.rs). Same posture as every prior P6.x
slice: no IrItem variant references these yet — IrItem::Agent/
Service remain unconstructed (see IrItem’s own doc comment for
exactly what still blocks them).
P6.8 (#1165) adds CommitShape/IrPredicate (Part 6.7’s own
trailing two types, R6.15) — a handler body’s own resolved one-of-three
commit shape, decided once here from a mutating Callee::Store write or
a bare :=, rather than re-derived at emission time by the shipped
emitter’s own name-matching block_writes_state (R6.5). Same posture
again: no IrItem variant references either yet — IrHandler itself
still does not exist, and no rule in design/tracks/the-ir.md’s own
slice table commissions it (see IrItem’s own doc comment).
P6.9 (#1167) adds IrStmt::Assign/ActorBinder/IrHandler
(Part 6.7’s own trailing construct, R6.16) — a real, standalone,
constructible agent on call handler, plus the Statement::Assign
prerequisite gap (todo!() since P6.1, twice deferred by P6.7/P6.8’s own
Risks) it needed closed first: bynk_lower::lower_handler_ir cannot lower a
store-writing handler body without a real IrStmt target for :=.
binder: Option<ActorBinder> is always None from
bynk_lower::lower_handler_ir this slice — an agent handler structurally
cannot carry one (bynk.actor.by_on_agent); a real service handler’s own
non-None binder needs a bynk-check change this slice does not make
(see IrHandler’s own doc comment). Same posture again: no IrItem
variant references IrHandler yet — IrItem::Agent/Service remain
unconstructed (see IrItem’s own doc comment).
P6.10 (#1169) adds IrItem::Agent — the assembly P6.9’s own Risks
section named without numbering it: every ingredient (StoreFieldIr
since P6.7, CommitShape/IrPredicate since P6.8, IrHandler since
P6.9) was already real, but nothing combined them into one IrItem
value, and IrItem had no variant to carry the result. This is the
first IrItem variant with a real consumer shape below the top level —
bynk_lower::lower_agent_item_ir calls every prior slice’s own standalone
constructor rather than re-deriving any of their logic, the same
“wire, don’t re-derive” posture this whole module has held since P6.0.
IrItem::Service/Actor/Capability/Provider remain deferred, each
for its own reason (see IrItem’s own doc comment).
P6.11 (#1171) adds IrItem::Service — the sibling assembly to
P6.10’s Agent, closing both blockers IrItem’s own doc comment named:
#1170 made a service handler’s binder readable post-certify for the
first time, and this slice specifies the two types the reference names
but never defines for a service (ProtocolIr, PolicyIr).
bynk_lower::lower_service_handler_ir is a new sibling of
bynk_lower::lower_handler_ir, not a widening of it — the two seed
disjoint scopes, and widening would have deleted the agent-only by
assertion that today catches a service handler reaching the wrong entry
point. A from websocket lifecycle handler’s own body
(on open/on message/on close) is the one named exception: the
checker-injected synthetic connection param has no IR target yet, so
lowering one hits an explicit todo!() rather than a silently wrong
tree — see bynk_lower::lower_service_handler_ir’s own doc comment.
IrItem::Actor/Capability/Provider remain deferred, each with its
own already-tracked, genuinely unsettled blocker (see IrItem’s own
doc comment).
P6.12 (#1173) adds IrItem::Capability — the reference’s own
ops: Vec<OpSig> sketch (bynk-greenfield-compiler.md:1134) named a
type it never defined anywhere in the document; OpSig is that
missing shape, adapted from bynk_syntax::ast::CapabilityOp under this
module’s own “no arena” substitution (Vec<(String, TyId)> for
params, mirroring IrItem::Fn::params exactly).
bynk_lower::lower_capability_item_ir resolves each op’s own params/
return_type in that op’s own rigid-variable scope (op.type_params)
— a capability op’s type parameters are scoped to the op itself, not
the capability, unlike a method’s generic receiver — the same per-op
treatment the checker’s own CapabilityOpInfo already gives a generic
op (context_checks.rs’s build_capability_op_info). IrItem::Actor/
Provider remain deferred, each with its own already-tracked,
genuinely unsettled blocker (see IrItem’s own doc comment).
P6.14 (#1174) adds IrItem::Provider — #1174’s own grounding pass
found this buildable, unlike its sibling Actor: the reference’s own
body: ProviderBody // Bynk ops | External(module) comment names a type
it never defines, the same gap OpSig closed for Capability, and
ProviderDecl::external already carries the exact dispatch it needs.
bynk_lower::lower_provider_op_ir is a new sibling of
bynk_lower::lower_fn_body_ir, not a widening of it, for the same “each
body-lowering entry point seeds its own scope” reason
bynk_lower::lower_handler_body_ir’s own doc comment already gives: no
self, no store cells, just a provider op’s own params — given
capability calls need no scope entry of their own, the same
already-generic Callee-wrapping bynk_lower::lower_handler_body_ir’s own
doc comment credits for handler bodies. IrItem::Actor remains
deferred, its own already-tracked blocker unchanged by this slice (see
IrItem’s own doc comment).
Structs§
- Actor
Binder - P6.9’s real
ActorBinder([DECISION A], #1167) — referenced by the reference’s ownIrHandlersketch (bynk-greenfield-compiler.md:1169-1177,binder: Option<ActorBinder>) but never defined anywhere in the document — the same “referenced, not specified” gap #1161’s own Decision C named forEmbedIr, #1163’s own Decision C named forIndexIr, and #1165’s own Decision A named forIrPredicate. Mirrors the checker’s own already-resolvedactor_binding: Option<(String, TyId)>shape (bynk-check/src/checker.rs’sHandlerBodyCheck::actor_binding):binderis the bound name,tythe sealedTy::Actor(identity)orTy::ActorSum(members)— both ordinary, already-realTyIds, no synthetic type needed the way<Agent>Statewas for P6.8’sstate_ty. No dedicatedlower_actor_binder_irconstructor: the pair has no further structure to derive, mirroringEmbedIr’s/IndexIr’s own “no further structure, plain tuple/alias” precedent — as of P6.11 (#1171),bynk_lower::lower_service_handler_iris the real caller that readsTypedCommons::actor_bindings(#1170) and writesActorBinder { binder, ty }directly;bynk_lower::lower_handler_ir(agent-only) still never does — seeIrHandler’s own doc comment. - CacheIr
- #1228’s own
CacheIr— a GET handler’s own@cache(maxAge:, scope:)freshness policy, interpreted the same “raw{name, value: Expr}pairs mean nothing on their own” disciplinePolicyIr’s own doc comment argues for, but deliberately not aPolicyIrfield.PolicyIritself is built only bybynk_lower::lower_policy_ir, which onlybynk_lower::lower_service_item_ircalls — and nothing in the shipped emitter constructs a realIrItem::Serviceyet (every call site is this module’s own test suite). NestingCacheIrunderPolicyIrwould land it inert: real inbynk_lower’s own tests, with zero effect on any emitted route.@cacheis also handler-scoped, not service-scoped (PolicyIr’s whole shape), so it was never a natural fit regardless.bynk_lower::lower_route_cache_iris a standalone reader instead, 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. - 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, the same phase boundaryIrItem’s own doc comment already names forProviderBody::External’s missingmodulefield — this type only preserves whatCapRefitself carries, unresolved. - Connection
Binder - P6.13’s real
ConnectionBinder([DECISION G], #1179) — the synthetic leadingconnection: Connection[out]binding afrom websocketlifecycle handler’s body receives,bynk_lower::lower_service_handler_ir’s own return value for exactly theon open/on message/on closehandlers of aServiceProtocol::WebSocketservice. Mirrors the checker’s ownopen_connection_param(bynk-check/src/context_checks.rs:2020-2032):tyis the resolvedTy::Connection(out_ty), always present regardless of handler kind — unlikeActorBinder, there is noNonecase here, since a websocket lifecycle handler is checker-guaranteed to receive this binding (bynk.service.websocket_open_arity,context_checks.rs:742-763, plus the unconditional injection atcontext_checks.rs:1944-1954). - CorsIr
- The payload of
PolicyIr::cors— present only when the source declares acors { }block at all (ADR 0159’s own opt-in posture); the asymmetry withPolicyIr::securityis this struct’s load-bearing content, not an inconsistency. - 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
- P6.18: a
fn‘s own resolved signature, with nobodyand noreceiver— the narrow,OpSig-shaped sibling 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. Deliberately notIrItem::Fn: that variant mandates loweringbody: IrExprthroughbynk_lower::lower_fn_body_ir, which stilltodo!()s onExprKind::Question/Is(design/tracks/ the-ir.md §6’s own P6.3 correction) — forcing everyuses-imported method through that gate for a signature nothing here ever reads would make this reader strictly less total than the raw-TypeRefcode it replaces, for zero benefit.bynk_lower::lower_fn_sig_ir_from_typesresolvesparams/return_tyin the scope the method’s own[T, …]list names (mirroringbynk_lower::lower_op_sig_ir_from_commons‘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. - Global
Ref - The payload of
IrExprKind::Global— adapted per Decision C’s narrow scope, refined during implementation: a bare nullary sum-variant constructor reference (Miss,PaymentDeclined), identified structurally (exactly one sum type inTypedCommons::typesowns a variant of this name with an empty payload — the same “unique owner” testcheck_ident’s own unconditional fallback arm uses,bynk-check/src/checker/expressions.rs:103-130). - IrArm
- P6.4’s real
IrArm(Part 5.1, #1157) — the reference’s own bare sketch (struct IrArm { pat, guard, body, binds }) adapted per this module’s “no arena” substitution (local: LocalId -> String,pat: PatId -> IrPatowned directly) plus one field the sketch doesn’t carry:binding_mode(Decision C, R5.5, computed once during this arm’s own construction rather than re-walked by any later reader —design/bynk-greenfield-compiler.md:749-751). SeeBindingMode’s own doc comment for exactly what this one arm-level flag does and doesn’t tell a future reader. - IrExpr
- A lowered expression: its shape, its checked type, and the source span it
came from.
tyis required at construction (R6.1) — neverOption, never looked up lazily by a reader. - IrHandler
- IrPredicate
- P6.8’s real
IrPredicate([DECISION A], #1165) — referenced by the reference’s ownCommitShape::Transactional { invariants: Vec<IrPredicate>, transitions: Vec<IrPredicate> }andIrItem::Agent’s own sketch (bynk-greenfield-compiler.md:1130-1131/1182), but never defined anywhere in the document — the same “referenced, not specified” gap #1161’s own Decision C named forEmbedIrand #1163’s own Decision C named forIndexIr. One type serves both an agent’s owninvariantsandtransitionsfields, rather than two near-identical structs:Invariant/Transition(bynk_syntax::ast) already share this exact shape — a name plus aBool-typed predicate expression — andbynk_lower::lower_invariant_ir/bynk_lower::lower_transition_irdiffer only in how they seed the predicate’s own scope (an invariant over the agent’sstoreCellfields, a transition overold/new), not in what they produce.name: Stringis this module’s own “no arena” substitution ([DECISION B] extended) — a predicate has noDefIdof its own in the reference either, referenced only by position within its owningVec. - OpSig
- P6.12’s real
OpSig([DECISION A], #1173) — referenced by the reference’s ownIrItem::Capabilitysketch (bynk-greenfield-compiler.md:1134,ops: Vec<OpSig>) but never defined anywhere in the document — the same “referenced, not specified” gap #1161’s own Decision C named forEmbedIr, #1163’s own Decision C named forIndexIr, #1165’s own Decision A named forIrPredicate, and #1167’s own Decision A named forActorBinder. Adapted frombynk_syntax::ast::CapabilityOp— a signature only, no body — under this module’s already-established “no arena” substitutions:params: Vec<(String, TyId)>mirrorsIrItem::Fn::paramsexactly (Decision E, #1161), andtype_params: Vec<String>mirrors 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. - Policy
Ir - P6.11’s real
PolicyIr/CorsIr/SecurityIr([DECISION D], #1171) — the interpreted (not passed-through) form of afrom httpservice’scors/security/limitsblocks,bynk_lower::lower_policy_ir’s own return value. The reference namesPolicyIronce, in theIrItem::Servicesketch itself (:1132), and never elsewhere — the same “referenced, not specified” gap every trailing struct in this module has carried before its own slice specified it. - Provider
OpIr - P6.14’s real
ProviderOpIr([DECISION A], #1174) — oneProviderOp(bynk-syntax/src/ast.rs:604-611), signature and body, unlikeOpSig’s signature-only shape: aCapabilityOpnever carries a body (a capability is a contract), but aProviderOpalways does. Notype_params, unlikeOpSig:ProviderOpcarries none of its own (#1173’s ownOpSig::type_paramsdoc comment names the same absence forCapabilityDecl, but a capability op’s[T, …](#926) has no provider-op equivalent — nothing in the grammar or the checker’s owncheck_provider_declsgives a provider op a[T, …]list to parse). - Security
Ir - The payload of
PolicyIr::security— always present for an HTTP service (seePolicyIr::security’s own doc comment). - Store
Field Ir - P6.7’s real store-field state shape (
design/bynk-greenfield-compiler.md§6.6, R6.14, #1163) — the payload of an agentstorefield declaration,bynk_lower::lower_store_field_ir’s own return value. Mirrorschecker::StoreField’s own five-kind dispatch (bynk-check/src/checker.rs) in shape, but is persistent IR data with no consumer yet, not that checking pass’s own ephemeral, per-agent scratch value — the two are deliberately not unified (seebynk_lower::lower_store_field_ir’s own doc comment). NoIrItemvariant references this yet —IrItem::Agent/Serviceremain unconstructed (IrItem’s own doc comment names exactly what still blocks them).
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. - Binding
Mode - R5.5, Decision C —
OrDispatchiffIrPat::Oroccurs anywhere in the arm’s own pattern tree, computed once bybynk_lower::lower_arm_ir. - Commit
Shape - P6.8’s real
CommitShape(Part 6.7, R6.15, #1165) — a handler body’s own resolved one-of-three commit shape,bynk_lower::lower_commit_shape_ir’s own return value. Matches the reference’s own three-variant shape verbatim (bynk-greenfield-compiler.md:1179-1183) — no substitution needed,Transactional’s own payload already reusesIrPredicaterather than carryingInvariant/TransitionAST nodes directly. Shape-agnostic between an agent and a service handler ([DECISION F]): as of P6.11 (#1171),bynk_lower::lower_service_handler_iris the real service call site this decision predicted, passing emptyinvariants/transitionsslices, and the identical write-detection walk (bynk_lower::lower_commit_shape_ir’s own doc comment) naturally finds neither a mutatingCallee::Storenor a bare:=in a service body (a service declares nostorefields to write), soTransactionalis never constructed for one — the shipped emitter’s ownemit_servicealready only ever produces the other two shapes, for the same reason. - Const
Val - A constant value — the payload of
IrExprKind::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. - Exhaustive
- P6.4’s real
Exhaustive(Part 5.2, R5.6/R5.7, #1157) —Partial’s own witness payload reuses the checker’s already-shippedmissing_patternsshape (Vec<String>, human-readable witness descriptions) rather than inventing the reference’s own unspecifiedPatternWitnessstruct (Decision B). Both variants are real and matchable, but this slice’s ownbynk_lower::lower_exhaustive_ironly ever constructsTotal— see that function’s own doc comment for whyPartialis real, inhabited code, yet unreached here. - IrBinOp
IrExprKind::BinOp’s own operator tag (#1189) — everybynk_syntax::ast::BinOpmember that lowers intoBinOprather than its own dedicatedIrExprKindvariant or a desugar (seeBinOp’s own doc comment for which and why). Deliberately a plain tag with no payload of its own —lhs/rhsalready carry their own resolvedty, and unlike, say,StoreKindIr, no variant here needs anything beyond its own identity.- IrExpr
Kind - A lowered expression’s shape. Every node kind from Part 6.2 exists here
(Decision D);
bynk_lowerimplements real construction only for the subset named indesign/tracks/the-ir.md’s own P6.1 row — every other arm is a namedtodo!()in the lowering pass, not a missing variant here. - IrHandler
Kind - P6.9’s real
IrHandler([DECISION C], #1167) — an agenton callhandler’s own resolved shape,bynk_lower::lower_handler_ir’s own return value. Six of the reference’s own eight sketched fields are its verbatim shape (bynk-greenfield-compiler.md:1169-1177) under this module’s already-established substitutions:kind: IrHandlerKind— originallyHandlerKindreused verbatim frombynk_syntax::ast(the same “reused, not adapted” treatmentIrExprKind::Call’s ownCalleepayload got), converted to a real IR-native mirror by P6.24a once a purely-structural emitter reader (no body, noIrItem::Service) needed to match on it without spellingbynk_syntax::ast— seeIrHandlerKind’s own doc comment;params/givenare this module’s standard “no arena” substitution (params: Vec<(String, TyId)>mirrorsIrItem::Fn::paramsexactly;given: Vec<String>reads eachCapRef::key(), the same identityCallee::Capabilityalready uses);binder: Option<ActorBinder>perActorBinder’s own doc comment;body: IrExprisbynk_lower::lower_handler_ir’s own new handler-body lowering entry point (parallel to, but distinct from,bynk_lower::lower_fn_body_ir— that entry point’s own doc comment names exactly why a handler body cannot reuse it);commit: CommitShapecallsbynk_lower::lower_commit_shape_ir(P6.8, unchanged);effectful: boolreusesIrItem::Fn::effectful’s own derivation (Ty::Fn’s doc: effectful iffretisEffect[_]) unchanged. - IrHttp
Method IrHandlerKind::Http’s own method field — a field-for-field mirror ofbynk_syntax::ast::HttpMethod, same reasoning asIrHandlerKinditself.- IrInterp
Part IrExprKind::InterpStr’s own per-part payload (#1189) — the IR-side mirror ofbynk_syntax::ast::InterpPart, substitutingHole’s rawBox<Expr>for an already-loweredBox<IrExpr>under this module’s usual “carry the lowered form, not the source form” discipline (the same substitution every otherIrExprKindpayload already makes).- IrItem
- P6.6’s real declaration IR (
design/bynk-greenfield-compiler.md§6.6, #1161) — a top-level declaration’s own shape, the payloadbynk_lower::lower_type_item_ir/bynk_lower::lower_fn_item_irconstruct. Identity is adapted per this module’s own “no arena” substitution (DefId -> Arc<TypeDecl>/Arc<FnDecl>, the same substitutionRecord/GlobalRefalready made). - IrPat
- P6.4’s real Pattern IR (
design/bynk-greenfield-compiler.md§5.1, #1157): a pattern’s own recursive shape, six variants mapping one-to-one ontobynk_syntax::ast::Pattern’s own six (Wildcard,Binding,Literal,Variant,Refined,Or).bynk_lower::lower_pattern_iris the&Pattern -> IrPatconstructor, tested standalone — not yet wired intoIrExprKind::Match/Question/Isconstruction (P6.5’s own commission). NoPatIdarena — a pattern owns its children directly (Box<IrPat>), the same “no arena exists in this codebase” substitution this module’s own doc comment already applies throughout. - IrStmt
- A lowered statement — Bynk’s real statement surface
(
Let/EffectLet/Expect/Send/Do/Assign) folds down onto the reference’s own two-variantIrStmt(Part 6.2), extended by P6.9 (#1167) with a third:Send/DobecomeExprwrappingIrExprKind::Send/IrExprKind::Await;EffectLetbecomesLetwrapping anAwait.Assign(aCell:=write) is real as of P6.9 ([DECISION B], #1167) — this comment used to (twice) forward-referenceCallee::Storeterritory for it, on a premise P6.9’s own grounding pass found false:checker.rs’s ownStatement::Assignarm resolvesa.target.namedirectly againstctx.store_fieldsby bare name and never keys aCalleeat all (onlya.value, an ordinary sub-expression, ever gets one), so noExprId-keyed sink was ever actually needed —IrStmt::Assignis the ordinary two-fieldLet-shaped fix that was available the whole time.Expect(test-only) has no target here — not named by any rule this track commissions — and staystodo!()in the lowering pass, not silently dropped. - Match
Form - P6.5’s own real
MatchForm(#1159, R5.2/R5.3) — scoped to shape only (Decision A). The reference’s own table crosses tail-vs-value position with flat-vs-if-chain shape into four printed forms, but position is decided by where in the AST the caller already is — the same mechanism that already decides tail-vs-value for every otherIrExprKind, includingIf, which P6.1 already committed to modelling position-agnostically (this module’s ownIrExprKind::Ifdoc comment).Match’s ownscrutinee/arms/exhaustiveare identical regardless of position, so only the shape bit is recorded here — a future printer derives the tail-vs-value physical shape itself, the same way it would forIf. - 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 throughIrHandler::kind—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. - Provider
Body - P6.14’s real
ProviderBody([DECISION A], #1174) — referenced by the reference’s ownIrItem::Providersketch (bynk-greenfield-compiler.md:1135,body: ProviderBody // Bynk ops | External(module)) but never defined anywhere in the document as a real Rust type, the same “referenced, not specified” gapCapability’s ownOpSigcarried before #1173 settled it. MirrorsProviderDecl::external(bynk-syntax/src/ast.rs:592-595) exactly:true(no brace block, an adapter-supplied binding) becomesExternal,false(a real Bynk- authored implementation) becomesBynk. SeeIrItem’s own doc comment for whyExternalcarries nomodulefield despite the reference’s own parenthetical, and why it carriesgiveninstead of omitting that too. - 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 - P6.6’s real
TypeShape(Part 6.6, #1161) — a declared type’s own resolved structure, the payload ofIrItem::Type. 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).
Constants§
- MUTATING_
CELL_ OPS - v0.98 (ADR 0125):
<cell>.update(f)is a read-modify-write of the working state — the bare:=write form isStatement::Assign, checked separately and unconditionally, no method name involved. - MUTATING_
LOG_ OPS - v0.95:
<log>.appendmutates the durable array (ADR 0121) — every otherLogmethod is a query-lifting read. - MUTATING_
MAP_ CACHE_ OPS - Decision C (#1165): the closed sets of mutating storage-op names, one
pubconstant per kind group — read bybynk_lower::body_writes_state’s ownCallee::Store-keyed write-detection walk (P6.8, Decision B), which needs no receiver-name gate at all: aCallee::Storealready carries the field’s own resolved identity, not a name that could be shadowed. Until #1196, this module also had its own bare-Ident-receiver-name-matching reader (block_writes_state’s ownmutating_op, deleted) — a single shared source avoided the class of drift #1164’s own review caught twice for a different pair of independently hand-maintained copies (cache_ttl_millis’sDurationLitextraction,store_map_indexes’s dedup); now there is only the one reader. Live inbynk-ir(notbynk-emitorbynk-lowerspecifically, moved here frombynk-emit::emitterat the P7.12 crate carve, no behaviour change) since a futurebynk-emit-side reader (aServicehandler’s own write detection, say) may need them again, the same reasoning that kept thempub(crate)rather thanbynk-lower-private before the carve.Map/Cacheshare one list — both support the same four entry ops — rather than two identical ones. - MUTATING_
SET_ OPS - v0.83:
<set>.add/<set>.removemutate astore Set[T]field.
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.
Type Aliases§
- EmbedIr
- The payload of
TypeShape::Sum’s ownembeds— a resolvedembedsclause ([DECISION C], #1161): the source type paired with the target variant’s own tag name. A plain tuple, not a dedicated struct, mirroringIrPat::Variant’s ownfields: Vec<(String, Box<IrPat>)>precedent for a two-part fact with no further structure. - IndexIr
- The payload of
StoreFieldIr::indexed— one@indexed(by: …)key, identified by the indexed value-field’s own name ([DECISION C], #1163): referenced by the reference’s ownStoreFieldIr.indexed: Vec<IndexIr>but never defined anywhere in the document, the same “referenced, not specified” gap #1161’s own Decision C named forEmbedIr. No dedicated struct: the sibling table’s own emitted shape (Record<string, string[]>) is fixed by the map’s own key type, not the indexed field’s, so the indexed field’s resolved type is not needed downstream — mirrorsEmbedIr’s own “no further structure” precedent.