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>
2025-12-15 05:36:50 +09:00
|
|
|
|
use super::{Effect, EffectMask, MirInstruction, MirType, ValueId};
|
2025-09-17 07:54:45 +09:00
|
|
|
|
use crate::ast::{ASTNode, CallExpr};
|
2025-09-23 09:00:07 +09:00
|
|
|
|
use crate::mir::utils::is_current_block_terminated;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
use crate::mir::TypeOpKind;
|
2025-09-03 01:37:38 +09:00
|
|
|
|
|
|
|
|
|
|
impl super::MirBuilder {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
// Print statement: env.console.log(value) with early TypeOp handling
|
2025-09-03 01:37:38 +09:00
|
|
|
|
pub(super) fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
super::utils::builder_debug_log("enter build_print_statement");
|
2025-09-17 07:54:45 +09:00
|
|
|
|
// Prefer wrapper for simple function-call pattern (non-breaking refactor)
|
|
|
|
|
|
if let Ok(call) = CallExpr::try_from(expression.clone()) {
|
|
|
|
|
|
if (call.name == "isType" || call.name == "asType") && call.arguments.len() == 2 {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
super::utils::builder_debug_log(
|
|
|
|
|
|
"pattern: print(FunctionCall isType|asType) [via wrapper]",
|
|
|
|
|
|
);
|
|
|
|
|
|
if let Some(type_name) =
|
|
|
|
|
|
super::MirBuilder::extract_string_literal(&call.arguments[1])
|
|
|
|
|
|
{
|
|
|
|
|
|
super::utils::builder_debug_log(&format!(
|
|
|
|
|
|
"extract_string_literal OK: {}",
|
|
|
|
|
|
type_name
|
|
|
|
|
|
));
|
2025-09-17 07:54:45 +09:00
|
|
|
|
let val = self.build_expression(call.arguments[0].clone())?;
|
|
|
|
|
|
let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-11-21 06:25:17 +09:00
|
|
|
|
let op = if call.name == "isType" {
|
|
|
|
|
|
TypeOpKind::Check
|
|
|
|
|
|
} else {
|
|
|
|
|
|
TypeOpKind::Cast
|
|
|
|
|
|
};
|
|
|
|
|
|
super::utils::builder_debug_log(&format!(
|
|
|
|
|
|
"emit TypeOp {:?} value={} dst= {}",
|
|
|
|
|
|
op, val, dst
|
|
|
|
|
|
));
|
|
|
|
|
|
self.emit_instruction(MirInstruction::TypeOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op,
|
|
|
|
|
|
value: val,
|
|
|
|
|
|
ty,
|
|
|
|
|
|
})?;
|
2025-09-17 07:54:45 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: None,
|
|
|
|
|
|
iface_name: "env.console".to_string(),
|
|
|
|
|
|
method_name: "log".to_string(),
|
|
|
|
|
|
args: vec![dst],
|
|
|
|
|
|
effects: EffectMask::PURE.add(Effect::Io),
|
|
|
|
|
|
})?;
|
|
|
|
|
|
return Ok(dst);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
super::utils::builder_debug_log("extract_string_literal FAIL [via wrapper]");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-03 05:04:56 +09:00
|
|
|
|
match &expression {
|
|
|
|
|
|
// print(isType(val, "Type")) / print(asType(...))
|
2025-09-17 07:43:07 +09:00
|
|
|
|
ASTNode::FunctionCall {
|
|
|
|
|
|
name, arguments, ..
|
|
|
|
|
|
} if (name == "isType" || name == "asType") && arguments.len() == 2 => {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
super::utils::builder_debug_log("pattern: print(FunctionCall isType|asType)");
|
|
|
|
|
|
if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[1]) {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
super::utils::builder_debug_log(&format!(
|
|
|
|
|
|
"extract_string_literal OK: {}",
|
|
|
|
|
|
type_name
|
|
|
|
|
|
));
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let val = self.build_expression(arguments[0].clone())?;
|
|
|
|
|
|
let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let op = if name == "isType" {
|
|
|
|
|
|
TypeOpKind::Check
|
|
|
|
|
|
} else {
|
|
|
|
|
|
TypeOpKind::Cast
|
|
|
|
|
|
};
|
|
|
|
|
|
super::utils::builder_debug_log(&format!(
|
|
|
|
|
|
"emit TypeOp {:?} value={} dst= {}",
|
|
|
|
|
|
op, val, dst
|
|
|
|
|
|
));
|
|
|
|
|
|
self.emit_instruction(MirInstruction::TypeOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op,
|
|
|
|
|
|
value: val,
|
|
|
|
|
|
ty,
|
|
|
|
|
|
})?;
|
2025-09-03 05:04:56 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: None,
|
|
|
|
|
|
iface_name: "env.console".to_string(),
|
|
|
|
|
|
method_name: "log".to_string(),
|
|
|
|
|
|
args: vec![dst],
|
|
|
|
|
|
effects: EffectMask::PURE.add(Effect::Io),
|
|
|
|
|
|
})?;
|
|
|
|
|
|
return Ok(dst);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
super::utils::builder_debug_log("extract_string_literal FAIL");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// print(obj.is("Type")) / print(obj.as("Type"))
|
2025-09-17 07:43:07 +09:00
|
|
|
|
ASTNode::MethodCall {
|
|
|
|
|
|
object,
|
|
|
|
|
|
method,
|
|
|
|
|
|
arguments,
|
|
|
|
|
|
..
|
|
|
|
|
|
} if (method == "is" || method == "as") && arguments.len() == 1 => {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
super::utils::builder_debug_log("pattern: print(MethodCall is|as)");
|
|
|
|
|
|
if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[0]) {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
super::utils::builder_debug_log(&format!(
|
|
|
|
|
|
"extract_string_literal OK: {}",
|
|
|
|
|
|
type_name
|
|
|
|
|
|
));
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let obj_val = self.build_expression(*object.clone())?;
|
|
|
|
|
|
let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let dst = self.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let op = if method == "is" {
|
|
|
|
|
|
TypeOpKind::Check
|
|
|
|
|
|
} else {
|
|
|
|
|
|
TypeOpKind::Cast
|
|
|
|
|
|
};
|
|
|
|
|
|
super::utils::builder_debug_log(&format!(
|
|
|
|
|
|
"emit TypeOp {:?} obj={} dst= {}",
|
|
|
|
|
|
op, obj_val, dst
|
|
|
|
|
|
));
|
|
|
|
|
|
self.emit_instruction(MirInstruction::TypeOp {
|
|
|
|
|
|
dst,
|
|
|
|
|
|
op,
|
|
|
|
|
|
value: obj_val,
|
|
|
|
|
|
ty,
|
|
|
|
|
|
})?;
|
2025-09-03 05:04:56 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: None,
|
|
|
|
|
|
iface_name: "env.console".to_string(),
|
|
|
|
|
|
method_name: "log".to_string(),
|
|
|
|
|
|
args: vec![dst],
|
|
|
|
|
|
effects: EffectMask::PURE.add(Effect::Io),
|
|
|
|
|
|
})?;
|
|
|
|
|
|
return Ok(dst);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
super::utils::builder_debug_log("extract_string_literal FAIL");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_ => {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let value = self.build_expression(expression)?;
|
|
|
|
|
|
super::utils::builder_debug_log(&format!("fallback print value={}", value));
|
2025-09-24 01:05:44 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 3.2: Use unified call for print statements
|
2025-09-28 12:19:49 +09:00
|
|
|
|
let use_unified = super::calls::call_unified::is_unified_call_enabled();
|
2025-09-24 01:05:44 +09:00
|
|
|
|
|
|
|
|
|
|
if use_unified {
|
|
|
|
|
|
// New unified path - treat print as global function
|
|
|
|
|
|
self.emit_unified_call(
|
2025-11-21 06:25:17 +09:00
|
|
|
|
None, // print returns nothing
|
2025-09-24 01:05:44 +09:00
|
|
|
|
super::builder_calls::CallTarget::Global("print".to_string()),
|
|
|
|
|
|
vec![value],
|
|
|
|
|
|
)?;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Legacy path - use ExternCall
|
|
|
|
|
|
self.emit_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: None,
|
|
|
|
|
|
iface_name: "env.console".to_string(),
|
|
|
|
|
|
method_name: "log".to_string(),
|
|
|
|
|
|
args: vec![value],
|
|
|
|
|
|
effects: EffectMask::PURE.add(Effect::Io),
|
|
|
|
|
|
})?;
|
|
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
Ok(value)
|
2025-09-03 01:37:38 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
|
|
|
|
|
// Block: sequentially build statements and return last value or Void
|
2025-09-03 01:37:38 +09:00
|
|
|
|
pub(super) fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
|
2025-09-21 08:53:00 +09:00
|
|
|
|
// Scope hint for bare block (Program)
|
|
|
|
|
|
let scope_id = self.current_block.map(|bb| bb.as_u32()).unwrap_or(0);
|
|
|
|
|
|
self.hint_scope_enter(scope_id);
|
2025-12-13 01:30:04 +09:00
|
|
|
|
let _lex_scope = super::vars::lexical_scope::LexicalScopeGuard::new(self);
|
|
|
|
|
|
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let mut last_value = None;
|
2025-11-19 08:04:43 +09:00
|
|
|
|
let total = statements.len();
|
|
|
|
|
|
eprintln!("[DEBUG/build_block] Processing {} statements", total);
|
|
|
|
|
|
for (idx, statement) in statements.into_iter().enumerate() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG/build_block] Statement {}/{} current_block={:?} current_function={}",
|
|
|
|
|
|
idx + 1,
|
|
|
|
|
|
total,
|
|
|
|
|
|
self.current_block,
|
2025-12-15 23:41:30 +09:00
|
|
|
|
self.scope_ctx.current_function
|
2025-11-21 06:25:17 +09:00
|
|
|
|
.as_ref()
|
|
|
|
|
|
.map(|f| f.signature.name.as_str())
|
|
|
|
|
|
.unwrap_or("none")
|
|
|
|
|
|
);
|
2025-11-07 21:04:01 +09:00
|
|
|
|
last_value = Some(self.build_statement(statement)?);
|
2025-09-06 06:24:08 +09:00
|
|
|
|
// If the current block was terminated by this statement (e.g., return/throw),
|
|
|
|
|
|
// do not emit any further instructions for this block.
|
2025-09-23 09:00:07 +09:00
|
|
|
|
if is_current_block_terminated(self)? {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[DEBUG/build_block] Block terminated after statement {}",
|
|
|
|
|
|
idx + 1
|
|
|
|
|
|
);
|
2025-09-06 06:24:08 +09:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-21 08:53:00 +09:00
|
|
|
|
let out = last_value.unwrap_or_else(|| {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
// Use ConstantEmissionBox for Void
|
|
|
|
|
|
crate::mir::builder::emission::constant::emit_void(self)
|
2025-09-21 08:53:00 +09:00
|
|
|
|
});
|
|
|
|
|
|
// Scope leave only if block not already terminated
|
|
|
|
|
|
if !self.is_current_block_terminated() {
|
|
|
|
|
|
self.hint_scope_leave(scope_id);
|
|
|
|
|
|
}
|
2025-11-19 08:04:43 +09:00
|
|
|
|
eprintln!("[DEBUG/build_block] Completed, returning value {:?}", out);
|
2025-09-21 08:53:00 +09:00
|
|
|
|
Ok(out)
|
2025-09-03 01:37:38 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
2025-11-07 21:04:01 +09:00
|
|
|
|
/// Build a single statement node.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Note:
|
2025-11-08 00:46:34 +09:00
|
|
|
|
/// - While/ForRange は将来 Loop lowering へ委譲する拡張ポイントとして扱い、
|
|
|
|
|
|
/// 現状は他の専用ビルダ/既存パスと同様に build_expression に委譲する。
|
2025-12-10 00:01:53 +09:00
|
|
|
|
///
|
|
|
|
|
|
/// Phase 212.5: If statement のサポート追加
|
|
|
|
|
|
/// - Statement としての If(副作用のみが欲しい)を明示的に処理
|
|
|
|
|
|
/// - Expression としての If(値を使う)は build_expression 経由のまま
|
2025-11-07 21:04:01 +09:00
|
|
|
|
pub(super) fn build_statement(&mut self, node: ASTNode) -> Result<ValueId, String> {
|
2025-11-24 15:02:51 +09:00
|
|
|
|
// Align current_span to this statement node before lowering expressions under it.
|
2025-12-15 22:44:38 +09:00
|
|
|
|
self.metadata_ctx.set_current_span(node.span());
|
2025-11-08 00:46:34 +09:00
|
|
|
|
match node {
|
2025-12-10 00:01:53 +09:00
|
|
|
|
// Phase 212.5: Statement としての If 処理
|
|
|
|
|
|
ASTNode::If {
|
|
|
|
|
|
condition,
|
|
|
|
|
|
then_body,
|
|
|
|
|
|
else_body,
|
|
|
|
|
|
..
|
|
|
|
|
|
} => {
|
|
|
|
|
|
// Statement としての If - 既存 If lowering を呼ぶ
|
|
|
|
|
|
self.build_if_statement(*condition, then_body, else_body)?;
|
|
|
|
|
|
// Statement なので値は使わない(Void を返す)
|
|
|
|
|
|
Ok(crate::mir::builder::emission::constant::emit_void(self))
|
|
|
|
|
|
}
|
2025-11-08 00:46:34 +09:00
|
|
|
|
// 将来ここに While / ForRange / Match / Using など statement 専用分岐を追加する。
|
2025-11-07 21:04:01 +09:00
|
|
|
|
other => self.build_expression(other),
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 00:01:53 +09:00
|
|
|
|
/// Phase 212.5: Statement としての If 処理(副作用のみ)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// ループ内 if や top-level statement if はここを通る。
|
|
|
|
|
|
/// Expression としての if(値を使う場合)は build_expression 経由。
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Arguments
|
|
|
|
|
|
/// * `condition` - If の条件式
|
|
|
|
|
|
/// * `then_body` - then ブロックの statements
|
|
|
|
|
|
/// * `else_body` - else ブロックの statements (optional)
|
|
|
|
|
|
///
|
|
|
|
|
|
/// # Example
|
|
|
|
|
|
/// ```hako
|
|
|
|
|
|
/// if i > 0 {
|
|
|
|
|
|
/// sum = sum + 1 // ← Statement としての If
|
|
|
|
|
|
/// }
|
|
|
|
|
|
/// ```
|
|
|
|
|
|
pub(super) fn build_if_statement(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
condition: ASTNode,
|
|
|
|
|
|
then_body: Vec<ASTNode>,
|
|
|
|
|
|
else_body: Option<Vec<ASTNode>>,
|
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
|
use crate::ast::Span;
|
|
|
|
|
|
|
|
|
|
|
|
// then_body と else_body を ASTNode::Program に変換
|
|
|
|
|
|
let then_node = ASTNode::Program {
|
|
|
|
|
|
statements: then_body,
|
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
|
};
|
|
|
|
|
|
let else_node = else_body.map(|b| ASTNode::Program {
|
|
|
|
|
|
statements: b,
|
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 既存の If lowering を呼ぶ(cf_if は lower_if_form を呼ぶ)
|
|
|
|
|
|
// 戻り値は無視(Statement なので値は使わない)
|
|
|
|
|
|
let _result = self.cf_if(condition, then_node, else_node)?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-03 05:04:56 +09:00
|
|
|
|
// Local declarations with optional initializers
|
|
|
|
|
|
pub(super) fn build_local_statement(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
variables: Vec<String>,
|
|
|
|
|
|
initial_values: Vec<Option<Box<ASTNode>>>,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
2025-11-18 06:11:17 +09:00
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[build_local_statement] ENTRY: variables={:?}, initial_values.len()={}",
|
|
|
|
|
|
variables,
|
|
|
|
|
|
initial_values.len()
|
|
|
|
|
|
);
|
2025-11-18 06:11:17 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let mut last_value = None;
|
|
|
|
|
|
for (i, var_name) in variables.iter().enumerate() {
|
2025-09-28 12:19:49 +09:00
|
|
|
|
let var_id = if i < initial_values.len() && initial_values[i].is_some() {
|
2025-11-18 02:32:43 +09:00
|
|
|
|
// Evaluate the initializer expression
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let init_expr = initial_values[i].as_ref().unwrap();
|
2025-09-28 12:19:49 +09:00
|
|
|
|
let init_val = self.build_expression(*init_expr.clone())?;
|
2025-11-18 02:32:43 +09:00
|
|
|
|
|
|
|
|
|
|
// FIX: Allocate a new ValueId for this local variable
|
2025-11-18 06:11:17 +09:00
|
|
|
|
// Use next_value_id() which respects function context
|
|
|
|
|
|
let var_id = self.next_value_id();
|
|
|
|
|
|
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[build_local_statement] '{}': init_val={:?}, allocated var_id={:?}",
|
|
|
|
|
|
var_name, init_val, var_id
|
|
|
|
|
|
);
|
2025-11-18 06:11:17 +09:00
|
|
|
|
}
|
2025-11-18 02:32:43 +09:00
|
|
|
|
|
|
|
|
|
|
self.emit_instruction(crate::mir::MirInstruction::Copy {
|
|
|
|
|
|
dst: var_id,
|
2025-11-21 06:25:17 +09:00
|
|
|
|
src: init_val,
|
2025-11-18 02:32:43 +09:00
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
|
|
// Propagate metadata (type/origin) from initializer to variable
|
|
|
|
|
|
crate::mir::builder::metadata::propagate::propagate(self, init_val, var_id);
|
|
|
|
|
|
|
|
|
|
|
|
var_id
|
2025-09-03 05:04:56 +09:00
|
|
|
|
} else {
|
2025-09-28 12:19:49 +09:00
|
|
|
|
// Create a concrete register for uninitialized locals (Void)
|
2025-11-18 06:11:17 +09:00
|
|
|
|
let void_id = crate::mir::builder::emission::constant::emit_void(self);
|
|
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[build_local_statement] '{}': uninitialized, void_id={:?}",
|
|
|
|
|
|
var_name, void_id
|
|
|
|
|
|
);
|
2025-11-18 06:11:17 +09:00
|
|
|
|
}
|
|
|
|
|
|
void_id
|
2025-09-03 05:04:56 +09:00
|
|
|
|
};
|
2025-11-18 02:32:43 +09:00
|
|
|
|
|
2025-11-18 06:11:17 +09:00
|
|
|
|
if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[build_local_statement] Inserting '{}' -> {:?} into variable_map",
|
|
|
|
|
|
var_name, var_id
|
|
|
|
|
|
);
|
2025-11-18 06:11:17 +09:00
|
|
|
|
}
|
2025-12-13 01:30:04 +09:00
|
|
|
|
self.declare_local_in_current_scope(var_name, var_id)?;
|
2025-11-19 03:28:58 +09:00
|
|
|
|
// SlotRegistry にもローカル変数スロットを登録しておくよ(観測専用)
|
|
|
|
|
|
if let Some(reg) = self.current_slot_registry.as_mut() {
|
2025-12-15 23:27:24 +09:00
|
|
|
|
let ty = self.type_ctx.value_types.get(&var_id).cloned();
|
2025-11-19 03:28:58 +09:00
|
|
|
|
reg.ensure_slot(&var_name, ty);
|
|
|
|
|
|
}
|
2025-09-28 12:19:49 +09:00
|
|
|
|
last_value = Some(var_id);
|
2025-09-03 05:04:56 +09:00
|
|
|
|
}
|
2025-12-15 18:49:08 +09:00
|
|
|
|
// Phase 135 P0: Use function-level ValueId (SSOT) - build_local_statement is always in function context
|
|
|
|
|
|
Ok(last_value.unwrap_or_else(|| self.next_value_id()))
|
2025-09-03 01:37:38 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
|
|
|
|
|
// Return statement
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub(super) fn build_return_statement(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
value: Option<Box<ASTNode>>,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
2025-09-19 02:07:38 +09:00
|
|
|
|
// Enforce cleanup policy
|
|
|
|
|
|
if self.in_cleanup_block && !self.cleanup_allow_return {
|
|
|
|
|
|
return Err("return is not allowed inside cleanup block (enable NYASH_CLEANUP_ALLOW_RETURN=1 to permit)".to_string());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let return_value = if let Some(expr) = value {
|
|
|
|
|
|
self.build_expression(*expr)?
|
|
|
|
|
|
} else {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
crate::mir::builder::emission::constant::emit_void(self)
|
2025-09-03 05:04:56 +09:00
|
|
|
|
};
|
2025-09-19 02:07:38 +09:00
|
|
|
|
|
|
|
|
|
|
if self.return_defer_active {
|
|
|
|
|
|
// Defer: copy into slot and jump to target
|
|
|
|
|
|
if let (Some(slot), Some(target)) = (self.return_defer_slot, self.return_defer_target) {
|
|
|
|
|
|
self.return_deferred_emitted = true;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::Copy {
|
|
|
|
|
|
dst: slot,
|
|
|
|
|
|
src: return_value,
|
|
|
|
|
|
})?;
|
2025-09-28 20:38:09 +09:00
|
|
|
|
crate::mir::builder::metadata::propagate::propagate(self, return_value, slot);
|
2025-09-19 02:07:38 +09:00
|
|
|
|
if !self.is_current_block_terminated() {
|
2025-09-28 20:38:09 +09:00
|
|
|
|
crate::mir::builder::emission::branch::emit_jump(self, target)?;
|
2025-09-19 02:07:38 +09:00
|
|
|
|
}
|
|
|
|
|
|
Ok(return_value)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Fallback: no configured slot/target; emit a real return
|
2025-11-21 06:25:17 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::Return {
|
|
|
|
|
|
value: Some(return_value),
|
|
|
|
|
|
})?;
|
2025-09-19 02:07:38 +09:00
|
|
|
|
Ok(return_value)
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// Normal return
|
2025-11-21 06:25:17 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::Return {
|
|
|
|
|
|
value: Some(return_value),
|
|
|
|
|
|
})?;
|
2025-09-19 02:07:38 +09:00
|
|
|
|
Ok(return_value)
|
|
|
|
|
|
}
|
2025-09-03 01:37:38 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
|
|
|
|
|
// Nowait: prefer env.future.spawn_instance if method call; else FutureNew
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub(super) fn build_nowait_statement(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
variable: String,
|
|
|
|
|
|
expression: ASTNode,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
|
|
|
|
|
if let ASTNode::MethodCall {
|
|
|
|
|
|
object,
|
|
|
|
|
|
method,
|
|
|
|
|
|
arguments,
|
|
|
|
|
|
..
|
|
|
|
|
|
} = expression.clone()
|
|
|
|
|
|
{
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let recv_val = self.build_expression(*object)?;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
let mname_id =
|
|
|
|
|
|
crate::mir::builder::emission::constant::emit_string(self, method.clone());
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
|
|
|
|
|
|
arg_vals.push(recv_val);
|
|
|
|
|
|
arg_vals.push(mname_id);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
for a in arguments.into_iter() {
|
|
|
|
|
|
arg_vals.push(self.build_expression(a)?);
|
|
|
|
|
|
}
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let future_id = self.next_value_id();
|
2025-09-03 05:04:56 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::ExternCall {
|
|
|
|
|
|
dst: Some(future_id),
|
|
|
|
|
|
iface_name: "env.future".to_string(),
|
|
|
|
|
|
method_name: "spawn_instance".to_string(),
|
|
|
|
|
|
args: arg_vals,
|
|
|
|
|
|
effects: crate::mir::effect::EffectMask::PURE.add(crate::mir::effect::Effect::Io),
|
|
|
|
|
|
})?;
|
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>
2025-12-15 05:36:50 +09:00
|
|
|
|
// Future spawn returns a Future<T>; the inner type is not statically known here.
|
|
|
|
|
|
// Register at least Future<Unknown> to avoid later fail-fast type inference panics.
|
2025-12-15 23:27:24 +09:00
|
|
|
|
self.type_ctx.value_types
|
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>
2025-12-15 05:36:50 +09:00
|
|
|
|
.insert(future_id, MirType::Future(Box::new(MirType::Unknown)));
|
2025-12-16 03:48:44 +09:00
|
|
|
|
self.variable_ctx.variable_map.insert(variable.clone(), future_id);
|
2025-11-19 03:28:58 +09:00
|
|
|
|
if let Some(reg) = self.current_slot_registry.as_mut() {
|
|
|
|
|
|
reg.ensure_slot(&variable, None);
|
|
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
return Ok(future_id);
|
|
|
|
|
|
}
|
|
|
|
|
|
let expression_value = self.build_expression(expression)?;
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let future_id = self.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::FutureNew {
|
|
|
|
|
|
dst: future_id,
|
|
|
|
|
|
value: expression_value,
|
|
|
|
|
|
})?;
|
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>
2025-12-15 05:36:50 +09:00
|
|
|
|
let inner = self
|
2025-12-15 23:27:24 +09:00
|
|
|
|
.type_ctx.value_types
|
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>
2025-12-15 05:36:50 +09:00
|
|
|
|
.get(&expression_value)
|
|
|
|
|
|
.cloned()
|
|
|
|
|
|
.unwrap_or(MirType::Unknown);
|
2025-12-15 23:27:24 +09:00
|
|
|
|
self.type_ctx.value_types
|
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>
2025-12-15 05:36:50 +09:00
|
|
|
|
.insert(future_id, MirType::Future(Box::new(inner)));
|
2025-12-16 03:48:44 +09:00
|
|
|
|
self.variable_ctx.variable_map.insert(variable.clone(), future_id);
|
2025-11-19 03:28:58 +09:00
|
|
|
|
if let Some(reg) = self.current_slot_registry.as_mut() {
|
|
|
|
|
|
reg.ensure_slot(&variable, None);
|
|
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
Ok(future_id)
|
2025-09-03 01:37:38 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
|
|
|
|
|
// Await: insert Safepoint before/after and emit Await
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub(super) fn build_await_expression(
|
|
|
|
|
|
&mut self,
|
|
|
|
|
|
expression: ASTNode,
|
|
|
|
|
|
) -> Result<ValueId, String> {
|
2025-09-03 05:04:56 +09:00
|
|
|
|
let future_value = self.build_expression(expression)?;
|
|
|
|
|
|
self.emit_instruction(MirInstruction::Safepoint)?;
|
2025-11-17 00:48:18 +09:00
|
|
|
|
let result_id = self.next_value_id();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::Await {
|
|
|
|
|
|
dst: result_id,
|
|
|
|
|
|
future: future_value,
|
|
|
|
|
|
})?;
|
2025-12-15 23:27:24 +09:00
|
|
|
|
let result_type = match self.type_ctx.value_types.get(&future_value) {
|
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>
2025-12-15 05:36:50 +09:00
|
|
|
|
Some(MirType::Future(inner)) => (**inner).clone(),
|
|
|
|
|
|
_ => MirType::Unknown,
|
|
|
|
|
|
};
|
2025-12-15 23:27:24 +09:00
|
|
|
|
self.type_ctx.value_types.insert(result_id, result_type);
|
2025-09-03 05:04:56 +09:00
|
|
|
|
self.emit_instruction(MirInstruction::Safepoint)?;
|
|
|
|
|
|
Ok(result_id)
|
2025-09-03 01:37:38 +09:00
|
|
|
|
}
|
2025-09-03 05:04:56 +09:00
|
|
|
|
|
|
|
|
|
|
// me: resolve to param if present; else symbolic const (stable mapping)
|
2025-09-03 01:37:38 +09:00
|
|
|
|
pub(super) fn build_me_expression(&mut self) -> Result<ValueId, String> {
|
2025-12-16 03:48:44 +09:00
|
|
|
|
if let Some(id) = self.variable_ctx.variable_map.get("me").cloned() {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
return Ok(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
let me_tag = if let Some(ref cls) = self.current_static_box {
|
|
|
|
|
|
cls.clone()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
"__me__".to_string()
|
|
|
|
|
|
};
|
2025-09-28 20:38:09 +09:00
|
|
|
|
let me_value = crate::mir::builder::emission::constant::emit_string(self, me_tag);
|
2025-12-16 03:48:44 +09:00
|
|
|
|
self.variable_ctx.variable_map.insert("me".to_string(), me_value);
|
2025-11-19 03:28:58 +09:00
|
|
|
|
if let Some(reg) = self.current_slot_registry.as_mut() {
|
|
|
|
|
|
reg.ensure_slot("me", None);
|
|
|
|
|
|
}
|
2025-09-28 12:19:49 +09:00
|
|
|
|
// P0: Known 化 — 分かる範囲で me の起源クラスを付与(挙動不変)。
|
|
|
|
|
|
super::origin::infer::annotate_me_origin(self, me_value);
|
2025-09-03 05:04:56 +09:00
|
|
|
|
Ok(me_value)
|
2025-09-03 01:37:38 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|