P6.x: resolve Ok/Err/Some/None's IrExprKind identity gap (#1225) - #1227
Conversation
ReviewThe core reasoning holds up under checking. Release discipline: exactly one Three findings, none blocking. (The inline-comment API is not writable from this environment, so they are collected here with file:line.) 1. The whole resolution is that ideally per case ( Related, same test: 2.
3. The service-handler signature-only helper justifies not building a real Otherwise this reads clean: no remaining |
- The new test asserted tag/payload only, which would pass unchanged even
if tail.ty carried the wrong identity (or none) -- the actual claim the
ADR rests on. Renamed and extended
ok_err_some_none_all_lower_to_variant_by_tag_and_carry_their_sum_identity_on_ty
to assert tail.ty resolves to the constructed sum in every case, and
added a fifth case (ok_http_case) pinning the Ok/Result-vs-HttpResult
overload check_ok resolves by peeling the surrounding return type --
previously named in neither the tests nor the new doc comments.
- Swept the file for every other doc comment citing the now-closed gap,
not just the two the review named:
lower_store_field_shape_ir's own comment (only the is-expression half of
its rationale survives; the None half is resolved and now pinned by a
real fixture-shaped test), lower_service_handler_signature_ir's comment
(re-pointed at the still-open Question/Is gaps instead), and three more
the review didn't flag: lower_provider_given_ir's comment (same
re-pointing, phrased conservatively since Provider op bodies specifically
aren't verified to hit Question/Is), and two test doc comments
(http_service_fixture, sum_actor_binder_lowers_an_actor_sum) whose "works
around a pre-existing gap" framing was stale.
- Found by direct verification, not just doc review: the
service_handler_signature test's own claim that "a real IrHandler would
panic on this exact body" is no longer true -- that specific body
(Effect.pure(Ok("pong"))) no longer panics lower_service_handler_ir now
that Ok/Err/Some/None resolve. Corrected the doc comment to say so
rather than chase a new body shape that still panics; the test's own
broader claim (a real IrHandler is still unsafe to build unconditionally
at this call site) still holds via Question/Is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- The new test asserted tag/payload only, which would pass unchanged even
if tail.ty carried the wrong identity (or none) -- the actual claim the
ADR rests on. Renamed and extended
ok_err_some_none_all_lower_to_variant_by_tag_and_carry_their_sum_identity_on_ty
to assert tail.ty resolves to the constructed sum in every case, and
added a fifth case (ok_http_case) pinning the Ok/Result-vs-HttpResult
overload check_ok resolves by peeling the surrounding return type --
previously named in neither the tests nor the new doc comments.
- Swept the file for every other doc comment citing the now-closed gap,
not just the two the review named:
lower_store_field_shape_ir's own comment (only the is-expression half of
its rationale survives; the None half is resolved and now pinned by a
real fixture-shaped test), lower_service_handler_signature_ir's comment
(re-pointed at the still-open Question/Is gaps instead), and three more
the review didn't flag: lower_provider_given_ir's comment (same
re-pointing, phrased conservatively since Provider op bodies specifically
aren't verified to hit Question/Is), and two test doc comments
(http_service_fixture, sum_actor_binder_lowers_an_actor_sum) whose "works
around a pre-existing gap" framing was stale.
- Found by direct verification, not just doc review: the
service_handler_signature test's own claim that "a real IrHandler would
panic on this exact body" is no longer true -- that specific body
(Effect.pure(Ok("pong"))) no longer panics lower_service_handler_ir now
that Ok/Err/Some/None resolve. Corrected the doc comment to say so
rather than chase a new body shape that still panics; the test's own
broader claim (a real IrHandler is still unsafe to build unconditionally
at this call site) still holds via Question/Is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
919e3e2 to
8c006d2
Compare
- The new test asserted tag/payload only, which would pass unchanged even
if tail.ty carried the wrong identity (or none) -- the actual claim the
ADR rests on. Renamed and extended
ok_err_some_none_all_lower_to_variant_by_tag_and_carry_their_sum_identity_on_ty
to assert tail.ty resolves to the constructed sum in every case, and
added a fifth case (ok_http_case) pinning the Ok/Result-vs-HttpResult
overload check_ok resolves by peeling the surrounding return type --
previously named in neither the tests nor the new doc comments.
- Swept the file for every other doc comment citing the now-closed gap,
not just the two the review named:
lower_store_field_shape_ir's own comment (only the is-expression half of
its rationale survives; the None half is resolved and now pinned by a
real fixture-shaped test), lower_service_handler_signature_ir's comment
(re-pointed at the still-open Question/Is gaps instead), and three more
the review didn't flag: lower_provider_given_ir's comment (same
re-pointing, phrased conservatively since Provider op bodies specifically
aren't verified to hit Question/Is), and two test doc comments
(http_service_fixture, sum_actor_binder_lowers_an_actor_sum) whose "works
around a pre-existing gap" framing was stale.
- Found by direct verification, not just doc review: the
service_handler_signature test's own claim that "a real IrHandler would
panic on this exact body" is no longer true -- that specific body
(Effect.pure(Ok("pong"))) no longer panics lower_service_handler_ir now
that Ok/Err/Some/None resolve. Corrected the doc comment to say so
rather than chase a new body shape that still panics; the test's own
broader claim (a real IrHandler is still unsafe to build unconditionally
at this call site) still holds via Question/Is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8c006d2 to
99441db
Compare
Adds an ADR (design/pending/p6-option-result-variant-identity.md) resolving the standing blocker: IrExprKind::Variant carried sum: Arc<TypeDecl> since P6.2, unreachable for a built-in Option/Result construction since neither is ever backed by a TypeDecl (Ok/Err/Some/None aren't even calls -- dedicated ExprKind variants, checked through check_ok/check_err/check_some/check_none with no Callee ever recorded). Decision: drop `sum` rather than widen it. The wrapping IrExpr::ty (already present on every node, R6.1) already carries the identical identity as a TyId -- a constructor call's own checked type is the sum it constructs, and lower_call_ir already computes it as its first line, before ever branching into the Callee::Ctor arm. Mirrors IrPat::Variant's own scrutinee_ty: TyId precedent, the one place this module already had to solve the identical problem, resolved via bynk_check::checker's own already-pub variants_of -- proven to cover a user sum, Result, Option, ActorSum, and HttpResult uniformly, no special-casing gap for the built-in cases. Ok/Err/Some/None now lower to IrExprKind::Variant directly, closing the todo!() ir/lower.rs has carried since P6.2/P6.3. Verified against the real regression this closes: 223_store_cell_agent's own `Cell[Option[AuthId]] = None` no longer panics through the full, init-lowering lower_store_field_ir (previously worked around, not fixed, by lower_store_field_shape_ir's own never-lower-init posture, #1187's Agent state-field slice 2a). No shipped emitter consumer of this IR construction path exists yet (zero-diff bless run confirms no behavioural change to any emitted TypeScript). Question's own separate todo!() (its three-way ?-desugar fork) is unaffected and named again as still open -- a distinct design question this ADR does not settle. Closes #1225. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- The new test asserted tag/payload only, which would pass unchanged even
if tail.ty carried the wrong identity (or none) -- the actual claim the
ADR rests on. Renamed and extended
ok_err_some_none_all_lower_to_variant_by_tag_and_carry_their_sum_identity_on_ty
to assert tail.ty resolves to the constructed sum in every case, and
added a fifth case (ok_http_case) pinning the Ok/Result-vs-HttpResult
overload check_ok resolves by peeling the surrounding return type --
previously named in neither the tests nor the new doc comments.
- Swept the file for every other doc comment citing the now-closed gap,
not just the two the review named:
lower_store_field_shape_ir's own comment (only the is-expression half of
its rationale survives; the None half is resolved and now pinned by a
real fixture-shaped test), lower_service_handler_signature_ir's comment
(re-pointed at the still-open Question/Is gaps instead), and three more
the review didn't flag: lower_provider_given_ir's comment (same
re-pointing, phrased conservatively since Provider op bodies specifically
aren't verified to hit Question/Is), and two test doc comments
(http_service_fixture, sum_actor_binder_lowers_an_actor_sum) whose "works
around a pre-existing gap" framing was stale.
- Found by direct verification, not just doc review: the
service_handler_signature test's own claim that "a real IrHandler would
panic on this exact body" is no longer true -- that specific body
(Effect.pure(Ok("pong"))) no longer panics lower_service_handler_ir now
that Ok/Err/Some/None resolve. Corrected the doc comment to say so
rather than chase a new body shape that still panics; the test's own
broader claim (a real IrHandler is still unsafe to build unconditionally
at this call site) still holds via Question/Is.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
99441db to
8a3e8b2
Compare
Track doc had zero mentions of #1225 (Ok/Err/Some/None IrExprKind gap, resolved via PR #1227), #1226 (Slice 5 rescoped, split), or #1228 (this PR) despite §6's own text explicitly naming Slice 5 as "left for a future pass to scope fresh" — that pass happened and the doc never recorded it. Also fills a pre-existing dangling forward reference: 2a's own prose (§6) pointed to "§7 below" for the Ok/Err/Some/None gap, but §7's table never actually got that row. Now that #1225 has resolved the construction-side identity question, the correction is recorded inline in §6 and §7 gets the row that actually belongs there — Question's own still-open three-way desugar fork. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…dler::annotations (#1229) * P6.x: @cache/@limit route annotations read bynk-emit::ir (#1228) lower_route_cache_ir/lower_route_limit_ir replace workers_entry.rs's own hand-rolled ExprKind::DurationLit/Ident/IntLit matching (cache_policy_for, the annotation half of effective_max_body) with standalone ir::lower readers, following lower_policy_ir's own no-CheckedProgram precedent — deliberately bypassing the dormant PolicyIr/IrItem::Service pipeline rather than nesting under it. CachePolicy is gone; HttpRoute::cache now carries ir::CacheIr directly (identical field shape). Byte-identical output, confirmed by a zero-diff bless run (298_http_caching/300_http_limits). ast_importers stays at 7 (workers_entry.rs already counted for other reasons). Closes #1228 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * design/tracks/the-ir.md: record #1225/#1226/#1228 in §6/§7 Track doc had zero mentions of #1225 (Ok/Err/Some/None IrExprKind gap, resolved via PR #1227), #1226 (Slice 5 rescoped, split), or #1228 (this PR) despite §6's own text explicitly naming Slice 5 as "left for a future pass to scope fresh" — that pass happened and the doc never recorded it. Also fills a pre-existing dangling forward reference: 2a's own prose (§6) pointed to "§7 below" for the Ok/Err/Some/None gap, but §7's table never actually got that row. Now that #1225 has resolved the construction-side identity question, the correction is recorded inline in §6 and §7 gets the row that actually belongs there — Question's own still-open three-way desugar fork. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Address review: direct-call tests for lower_route_cache_ir/lower_route_limit_ir Review of #1229 found no unit tests for the two new lowerers, unlike every other standalone lowerer in this file — and named the specific gap: the untested branches (non-GET guard, maxAge-less @cache discarding an otherwise-parsed scope, non-positive maxBody) are exactly what the checker already rejects, so bless-run coverage alone never reaches them. Five tests added, parsed-but-not-checked (same posture the Events pattern test already established for the identical reason: no &CheckedProgram, no resolution, no panic surface). Non-blocking finding (maxAge millis truncating to 0s below 1 second) filed separately as #1230 rather than fixed here — pre-existing, faithfully preserved, out of this PR's own scope per the reviewer's own framing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Summary
Closes #1225 — the
Ok/Err/Some/NoneIrExprKindidentity gap, open since P6.2/P6.3 (#1143/#1145).The gap.
IrExprKind::Variant { sum: Arc<TypeDecl>, tag: String, payload: Vec<IrExpr> }carried a declaration-identity field mirroringRecord::def/GlobalRef::sum. A real user-declared sum's constructor (Circle(n), driven byCallee::Ctor { sum: Arc<TypeDecl>, tag }) has one to give it.Ok/Err/Some/Nonenever do —Option/Resultare dedicatedTyvariants (Ty::Option/Ty::Result), neverTy::Named, never backed by aTypeDecl. Worse: they aren't even calls — dedicatedExprKind::Ok/Err/Some/NoneAST variants, checked throughcheck_ok/check_err/check_some/check_nonewith noCalleeever recorded at all.The ADR (
design/pending/p6-option-result-variant-identity.md): dropsumrather than widen it.IrExpr::ty— already present on every node (R6.1) — already carries the identical identity as aTyId: a constructor call's own checked type is the sum it constructs, andlower_call_iralready computestyas its first line, before ever branching into theCallee::Ctorarm. This mirrorsIrPat::Variant's ownscrutinee_ty: TyIdprecedent exactly — the one place this module already solved the identical problem, resolved viabynk_check::checker::variants_of(alreadypubfor this), proven to cover a user sum,Result,Option,ActorSum, andHttpResultuniformly with no built-in special-casing gap.Implementation.
Variantbecomes{ tag: String, payload: Vec<IrExpr> }.Ok/Err/Some/Nonenow lower to it directly (tagthe constructor name,payloadthe lowered inner expression, empty forNone), same as a realCallee::Ctorconstruction. Closes thetodo!()ir/lower.rshas carried since P6.2/P6.3.Verified against the real regression this closes:
223_store_cell_agent's ownstore paymentRef: Cell[Option[AuthId]] = Noneno longer panics through the full,init-loweringlower_store_field_ir— previously worked around, not fixed, bylower_store_field_shape_ir's own never-lower-initposture (#1187's Agent state-field slice 2a, #1206).No shipped emitter consumer of this construction path exists yet —
ir::lower's wholeIrItem/IrExproutput remains dormant, exercised only by its own test suite — so this lands with zero behavioural change to any emitted TypeScript, confirmed by a zero-diff bless run.Question's own separatetodo!()(its three-way?-desugar fork) is unaffected and named again as still open — a distinct design question this ADR does not settle, per #1225's own scope note.Test plan
cargo build --workspacecargo clippy --workspace --all-targetscargo fmt --all -- --checkcargo test -p bynk-emit --lib— all 220 tests pass, including newok_err_some_none_all_lower_to_variant_by_tagandstore_field_cell_option_init_none_lowers_without_panicking, and the updatedbare_and_qualified_variant_construction_both_lower_to_variantBYNK_BLESS=1 cargo test -p bynkc --test e2e bless_positive_fixtures— zero fixture diff (no shipped consumer of this path exists yet)cargo test -p bynkc --test tsc_verify— realtsc --strict, all 6 passcargo test --workspace— full suite🤖 Generated with Claude Code