Files
hakorune/src/mir/mod.rs

587 lines
22 KiB
Rust
Raw Normal View History

/*!
* 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)
feat(mir): Phase 1 - Loop Canonicalizer type definitions Implements the foundation for loop canonicalization - a preprocessing layer that decomposes AST loops into normalized skeleton representation to prevent combinatorial explosion in pattern detection. ## Implementation (Phase 1) Created `src/mir/loop_canonicalizer/mod.rs` with: 1. **Core Types**: - `LoopSkeleton`: Canonical loop representation - `SkeletonStep`: Step kinds (HeaderCond, BreakCheck, ContinueCheck, Update, Body) - `UpdateKind`: Carrier update classification (ConstStep, Conditional, Arbitrary) - `ExitContract`: Exit presence tracking (break/continue/return) - `CarrierSlot`: Loop variables with roles and update rules - `CarrierRole`: Semantic roles (Counter, Accumulator, ConditionVar, Derived) - `CapturedSlot`: Outer scope variable capture 2. **Capability Guard**: - `RoutingDecision`: Pattern selection with diagnostics - `capability_tags` module: Standardized Fail-Fast vocabulary (CAP_MISSING_CONST_STEP, CAP_MISSING_SINGLE_BREAK, etc.) 3. **Helper Methods**: - Skeleton counting (break_checks, continue_checks) - Carrier name extraction - Exit contract queries - Display implementations for debugging 4. **Unit Tests**: 6 tests covering all basic functionality ## Design Principles - **Input**: AST only (no JoinIR dependencies) - **Output**: LoopSkeleton only (no BlockId/ValueId) - **Boundary**: Clear separation from lowering concerns - **Fail-Fast**: Capability-based rejection with clear reasons ## Next Steps (Not in this commit) - Phase 2: `LoopCanonicalizer::canonicalize(ast) -> Result<LoopSkeleton>` - Phase 3: Test with skip_whitespace fixture - Phase 4: Integration with existing JoinIR lowering ## Acceptance Criteria ✅ `cargo build --release` succeeds ✅ `cargo test --release --lib` passes (1039 tests) ✅ 6 new unit tests for loop_canonicalizer pass ✅ No regressions in existing tests Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-16 04:59:26 +09:00
pub mod loop_canonicalizer; // Phase 1: Loop skeleton canonicalization (AST preprocessing)
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 の薄いエイリアス
feat(joinir): Phase 188 Pattern 1 Core Implementation + Phase 189 Planning Phase 188 Status: Planning & Foundation Complete (100%) Completed Tasks: ✅ Task 188-1: Error Inventory (5 patterns identified) ✅ Task 188-2: Pattern Classification (3 patterns selected) ✅ Task 188-3: Design (51KB comprehensive blueprint) ✅ Task 188-4: Implementation Foundation (1,802 lines scaffolding) ✅ Task 188-5: Verification & Documentation ✅ Pattern 1 Core Implementation: Detection + Lowering + Routing Pattern 1 Implementation (322 lines): - Pattern Detection: is_simple_while_pattern() in loop_pattern_detection.rs - JoinIR Lowering: lower_simple_while_to_joinir() in simple_while_minimal.rs (219 lines) - Generates 3 functions: entry, loop_step (tail-recursive), k_exit - Implements condition negation: exit_cond = !(i < 3) - Tail-recursive Call pattern with state propagation - Routing: Added "main" to function routing list in control_flow.rs - Build: ✅ SUCCESS (0 errors, 34 warnings) Infrastructure Blocker Identified: - merge_joinir_mir_blocks() only handles single-function JoinIR modules - Pattern 1 generates 3 functions (entry + loop_step + k_exit) - Current implementation only merges first function → loop body never executes - Root cause: control_flow.rs line ~850 takes only .next() function Phase 189 Planning Complete: - Goal: Refactor merge_joinir_mir_blocks() for multi-function support - Strategy: Sequential Merge (Option A) - merge all functions in call order - Effort estimate: 5.5-7.5 hours - Deliverables: README.md (16KB), current-analysis.md (15KB), QUICKSTART.md (5.8KB) Files Modified/Created: - src/mir/loop_pattern_detection.rs (+50 lines) - Pattern detection - src/mir/join_ir/lowering/simple_while_minimal.rs (+219 lines) - Lowering - src/mir/join_ir/lowering/loop_patterns.rs (+803 lines) - Foundation skeleton - src/mir/join_ir/lowering/mod.rs (+2 lines) - Module registration - src/mir/builder/control_flow.rs (+1 line) - Routing fix - src/mir/builder/loop_frontend_binding.rs (+20 lines) - Binding updates - tools/test_phase188_foundation.sh (executable) - Foundation verification - CURRENT_TASK.md (updated) - Phase 188/189 status Next: Phase 189 implementation (merge_joinir_mir_blocks refactor) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 07:47:22 +09:00
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;
2025-11-20 09:29:23 +09:00
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;
// Phase 140-P4-A: Re-export for loop_canonicalizer SSOT
pub(crate) use builder::{detect_skip_whitespace_pattern, SkipWhitespaceInfo};
feat(canonicalizer): Phase 143-P0 - parse_number pattern support Add parse_number pattern recognition to canonicalizer, expanding adaptation range for digit collection loops with break in THEN clause. ## Changes ### New Recognizer (ast_feature_extractor.rs) - `detect_parse_number_pattern()`: Detects `if invalid { break }` pattern - `ParseNumberInfo`: Struct for extracted pattern info - ~150 lines added ### Canonicalizer Integration (canonicalizer.rs) - Parse_number pattern detection before skip_whitespace - LoopSkeleton construction with 4 steps (Header + Body x2 + Update) - Routes to Pattern2Break (has_break=true) - ~60 lines modified ### Export Chain (6 files) - patterns/mod.rs → joinir/mod.rs → control_flow/mod.rs - builder.rs → mir/mod.rs - 8 lines total ### Tests - `test_parse_number_pattern_recognized()`: Unit test for recognition - Strict parity verification: GREEN (canonical and router agree) - ~130 lines added ## Pattern Comparison | Aspect | Skip Whitespace | Parse Number | |--------|----------------|--------------| | Break location | ELSE clause | THEN clause | | Pattern | `if cond { update } else { break }` | `if invalid { break } rest... update` | | Body after if | None | Required (result append) | ## Results - ✅ Skeleton creation successful - ✅ RoutingDecision matches router (Pattern2Break) - ✅ Strict parity OK (canonicalizer ↔ router agreement) - ✅ Unit test PASS - ✅ Manual test: test_pattern2_parse_number.hako executes correctly ## Statistics - New patterns: 1 (parse_number) - Total patterns: 3 (skip_whitespace, parse_number, continue) - Lines added: ~280 - Files modified: 8 - Parity status: Green ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-16 09:08:37 +09:00
// Phase 142-P1: Re-export continue pattern detection for loop_canonicalizer
pub(crate) use builder::{detect_continue_pattern, ContinuePatternInfo};
// Phase 143-P0: Re-export parse_number pattern detection for loop_canonicalizer
pub(crate) use builder::{detect_parse_number_pattern, ParseNumberInfo};
feat(mir): Phase 143 P1 - Add parse_string pattern to canonicalizer Expand loop canonicalizer to recognize parse_string patterns with both continue (escape handling) and return (quote found) statements. ## Implementation ### New Pattern Detection (ast_feature_extractor.rs) - Add `detect_parse_string_pattern()` function - Support nested continue detection using `has_continue_node()` helper - Recognize both return and continue in same loop body - Return ParseStringInfo { carrier_name, delta, body_stmts } - ~120 lines added ### Canonicalizer Integration (canonicalizer.rs) - Try parse_string pattern first (most specific) - Build LoopSkeleton with HeaderCond, Body, Update steps - Set ExitContract: has_continue=true, has_return=true - Route to Pattern4Continue (both exits present) - ~45 lines modified ### Export Chain - Add re-exports through 7 module levels: ast_feature_extractor → patterns → joinir → control_flow → builder → mir - 10 lines total across 7 files ### Unit Test - Add `test_parse_string_pattern_recognized()` in canonicalizer.rs - Verify skeleton structure (3+ steps) - Verify carrier (name="p", delta=1, role=Counter) - Verify exit contract (continue=true, return=true, break=false) - Verify routing decision (Pattern4Continue, no missing_caps) - ~180 lines added ## Target Pattern `tools/selfhost/test_pattern4_parse_string.hako` Pattern structure: - Check for closing quote → return - Check for escape sequence → continue (nested inside another if) - Regular character processing → p++ ## Results - ✅ Strict parity green: Pattern4Continue - ✅ All 19 unit tests pass - ✅ Nested continue detection working - ✅ ExitContract correctly set (first pattern with both continue+return) - ✅ Default behavior unchanged ## Technical Challenges 1. Nested continue detection required recursive search 2. First pattern with both has_continue=true AND has_return=true 3. Variable step updates (p++ vs p+=2) handled with base delta ## Statistics - New patterns: 1 (parse_string) - Total patterns: 4 (skip_whitespace, parse_number, continue, parse_string) - New capabilities: 0 (uses existing ConstStep) - Lines added: ~300 - Files modified: 9 - Parity status: Green ✅ Phase 143 P1: Complete 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-16 12:37:47 +09:00
// Phase 143-P1: Re-export parse_string pattern detection for loop_canonicalizer
pub(crate) use builder::{detect_parse_string_pattern, ParseStringInfo};
feat(hako_check): Phase 154 MIR CFG integration & HC020 dead block detection Implements block-level unreachable code detection using MIR CFG information. Complements Phase 153's method-level HC019 with fine-grained analysis. Core Infrastructure (Complete): - CFG Extractor: Extract block reachability from MirModule - DeadBlockAnalyzerBox: HC020 rule for unreachable blocks - CLI Integration: --dead-blocks flag and rule execution - Test Cases: 4 comprehensive patterns (early return, constant false, infinite loop, break) - Smoke Test: Validation script for all test cases Implementation Details: - src/mir/cfg_extractor.rs: New module for CFG→JSON extraction - tools/hako_check/rules/rule_dead_blocks.hako: HC020 analyzer box - tools/hako_check/cli.hako: Added --dead-blocks flag and HC020 integration - apps/tests/hako_check/test_dead_blocks_*.hako: 4 test cases Architecture: - Follows Phase 153 boxed modular pattern (DeadCodeAnalyzerBox) - Optional CFG field in Analysis IR (backward compatible) - Uses MIR's built-in reachability computation - Gracefully skips if CFG unavailable Known Limitation: - CFG data bridge pending (Phase 155): analysis_consumer.hako needs MIR access - Current: DeadBlockAnalyzerBox implemented, but CFG not yet in Analysis IR - Estimated 2-3 hours to complete bridge in Phase 155 Test Coverage: - Unit tests: cfg_extractor (simple CFG, unreachable blocks) - Integration tests: 4 test cases ready (will activate with bridge) - Smoke test: tools/hako_check_deadblocks_smoke.sh Documentation: - phase154_mir_cfg_inventory.md: CFG structure investigation - phase154_implementation_summary.md: Complete implementation guide - hako_check_design.md: HC020 rule documentation Next Phase 155: - Implement CFG data bridge (extract_mir_cfg builtin) - Update analysis_consumer.hako to call bridge - Activate HC020 end-to-end testing 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:00:45 +09:00
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};
2025-11-20 09:29:23 +09:00
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"
);
}
}