LoopBuilder: bind variable_map to Phi result on seal
- After inserting Phi at loop header, update variable_map so that subsequent uses (after the loop) refer to the Phi result instead of the latch/body value. This fixes dominance issues in verifier. - Add tests: loop phi normalization and loop+nested-if phi; both pass.
This commit is contained in:
@ -438,3 +438,93 @@ struct BoxVTable {
|
||||
- [ ] プラグインBoxの透過的な動作
|
||||
- [ ] パフォーマンス改善の確認
|
||||
- [ ] メモリ使用量の変化なし
|
||||
|
||||
## 🚀 究極の統一:ビルトインBox完全プラグイン化構想
|
||||
|
||||
### 現状の二重実装問題
|
||||
- **plugin_loader.rs** (1217行) - ビルトインBoxの動的ライブラリ化
|
||||
- **plugin_loader_v2.rs** (906行) - プラグインBoxシステム
|
||||
- 合計2000行以上の重複!
|
||||
|
||||
### 完全プラグイン化の提案
|
||||
|
||||
#### すべてをプラグインに統一
|
||||
```rust
|
||||
// 現在
|
||||
ビルトインFileBox → 静的リンク
|
||||
プラグインFileBox → 動的ロード(.so)
|
||||
|
||||
// 統一後
|
||||
すべてのBox → プラグイン(.so)として実装
|
||||
```
|
||||
|
||||
#### コアBoxの自動ロード戦略
|
||||
```rust
|
||||
const CORE_BOXES: &[&str] = &[
|
||||
"libnyash_string_box.so", // StringBox(必須)
|
||||
"libnyash_integer_box.so", // IntegerBox(必須)
|
||||
"libnyash_bool_box.so", // BoolBox(必須)
|
||||
"libnyash_console_box.so", // ConsoleBox(print用)
|
||||
];
|
||||
|
||||
// 起動時に自動ロード
|
||||
fn init_core_boxes() {
|
||||
for plugin in CORE_BOXES {
|
||||
plugin_loader.load_required(plugin)
|
||||
.expect("Core box loading failed");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### メリット
|
||||
1. **コード削減**: plugin_loader.rs (1217行) を完全削除
|
||||
2. **統一性**: Everything is Boxの究極の実現
|
||||
3. **柔軟性**: StringBoxすら置き換え可能
|
||||
4. **ビルド高速化**: 本体が軽量に
|
||||
5. **配布の柔軟性**: 必要なBoxだけ選択可能
|
||||
|
||||
### 考慮事項
|
||||
|
||||
#### パフォーマンス
|
||||
- FFI境界のオーバーヘッドは**ナノ秒レベル**
|
||||
- 実用上の影響なし
|
||||
|
||||
#### デバッグの課題と対策
|
||||
```rust
|
||||
// 課題:エラー時のスタックトレース
|
||||
thread 'main' panicked at 'FFI boundary: 0x7f8b2c001234'
|
||||
|
||||
// 対策1:プラグイン側でのロギング
|
||||
#[no_mangle]
|
||||
pub extern "C" fn box_method_toString() {
|
||||
eprintln!("[StringBox::toString] called from {:?}", std::thread::current().id());
|
||||
}
|
||||
|
||||
// 対策2:デバッグシンボル保持
|
||||
cargo build --features debug-symbols
|
||||
|
||||
// 対策3:プラグイン単体テストの充実
|
||||
#[test]
|
||||
fn test_string_box_methods() { /* ... */ }
|
||||
```
|
||||
|
||||
### 実装ロードマップ
|
||||
1. **Phase A**: コアBoxのプラグイン化
|
||||
- StringBox, IntegerBox, BoolBox, ConsoleBox
|
||||
2. **Phase B**: 起動時自動ロード機構
|
||||
3. **Phase C**: plugin_loader.rs削除
|
||||
4. **Phase D**: ドキュメント・テスト整備
|
||||
|
||||
### 設定ファイル案
|
||||
```toml
|
||||
# ~/.nyash/config.toml
|
||||
[plugins]
|
||||
core_path = "./plugins/core/"
|
||||
search_paths = ["./plugins", "/usr/lib/nyash/plugins"]
|
||||
|
||||
[core_boxes]
|
||||
required = ["string", "integer", "bool", "console"]
|
||||
optional = ["file", "math", "time"]
|
||||
```
|
||||
|
||||
これにより、「Everything is Box」哲学が実装レベルでも完全に実現される!
|
||||
@ -162,6 +162,8 @@ impl<'a> LoopBuilder<'a> {
|
||||
|
||||
// 完成したPhi nodeを発行
|
||||
self.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?;
|
||||
// 重要: ループ外から参照される変数はPhi結果に束縛し直す
|
||||
self.update_variable(phi.var_name.clone(), phi.phi_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -753,4 +753,95 @@ mod tests {
|
||||
assert!(errs.iter().any(|e| matches!(e, VerificationError::MergeUsesPredecessorValue{..} | VerificationError::DominatorViolation{..})),
|
||||
"Expected merge/dominator error, got: {:?}", errs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_loop_phi_normalization() {
|
||||
// Program:
|
||||
// local i = 0
|
||||
// loop(false) { i = 1 }
|
||||
// i
|
||||
let ast = ASTNode::Program {
|
||||
statements: vec![
|
||||
ASTNode::Local {
|
||||
variables: vec!["i".to_string()],
|
||||
initial_values: vec![Some(Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }))],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Loop {
|
||||
condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }),
|
||||
body: vec![ ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable { name: "i".to_string(), span: Span::unknown() }),
|
||||
value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }),
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Variable { name: "i".to_string(), span: Span::unknown() },
|
||||
],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let mut builder = MirBuilder::new();
|
||||
let module = builder.build_module(ast).expect("build mir");
|
||||
|
||||
// Verify SSA/dominance: should pass
|
||||
let mut verifier = MirVerifier::new();
|
||||
let res = verifier.verify_module(&module);
|
||||
if let Err(errs) = &res { eprintln!("Verifier errors: {:?}", errs); }
|
||||
assert!(res.is_ok(), "MIR loop with phi normalization should pass verification");
|
||||
|
||||
// Ensure phi is printed (header phi for variable i)
|
||||
let printer = MirPrinter::verbose();
|
||||
let mir_text = printer.print_module(&module);
|
||||
assert!(mir_text.contains("phi"), "Printed MIR should contain a phi for loop header\n{}", mir_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_loop_nested_if_phi() {
|
||||
// Program:
|
||||
// local x = 0
|
||||
// loop(false) { if true { x = 1 } else { x = 2 } }
|
||||
// x
|
||||
let ast = ASTNode::Program {
|
||||
statements: vec![
|
||||
ASTNode::Local {
|
||||
variables: vec!["x".to_string()],
|
||||
initial_values: vec![Some(Box::new(ASTNode::Literal { value: LiteralValue::Integer(0), span: Span::unknown() }))],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Loop {
|
||||
condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(false), span: Span::unknown() }),
|
||||
body: vec![ ASTNode::If {
|
||||
condition: Box::new(ASTNode::Literal { value: LiteralValue::Bool(true), span: Span::unknown() }),
|
||||
then_body: vec![ ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
|
||||
value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(1), span: Span::unknown() }),
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
else_body: Some(vec![ ASTNode::Assignment {
|
||||
target: Box::new(ASTNode::Variable { name: "x".to_string(), span: Span::unknown() }),
|
||||
value: Box::new(ASTNode::Literal { value: LiteralValue::Integer(2), span: Span::unknown() }),
|
||||
span: Span::unknown(),
|
||||
}]),
|
||||
span: Span::unknown(),
|
||||
}],
|
||||
span: Span::unknown(),
|
||||
},
|
||||
ASTNode::Variable { name: "x".to_string(), span: Span::unknown() },
|
||||
],
|
||||
span: Span::unknown(),
|
||||
};
|
||||
|
||||
let mut builder = MirBuilder::new();
|
||||
let module = builder.build_module(ast).expect("build mir");
|
||||
|
||||
let mut verifier = MirVerifier::new();
|
||||
let res = verifier.verify_module(&module);
|
||||
if let Err(errs) = &res { eprintln!("Verifier errors: {:?}", errs); }
|
||||
assert!(res.is_ok(), "Nested if in loop should pass verification with proper phis");
|
||||
|
||||
let printer = MirPrinter::verbose();
|
||||
let mir_text = printer.print_module(&module);
|
||||
assert!(mir_text.contains("phi"), "Printed MIR should contain phi nodes for nested if/loop\n{}", mir_text);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user