P6.x: convert instantiate_provider_expr to bynk-emit::ir's CapRefIr (Provider given/deps wiring) - #1200
Conversation
…Provider given/deps wiring) Scope: project.rs's instantiate_provider_expr (the deps-object constructor for a `provides` binding, both Bynk-authored and external/adapter-supplied) now reads a provider's own given clause through bynk-emit::ir::CapRefIr instead of walking bynk_syntax::ast::CapRef directly. Byte-identical output confirmed across the entire fixture suite. Real fix found along the way, not just a data-source swap: ProviderBody:: External was a bare unit variant carrying no given field at all, silently dropping an external provider's own given clause — despite its own doc comment already claiming "given lowers unconditionally" (P6.14, #1186). ProviderDecl::given is populated the same way regardless of external (nothing in the grammar or checker gates it), so this was a real gap between the type's documented intent and its actual shape, not a deliberate omission. ProviderBody::External now carries given: Vec<CapRefIr> too, and lower_provider_item_ir populates it for both branches via the new lower_provider_given_ir. Why a standalone lower_provider_given_ir, not the full IrItem::Provider: building a real IrItem::Provider at project.rs's call site would be unsafe for a Bynk provider specifically — ProviderBody::Bynk::ops unconditionally lowers every op's body through lower_provider_op_ir -> lower_expr_ir, which still can't handle Ok/Err/Some/None construction (the same gap that made Agent/Service's own full IrItem construction impractical at their emitter call sites, #1196/#1198). lower_provider_given_ir never touches ops/bodies, mirroring #1198's own lower_service_handler_signature_ir precedent: a narrow standalone reader, not the full assembly. What's not changed: plan_agent_given_deps (project.rs) stays AST-driven — it's Agent handler given, a separate concern from Provider given, and IrHandler::given is bare names only (no context/prefix data), blocked on Agent's own still-deferred given-with-context IR work. Raw CapRef/ ProviderDecl.given reads persist elsewhere too (emitter/workers.rs's own deps construction and cross-context scan, project.rs's own second given scan, and EmitProjectCtx::agent_method_givens, which stores raw Vec<CapRef> directly) — this slice does not remove bynk_syntax::ast::CapRef coupling from the crate, only from this one call site. ast_importers unaffected (still 8). New test coverage: lower_provider_item_ir's own external-provider test now exercises a non-empty given (previously always empty), pinning that ProviderBody::External carries it correctly where it used to be silently dropped. Part of #1137. A step toward project.rs's own eventual slice 6 cleanup (design/tracks/the-ir.md §6, per the reconciliation in a companion PR) — not slice 6 itself, and does not claim to be. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Review: sound — no blocking findingsI verified the equivalence claim rather than taking it on faith, since the whole PR rests on it. The data-source swap is exactly semantics-preserving. The branch that could have diverged is genuinely covered. The The Scoping judgement is right. Standalone Nothing else in Release discipline: correct. Exactly one increment file, Only observation, not a request: the The PR body's own "what's not changed" list ( LGTM. |
…it and called_cross_context_services
Scope: project.rs's own check-then-emit pipeline previously built each
file's TypedCommons/CheckedProgram (and the Callee classification it
carries) inside check_unit_files, then dropped it at the end of that same
loop iteration -- before the later, project-wide Workers-target loop
(build_output/emit_composition_root) that needs project-wide facts about
every unit ever runs. This added the missing plumbing: RunChecks::Checked
gains a new unit_callees: HashMap<String, HashMap<ExprId, Callee>> field,
merged per unit (across that unit's own files -- ExprId is a single
project-wide counter, so merging never collides) inside check_unit_files,
threaded through finish_build -> build_output -> emit_composition_root.
On top of that plumbing, two functions convert from re-deriving a
classification the checker already resolved, to reading it directly:
- unit_table_uses_emit: Events.emit detection now matches the checker's
own Callee::Capability{cap:"Events",op:"emit"} (Events.emit dispatches
through the ordinary capability-call path, bynk-check/src/checker/
calls.rs) instead of emitter::block_uses_emit's bare-Ident("Events")-
receiver name match.
- called_cross_context_services: cross-context call detection now reads
Callee::Cross{unit,service} directly instead of flattening a receiver's
own ident chain and string-matching it against consumed/aliases -- the
identical resolution CrossContextInfo::resolve_prefix already did once,
at check time, per call site.
emit_worker_compose/emit_worker_entry/emit_wrangler_toml (emitter/
workers.rs, emitter/workers_entry.rs, emitter/wrangler.rs) each take a
precomputed uses_emit: bool now, not the Callee map itself -- passing the
map's own element type into wrangler.rs would have reintroduced the
literal `bynk_syntax::ast` spelling slice 2 (#1191) specifically removed
from that file (a real regression caught by this repo's own
greenfield_status_table_is_current test during this change, not assumed
safe).
Correctness evidence: BYNK_BLESS=1 cargo test -p bynkc --test e2e
bless_positive_fixtures touched zero files across the whole positive
fixture suite, including the 5 fixtures that use Events.emit and the 72
that use consumes (cross-context) -- both converted code paths are
exercised for real, not just structurally unchanged. ast_importers
unaffected (still 8, after fixing the wrangler.rs regression above).
Part of #1137. A further step toward project.rs's own eventual slice 6
cleanup (design/tracks/the-ir.md section 6) -- own_contract_hashes and
plan_agent_given_deps remain out of scope, named in an earlier PR
(#1200) as, respectively, too risky to touch (a stable contract hash used
for deploy-time compatibility) and blocked on Agent's own still-deferred
given-with-context IR work.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
project.rs'sinstantiate_provider_expr(the deps-object constructor for aprovidesbinding, both Bynk-authored and external/adapter-supplied) now reads a provider's own
givenclausethrough
bynk-emit::ir::CapRefIrinstead of walkingbynk_syntax::ast::CapRefdirectly.found still blocking
project.rs's own eventual slice 6 cleanup.A real fix found along the way, not just a data-source swap
ProviderBody::Externalwas a bare unit variant carrying nogivenfield at all — silently dropping anexternal provider's own
givenclause, despite its own doc comment already claiming "givenlowersunconditionally" (P6.14, #1186).
ProviderDecl::givenis populated the same way regardless ofexternal— nothing in the grammar or checker gates it — so this was a real gap between the type'sdocumented intent and its actual shape, not a deliberate omission.
ProviderBody::Externalnow carriesgiven: Vec<CapRefIr>too, andlower_provider_item_irpopulates it for both branches via a newlower_provider_given_ir.Why a standalone
lower_provider_given_ir, not the fullIrItem::ProviderBuilding a real
IrItem::Provideratproject.rs's call site would be unsafe for a Bynk providerspecifically:
ProviderBody::Bynk::opsunconditionally lowers every op's body throughlower_provider_op_ir→lower_expr_ir, which still can't handleOk/Err/Some/Noneconstruction— the same gap that made Agent/Service's own full
IrItemconstruction impractical at their emittercall sites (#1196/#1198).
lower_provider_given_irnever touchesops/bodies, mirroring #1198's ownlower_service_handler_signature_irprecedent: a narrow standalone reader, not the full assembly.What's not changed
plan_agent_given_deps(project.rs) stays AST-driven — it's Agent handlergiven, a separateconcern from Provider
given, andIrHandler::givenis bare names only (no context/prefix data),blocked on Agent's own still-deferred given-with-context IR work.
CapRef/ProviderDecl.givenreads persist elsewhere too:emitter/workers.rs's own depsconstruction and cross-context scan,
project.rs's own secondgivenscan, andEmitProjectCtx::agent_method_givens, which stores rawVec<CapRef>directly. This slice does notremove
bynk_syntax::ast::CapRefcoupling from the crate, only from this one call site.ast_importersunaffected (still 8) — stated explicitly.Test coverage
lower_provider_item_ir's own external-provider test now exercises a non-emptygiven(previouslyalways empty), pinning that
ProviderBody::Externalcarries it correctly where it used to be silentlydropped.
BYNK_BLESS=1 cargo test -p bynkc --test e2e bless_positive_fixtures— zero fileschanged across the whole positive fixture suite.
cargo build --workspace/cargo clippy --workspace --all-targets/cargo fmt --all -- --check/cargo test --workspaceall clean (164 test-result blocks, 0 failures; 212/212 inbynk-emit's ownlib suite).
Done when
instantiate_provider_exprreadsCapRefIr, not rawCapRef, for a provider's owngiven.ProviderBody::Externalcarriesgiven— the silent-drop gap fixed, not just worked around.lower_provider_given_ir, avoiding theOk/Err/Some/Nonebody-loweringpanic risk a full
IrItem::Providerconstruction would carry.ast_importersunaffected (still 8) — stated explicitly.plan_agent_given_deps,workers.rs's owngivenreads,agent_method_givens's rawCapRefstorage.design/pending/p6-provider-given-deps-wiring.mdadded (patch level — internal only, no languagesurface change).
Part of Phase 6 — The IR (spine) #1137 — a step toward
project.rs's own slice 6 cleanup, not slice 6 itself.