fix(mir): Phase 25.1 StaticCompiler receiver型推論バグ根治

## 問題
Stage-1ブリッジで `StringHelpers.skip_ws/2` 呼び出し時に:
- ParserBox.length(receiver=ValueId未定義) でVM落ち
- receiver が捏造される(型情報なし)

## 根本原因
1. CalleeResolver: receiver型なし→捏造ValueId生成
2. 順序問題: materialization → guard(逆であるべき)
3. 型注釈不足: String定数・BinOp結果に型情報なし

## 解決策(3 Phase)

### Phase 1: guard.rs Global フォールバック (lines 100-146)
- receiver型なし→Global変換で捏造ValueId防止
- Phase 3-C: 文字列メソッド→StringBox正規化追加

### Phase 2: unified_emitter.rs 順序反転 (lines 176-188)
- guard FIRST → materialization (Method のみ)
- Global 呼び出しの receiver 実体化を回避

### Phase 3-A: emit_string 型注釈 (constant.rs:46-49)
- String定数に value_types + value_origin_newbox 登録

### Phase 3-B: BinOp(Add) 型注釈強化 (ops.rs:70-86,145-166,178-198)
- value_origin_newbox もチェック(value_types のみ→両方)
- 結果も両方のマップに登録

### Phase 3-C: StaticCompiler 文字列メソッド正規化 (guard.rs:106-129)
- length/substring等→StringBox.method に統一
- ParserBox.length → StringBox.length

## 検証
 RC=0 達成(apps/tests/stage1_skip_ws_repro.hako)
 正規化トレース確認(ParserBox→StringBox)
 receiver捏造完全防止

Co-Authored-By: ChatGPT5 <chatgpt@openai.com>
This commit is contained in:
nyash-codex
2025-11-21 13:53:50 +09:00
parent 4565f7476d
commit eb10fc7159
4 changed files with 72 additions and 27 deletions

View File

@ -67,19 +67,20 @@ impl super::MirBuilder {
super::builder_calls::CallTarget::Global(name),
vec![lhs, rhs],
)?;
// 型注釈(従来と同等
// 型注釈(Phase 3-B: value_origin_newbox もチェック&両方登録
let lhs_is_str = match self.value_types.get(&lhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
_ => self.value_origin_newbox.get(&lhs).map(|s| s == "StringBox").unwrap_or(false),
};
let rhs_is_str = match self.value_types.get(&rhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
_ => self.value_origin_newbox.get(&rhs).map(|s| s == "StringBox").unwrap_or(false),
};
if lhs_is_str || rhs_is_str {
self.value_types.insert(dst, MirType::String);
self.value_types.insert(dst, MirType::Box("StringBox".to_string()));
self.value_origin_newbox.insert(dst, "StringBox".to_string());
} else {
self.value_types.insert(dst, MirType::Integer);
}
@ -142,18 +143,20 @@ impl super::MirBuilder {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
if matches!(op, crate::mir::BinaryOp::Add) {
// Phase 3-B: value_origin_newbox もチェック&両方登録
let lhs_is_str = match self.value_types.get(&lhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
_ => self.value_origin_newbox.get(&lhs).map(|s| s == "StringBox").unwrap_or(false),
};
let rhs_is_str = match self.value_types.get(&rhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
_ => self.value_origin_newbox.get(&rhs).map(|s| s == "StringBox").unwrap_or(false),
};
if lhs_is_str || rhs_is_str {
self.value_types.insert(dst, MirType::String);
self.value_types.insert(dst, MirType::Box("StringBox".to_string()));
self.value_origin_newbox.insert(dst, "StringBox".to_string());
} else {
self.value_types.insert(dst, MirType::Integer);
}
@ -173,18 +176,20 @@ impl super::MirBuilder {
self.emit_instruction(MirInstruction::BinOp { dst, op, lhs, rhs })?;
}
if matches!(op, crate::mir::BinaryOp::Add) {
// Phase 3-B: value_origin_newbox もチェック&両方登録
let lhs_is_str = match self.value_types.get(&lhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
_ => self.value_origin_newbox.get(&lhs).map(|s| s == "StringBox").unwrap_or(false),
};
let rhs_is_str = match self.value_types.get(&rhs) {
Some(MirType::String) => true,
Some(MirType::Box(bt)) if bt == "StringBox" => true,
_ => false,
_ => self.value_origin_newbox.get(&rhs).map(|s| s == "StringBox").unwrap_or(false),
};
if lhs_is_str || rhs_is_str {
self.value_types.insert(dst, MirType::String);
self.value_types.insert(dst, MirType::Box("StringBox".to_string()));
self.value_origin_newbox.insert(dst, "StringBox".to_string());
} else {
self.value_types.insert(dst, MirType::Integer);
}