diff --git a/apps/lib/json_native/lexer/scanner.nyash b/apps/lib/json_native/lexer/scanner.nyash index 44eff61a..a2cc0760 100644 --- a/apps/lib/json_native/lexer/scanner.nyash +++ b/apps/lib/json_native/lexer/scanner.nyash @@ -293,6 +293,11 @@ box JsonScanner { // 文字列リテラルを読み取り(クォート含む) read_string_literal() { local start_pos = me.position + // Save starting position to _tmp_pos so that substring/range checks + // do not depend on a previous reader’s value (PHI-safe, loop-safe). + // Other high-level readers (read_number/read_identifier) already + // initialize _tmp_pos; string literal must do the same. + me._tmp_pos = me.position // 開始クォート if me.current() != "\"" { diff --git a/src/backend/mir_interpreter/handlers/boxes_string.rs b/src/backend/mir_interpreter/handlers/boxes_string.rs index 48d1f609..cebcfbbc 100644 --- a/src/backend/mir_interpreter/handlers/boxes_string.rs +++ b/src/backend/mir_interpreter/handlers/boxes_string.rs @@ -12,13 +12,21 @@ pub(super) fn try_handle_string_box( eprintln!("[vm-trace] try_handle_string_box(method={})", method); } let recv = this.reg_load(box_val)?; - // Normalize receiver to trait-level StringBox to bridge old/new StringBox implementations - let sb_norm: crate::box_trait::StringBox = match recv.clone() { - VMValue::String(s) => crate::box_trait::StringBox::new(s), - VMValue::BoxRef(b) => b.to_string_box(), - other => other.to_nyash_box().to_string_box(), + // Handle ONLY when the receiver is actually a string. + // Do NOT coerce arbitrary boxes to StringBox (e.g., ArrayBox.length()). + let sb_norm_opt: Option = match recv.clone() { + VMValue::String(s) => Some(crate::box_trait::StringBox::new(s)), + VMValue::BoxRef(b) => { + if b.as_any().downcast_ref::().is_some() { + Some(b.to_string_box()) + } else { + None + } + } + _ => None, }; - // Only handle known string methods here + let Some(sb_norm) = sb_norm_opt else { return Ok(false) }; + // Only handle known string methods here (receiver is confirmed string) match method { "length" => { let ret = sb_norm.length(); diff --git a/src/mir/phi_core/loop_phi.rs b/src/mir/phi_core/loop_phi.rs index 7dcb4fe5..92b2f0a5 100644 --- a/src/mir/phi_core/loop_phi.rs +++ b/src/mir/phi_core/loop_phi.rs @@ -120,7 +120,17 @@ pub fn seal_incomplete_phis_with( let value_after = ops .get_variable_at_block(&phi.var_name, latch_id) .ok_or_else(|| format!("Variable {} not found at latch block", phi.var_name))?; - phi.known_inputs.push((latch_id, value_after)); + + // 🔧 ループ不変変数の自己参照PHI問題を修正 + // value_afterがPHI自身の場合(ループ内で変更されていない変数)は、 + // preheaderの値を使用する + let latch_value = if value_after == phi.phi_id { + // ループ不変変数:preheaderの値を使用 + phi.known_inputs[0].1 // preheaderからの初期値 + } else { + value_after + }; + phi.known_inputs.push((latch_id, latch_value)); ops.debug_verify_phi_inputs(block_id, &phi.known_inputs); ops.emit_phi_at_block_start(block_id, phi.phi_id, phi.known_inputs)?;