Files
hakorune/docs/development/current/main/phase78-bindingid-promoted-carriers.md

62 lines
3.0 KiB
Markdown
Raw Normal View History

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
## 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.