2025-08-12 11:33:48 +00:00
|
|
|
|
/*!
|
|
|
|
|
|
* Nyash MIR (Mid-level Intermediate Representation) - Stage 1 Implementation
|
2025-09-17 07:43:07 +09:00
|
|
|
|
*
|
2025-08-12 11:33:48 +00:00
|
|
|
|
* ChatGPT5-designed MIR infrastructure for native compilation support
|
|
|
|
|
|
* Based on SSA form with effect tracking and Box-aware optimizations
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-09-17 07:43:07 +09:00
|
|
|
|
#[cfg(feature = "aot-plan-import")]
|
|
|
|
|
|
pub mod aot_plan_import;
|
2025-08-12 11:33:48 +00:00
|
|
|
|
pub mod basic_block;
|
2025-12-13 05:34:56 +09:00
|
|
|
|
pub mod binding_id; // Phase 74: BindingId infrastructure
|
2025-08-12 11:33:48 +00:00
|
|
|
|
pub mod builder;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub mod definitions; // Unified MIR definitions (MirCall, Callee, etc.)
|
2025-08-12 11:33:48 +00:00
|
|
|
|
pub mod effect;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub mod function;
|
2025-12-11 20:54:33 +09:00
|
|
|
|
pub mod if_in_loop_phi; // Phase 187-2: Minimal if-in-loop PHI emitter (extracted from loop_builder)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub mod instruction;
|
|
|
|
|
|
pub mod instruction_introspection; // Introspection helpers for tests (instruction names)
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub mod instruction_kinds; // small kind-specific metadata (Const/BinOp)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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)
|
2025-11-24 14:17:02 +09:00
|
|
|
|
pub mod naming; // Static box / entry naming rules(NamingBox)
|
2025-08-24 00:05:12 +09:00
|
|
|
|
pub mod optimizer;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub mod ssot; // Shared helpers (SSOT) for instruction lowering
|
|
|
|
|
|
pub mod types; // core MIR enums (ConstValue, Ops, MirType)
|
2025-09-23 07:13:32 +09:00
|
|
|
|
pub mod utils; // Phase 15 control flow utilities for root treatment
|
2025-11-21 06:25:17 +09:00
|
|
|
|
// pub mod lowerers; // reserved: Stage-3 loop lowering (while/for-range)
|
2025-12-11 20:54:33 +09:00
|
|
|
|
pub mod cfg_extractor; // Phase 154: CFG extraction for hako_check
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub mod control_form;
|
|
|
|
|
|
pub mod function_emission; // FunctionEmissionBox(MirFunction直編集の発行ヘルパ)
|
|
|
|
|
|
pub mod hints; // scaffold: zero-cost guidance (no-op)
|
2025-11-24 14:17:02 +09:00
|
|
|
|
pub mod join_ir; // Phase 26-H: 関数正規化IR(JoinIR)
|
|
|
|
|
|
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 ブリッジ
|
2025-11-26 10:17:37 +09:00
|
|
|
|
pub mod join_ir_vm_bridge_dispatch; // Phase 30 F-4.4: JoinIR VM ブリッジ dispatch helper
|
2025-11-24 14:17:02 +09:00
|
|
|
|
pub mod loop_form; // ControlForm::LoopShape の薄いエイリアス
|
2025-12-05 07:47:22 +09:00
|
|
|
|
pub mod loop_pattern_detection; // Phase 188: Loop pattern detection for JoinIR lowering
|
2025-09-17 05:56:33 +09:00
|
|
|
|
pub mod optimizer_passes; // optimizer passes (normalize/diagnostics)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub mod optimizer_stats; // extracted stats struct
|
|
|
|
|
|
pub mod passes;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub mod phi_core; // Phase 1 scaffold: unified PHI entry (re-exports only)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub mod printer;
|
2025-09-22 07:54:25 +09:00
|
|
|
|
mod printer_helpers; // internal helpers extracted from printer.rs
|
2025-11-24 14:17:02 +09:00
|
|
|
|
pub mod query; // Phase 26-G: MIR read/write/CFGビュー (MirQuery)
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub mod region; // Phase 25.1l: Region/GC観測レイヤ(LoopForm v2 × RefKind)
|
2025-08-26 20:48:48 +09:00
|
|
|
|
pub mod slot_registry; // Phase 9.79b.1: method slot resolution (IDs)
|
2025-11-28 17:42:19 +09:00
|
|
|
|
mod spanned_instruction;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub mod value_id;
|
2025-11-20 09:29:23 +09:00
|
|
|
|
pub mod value_kind; // Phase 26-A: ValueId型安全化
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub mod verification;
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub mod verification_types; // extracted error types // Optimization subpasses (e.g., type_hints) // Phase 25.1f: Loop/If 共通ビュー(ControlForm)
|
2025-08-12 11:33:48 +00:00
|
|
|
|
|
|
|
|
|
|
// Re-export main types for easy access
|
|
|
|
|
|
pub use basic_block::{BasicBlock, BasicBlockId, BasicBlockIdGenerator};
|
2025-12-13 05:34:56 +09:00
|
|
|
|
pub use binding_id::BindingId; // Phase 74: BindingId infrastructure
|
2025-08-12 11:33:48 +00:00
|
|
|
|
pub use builder::MirBuilder;
|
2025-12-16 07:09:22 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 140-P4-A: Re-export for loop_canonicalizer SSOT
|
|
|
|
|
|
pub(crate) use builder::{detect_skip_whitespace_pattern, SkipWhitespaceInfo};
|
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};
|
2025-12-04 15:00:45 +09:00
|
|
|
|
pub use cfg_extractor::extract_cfg_info; // Phase 154: CFG extraction
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub use definitions::{CallFlags, Callee, MirCall}; // Unified call definitions
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub use effect::{Effect, EffectMask};
|
|
|
|
|
|
pub use function::{FunctionSignature, MirFunction, MirModule};
|
2025-09-22 21:52:39 +09:00
|
|
|
|
pub use instruction::MirInstruction;
|
2025-11-24 14:17:02 +09:00
|
|
|
|
pub use join_ir_runner::{run_joinir_function, JoinRuntimeError, JoinValue};
|
2025-08-24 00:05:12 +09:00
|
|
|
|
pub use optimizer::MirOptimizer;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub use printer::MirPrinter;
|
2025-11-24 14:17:02 +09:00
|
|
|
|
pub use query::{MirQuery, MirQueryBox};
|
2025-08-26 20:48:48 +09:00
|
|
|
|
pub use slot_registry::{BoxTypeId, MethodSlot};
|
2025-11-28 17:42:19 +09:00
|
|
|
|
pub use spanned_instruction::{SpannedInstRef, SpannedInstruction};
|
2025-11-21 06:25:17 +09:00
|
|
|
|
pub use types::{
|
|
|
|
|
|
BarrierOp, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, WeakRefOp,
|
|
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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型安全化
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub use verification::MirVerifier;
|
|
|
|
|
|
pub use verification_types::VerificationError;
|
2025-09-23 07:13:32 +09:00
|
|
|
|
// Phase 15 control flow utilities (段階的根治戦略)
|
|
|
|
|
|
pub use utils::{
|
2025-11-21 06:25:17 +09:00
|
|
|
|
capture_actual_predecessor_and_jump, collect_phi_incoming_if_reachable,
|
|
|
|
|
|
execute_statement_with_termination_check, is_current_block_terminated,
|
2025-09-23 07:13:32 +09:00
|
|
|
|
};
|
2025-08-12 11:33:48 +00:00
|
|
|
|
|
|
|
|
|
|
/// 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,
|
2025-08-24 01:58:41 +09:00
|
|
|
|
optimize: bool,
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl MirCompiler {
|
|
|
|
|
|
/// Create a new MIR compiler
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
builder: MirBuilder::new(),
|
|
|
|
|
|
verifier: MirVerifier::new(),
|
2025-08-24 01:58:41 +09:00
|
|
|
|
optimize: true,
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/// Create with options
|
|
|
|
|
|
pub fn with_options(optimize: bool) -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
builder: MirBuilder::new(),
|
|
|
|
|
|
verifier: MirVerifier::new(),
|
|
|
|
|
|
optimize,
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// Compile AST to MIR module with verification
|
2025-11-24 14:17:02 +09:00
|
|
|
|
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();
|
|
|
|
|
|
}
|
2025-08-12 11:33:48 +00:00
|
|
|
|
// Convert AST to MIR using builder
|
2025-08-24 00:05:12 +09:00
|
|
|
|
let mut module = self.builder.build_module(ast)?;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-24 01:58:41 +09:00
|
|
|
|
if self.optimize {
|
|
|
|
|
|
let mut optimizer = MirOptimizer::new();
|
|
|
|
|
|
let stats = optimizer.optimize_module(&mut module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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
|
|
|
|
|
|
));
|
2025-08-24 01:58:41 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
🎉 Phase 11.8/12.7: MIR Core-13 完全実装 + 糖衣構文ドキュメント更新
主要な変更:
- MIR Core-13命令セット確定(Load/Store削除の革命的設計)
- Const, BinOp, Compare(値・計算)
- Jump, Branch, Return, Phi(制御)
- Call, BoxCall, ExternCall(呼び出し)
- TypeOp, Safepoint, Barrier(メタ)
- Phase 12.7糖衣構文ドキュメント整理(超圧縮重視、可逆変換保証)
- MIRビルダーのモジュール分割完了
- vtableテストスイート拡充
- AI協調開発ツール追加(並列リファクタリング支援)
詳細:
- src/mir/instruction_introspection.rs: core13_instruction_names()追加
- MIRビルダー分割: decls.rs, exprs_*.rs, fields.rs
- plugin_loader_v2: errors.rs, host_bridge.rs分離
- 論文用データ: mir13-final.md作成
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-04 11:34:15 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
// Verify the generated MIR
|
|
|
|
|
|
let verification_result = self.verifier.verify_module(&module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
Ok(MirCompileResult {
|
|
|
|
|
|
module,
|
|
|
|
|
|
verification_result,
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-24 14:17:02 +09:00
|
|
|
|
/// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
/// 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};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_basic_mir_compilation() {
|
|
|
|
|
|
let mut compiler = MirCompiler::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
// Create a simple literal AST node
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let ast = ASTNode::Literal {
|
2025-08-12 11:33:48 +00:00
|
|
|
|
value: LiteralValue::Integer(42),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
span: crate::ast::Span::unknown(),
|
2025-08-12 11:33:48 +00:00
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
// Compile to MIR
|
|
|
|
|
|
let result = compiler.compile(ast);
|
|
|
|
|
|
assert!(result.is_ok(), "Basic MIR compilation should succeed");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
let compile_result = result.unwrap();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
!compile_result.module.functions.is_empty(),
|
|
|
|
|
|
"Module should contain at least one function"
|
|
|
|
|
|
);
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_mir_dump() {
|
|
|
|
|
|
let mut compiler = MirCompiler::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
|
|
|
|
|
let ast = ASTNode::Literal {
|
2025-08-12 11:33:48 +00:00
|
|
|
|
value: LiteralValue::Integer(42),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
span: crate::ast::Span::unknown(),
|
2025-08-12 11:33:48 +00:00
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
let result = compiler.compile(ast).unwrap();
|
|
|
|
|
|
let mir_dump = compiler.dump_mir(&result.module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-12 11:33:48 +00:00
|
|
|
|
assert!(!mir_dump.is_empty(), "MIR dump should not be empty");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
mir_dump.contains("define"),
|
|
|
|
|
|
"MIR dump should contain function definition"
|
|
|
|
|
|
);
|
2025-08-12 11:33:48 +00:00
|
|
|
|
}
|
2025-08-25 20:09:12 +09:00
|
|
|
|
|
|
|
|
|
|
#[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![
|
2025-09-17 07:43:07 +09:00
|
|
|
|
ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::Integer(42),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
},
|
|
|
|
|
|
ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::String("Integer".to_string()),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
},
|
2025-08-25 20:09:12 +09:00
|
|
|
|
],
|
|
|
|
|
|
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| {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
f.blocks.values().any(|b| {
|
2025-11-24 15:02:51 +09:00
|
|
|
|
b.all_spanned_instructions()
|
|
|
|
|
|
.any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
|
2025-09-17 07:43:07 +09:00
|
|
|
|
})
|
2025-08-25 20:09:12 +09:00
|
|
|
|
});
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
has_typeop,
|
|
|
|
|
|
"Expected TypeOp lowering for print(isType(...))"
|
|
|
|
|
|
);
|
2025-08-25 20:09:12 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_lowering_is_method_call_in_print() {
|
|
|
|
|
|
// Build AST: print( (42).is("Integer") )
|
|
|
|
|
|
let ast = ASTNode::Print {
|
|
|
|
|
|
expression: Box::new(ASTNode::MethodCall {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
object: Box::new(ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::Integer(42),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}),
|
2025-08-25 20:09:12 +09:00
|
|
|
|
method: "is".to_string(),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
arguments: vec![ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::String("Integer".to_string()),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}],
|
2025-08-25 20:09:12 +09:00
|
|
|
|
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| {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
f.blocks.values().any(|b| {
|
2025-11-24 15:02:51 +09:00
|
|
|
|
b.all_spanned_instructions()
|
|
|
|
|
|
.any(|sp| matches!(sp.inst, MirInstruction::TypeOp { .. }))
|
2025-09-17 07:43:07 +09:00
|
|
|
|
})
|
2025-08-25 20:09:12 +09:00
|
|
|
|
});
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
has_typeop,
|
|
|
|
|
|
"Expected TypeOp lowering for print(obj.is(...))"
|
|
|
|
|
|
);
|
2025-08-25 20:09:12 +09:00
|
|
|
|
}
|
2025-08-25 20:19:04 +09:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-09-04 20:14:26 +09:00
|
|
|
|
#[ignore = "MIR13 migration: extern console.log expectation pending"]
|
2025-08-25 20:19:04 +09:00
|
|
|
|
fn test_lowering_extern_console_log() {
|
|
|
|
|
|
// Build AST: console.log("hi") → ExternCall env.console.log
|
2025-08-26 05:49:23 +09:00
|
|
|
|
let ast = ASTNode::MethodCall {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
object: Box::new(ASTNode::Variable {
|
|
|
|
|
|
name: "console".to_string(),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}),
|
2025-08-26 05:49:23 +09:00
|
|
|
|
method: "log".to_string(),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
arguments: vec![ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::String("hi".to_string()),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}],
|
2025-08-25 20:19:04 +09:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
dump.contains("extern_call env.console.log"),
|
|
|
|
|
|
"Expected extern_call env.console.log in MIR dump. Got:\n{}",
|
|
|
|
|
|
dump
|
|
|
|
|
|
);
|
2025-08-25 20:19:04 +09:00
|
|
|
|
}
|
2025-08-25 22:00:08 +09:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_lowering_boxcall_array_push() {
|
|
|
|
|
|
// Build AST: (new ArrayBox()).push(1)
|
2025-08-26 05:49:23 +09:00
|
|
|
|
let ast = ASTNode::MethodCall {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
object: Box::new(ASTNode::New {
|
|
|
|
|
|
class: "ArrayBox".to_string(),
|
|
|
|
|
|
arguments: vec![],
|
|
|
|
|
|
type_arguments: vec![],
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}),
|
2025-08-26 05:49:23 +09:00
|
|
|
|
method: "push".to_string(),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
arguments: vec![ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::Integer(1),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}],
|
2025-08-25 22:00:08 +09:00
|
|
|
|
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>(...)`)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
dump.contains(".push("),
|
|
|
|
|
|
"Expected BoxCall to .push(...). Got:\n{}",
|
|
|
|
|
|
dump
|
|
|
|
|
|
);
|
2025-08-25 22:00:08 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-26 20:48:48 +09:00
|
|
|
|
#[test]
|
2025-09-04 20:14:26 +09:00
|
|
|
|
#[ignore = "MIR13 migration: method id naming in printer pending"]
|
2025-08-26 20:48:48 +09:00
|
|
|
|
fn test_boxcall_method_id_on_universal_slot() {
|
|
|
|
|
|
// Build AST: (new ArrayBox()).toString()
|
|
|
|
|
|
let ast = ASTNode::MethodCall {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
object: Box::new(ASTNode::New {
|
|
|
|
|
|
class: "ArrayBox".to_string(),
|
|
|
|
|
|
arguments: vec![],
|
|
|
|
|
|
type_arguments: vec![],
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}),
|
2025-08-26 20:48:48 +09:00
|
|
|
|
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
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
dump.contains("toString[#0]"),
|
|
|
|
|
|
"Expected method_id #0 for toString. Dump:\n{}",
|
|
|
|
|
|
dump
|
|
|
|
|
|
);
|
2025-08-26 20:48:48 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-25 22:00:08 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_lowering_await_expression() {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if crate::config::env::mir_core13_pure() {
|
|
|
|
|
|
eprintln!("[TEST] skip await under Core-13 pure mode");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-08-25 22:00:08 +09:00
|
|
|
|
// Build AST: await 1 (semantic is nonsensical but should emit Await)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let ast = ASTNode::AwaitExpression {
|
|
|
|
|
|
expression: Box::new(ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::Integer(1),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
};
|
2025-08-25 22:00:08 +09:00
|
|
|
|
let mut compiler = MirCompiler::new();
|
|
|
|
|
|
let result = compiler.compile(ast).expect("compile should succeed");
|
|
|
|
|
|
let dump = MirPrinter::new().print_module(&result.module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
dump.contains("await"),
|
|
|
|
|
|
"Expected await in MIR dump. Got:\n{}",
|
|
|
|
|
|
dump
|
|
|
|
|
|
);
|
2025-08-25 22:00:08 +09:00
|
|
|
|
}
|
2025-09-02 09:26:09 +09:00
|
|
|
|
|
2025-11-20 03:56:12 +09:00
|
|
|
|
// Legacy await / safepoint モデルのテスト(Core-13/Pure 以降とは挙動差あり).
|
2025-09-02 09:26:09 +09:00
|
|
|
|
#[test]
|
2025-11-20 03:56:12 +09:00
|
|
|
|
#[ignore]
|
2025-09-02 09:26:09 +09:00
|
|
|
|
fn test_await_has_checkpoints() {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if crate::config::env::mir_core13_pure() {
|
|
|
|
|
|
eprintln!("[TEST] skip await under Core-13 pure mode");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-02 09:26:09 +09:00
|
|
|
|
use crate::ast::{LiteralValue, Span};
|
|
|
|
|
|
// Build: await 1
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let ast = ASTNode::AwaitExpression {
|
|
|
|
|
|
expression: Box::new(ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::Integer(1),
|
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
|
}),
|
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
|
};
|
2025-09-02 09:26:09 +09:00
|
|
|
|
let mut compiler = MirCompiler::new();
|
|
|
|
|
|
let result = compiler.compile(ast).expect("compile");
|
|
|
|
|
|
// Verifier should pass (await flanked by safepoints)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
result.verification_result.is_ok(),
|
|
|
|
|
|
"Verifier failed for await checkpoints: {:?}",
|
|
|
|
|
|
result.verification_result
|
|
|
|
|
|
);
|
2025-09-02 09:26:09 +09:00
|
|
|
|
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();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
sp_count >= 2,
|
|
|
|
|
|
"Expected >=2 safepoints around await, got {}. Dump:\n{}",
|
|
|
|
|
|
sp_count,
|
|
|
|
|
|
dump
|
|
|
|
|
|
);
|
2025-09-02 09:26:09 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-20 03:56:12 +09:00
|
|
|
|
// Legacy await rewrite テスト(現行の Future 統合とは独立にアーカイブ扱い).
|
2025-09-02 09:26:09 +09:00
|
|
|
|
#[test]
|
2025-11-20 03:56:12 +09:00
|
|
|
|
#[ignore]
|
2025-09-02 09:26:09 +09:00
|
|
|
|
fn test_rewritten_await_still_checkpoints() {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if crate::config::env::mir_core13_pure() {
|
|
|
|
|
|
eprintln!("[TEST] skip await under Core-13 pure mode");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-09-02 09:26:09 +09:00
|
|
|
|
use crate::ast::{LiteralValue, Span};
|
|
|
|
|
|
// Enable rewrite so Await → ExternCall(env.future.await)
|
|
|
|
|
|
std::env::set_var("NYASH_REWRITE_FUTURE", "1");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
let ast = ASTNode::AwaitExpression {
|
|
|
|
|
|
expression: Box::new(ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::Integer(1),
|
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
|
}),
|
|
|
|
|
|
span: Span::unknown(),
|
|
|
|
|
|
};
|
2025-09-02 09:26:09 +09:00
|
|
|
|
let mut compiler = MirCompiler::new();
|
|
|
|
|
|
let result = compiler.compile(ast).expect("compile");
|
|
|
|
|
|
// Verifier should still pass (checkpoint verification includes ExternCall await)
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
result.verification_result.is_ok(),
|
|
|
|
|
|
"Verifier failed for rewritten await checkpoints: {:?}",
|
|
|
|
|
|
result.verification_result
|
|
|
|
|
|
);
|
2025-09-02 09:26:09 +09:00
|
|
|
|
let dump = compiler.dump_mir(&result.module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
dump.contains("env.future.await"),
|
|
|
|
|
|
"Expected rewritten await extern call. Dump:\n{}",
|
|
|
|
|
|
dump
|
|
|
|
|
|
);
|
2025-09-02 09:26:09 +09:00
|
|
|
|
let sp_count = dump.matches("safepoint").count();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
sp_count >= 2,
|
|
|
|
|
|
"Expected >=2 safepoints around rewritten await, got {}. Dump:\n{}",
|
|
|
|
|
|
sp_count,
|
|
|
|
|
|
dump
|
|
|
|
|
|
);
|
2025-09-02 09:26:09 +09:00
|
|
|
|
// Cleanup env
|
|
|
|
|
|
std::env::remove_var("NYASH_REWRITE_FUTURE");
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
#[test]
|
2025-09-04 20:14:26 +09:00
|
|
|
|
#[ignore = "MIR13 migration: throw/safepoint expectations pending"]
|
2025-08-13 06:23:28 +00:00
|
|
|
|
fn test_throw_compilation() {
|
|
|
|
|
|
let mut compiler = MirCompiler::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
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(),
|
|
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let result = compiler.compile(throw_ast);
|
|
|
|
|
|
assert!(result.is_ok(), "Throw compilation should succeed");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let compile_result = result.unwrap();
|
|
|
|
|
|
let mir_dump = compiler.dump_mir(&compile_result.module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
mir_dump.contains("throw"),
|
|
|
|
|
|
"MIR should contain throw instruction"
|
|
|
|
|
|
);
|
|
|
|
|
|
assert!(
|
|
|
|
|
|
mir_dump.contains("safepoint"),
|
|
|
|
|
|
"MIR should contain safepoint instruction"
|
|
|
|
|
|
);
|
2025-08-13 06:23:28 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
#[test]
|
2025-09-04 20:14:26 +09:00
|
|
|
|
#[ignore = "MIR13 migration: loop safepoint expectation pending"]
|
2025-08-13 06:23:28 +00:00
|
|
|
|
fn test_loop_compilation() {
|
|
|
|
|
|
let mut compiler = MirCompiler::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let loop_ast = ASTNode::Loop {
|
|
|
|
|
|
condition: Box::new(ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::Bool(true),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
body: vec![ASTNode::Print {
|
|
|
|
|
|
expression: Box::new(ASTNode::Literal {
|
|
|
|
|
|
value: LiteralValue::String("Loop body".to_string()),
|
2025-08-13 06:23:28 +00:00
|
|
|
|
span: crate::ast::Span::unknown(),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
}),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}],
|
2025-08-13 06:23:28 +00:00
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let result = compiler.compile(loop_ast);
|
|
|
|
|
|
assert!(result.is_ok(), "Loop compilation should succeed");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let compile_result = result.unwrap();
|
|
|
|
|
|
let mir_dump = compiler.dump_mir(&compile_result.module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
mir_dump.contains("br"),
|
|
|
|
|
|
"MIR should contain branch instructions"
|
|
|
|
|
|
);
|
|
|
|
|
|
assert!(
|
|
|
|
|
|
mir_dump.contains("safepoint"),
|
|
|
|
|
|
"MIR should contain safepoint instructions"
|
|
|
|
|
|
);
|
2025-08-13 06:23:28 +00:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
|
|
|
|
|
#[test]
|
2025-08-13 06:23:28 +00:00
|
|
|
|
fn test_try_catch_compilation() {
|
2025-09-07 07:28:53 +09:00
|
|
|
|
// Core-13 pure モードでは Try/Catch 命令は許容集合外のためスキップ
|
|
|
|
|
|
if crate::config::env::mir_core13_pure() {
|
|
|
|
|
|
eprintln!("[TEST] skip try/catch under Core-13 pure mode");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let mut compiler = MirCompiler::new();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let try_catch_ast = ASTNode::TryCatch {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
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 {
|
2025-08-13 06:23:28 +00:00
|
|
|
|
expression: Box::new(ASTNode::Literal {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
value: LiteralValue::String("Catch block".to_string()),
|
2025-08-13 06:23:28 +00:00
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}),
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
2025-09-17 07:43:07 +09:00
|
|
|
|
}],
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
}],
|
2025-08-13 06:23:28 +00:00
|
|
|
|
finally_body: None,
|
|
|
|
|
|
span: crate::ast::Span::unknown(),
|
|
|
|
|
|
};
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let result = compiler.compile(try_catch_ast);
|
|
|
|
|
|
assert!(result.is_ok(), "TryCatch compilation should succeed");
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-13 06:23:28 +00:00
|
|
|
|
let compile_result = result.unwrap();
|
|
|
|
|
|
let mir_dump = compiler.dump_mir(&compile_result.module);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
assert!(
|
|
|
|
|
|
mir_dump.contains("catch"),
|
|
|
|
|
|
"MIR should contain catch instruction"
|
|
|
|
|
|
);
|
2025-08-13 06:23:28 +00:00
|
|
|
|
}
|
2025-08-20 04:45:26 +09:00
|
|
|
|
}
|