feat(llvm): Phase 132-P0 - block_end_values tuple-key fix for cross-function isolation

## Problem
`block_end_values` used block ID only as key, causing collisions when
multiple functions share the same block IDs (e.g., bb0 in both
condition_fn and main).

## Root Cause
- condition_fn's bb0 → block_end_values[0]
- main's bb0 → block_end_values[0] (OVERWRITES!)
- PHI resolution gets wrong snapshot → dominance error

## Solution (Box-First principle)
Change key from `int` to `Tuple[str, int]` (func_name, block_id):

```python
# Before
block_end_values: Dict[int, Dict[int, ir.Value]]

# After
block_end_values: Dict[Tuple[str, int], Dict[int, ir.Value]]
```

## Files Modified (Python - 6 files)

1. `llvm_builder.py` - Type annotation update
2. `function_lower.py` - Pass func_name to lower_blocks
3. `block_lower.py` - Use tuple keys for snapshot save/load
4. `resolver.py` - Add func_name parameter to resolve_incoming
5. `wiring.py` - Thread func_name through PHI wiring
6. `phi_manager.py` - Debug traces

## Files Modified (Rust - cleanup)

- Removed deprecated `loop_to_join.rs` (297 lines deleted)
- Updated pattern lowerers for cleaner exit handling
- Added lifecycle management improvements

## Verification

-  Pattern 1: VM RC: 3, LLVM Result: 3 (no regression)
- ⚠️ Case C: Still has dominance error (separate root cause)
  - Needs additional scope fixes (phi_manager, resolver caches)

## Design Principles

- **Box-First**: Each function is an isolated Box with scoped state
- **SSOT**: (func_name, block_id) uniquely identifies block snapshots
- **Fail-Fast**: No cross-function state contamination

## Known Issues (Phase 132-P1)

Other function-local state needs same treatment:
- phi_manager.predeclared
- resolver caches (i64_cache, ptr_cache, etc.)
- builder._jump_only_blocks

## Documentation

- docs/development/current/main/investigations/phase132-p0-case-c-root-cause.md
- docs/development/current/main/investigations/phase132-p0-tuple-key-implementation.md

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-15 05:36:50 +09:00
parent 18d56d5b88
commit 3f58f34592
22 changed files with 1076 additions and 384 deletions

View File

@ -389,6 +389,11 @@ impl super::MirBuilder {
// After PHI types are corrected, re-infer BinOp result types
self.repropagate_binop_types(&mut function);
// Phase 84-5 guard hardening: ensure call/await results are registered in `value_types`
// before return type inference. This avoids "impossible" debug panics when the builder
// emitted a value-producing instruction without annotating its dst type.
self.annotate_missing_result_types_from_calls_and_await(&function, &module);
// Phase 131-9: Update function metadata with corrected types
// MUST happen after PHI type correction above AND BinOp re-propagation
function.metadata.value_types = self.value_types.clone();
@ -600,6 +605,69 @@ impl super::MirBuilder {
Ok(module)
}
fn annotate_missing_result_types_from_calls_and_await(
&mut self,
function: &super::MirFunction,
module: &MirModule,
) {
use crate::mir::definitions::Callee;
use crate::mir::MirInstruction;
for (_bid, bb) in function.blocks.iter() {
for inst in bb.instructions.iter() {
match inst {
MirInstruction::Await { dst, future } => {
if self.value_types.contains_key(dst) {
continue;
}
let inferred = match self.value_types.get(future) {
Some(MirType::Future(inner)) => (**inner).clone(),
_ => MirType::Unknown,
};
self.value_types.insert(*dst, inferred);
}
MirInstruction::Call {
dst: Some(dst),
callee: Some(callee),
..
} => {
if self.value_types.contains_key(dst) {
continue;
}
let inferred = match callee {
Callee::Global(name) => module
.functions
.get(name)
.map(|f| f.signature.return_type.clone())
.or_else(|| {
crate::mir::builder::types::annotation::annotate_from_function(
self, *dst, name,
);
self.value_types.get(dst).cloned()
})
.unwrap_or(MirType::Unknown),
Callee::Constructor { box_type } => {
let ret = MirType::Box(box_type.clone());
self.value_origin_newbox.insert(*dst, box_type.clone());
ret
}
_ => MirType::Unknown,
};
self.value_types.insert(*dst, inferred);
}
MirInstruction::ExternCall { dst: Some(dst), .. }
| MirInstruction::BoxCall { dst: Some(dst), .. }
| MirInstruction::PluginInvoke { dst: Some(dst), .. } => {
if !self.value_types.contains_key(dst) {
self.value_types.insert(*dst, MirType::Unknown);
}
}
_ => {}
}
}
}
}
// Phase 131-11-E: Re-propagate BinOp result types after PHI resolution
// This fixes cases where BinOp instructions were created before PHI types were known
fn repropagate_binop_types(&mut self, function: &mut super::MirFunction) {