chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更

Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-21 06:25:17 +09:00
parent baf028a94f
commit f9d100ce01
366 changed files with 14322 additions and 5236 deletions

View File

@ -9,50 +9,50 @@ use super::{
BasicBlock, BasicBlockId, BasicBlockIdGenerator, CompareOp, ConstValue, Effect, EffectMask,
FunctionSignature, MirFunction, MirInstruction, MirModule, MirType, ValueId, ValueIdGenerator,
};
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::region::RegionId;
use crate::ast::{ASTNode, LiteralValue};
use crate::mir::builder::builder_calls::CallTarget;
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::region::RegionId;
use std::collections::HashMap;
use std::collections::HashSet;
mod calls; // Call system modules (refactored from builder_calls)
mod builder_calls;
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
mod calls; // Call system modules (refactored from builder_calls)
mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
mod method_call_handlers; // Method call handler separation (Phase 3)
mod decls; // declarations lowering split
mod exprs; // expression lowering split
mod exprs_call; // call(expr)
// include lowering removed (using is handled in runner)
mod exprs_call;
mod method_call_handlers; // Method call handler separation (Phase 3) // call(expr)
// include lowering removed (using is handled in runner)
mod control_flow; // thin wrappers to centralize control-flow entrypoints
mod exprs_lambda; // lambda lowering
mod exprs_peek; // peek expression
mod exprs_qmark; // ?-propagate
mod fields; // field access/assignment lowering split
mod if_form;
mod lifecycle;
pub(crate) mod loops;
mod ops;
mod phi;
mod phi_merge; // Phase 25.1q: Unified PHI merge helper
mod if_form;
mod control_flow; // thin wrappers to centralize control-flow entrypoints
mod lifecycle; // prepare/lower_root/finalize split
// legacy large-match remains inline for now (planned extraction)
mod plugin_sigs; // plugin signature loader
mod stmts;
mod utils;
mod vars; // variables/scope helpers // small loop helpers (header/exit context)
mod origin; // P0: origin inferenceme/Knownと PHI 伝播(軽量)
mod observe; // P0: dev-only observability helpersssa/resolve
mod rewrite; // P1: Known rewrite & special consolidation
mod ssa; // LocalSSA helpers (in-block materialization)
mod schedule; // BlockScheduleBox物理順序: PHI→materialize→body
mod receiver; // ReceiverMaterializationBoxMethod recv の pin+LocalSSA 集約)
mod metadata; // MetadataPropagationBoxtype/originの伝播
mod emission; // emission::*Const/Compare/Branch の薄い発行箱)
mod types; // types::annotation / inference型注釈/推論の箱: 推論は後段)
mod router; // RouterPolicyBoxUnified vs BoxCall
mod phi_merge; // Phase 25.1q: Unified PHI merge helper // prepare/lower_root/finalize split
// legacy large-match remains inline for now (planned extraction)
mod emission; // emission::*Const/Compare/Branch の薄い発行箱)
mod emit_guard; // EmitGuardBoxemit直前の最終関所
mod metadata; // MetadataPropagationBoxtype/originの伝播
mod name_const; // NameConstBox関数名Const生成
pub(crate) mod type_registry; // TypeRegistryBox型情報管理の一元化
mod observe; // P0: dev-only observability helpersssa/resolve
mod origin; // P0: origin inferenceme/Knownと PHI 伝播(軽量)
mod plugin_sigs; // plugin signature loader
mod receiver; // ReceiverMaterializationBoxMethod recv の pin+LocalSSA 集約)
mod rewrite; // P1: Known rewrite & special consolidation
mod router; // RouterPolicyBoxUnified vs BoxCall
mod schedule; // BlockScheduleBox物理順序: PHI→materialize→body
mod ssa; // LocalSSA helpers (in-block materialization)
mod stmts;
pub(crate) mod type_registry;
mod types; // types::annotation / inference型注釈/推論の箱: 推論は後段)
mod utils;
mod vars; // variables/scope helpers // small loop helpers (header/exit context) // TypeRegistryBox型情報管理の一元化
// Unified member property kinds for computed/once/birth_once
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -62,7 +62,6 @@ pub(crate) enum PropertyKind {
BirthOnce,
}
/// MIR builder for converting AST to SSA form
pub struct MirBuilder {
/// Current module being built
@ -152,7 +151,6 @@ pub struct MirBuilder {
pub(super) current_region_stack: Vec<RegionId>,
// include guards removed
/// Loop context stacks for lowering break/continue inside nested control flow
/// Top of stack corresponds to the innermost active loop
pub(super) loop_header_stack: Vec<BasicBlockId>,
@ -288,19 +286,31 @@ impl MirBuilder {
}
/// Push/pop helpers for If merge context (best-effort; optional usage)
pub(super) fn push_if_merge(&mut self, bb: BasicBlockId) { self.if_merge_stack.push(bb); }
pub(super) fn pop_if_merge(&mut self) { let _ = self.if_merge_stack.pop(); }
pub(super) fn push_if_merge(&mut self, bb: BasicBlockId) {
self.if_merge_stack.push(bb);
}
pub(super) fn pop_if_merge(&mut self) {
let _ = self.if_merge_stack.pop();
}
/// Suppress entry pin copy for the next start_new_block (used for merge blocks).
pub(super) fn suppress_next_entry_pin_copy(&mut self) { self.suppress_pin_entry_copy_next = true; }
pub(super) fn suppress_next_entry_pin_copy(&mut self) {
self.suppress_pin_entry_copy_next = true;
}
// ---- Hint helpers (no-op by default) ----
#[inline]
pub(crate) fn hint_scope_enter(&mut self, id: u32) { self.hint_sink.scope_enter(id); }
pub(crate) fn hint_scope_enter(&mut self, id: u32) {
self.hint_sink.scope_enter(id);
}
#[inline]
pub(crate) fn hint_scope_leave(&mut self, id: u32) { self.hint_sink.scope_leave(id); }
pub(crate) fn hint_scope_leave(&mut self, id: u32) {
self.hint_sink.scope_leave(id);
}
#[inline]
pub(crate) fn hint_join_result<S: Into<String>>(&mut self, var: S) { self.hint_sink.join_result(var.into()); }
pub(crate) fn hint_join_result<S: Into<String>>(&mut self, var: S) {
self.hint_sink.join_result(var.into());
}
// ----------------------
// Debug scope helpers (region_id for DebugHub events)
@ -332,10 +342,7 @@ impl MirBuilder {
// ----------------------
#[inline]
pub(super) fn compile_trace_enabled() -> bool {
std::env::var("NYASH_MIR_COMPILE_TRACE")
.ok()
.as_deref()
== Some("1")
std::env::var("NYASH_MIR_COMPILE_TRACE").ok().as_deref() == Some("1")
}
#[inline]
@ -395,7 +402,6 @@ impl MirBuilder {
.unwrap_or_default()
}
/// Build a complete MIR module from AST
pub fn build_module(&mut self, ast: ASTNode) -> Result<MirModule, String> {
self.prepare_module()?;
@ -403,7 +409,6 @@ impl MirBuilder {
self.finalize_module(result_value)
}
/// Build an expression and return its value ID
pub(super) fn build_expression(&mut self, ast: ASTNode) -> Result<ValueId, String> {
// Delegated to exprs.rs to keep this file lean
@ -412,11 +417,17 @@ impl MirBuilder {
self.recursion_depth += 1;
if self.recursion_depth > MAX_RECURSION_DEPTH {
eprintln!("\n[FATAL] ============================================");
eprintln!("[FATAL] Recursion depth exceeded {} in build_expression", MAX_RECURSION_DEPTH);
eprintln!(
"[FATAL] Recursion depth exceeded {} in build_expression",
MAX_RECURSION_DEPTH
);
eprintln!("[FATAL] Current depth: {}", self.recursion_depth);
eprintln!("[FATAL] AST node type: {:?}", std::mem::discriminant(&ast));
eprintln!("[FATAL] ============================================\n");
return Err(format!("Recursion depth exceeded: {} (possible infinite loop)", self.recursion_depth));
return Err(format!(
"Recursion depth exceeded: {} (possible infinite loop)",
self.recursion_depth
));
}
let result = self.build_expression_impl(ast);
@ -424,7 +435,6 @@ impl MirBuilder {
result
}
/// Build a literal value
pub(super) fn build_literal(&mut self, literal: LiteralValue) -> Result<ValueId, String> {
// Determine type without moving literal
@ -438,9 +448,13 @@ impl MirBuilder {
// Emit via ConstantEmissionBox仕様不変の統一ルート
let dst = match literal {
LiteralValue::Integer(n) => crate::mir::builder::emission::constant::emit_integer(self, n),
LiteralValue::Integer(n) => {
crate::mir::builder::emission::constant::emit_integer(self, n)
}
LiteralValue::Float(f) => crate::mir::builder::emission::constant::emit_float(self, f),
LiteralValue::String(s) => crate::mir::builder::emission::constant::emit_string(self, s),
LiteralValue::String(s) => {
crate::mir::builder::emission::constant::emit_string(self, s)
}
LiteralValue::Bool(b) => crate::mir::builder::emission::constant::emit_bool(self, b),
LiteralValue::Null => crate::mir::builder::emission::constant::emit_null(self),
LiteralValue::Void => crate::mir::builder::emission::constant::emit_void(self),
@ -453,7 +467,6 @@ impl MirBuilder {
Ok(dst)
}
/// Build variable access
pub(super) fn build_variable_access(&mut self, name: String) -> Result<ValueId, String> {
// Step 5-5-G: __pin$ variables should NEVER be accessed from variable_map
@ -478,7 +491,8 @@ impl MirBuilder {
msg.push_str("\nHint: 'local' is a Stage-3 keyword. Enable NYASH_PARSER_STAGE3=1 (and HAKO_PARSER_STAGE3=1 for Stage-B).");
msg.push_str("\nFor AotPrep verification, use tools/hakorune_emit_mir.sh which sets these automatically.");
} else if (name == "flow" || name == "try" || name == "catch" || name == "throw")
&& !crate::config::env::parser_stage3() {
&& !crate::config::env::parser_stage3()
{
msg.push_str(&format!("\nHint: '{}' is a Stage-3 keyword. Enable NYASH_PARSER_STAGE3=1 (and HAKO_PARSER_STAGE3=1 for Stage-B).", name));
}
@ -486,7 +500,9 @@ impl MirBuilder {
if !suggest.is_empty() {
msg.push_str("\nHint: symbol appears in using module(s): ");
msg.push_str(&suggest.join(", "));
msg.push_str("\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].");
msg.push_str(
"\nConsider adding 'using <module> [as Alias]' or check nyash.toml [using].",
);
}
Err(msg)
}
@ -527,8 +543,6 @@ impl MirBuilder {
Ok(value_id)
}
/// Emit an instruction to the current basic block
pub(super) fn emit_instruction(&mut self, instruction: MirInstruction) -> Result<(), String> {
let block_id = self.current_block.ok_or("No current basic block")?;
@ -551,9 +565,23 @@ impl MirBuilder {
// CRITICAL: Final receiver materialization for MethodCall
// This ensures the receiver has an in-block definition in the same block as the Call.
// Must happen BEFORE function mutable borrow to avoid borrowck conflicts.
if let MirInstruction::Call { callee: Some(callee), dst, args, effects, .. } = &instruction {
if let MirInstruction::Call {
callee: Some(callee),
dst,
args,
effects,
..
} = &instruction
{
use crate::mir::definitions::call_unified::Callee;
if let Callee::Method { box_name, method, receiver: Some(r), certainty, box_kind } = callee.clone() {
if let Callee::Method {
box_name,
method,
receiver: Some(r),
certainty,
box_kind,
} = callee.clone()
{
// LocalSSA: ensure receiver has a Copy in current_block
let r_local = crate::mir::builder::ssa::local::recv(self, r);
@ -579,7 +607,9 @@ impl MirBuilder {
// Pre-capture branch/jump targets for predecessor update after we finish
// mutably borrowing the current block.
let (then_t, else_t, jump_t) = match &instruction {
MirInstruction::Branch { then_bb, else_bb, .. } => (Some(*then_bb), Some(*else_bb), None),
MirInstruction::Branch {
then_bb, else_bb, ..
} => (Some(*then_bb), Some(*else_bb), None),
MirInstruction::Jump { target } => (None, None, Some(*target)),
_ => (None, None, None),
};
@ -607,7 +637,13 @@ impl MirBuilder {
return Err("builder invariant violated: MirInstruction::Call.callee must be Some (unified call)".into());
} else if std::env::var("NYASH_LOCAL_SSA_TRACE").ok().as_deref() == Some("1") {
use crate::mir::definitions::call_unified::Callee;
if let Some(Callee::Method { box_name, method, receiver: Some(r), .. }) = callee {
if let Some(Callee::Method {
box_name,
method,
receiver: Some(r),
..
}) = callee
{
eprintln!(
"[emit-inst] fn={} bb={:?} Call {}.{} recv=%{}",
current_fn_name,
@ -617,9 +653,16 @@ impl MirBuilder {
r.0
);
}
} else if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1") {
} else if std::env::var("NYASH_BUILDER_TRACE_RECV").ok().as_deref() == Some("1")
{
use crate::mir::definitions::call_unified::Callee;
if let Some(Callee::Method { box_name, method, receiver: Some(r), .. }) = callee {
if let Some(Callee::Method {
box_name,
method,
receiver: Some(r),
..
}) = callee
{
let names: Vec<String> = self
.variable_map
.iter()
@ -688,18 +731,24 @@ impl MirBuilder {
}
block.add_instruction(instruction);
// Drop the mutable borrow of `block` before updating other blocks
}
}
// Update predecessor sets for branch/jump immediately so that
// debug_verify_phi_inputs can observe a consistent CFG without
// requiring a full function.update_cfg() pass.
if let Some(t) = then_t {
if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); }
if let Some(succ) = function.get_block_mut(t) {
succ.add_predecessor(block_id);
}
}
if let Some(t) = else_t {
if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); }
if let Some(succ) = function.get_block_mut(t) {
succ.add_predecessor(block_id);
}
}
if let Some(t) = jump_t {
if let Some(succ) = function.get_block_mut(t) { succ.add_predecessor(block_id); }
if let Some(succ) = function.get_block_mut(t) {
succ.add_predecessor(block_id);
}
}
Ok(())
} else {
@ -726,7 +775,10 @@ impl MirBuilder {
}
}
}
Err(format!("PHI instruction {} not found in block {}", phi_id, block))
Err(format!(
"PHI instruction {} not found in block {}",
phi_id, block
))
} else {
Err(format!("Block {} not found", block))
}
@ -739,7 +791,6 @@ impl MirBuilder {
// フェーズM: insert_edge_copy()メソッド削除no_phi_mode撤廃により不要
/// Build new expression: new ClassName(arguments)
pub(super) fn build_new_expression(
&mut self,
@ -825,14 +876,15 @@ impl MirBuilder {
// 代替: 既存互換として BoxCall("birth")(プラグイン/ビルトインの初期化に対応)
if class != "StringBox" {
let arity = arg_values.len();
let lowered = crate::mir::builder::calls::function_lowering::generate_method_function_name(
&class,
"birth",
arity,
);
let lowered =
crate::mir::builder::calls::function_lowering::generate_method_function_name(
&class, "birth", arity,
);
let use_lowered = if let Some(ref module) = self.current_module {
module.functions.contains_key(&lowered)
} else { false };
} else {
false
};
if use_lowered {
// Call Global("Class.birth/Arity") with argv = [me, args...]
let mut argv: Vec<ValueId> = Vec::with_capacity(1 + arity);
@ -847,7 +899,10 @@ impl MirBuilder {
let is_user_box = self.user_defined_boxes.contains(&class);
// Dev safety: allow disabling birth() injection for builtins to avoid
// unified-call method dispatch issues while migrating. Off by default unless explicitly enabled.
let allow_builtin_birth = std::env::var("NYASH_DEV_BIRTH_INJECT_BUILTINS").ok().as_deref() == Some("1");
let allow_builtin_birth = std::env::var("NYASH_DEV_BIRTH_INJECT_BUILTINS")
.ok()
.as_deref()
== Some("1");
if !is_user_box && allow_builtin_birth {
let birt_mid = resolve_slot_by_type_name(&class, "birth");
self.emit_box_or_plugin_call(
@ -865,7 +920,6 @@ impl MirBuilder {
Ok(dst)
}
/// Check if the current basic block is terminated
fn is_current_block_terminated(&self) -> bool {
if let (Some(block_id), Some(ref function)) = (self.current_block, &self.current_function) {
@ -919,7 +973,6 @@ impl MirBuilder {
}
}
impl Default for MirBuilder {
fn default() -> Self {
Self::new()