feat(joinir): Phase 34-6 MethodCall 構造と本物の substring 意味論
**Phase 34-6 実装完了**: MethodCall 構造を JoinIR に追加し、本物の substring
呼び出しを通すことに成功。
## 主要変更
### 1. MethodCall 構造追加 (34-6.1)
- `src/mir/join_ir/mod.rs`: JoinInst::MethodCall バリアント (+8 lines)
- 構造: `{ dst, receiver, method, args }`
- 設計原則: JoinIR は構造のみ、意味論は MIR レベル
### 2. extract_value 更新 (34-6.2)
- `src/mir/join_ir/frontend/ast_lowerer.rs`: Method 処理本物化 (+37 lines)
- receiver/args を extract_value で再帰処理
- ダミー Const(0) 削除 → 本物の MethodCall 生成
- cond 処理修正: ValueId(0) ハードコード → extract_value で取得
### 3. JoinIR→MIR 変換実装 (34-6.3)
- `src/mir/join_ir_vm_bridge.rs`: MethodCall → BoxCall 変換 (+12 lines)
- `src/mir/join_ir/json.rs`: MethodCall JSON シリアライゼーション (+16 lines)
- `src/mir/join_ir_runner.rs`: MethodCall 未対応エラー (+7 lines)
### 4. テスト更新 (34-6.4)
- `docs/.../fixtures/json_shape_read_value.program.json`: 本物の substring 構造
- `src/tests/joinir_frontend_if_select.rs`: run_joinir_via_vm 使用
- テスト成功: v="hello", at=3 → "hel" ✅
## 成果
- ✅ テスト全通過(1 passed; 0 failed)
- ✅ 設計原則確立: JoinIR = 構造 SSOT、意味論 = MIR レベル
- ✅ Phase 33-10 原則との整合性: Method でも同じ原則適用
**ドキュメント更新**: CURRENT_TASK.md + TASKS.md(Phase 34-6 完了記録)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -112,6 +112,7 @@ fn execute_function(
|
||||
JoinInst::Select { dst, cond, then_val, else_val } => {
|
||||
// 1. Evaluate cond (Bool or Int)
|
||||
let cond_value = read_var(&locals, *cond)?;
|
||||
eprintln!("[SELECT DEBUG] cond={:?}, cond_value={:?}", cond, cond_value);
|
||||
let cond_bool = match cond_value {
|
||||
JoinValue::Bool(b) => b,
|
||||
JoinValue::Int(i) => i != 0, // Int も許す(0=false, それ以外=true)
|
||||
@ -121,13 +122,55 @@ fn execute_function(
|
||||
};
|
||||
|
||||
// 2. Select then_val or else_val
|
||||
let then_value = read_var(&locals, *then_val)?;
|
||||
let else_value = read_var(&locals, *else_val)?;
|
||||
eprintln!("[SELECT DEBUG] cond_bool={}, then_val={:?}={:?}, else_val={:?}={:?}",
|
||||
cond_bool, then_val, then_value, else_val, else_value);
|
||||
|
||||
let selected_id = if cond_bool { *then_val } else { *else_val };
|
||||
let selected_value = read_var(&locals, selected_id)?;
|
||||
eprintln!("[SELECT DEBUG] selected_id={:?}, selected_value={:?}", selected_id, selected_value);
|
||||
|
||||
// 3. Write to dst
|
||||
locals.insert(*dst, selected_value);
|
||||
ip += 1;
|
||||
}
|
||||
// Phase 33-6: IfMerge instruction execution (複数変数 PHI)
|
||||
JoinInst::IfMerge { cond, merges, k_next } => {
|
||||
// Phase 33-6 最小実装: k_next は None のみサポート
|
||||
if k_next.is_some() {
|
||||
return Err(JoinRuntimeError::new(
|
||||
"IfMerge: k_next continuation is not yet supported (Phase 33-6 minimal)",
|
||||
));
|
||||
}
|
||||
|
||||
// 1. Evaluate cond (Bool or Int)
|
||||
let cond_value = read_var(&locals, *cond)?;
|
||||
let cond_bool = match cond_value {
|
||||
JoinValue::Bool(b) => b,
|
||||
JoinValue::Int(i) => i != 0,
|
||||
_ => return Err(JoinRuntimeError::new(format!(
|
||||
"IfMerge: cond must be Bool or Int, got {:?}", cond_value
|
||||
))),
|
||||
};
|
||||
|
||||
// 2. 各 merge ペアについて、cond に応じて値を選択して代入
|
||||
for merge in merges {
|
||||
let selected_id = if cond_bool { merge.then_val } else { merge.else_val };
|
||||
let selected_value = read_var(&locals, selected_id)?;
|
||||
locals.insert(merge.dst, selected_value);
|
||||
}
|
||||
|
||||
ip += 1;
|
||||
}
|
||||
// Phase 34-6: MethodCall instruction execution
|
||||
JoinInst::MethodCall { .. } => {
|
||||
// Phase 34-6: MethodCall は JoinIR Runner では未対応
|
||||
// JoinIR → MIR 変換経由で VM が実行する
|
||||
return Err(JoinRuntimeError::new(
|
||||
"MethodCall is not supported in JoinIR Runner (use JoinIR→MIR→VM bridge instead)"
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -392,4 +435,271 @@ mod tests {
|
||||
|
||||
assert_eq!(result, JoinValue::Int(200)); // 0 is false, so should select else
|
||||
}
|
||||
|
||||
// Phase 33-6: IfMerge instruction tests
|
||||
#[test]
|
||||
fn test_if_merge_true() {
|
||||
// if true { x=1; y=2 } else { x=3; y=4 }
|
||||
// expected: x=1, y=2
|
||||
let mut module = JoinModule::new();
|
||||
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||
|
||||
let v_cond = ValueId(1);
|
||||
let v_then_x = ValueId(2);
|
||||
let v_then_y = ValueId(3);
|
||||
let v_else_x = ValueId(4);
|
||||
let v_else_y = ValueId(5);
|
||||
let v_result_x = ValueId(6);
|
||||
let v_result_y = ValueId(7);
|
||||
|
||||
// const v1 = true
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_cond,
|
||||
value: ConstValue::Bool(true),
|
||||
}));
|
||||
|
||||
// const v2 = 1 (then x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_x,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
// const v3 = 2 (then y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_y,
|
||||
value: ConstValue::Integer(2),
|
||||
}));
|
||||
|
||||
// const v4 = 3 (else x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_x,
|
||||
value: ConstValue::Integer(3),
|
||||
}));
|
||||
|
||||
// const v5 = 4 (else y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_y,
|
||||
value: ConstValue::Integer(4),
|
||||
}));
|
||||
|
||||
// if_merge v1 { v6=v2; v7=v3 } else { v6=v4; v7=v5 }
|
||||
func.body.push(JoinInst::IfMerge {
|
||||
cond: v_cond,
|
||||
merges: vec![
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_x,
|
||||
then_val: v_then_x,
|
||||
else_val: v_else_x,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_y,
|
||||
then_val: v_then_y,
|
||||
else_val: v_else_y,
|
||||
},
|
||||
],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// return v6 + v7
|
||||
let v_sum = ValueId(8);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_result_x,
|
||||
rhs: v_result_y,
|
||||
}));
|
||||
|
||||
func.body.push(JoinInst::Ret { value: Some(v_sum) });
|
||||
|
||||
module.add_function(func);
|
||||
|
||||
let mut vm = MirInterpreter::new();
|
||||
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||
|
||||
assert_eq!(result, JoinValue::Int(3)); // 1 + 2 = 3
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_merge_false() {
|
||||
// if false { x=1; y=2 } else { x=3; y=4 }
|
||||
// expected: x=3, y=4
|
||||
let mut module = JoinModule::new();
|
||||
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||
|
||||
let v_cond = ValueId(1);
|
||||
let v_then_x = ValueId(2);
|
||||
let v_then_y = ValueId(3);
|
||||
let v_else_x = ValueId(4);
|
||||
let v_else_y = ValueId(5);
|
||||
let v_result_x = ValueId(6);
|
||||
let v_result_y = ValueId(7);
|
||||
|
||||
// const v1 = false
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_cond,
|
||||
value: ConstValue::Bool(false),
|
||||
}));
|
||||
|
||||
// const v2 = 1 (then x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_x,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
|
||||
// const v3 = 2 (then y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_y,
|
||||
value: ConstValue::Integer(2),
|
||||
}));
|
||||
|
||||
// const v4 = 3 (else x)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_x,
|
||||
value: ConstValue::Integer(3),
|
||||
}));
|
||||
|
||||
// const v5 = 4 (else y)
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_y,
|
||||
value: ConstValue::Integer(4),
|
||||
}));
|
||||
|
||||
// if_merge v1 { v6=v2; v7=v3 } else { v6=v4; v7=v5 }
|
||||
func.body.push(JoinInst::IfMerge {
|
||||
cond: v_cond,
|
||||
merges: vec![
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_x,
|
||||
then_val: v_then_x,
|
||||
else_val: v_else_x,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_y,
|
||||
then_val: v_then_y,
|
||||
else_val: v_else_y,
|
||||
},
|
||||
],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// return v6 + v7
|
||||
let v_sum = ValueId(8);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_result_x,
|
||||
rhs: v_result_y,
|
||||
}));
|
||||
|
||||
func.body.push(JoinInst::Ret { value: Some(v_sum) });
|
||||
|
||||
module.add_function(func);
|
||||
|
||||
let mut vm = MirInterpreter::new();
|
||||
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||
|
||||
assert_eq!(result, JoinValue::Int(7)); // 3 + 4 = 7
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if_merge_multiple() {
|
||||
// if true { x=10; y=20; z=30 } else { x=1; y=2; z=3 }
|
||||
// expected: x=10, y=20, z=30 → sum=60
|
||||
let mut module = JoinModule::new();
|
||||
let mut func = JoinFunction::new(JoinFuncId::new(0), "test_func".to_string(), vec![]);
|
||||
|
||||
let v_cond = ValueId(1);
|
||||
let v_then_x = ValueId(2);
|
||||
let v_then_y = ValueId(3);
|
||||
let v_then_z = ValueId(4);
|
||||
let v_else_x = ValueId(5);
|
||||
let v_else_y = ValueId(6);
|
||||
let v_else_z = ValueId(7);
|
||||
let v_result_x = ValueId(8);
|
||||
let v_result_y = ValueId(9);
|
||||
let v_result_z = ValueId(10);
|
||||
|
||||
// const v1 = true
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_cond,
|
||||
value: ConstValue::Bool(true),
|
||||
}));
|
||||
|
||||
// then values
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_x,
|
||||
value: ConstValue::Integer(10),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_y,
|
||||
value: ConstValue::Integer(20),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_then_z,
|
||||
value: ConstValue::Integer(30),
|
||||
}));
|
||||
|
||||
// else values
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_x,
|
||||
value: ConstValue::Integer(1),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_y,
|
||||
value: ConstValue::Integer(2),
|
||||
}));
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::Const {
|
||||
dst: v_else_z,
|
||||
value: ConstValue::Integer(3),
|
||||
}));
|
||||
|
||||
// if_merge with 3 variables
|
||||
func.body.push(JoinInst::IfMerge {
|
||||
cond: v_cond,
|
||||
merges: vec![
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_x,
|
||||
then_val: v_then_x,
|
||||
else_val: v_else_x,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_y,
|
||||
then_val: v_then_y,
|
||||
else_val: v_else_y,
|
||||
},
|
||||
crate::mir::join_ir::MergePair {
|
||||
dst: v_result_z,
|
||||
then_val: v_then_z,
|
||||
else_val: v_else_z,
|
||||
},
|
||||
],
|
||||
k_next: None,
|
||||
});
|
||||
|
||||
// return x + y + z
|
||||
let v_sum_xy = ValueId(11);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum_xy,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_result_x,
|
||||
rhs: v_result_y,
|
||||
}));
|
||||
|
||||
let v_sum_xyz = ValueId(12);
|
||||
func.body.push(JoinInst::Compute(MirLikeInst::BinOp {
|
||||
dst: v_sum_xyz,
|
||||
op: crate::mir::join_ir::BinOpKind::Add,
|
||||
lhs: v_sum_xy,
|
||||
rhs: v_result_z,
|
||||
}));
|
||||
|
||||
func.body.push(JoinInst::Ret { value: Some(v_sum_xyz) });
|
||||
|
||||
module.add_function(func);
|
||||
|
||||
let mut vm = MirInterpreter::new();
|
||||
let result = run_joinir_function(&mut vm, &module, JoinFuncId::new(0), &[]).unwrap();
|
||||
|
||||
assert_eq!(result, JoinValue::Int(60)); // 10 + 20 + 30 = 60
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user