use super::{ BasicBlockId, EffectMask, FunctionSignature, MirInstruction, MirModule, MirType, ValueId, }; use crate::ast::ASTNode; // Lifecycle routines extracted from builder.rs fn has_main_static(ast: &ASTNode) -> bool { use crate::ast::ASTNode as N; if let N::Program { statements, .. } = ast { for st in statements { if let N::BoxDeclaration { name, methods, is_static, .. } = st { if *is_static && name == "Main" { if let Some(m) = methods.get("main") { if let N::FunctionDeclaration { .. } = m { return true; } } } } } } false } /// Phase 63-6-3: PHI命令から型ヒントを取得 /// /// ValueId が PHI 命令の結果である場合、その type_hint を返す。 /// P1 ケース(IfSelectTest.simple/local)でのみ使用を想定。 /// /// # Arguments /// /// * `function` - 対象のMIR関数 /// * `value_id` - 型ヒントを取得したいValueId /// /// # Returns /// /// PHI命令に type_hint があれば Some(MirType)、なければ None fn get_phi_type_hint( function: &super::MirFunction, value_id: ValueId, ) -> Option { // 全ブロックを走査してPHI命令を探す for (_block_id, block) in function.blocks.iter() { for inst in block.instructions.iter() { if let MirInstruction::Phi { dst, type_hint, .. } = inst { if *dst == value_id { // Phase 63-6-3: PHI の type_hint をそのまま返す return type_hint.clone(); } } } } None } impl super::MirBuilder { /// Unified declaration indexing (Phase A): collect symbols before lowering /// - user_defined_boxes: non-static Box names (for NewBox birth() skip) /// - static_method_index: name -> [(BoxName, arity)] (for bare-call fallback) fn index_declarations(&mut self, node: &ASTNode) { match node { ASTNode::Program { statements, .. } => { for st in statements { self.index_declarations(st); } } ASTNode::BoxDeclaration { name, methods, is_static, .. } => { if !*is_static { self.user_defined_boxes.insert(name.clone()); } else { for (mname, mast) in methods { if let ASTNode::FunctionDeclaration { params, .. } = mast { self.static_method_index .entry(mname.clone()) .or_insert_with(Vec::new) .push((name.clone(), params.len())); } } } } _ => {} } } pub(super) fn prepare_module(&mut self) -> Result<(), String> { let mut module = MirModule::new("main".to_string()); module.metadata.source_file = self.current_source_file(); let main_signature = FunctionSignature { name: "main".to_string(), params: vec![], return_type: MirType::Void, effects: EffectMask::PURE, }; let entry_block = self.block_gen.next(); let mut main_function = self.new_function_with_metadata(main_signature, entry_block); main_function.metadata.is_entry_point = true; self.current_module = Some(module); self.current_function = Some(main_function); self.current_block = Some(entry_block); // 関数スコープの SlotRegistry を初期化するよ(観測専用)。 // main 関数用のスロット登録箱として使う想定だよ。 self.current_slot_registry = Some(crate::mir::region::function_slot_registry::FunctionSlotRegistry::new()); // Region 観測レイヤ: main 関数の FunctionRegion を 1 つ作っておくよ。 crate::mir::region::observer::observe_function_region(self); // Hint: scope enter at function entry (id=0 for main) self.hint_scope_enter(0); if std::env::var("NYASH_BUILDER_SAFEPOINT_ENTRY") .ok() .as_deref() == Some("1") { self.emit_instruction(MirInstruction::Safepoint)?; } Ok(()) } pub(super) fn lower_root(&mut self, ast: ASTNode) -> Result { // Pre-index static methods to enable safe fallback for bare calls in using-prepended code let snapshot = ast.clone(); // Phase A: collect declarations in one pass (symbols available to lowering) self.index_declarations(&snapshot); // Decide root mode (App vs Script) once per module based on presence of static box Main.main // true => App mode (Main.main is entry) // false => Script/Test mode (top-level Program runs sequentially) let is_app_mode = self .root_is_app_mode .unwrap_or_else(|| has_main_static(&snapshot)); self.root_is_app_mode = Some(is_app_mode); // Phase B: top-level program lowering with declaration-first pass match ast { ASTNode::Program { statements, .. } => { use crate::ast::ASTNode as N; // First pass: lower declarations (static boxes except Main, and instance boxes) let mut main_static: Option<(String, std::collections::HashMap)> = None; for st in &statements { if let N::BoxDeclaration { name, methods, is_static, fields, constructors, weak_fields, .. } = st { if *is_static { if name == "Main" { main_static = Some((name.clone(), methods.clone())); } else { // Script/Test モードでは static box の lowering は exprs.rs 側に任せる if is_app_mode { // Dev: trace which static box is being lowered (env-gated) self.trace_compile(format!("lower static box {}", name)); // 🎯 箱理論: 各static boxに専用のコンパイルコンテキストを作成 // これにより、using文や前のboxからのメタデータ汚染を構造的に防止 // スコープを抜けると自動的にコンテキストが破棄される { let ctx = super::context::BoxCompilationContext::new(); self.compilation_context = Some(ctx); // Lower all static methods into standalone functions: BoxName.method/Arity for (mname, mast) in methods.iter() { if let N::FunctionDeclaration { params, body, .. } = mast { let func_name = format!( "{}.{}{}", name, mname, format!("/{}", params.len()) ); self.lower_static_method_as_function( func_name, params.clone(), body.clone(), )?; self.static_method_index .entry(mname.clone()) .or_insert_with(Vec::new) .push((name.clone(), params.len())); } } } // 🎯 箱理論: コンテキストをクリア(スコープ終了で自動破棄) // これにより、次のstatic boxは汚染されていない状態から開始される self.compilation_context = None; } } } else { // Instance box: register type and lower instance methods/ctors as functions self.user_defined_boxes.insert(name.clone()); self.build_box_declaration( name.clone(), methods.clone(), fields.clone(), weak_fields.clone(), )?; for (ctor_key, ctor_ast) in constructors.iter() { if let N::FunctionDeclaration { params, body, .. } = ctor_ast { // Keep constructor function name as "Box.birth/N" where ctor_key already encodes arity. // ctor_key format comes from parser as "birth/". let func_name = format!("{}.{}", name, ctor_key); self.lower_method_as_function( func_name, name.clone(), params.clone(), body.clone(), )?; } } for (mname, mast) in methods.iter() { if let N::FunctionDeclaration { params, body, is_static, .. } = mast { if !*is_static { let func_name = format!( "{}.{}{}", name, mname, format!("/{}", params.len()) ); self.lower_method_as_function( func_name, name.clone(), params.clone(), body.clone(), )?; } } } } } } // Second pass: mode-dependent entry lowering if is_app_mode { // App モード: Main.main をエントリとして扱う if let Some((box_name, methods)) = main_static { self.build_static_main_box(box_name, methods) } else { // 理論上は起こりにくいが、安全のため Script モードと同じフォールバックにする self.cf_block(statements) } } else { // Script/Test モード: トップレベル Program をそのまま順次実行 self.cf_block(statements) } } other => self.build_expression(other), } } pub(super) fn finalize_module(&mut self, result_value: ValueId) -> Result { // Hint: scope leave at function end (id=0 for main) self.hint_scope_leave(0); if let Some(block_id) = self.current_block { if let Some(ref mut function) = self.current_function { if let Some(block) = function.get_block_mut(block_id) { if !block.is_terminated() { block.add_instruction(MirInstruction::Return { value: Some(result_value), }); } if let Some(mt) = self.value_types.get(&result_value).cloned() { function.signature.return_type = mt; } } } } let mut module = self.current_module.take().unwrap(); let mut function = self.current_function.take().unwrap(); function.metadata.value_types = self.value_types.clone(); if matches!( function.signature.return_type, super::MirType::Void | super::MirType::Unknown ) { let mut inferred: Option = None; 'outer: for (_bid, bb) in function.blocks.iter() { for inst in bb.instructions.iter() { if let super::MirInstruction::Return { value: Some(v) } = inst { if let Some(mt) = self.value_types.get(v).cloned() { inferred = Some(mt); break 'outer; } // Phase 63-6-3: P1 ケース(IfSelectTest.*)で型ヒント使用 let hint = if function.signature.name.starts_with("IfSelectTest.") { get_phi_type_hint(&function, *v) } else { None }; if let Some(mt) = crate::mir::phi_core::if_phi::infer_type_from_phi_with_hint( hint, // Phase 63-6-3: P1 の場合は PHI の type_hint、それ以外は None &function, *v, &self.value_types, ) { inferred = Some(mt); break 'outer; } } } if let Some(super::MirInstruction::Return { value: Some(v) }) = &bb.terminator { if let Some(mt) = self.value_types.get(v).cloned() { inferred = Some(mt); break; } // Phase 63-6-3: P1 ケース(IfSelectTest.*)で型ヒント使用 let hint = if function.signature.name.starts_with("IfSelectTest.") { get_phi_type_hint(&function, *v) } else { None }; if let Some(mt) = crate::mir::phi_core::if_phi::infer_type_from_phi_with_hint( hint, // Phase 63-6-3: P1 の場合は PHI の type_hint、それ以外は None &function, *v, &self.value_types, ) { inferred = Some(mt); break; } } } if let Some(mt) = inferred { function.signature.return_type = mt; } } // Dev-only verify: NewBox → birth() invariant (warn if missing) if crate::config::env::using_is_dev() { let mut warn_count = 0usize; for (_bid, bb) in function.blocks.iter() { let insns = &bb.instructions; let mut idx = 0usize; while idx < insns.len() { if let MirInstruction::NewBox { dst, box_type, args, } = &insns[idx] { // Skip StringBox (literal optimization path) if box_type != "StringBox" { let expect_tail = format!("{}.birth/{}", box_type, args.len()); // Look ahead up to 3 instructions for either BoxCall("birth") on dst or Global(expect_tail) let mut ok = false; let mut j = idx + 1; let mut last_const_name: Option = None; while j < insns.len() && j <= idx + 3 { match &insns[j] { MirInstruction::BoxCall { box_val, method, .. } => { if method == "birth" && box_val == dst { ok = true; break; } } MirInstruction::Const { value, .. } => { if let super::ConstValue::String(s) = value { last_const_name = Some(s.clone()); } } MirInstruction::Call { func: _, .. } => { // If immediately preceded by matching Const String, accept if let Some(prev) = last_const_name.as_ref() { if prev == &expect_tail { ok = true; break; } } // Heuristic: in some forms, builder may reuse a shared const; best-effort only } _ => {} } j += 1; } if !ok { eprintln!("[warn] dev verify: NewBox {} at v{} not followed by birth() call (expect {})", box_type, dst, expect_tail); warn_count += 1; } } } idx += 1; } } if warn_count > 0 { eprintln!( "[warn] dev verify: NewBox→birth invariant warnings: {}", warn_count ); } } module.add_function(function); // Dev stub: provide condition_fn when missing to satisfy predicate calls in JSON lexers // Returns integer 1 (truthy) and accepts one argument (unused). // // NOTE: // - MirFunction::new() はシグネチャの params に応じて // [ValueId(0)..ValueId(param_count-1)] を事前に予約する。 // - ここでは追加の next_value_id()/params.push() は行わず、 // 予約済みのパラメータ集合をそのまま使う。 if module.functions.get("condition_fn").is_none() { let sig = FunctionSignature { name: "condition_fn".to_string(), params: vec![MirType::Integer], // accept one i64-like arg return_type: MirType::Integer, effects: EffectMask::PURE, }; let entry = BasicBlockId::new(0); let mut f = self.new_function_with_metadata(sig, entry); // body: const 1; return it(FunctionEmissionBox を使用) let one = crate::mir::function_emission::emit_const_integer(&mut f, entry, 1); crate::mir::function_emission::emit_return_value(&mut f, entry, one); module.add_function(f); } // main 関数スコープの Region スタックをポップするよ。 crate::mir::region::observer::pop_function_region(self); // main 関数スコープの SlotRegistry を解放するよ。 self.current_slot_registry = None; Ok(module) } }