Files
hakorune/src/mir/mod.rs
nyash-codex e1574af741 feat(mir): Phase 74 - BindingId infrastructure (dev-only)
Phase 74 implements BindingId as parallel allocation alongside ValueId for
lexical scope tracking and shadowing-aware variable identity.

Changes:
- binding_id.rs: New BindingId type with overflow protection (5 unit tests)
- builder.rs: Added next_binding_id counter and binding_map (4 integration tests)
- lexical_scope.rs: Extended restoration logic for BindingId management
- mod.rs: Public re-export of BindingId

Tests: 9/9 new PASS, lib 958/958 PASS (no regressions)
Architecture: Parallel BindingId/ValueId allocation for deterministic shadowing

Phase 75-77 will build on this infrastructure for type-safe promotion tracking.

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-13 05:34:56 +09:00

577 lines
21 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* Nyash MIR (Mid-level Intermediate Representation) - Stage 1 Implementation
*
* ChatGPT5-designed MIR infrastructure for native compilation support
* Based on SSA form with effect tracking and Box-aware optimizations
*/
#[cfg(feature = "aot-plan-import")]
pub mod aot_plan_import;
pub mod basic_block;
pub mod binding_id; // Phase 74: BindingId infrastructure
pub mod builder;
pub mod definitions; // Unified MIR definitions (MirCall, Callee, etc.)
pub mod effect;
pub mod function;
pub mod if_in_loop_phi; // Phase 187-2: Minimal if-in-loop PHI emitter (extracted from loop_builder)
pub mod instruction;
pub mod instruction_introspection; // Introspection helpers for tests (instruction names)
pub mod instruction_kinds; // small kind-specific metadata (Const/BinOp)
pub mod loop_api; // Minimal LoopBuilder facade (adapter-ready)
pub mod naming; // Static box / entry naming rulesNamingBox
pub mod optimizer;
pub mod ssot; // Shared helpers (SSOT) for instruction lowering
pub mod types; // core MIR enums (ConstValue, Ops, MirType)
pub mod utils; // Phase 15 control flow utilities for root treatment
// pub mod lowerers; // reserved: Stage-3 loop lowering (while/for-range)
pub mod cfg_extractor; // Phase 154: CFG extraction for hako_check
pub mod control_form;
pub mod function_emission; // FunctionEmissionBoxMirFunction直編集の発行ヘルパ
pub mod hints; // scaffold: zero-cost guidance (no-op)
pub mod join_ir; // Phase 26-H: 関数正規化IRJoinIR
pub mod join_ir_ops; // Phase 27.8: JoinIR 命令意味箱ops box
pub mod join_ir_runner; // Phase 27.2: JoinIR 実行器(実験用)
pub mod join_ir_vm_bridge; // Phase 27-shortterm S-4: JoinIR → Rust VM ブリッジ
pub mod join_ir_vm_bridge_dispatch; // Phase 30 F-4.4: JoinIR VM ブリッジ dispatch helper
pub mod loop_form; // ControlForm::LoopShape の薄いエイリアス
pub mod loop_pattern_detection; // Phase 188: Loop pattern detection for JoinIR lowering
pub mod optimizer_passes; // optimizer passes (normalize/diagnostics)
pub mod optimizer_stats; // extracted stats struct
pub mod passes;
pub mod phi_core; // Phase 1 scaffold: unified PHI entry (re-exports only)
pub mod printer;
mod printer_helpers; // internal helpers extracted from printer.rs
pub mod query; // Phase 26-G: MIR read/write/CFGビュー (MirQuery)
pub mod region; // Phase 25.1l: Region/GC観測レイヤLoopForm v2 × RefKind
pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs)
mod spanned_instruction;
pub mod value_id;
pub mod value_kind; // Phase 26-A: ValueId型安全化
pub mod verification;
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Phase 25.1f: Loop/If 共通ビューControlForm
// Re-export main types for easy access
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
pub use binding_id::BindingId; // Phase 74: BindingId infrastructure
pub use builder::MirBuilder;
pub use cfg_extractor::extract_cfg_info; // Phase 154: CFG extraction
pub use definitions::{CallFlags, Callee, MirCall}; // Unified call definitions
pub use effect::{Effect, EffectMask};
pub use function::{FunctionSignature, MirFunction, MirModule};
pub use instruction::MirInstruction;
pub use join_ir_runner::{run_joinir_function, JoinRuntimeError, JoinValue};
pub use optimizer::MirOptimizer;
pub use printer::MirPrinter;
pub use query::{MirQuery, MirQueryBox};
pub use slot_registry::{BoxTypeId, MethodSlot};
pub use spanned_instruction::{SpannedInstRef, SpannedInstruction};
pub use types::{
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
};
pub use value_id::{LocalId, ValueId, ValueIdGenerator};
pub use value_kind::{MirValueKind, TypedValueId}; // Phase 26-A: ValueId型安全化
pub use verification::MirVerifier;
pub use verification_types::VerificationError;
// Phase 15 control flow utilities (段階的根治戦略)
pub use utils::{
capture_actual_predecessor_and_jump, collect_phi_incoming_if_reachable,
execute_statement_with_termination_check, is_current_block_terminated,
};
/// MIR compilation result
#[derive(Debug, Clone)]
pub struct MirCompileResult {
pub module: MirModule,
pub verification_result: Result<(), Vec<VerificationError>>,
}
/// MIR compiler - converts AST to MIR/SSA form
pub struct MirCompiler {
builder: MirBuilder,
verifier: MirVerifier,
optimize: bool,
}
impl MirCompiler {
/// Create a new MIR compiler
pub fn new() -> Self {
Self {
builder: MirBuilder::new(),
verifier: MirVerifier::new(),
optimize: true,
}
}
/// Create with options
pub fn with_options(optimize: bool) -> Self {
Self {
builder: MirBuilder::new(),
verifier: MirVerifier::new(),
optimize,
}
}
/// Compile AST to MIR module with verification
pub fn compile_with_source(
&mut self,
ast: crate::ast::ASTNode,
source_file: Option<&str>,
) -> Result<MirCompileResult, String> {
if let Some(src) = source_file {
self.builder.set_source_file_hint(src.to_string());
} else {
self.builder.clear_source_file_hint();
}
// Convert AST to MIR using builder
let mut module = self.builder.build_module(ast)?;
if self.optimize {
let mut optimizer = MirOptimizer::new();
let stats = optimizer.optimize_module(&mut module);
if (crate::config::env::opt_diag_fail() || crate::config::env::opt_diag_forbid_legacy())
&& stats.diagnostics_reported > 0
{
return Err(format!(
"Diagnostic failure: {} issues detected (unlowered/legacy)",
stats.diagnostics_reported
));
}
}
// Verify the generated MIR
let verification_result = self.verifier.verify_module(&module);
Ok(MirCompileResult {
module,
verification_result,
})
}
/// Compile AST to MIR module with verification (no source hint).
pub fn compile(&mut self, ast: crate::ast::ASTNode) -> Result<MirCompileResult, String> {
self.compile_with_source(ast, None)
}
/// Dump MIR to string for debugging
pub fn dump_mir(&self, module: &MirModule) -> String {
MirPrinter::new().print_module(module)
}
}
impl Default for MirCompiler {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ast::{ASTNode, LiteralValue};
#[test]
fn test_basic_mir_compilation() {
let mut compiler = MirCompiler::new();
// Create a simple literal AST node
let ast = ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
};
// Compile to MIR
let result = compiler.compile(ast);
assert!(result.is_ok(), "Basic MIR compilation should succeed");
let compile_result = result.unwrap();
assert!(
!compile_result.module.functions.is_empty(),
"Module should contain at least one function"
);
}
#[test]
fn test_mir_dump() {
let mut compiler = MirCompiler::new();
let ast = ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(ast).unwrap();
let mir_dump = compiler.dump_mir(&result.module);
assert!(!mir_dump.is_empty(), "MIR dump should not be empty");
assert!(
mir_dump.contains("define"),
"MIR dump should contain function definition"
);
}
#[test]
fn test_lowering_is_type_function_call_in_print() {
// Build AST: print(isType(42, "Integer"))
let ast = ASTNode::Print {
expression: Box::new(ASTNode::FunctionCall {
name: "isType".to_string(),
arguments: vec![
ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
},
ASTNode::Literal {
value: LiteralValue::String("Integer".to_string()),
span: crate::ast::Span::unknown(),
},
],
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
// Ensure TypeOp exists in the resulting MIR
let has_typeop = result.module.functions.values().any(|f| {
f.blocks.values().any(|b| {
b.all_spanned_instructions()
.any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
})
});
assert!(
has_typeop,
"Expected TypeOp lowering for print(isType(...))"
);
}
#[test]
fn test_lowering_is_method_call_in_print() {
// Build AST: print( (42).is("Integer") )
let ast = ASTNode::Print {
expression: Box::new(ASTNode::MethodCall {
object: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(42),
span: crate::ast::Span::unknown(),
}),
method: "is".to_string(),
arguments: vec![ASTNode::Literal {
value: LiteralValue::String("Integer".to_string()),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
// Ensure TypeOp exists in the resulting MIR
let has_typeop = result.module.functions.values().any(|f| {
f.blocks.values().any(|b| {
b.all_spanned_instructions()
.any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
})
});
assert!(
has_typeop,
"Expected TypeOp lowering for print(obj.is(...))"
);
}
#[test]
#[ignore = "MIR13 migration: extern console.log expectation pending"]
fn test_lowering_extern_console_log() {
// Build AST: console.log("hi") → ExternCall env.console.log
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::Variable {
name: "console".to_string(),
span: crate::ast::Span::unknown(),
}),
method: "log".to_string(),
arguments: vec![ASTNode::Literal {
value: LiteralValue::String("hi".to_string()),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::verbose().print_module(&result.module);
assert!(
dump.contains("extern_call env.console.log"),
"Expected extern_call env.console.log in MIR dump. Got:\n{}",
dump
);
}
#[test]
fn test_lowering_boxcall_array_push() {
// Build AST: (new ArrayBox()).push(1)
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::New {
class: "ArrayBox".to_string(),
arguments: vec![],
type_arguments: vec![],
span: crate::ast::Span::unknown(),
}),
method: "push".to_string(),
arguments: vec![ASTNode::Literal {
value: LiteralValue::Integer(1),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::new().print_module(&result.module);
// Expect a BoxCall to push (printer formats as `call <box>.<method>(...)`)
assert!(
dump.contains(".push("),
"Expected BoxCall to .push(...). Got:\n{}",
dump
);
}
#[test]
#[ignore = "MIR13 migration: method id naming in printer pending"]
fn test_boxcall_method_id_on_universal_slot() {
// Build AST: (new ArrayBox()).toString()
let ast = ASTNode::MethodCall {
object: Box::new(ASTNode::New {
class: "ArrayBox".to_string(),
arguments: vec![],
type_arguments: vec![],
span: crate::ast::Span::unknown(),
}),
method: "toString".to_string(),
arguments: vec![],
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::new().print_module(&result.module);
// Expect a BoxCall with numeric method id [#0] for toString universal slot
assert!(
dump.contains("toString[#0]"),
"Expected method_id #0 for toString. Dump:\n{}",
dump
);
}
#[test]
fn test_lowering_await_expression() {
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip await under Core-13 pure mode");
return;
}
// Build AST: await 1 (semantic is nonsensical but should emit Await)
let ast = ASTNode::AwaitExpression {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile should succeed");
let dump = MirPrinter::new().print_module(&result.module);
assert!(
dump.contains("await"),
"Expected await in MIR dump. Got:\n{}",
dump
);
}
// Legacy await / safepoint モデルのテストCore-13/Pure 以降とは挙動差あり).
#[test]
#[ignore]
fn test_await_has_checkpoints() {
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip await under Core-13 pure mode");
return;
}
use crate::ast::{LiteralValue, Span};
// Build: await 1
let ast = ASTNode::AwaitExpression {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile");
// Verifier should pass (await flanked by safepoints)
assert!(
result.verification_result.is_ok(),
"Verifier failed for await checkpoints: {:?}",
result.verification_result
);
let dump = compiler.dump_mir(&result.module);
// Expect at least two safepoints in the function (before/after await)
let sp_count = dump.matches("safepoint").count();
assert!(
sp_count >= 2,
"Expected >=2 safepoints around await, got {}. Dump:\n{}",
sp_count,
dump
);
}
// Legacy await rewrite テスト(現行の Future 統合とは独立にアーカイブ扱い).
#[test]
#[ignore]
fn test_rewritten_await_still_checkpoints() {
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip await under Core-13 pure mode");
return;
}
use crate::ast::{LiteralValue, Span};
// Enable rewrite so Await → ExternCall(env.future.await)
std::env::set_var("NYASH_REWRITE_FUTURE", "1");
let ast = ASTNode::AwaitExpression {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::Integer(1),
span: Span::unknown(),
}),
span: Span::unknown(),
};
let mut compiler = MirCompiler::new();
let result = compiler.compile(ast).expect("compile");
// Verifier should still pass (checkpoint verification includes ExternCall await)
assert!(
result.verification_result.is_ok(),
"Verifier failed for rewritten await checkpoints: {:?}",
result.verification_result
);
let dump = compiler.dump_mir(&result.module);
assert!(
dump.contains("env.future.await"),
"Expected rewritten await extern call. Dump:\n{}",
dump
);
let sp_count = dump.matches("safepoint").count();
assert!(
sp_count >= 2,
"Expected >=2 safepoints around rewritten await, got {}. Dump:\n{}",
sp_count,
dump
);
// Cleanup env
std::env::remove_var("NYASH_REWRITE_FUTURE");
}
#[test]
#[ignore = "MIR13 migration: throw/safepoint expectations pending"]
fn test_throw_compilation() {
let mut compiler = MirCompiler::new();
let throw_ast = ASTNode::Throw {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Test exception".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(throw_ast);
assert!(result.is_ok(), "Throw compilation should succeed");
let compile_result = result.unwrap();
let mir_dump = compiler.dump_mir(&compile_result.module);
assert!(
mir_dump.contains("throw"),
"MIR should contain throw instruction"
);
assert!(
mir_dump.contains("safepoint"),
"MIR should contain safepoint instruction"
);
}
#[test]
#[ignore = "MIR13 migration: loop safepoint expectation pending"]
fn test_loop_compilation() {
let mut compiler = MirCompiler::new();
let loop_ast = ASTNode::Loop {
condition: Box::new(ASTNode::Literal {
value: LiteralValue::Bool(true),
span: crate::ast::Span::unknown(),
}),
body: vec![ASTNode::Print {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Loop body".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(loop_ast);
assert!(result.is_ok(), "Loop compilation should succeed");
let compile_result = result.unwrap();
let mir_dump = compiler.dump_mir(&compile_result.module);
assert!(
mir_dump.contains("br"),
"MIR should contain branch instructions"
);
assert!(
mir_dump.contains("safepoint"),
"MIR should contain safepoint instructions"
);
}
#[test]
fn test_try_catch_compilation() {
// Core-13 pure モードでは Try/Catch 命令は許容集合外のためスキップ
if crate::config::env::mir_core13_pure() {
eprintln!("[TEST] skip try/catch under Core-13 pure mode");
return;
}
let mut compiler = MirCompiler::new();
let try_catch_ast = ASTNode::TryCatch {
try_body: vec![ASTNode::Print {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Try block".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
}],
catch_clauses: vec![crate::ast::CatchClause {
exception_type: Some("Exception".to_string()),
variable_name: Some("e".to_string()),
body: vec![ASTNode::Print {
expression: Box::new(ASTNode::Literal {
value: LiteralValue::String("Catch block".to_string()),
span: crate::ast::Span::unknown(),
}),
span: crate::ast::Span::unknown(),
}],
span: crate::ast::Span::unknown(),
}],
finally_body: None,
span: crate::ast::Span::unknown(),
};
let result = compiler.compile(try_catch_ast);
assert!(result.is_ok(), "TryCatch compilation should succeed");
let compile_result = result.unwrap();
let mir_dump = compiler.dump_mir(&compile_result.module);
assert!(
mir_dump.contains("catch"),
"MIR should contain catch instruction"
);
}
}