Files
hakorune/docs/development/current/main/phase78-bindingid-promoted-carriers.md
nyash-codex 8b48bec962 feat(joinir): Phase 78 - BindingId infrastructure for promoted carriers (dev-only)
Phase 78 adds infrastructure to assign BindingIds to synthetic promoted
carriers (e.g., is_digit_pos, is_ch_match), enabling type-safe promoted
variable lookup without string-based naming conventions.

Key Changes:
1. CarrierVar.binding_id field (dev-only):
   - Added Option<BindingId> to track BindingId for each carrier
   - Updated all constructors and struct instantiations

2. CarrierBindingAssigner Box (new file, 273 lines):
   - Allocates BindingIds for promoted carriers via builder.allocate_binding_id()
   - Records original → promoted mapping in promoted_bindings
   - Sets binding_id field on promoted CarrierVar
   - Includes 3 comprehensive unit tests

3. ConditionEnv.register_carrier_binding() (new method):
   - Registers carrier BindingId → ValueId mappings
   - Enables type-safe lookup via binding_id_map

4. Logging cleanup:
   - Gated 6 eprintln! statements with NYASH_JOINIR_DEBUG
   - Unified logging tags to [binding_pilot/*]

Design Decisions:
- Promoters create CarrierInfo, lowering code assigns BindingIds
- CarrierBindingAssigner called from Pattern2/4 lowering (has builder access)
- Clear documentation prevents misuse (promoters lack builder access)

Files modified (18):
- carrier_info.rs: binding_id field added to CarrierVar
- carrier_binding_assigner.rs: New Box for BindingId allocation
- condition_env.rs: register_carrier_binding() method
- mod.rs: Module exports
- pattern2_with_break.rs, pattern4_with_continue.rs: Updated for binding_id
- loop_body_*_promoter.rs: Logging cleanup + binding_id in structs
- phase78-bindingid-promoted-carriers.md: Architecture documentation

Tests: 970/970 PASS (zero regressions)
Status: Infrastructure complete, integration deferred to Phase 79

Next Phase: Wire CarrierBindingAssigner in Pattern2/4 lowering + E2E tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-13 16:20:33 +09:00

62 lines
3.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Phase 78: BindingId for Promoted Carriers (dev-only)
### Goal
Make LoopBodyLocal promotion (DigitPos/Trim) **BindingId-aware** without relying on fragile name-based hacks like `format!("is_{}", name)`.
This phase introduces a dev-only identity link:
- `BindingId(original LoopBodyLocal)``BindingId(promoted carrier)``ValueId(join carrier param)`
### Problem
Promotion creates synthetic carrier names (例: `is_digit_pos`, `is_ch_match`) that do not exist as source-level bindings.
- `PromotedBindingRecorder` expects both original/promoted names in `MirBuilder.binding_map`.
- For promoted carriers this is not true by construction.
- For LoopBodyLocal variables, promotion can happen before their `local` is lowered, so the original name may also be absent.
### Solution (current state)
#### 1) CarrierVar gets optional BindingId (dev-only)
- `src/mir/join_ir/lowering/carrier_info.rs`
- `CarrierVar.binding_id: Option<BindingId>` (feature `normalized_dev`)
#### 2) CarrierBindingAssigner box
- `src/mir/join_ir/lowering/carrier_binding_assigner.rs`
- Ensures both sides have BindingIds:
- If `original_name` is missing in `builder.binding_map`, allocate a temporary BindingId.
- If `promoted_carrier_name` is missing, allocate a temporary BindingId.
- Records:
- `carrier_info.promoted_bindings[original_bid] = promoted_bid`
- `CarrierVar.binding_id = Some(promoted_bid)` for the promoted carrier
- Restores `builder.binding_map` after recording (synthetic names do not leak into the source map).
#### 3) Pattern2/Pattern4 wiring
- Pattern2 (break): `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
- Calls `CarrierBindingAssigner::assign_promoted_binding()` immediately after promotion.
- Registers `BindingId → ValueId` via `ConditionEnv.register_carrier_binding()` after carrier join_id allocation.
- Keeps legacy name-based aliasing (`digit_pos``is_digit_pos`) for now, but removes local “guess naming” by using the promoted carrier name returned by the promoter.
- Pattern4 (continue): `src/mir/builder/control_flow/joinir/patterns/pattern4_with_continue.rs`
- Calls `CarrierBindingAssigner::assign_promoted_binding()` after promotion (analysis metadata only, since Pattern4s current lowering path does not use JoinValueSpace params).
### Tests
- Unit test (dev-only): `src/mir/builder/control_flow/joinir/patterns/pattern2_with_break.rs`
- `phase78_promoted_binding_is_recorded_for_digitpos`
- Asserts:
- `promoted_bindings` has one mapping
- promoted carrier has `binding_id`
- `ConditionEnv.binding_id_map[promoted_bid] == promoted_carrier.join_id`
### Open Follow-ups (Phase 79+)
1. Wire `ScopeManager::lookup_with_binding()` into ExprLowerer/ConditionLoweringBox call-sites (so BindingId actually drives resolution).
2. Extend coverage to Trim and other promotion shapes (additional unit/E2E tests).
3. Shrink/remove legacy name-based promoted lookup (`resolve_promoted_join_id`) after call-sites consistently provide BindingId.