350 lines
15 KiB
Rust
350 lines
15 KiB
Rust
/*!
|
||
* MIR Builder - Converts AST to MIR/SSA form
|
||
*
|
||
* Implements AST → MIR conversion with SSA construction
|
||
*/
|
||
|
||
use super::{
|
||
BasicBlock, BasicBlockId, CompareOp, ConstValue, Effect, EffectMask, FunctionSignature,
|
||
MirFunction, MirInstruction, MirModule, MirType, ValueId,
|
||
};
|
||
pub(crate) use builder_calls::CallTarget;
|
||
use std::collections::HashMap;
|
||
mod binding_context; // Phase 136 follow-up (Step 4/7): BindingContext extraction
|
||
mod builder_build;
|
||
mod builder_debug;
|
||
mod builder_emit;
|
||
mod builder_init;
|
||
mod builder_metadata;
|
||
mod builder_method_index;
|
||
mod builder_value_kind;
|
||
#[cfg(test)]
|
||
mod builder_test_api;
|
||
#[cfg(test)]
|
||
mod phi_observation_tests;
|
||
mod builder_calls;
|
||
mod call_resolution; // ChatGPT5 Pro: Type-safe call resolution utilities
|
||
mod calls; // Call system modules (refactored from builder_calls)
|
||
mod compilation_context; // Phase 136 follow-up (Step 7/7): CompilationContext extraction
|
||
mod context; // BoxCompilationContext - 箱理論による静的Boxコンパイル時のコンテキスト分離
|
||
mod core_context; // Phase 136 follow-up (Step 2/7): CoreContext extraction
|
||
mod decls; // declarations lowering split
|
||
mod exprs; // expression lowering split
|
||
mod exprs_call;
|
||
mod metadata_context; // Phase 136 follow-up (Step 6/7): MetadataContext extraction
|
||
mod method_call_handlers;
|
||
mod variable_context; // Phase 136 follow-up (Step 5/7): VariableContext extraction // 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
|
||
|
||
// Phase 140-P4-A: Re-export for loop_canonicalizer SSOT (crate-wide visibility)
|
||
pub(crate) use control_flow::detect_skip_whitespace_pattern;
|
||
// Phase 104: Re-export read_digits(loop(true)) detection for loop_canonicalizer
|
||
pub(crate) use control_flow::detect_read_digits_loop_true_pattern;
|
||
// Phase 142-P1: Re-export continue pattern detection for loop_canonicalizer
|
||
pub(crate) use control_flow::detect_continue_pattern;
|
||
// Phase 143-P0: Re-export parse_number pattern detection for loop_canonicalizer
|
||
pub(crate) use control_flow::detect_parse_number_pattern;
|
||
pub(crate) use control_flow::detect_parse_string_pattern;
|
||
// Phase 91 P5b: Re-export escape skip pattern detection for loop_canonicalizer
|
||
pub(crate) use control_flow::detect_escape_skip_pattern;
|
||
|
||
/// Phase 129: Public (crate) wrapper for StepTree capability guard.
|
||
///
|
||
/// `control_flow` is intentionally private to keep control-flow entrypoints centralized.
|
||
/// Shadow pipelines outside `mir::builder` must call this wrapper instead of reaching into
|
||
/// `control_flow::*` directly.
|
||
pub(crate) fn check_step_tree_capabilities(
|
||
tree: &crate::mir::control_tree::StepTree,
|
||
func_name: &str,
|
||
strict: bool,
|
||
dev: bool,
|
||
) -> Result<(), String> {
|
||
control_flow::joinir::control_tree_capability_guard::check(tree, func_name, strict, dev)
|
||
}
|
||
mod exprs_lambda; // lambda lowering
|
||
mod exprs_peek; // peek expression
|
||
mod exprs_qmark; // ?-propagate
|
||
mod fields; // field access/assignment lowering split
|
||
mod weak_field_validator; // Phase 285A1: Weak field contract validator
|
||
mod if_form;
|
||
pub mod joinir_id_remapper; // Phase 189: JoinIR ID remapping (ValueId/BlockId translation) - Public for tests
|
||
mod joinir_inline_boundary_injector; // Phase 189: JoinInlineBoundary Copy instruction injector
|
||
mod lifecycle;
|
||
mod loop_frontend_binding; // Phase 50: Loop Frontend Binding (JoinIR variable mapping)
|
||
pub(crate) mod loops;
|
||
mod ops;
|
||
mod phi;
|
||
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; // EmitGuardBox(emit直前の最終関所)
|
||
mod metadata; // MetadataPropagationBox(type/originの伝播)
|
||
mod name_const; // NameConstBox(関数名Const生成)
|
||
mod observe; // P0: dev-only observability helpers(ssa/resolve)
|
||
mod origin; // P0: origin inference(me/Known)と PHI 伝播(軽量)
|
||
mod plugin_sigs; // plugin signature loader
|
||
mod receiver; // ReceiverMaterializationBox(Method recv の pin+LocalSSA 集約)
|
||
mod rewrite; // P1: Known rewrite & special consolidation
|
||
mod router; // RouterPolicyBox(Unified vs BoxCall)
|
||
mod schedule; // BlockScheduleBox(物理順序: PHI→materialize→body)
|
||
mod scope_context; // Phase 136 follow-up (Step 3/7): ScopeContext extraction
|
||
mod ssa; // LocalSSA helpers (in-block materialization)
|
||
mod stmts;
|
||
mod type_context; // Phase 136 follow-up: TypeContext extraction
|
||
mod type_facts; // Phase 136 follow-up: Type inference facts box
|
||
pub(crate) mod type_registry;
|
||
mod types; // types::annotation / inference(型注釈/推論の箱: 推論は後段)
|
||
mod utils;
|
||
mod vars; // variables/scope helpers // small loop helpers (header/exit context) // TypeRegistryBox(型情報管理の一元化)
|
||
// Phase 288 Box化: repl_session moved to src/runner/repl/repl_session.rs
|
||
|
||
// Unified member property kinds for computed/once/birth_once
|
||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||
pub(crate) enum PropertyKind {
|
||
Computed,
|
||
Once,
|
||
BirthOnce,
|
||
}
|
||
|
||
/// MIR builder for converting AST to SSA form
|
||
pub struct MirBuilder {
|
||
/// Current module being built
|
||
pub(super) current_module: Option<MirModule>,
|
||
|
||
/// Current basic block being built
|
||
pub(super) current_block: Option<BasicBlockId>,
|
||
|
||
/// Phase 136 follow-up (Step 2/7): Core ID generation context
|
||
/// Consolidates value_gen, block_gen, next_binding_id, temp_slot_counter, debug_join_counter.
|
||
/// Direct field access for backward compatibility (migration in progress).
|
||
pub(super) core_ctx: core_context::CoreContext,
|
||
|
||
/// Phase 136 follow-up: Type information context
|
||
/// Consolidates value_types, value_kinds, value_origin_newbox for better organization.
|
||
/// Direct field access for backward compatibility (migration in progress).
|
||
pub(super) type_ctx: type_context::TypeContext,
|
||
|
||
/// Phase 136 follow-up (Step 3/7): Scope and control flow context
|
||
/// Consolidates lexical_scope_stack, loop stacks, if_merge_stack, current_function,
|
||
/// function_param_names, debug_scope_stack for better organization.
|
||
/// Direct field access for backward compatibility (migration in progress).
|
||
pub(super) scope_ctx: scope_context::ScopeContext,
|
||
|
||
/// Phase 136 follow-up (Step 4/7): Binding context
|
||
/// Consolidates binding_map (String -> BindingId mapping).
|
||
/// Direct field access for backward compatibility (migration in progress).
|
||
pub(super) binding_ctx: binding_context::BindingContext,
|
||
|
||
/// Phase 136 follow-up (Step 5/7): Variable context
|
||
/// Consolidates variable_map (String -> ValueId mapping for SSA conversion).
|
||
/// Direct field access for backward compatibility (migration in progress).
|
||
pub(super) variable_ctx: variable_context::VariableContext,
|
||
|
||
/// Phase 136 follow-up (Step 6/7): Metadata context
|
||
/// Consolidates current_span, source_file, hint_sink, current_region_stack.
|
||
/// Direct field access for backward compatibility (migration in progress).
|
||
pub(super) metadata_ctx: metadata_context::MetadataContext,
|
||
|
||
/// Phase 136 follow-up (Step 7/7): Compilation context
|
||
/// Consolidates compilation_context, current_static_box, user_defined_boxes, reserved_value_ids,
|
||
/// fn_body_ast, weak_fields_by_box, property_getters_by_box, field_origin_class, field_origin_by_box,
|
||
/// static_method_index, method_tail_index, type_registry, current_slot_registry, plugin_method_sigs.
|
||
/// Direct field access for backward compatibility (migration in progress).
|
||
pub(super) comp_ctx: compilation_context::CompilationContext,
|
||
|
||
/// Pending phi functions to be inserted
|
||
#[allow(dead_code)]
|
||
pub(super) pending_phis: Vec<(BasicBlockId, ValueId, String)>,
|
||
|
||
// Phase 2-5: binding_map removed - use binding_ctx.binding_map instead
|
||
|
||
// include guards removed
|
||
// フェーズM: no_phi_modeフィールド削除(常にPHI使用)
|
||
|
||
// ---- Try/Catch/Cleanup lowering context ----
|
||
/// When true, `return` statements are deferred: they assign to `return_defer_slot`
|
||
/// and jump to `return_defer_target` (typically the cleanup/exit block).
|
||
pub(super) return_defer_active: bool,
|
||
/// Slot value to receive deferred return values (edge-copy mode friendly).
|
||
pub(super) return_defer_slot: Option<ValueId>,
|
||
/// Target block to jump to on deferred return.
|
||
pub(super) return_defer_target: Option<BasicBlockId>,
|
||
/// Set to true when a deferred return has been emitted in the current context.
|
||
pub(super) return_deferred_emitted: bool,
|
||
/// True while lowering the cleanup block.
|
||
pub(super) in_cleanup_block: bool,
|
||
/// Policy flags (snapshotted at entry of try/catch lowering)
|
||
pub(super) cleanup_allow_return: bool,
|
||
pub(super) cleanup_allow_throw: bool,
|
||
|
||
/// If true, skip entry materialization of pinned slots on the next start_new_block call.
|
||
suppress_pin_entry_copy_next: bool,
|
||
|
||
// ----------------------
|
||
// Debug scope context (dev only; zero-cost when unused)
|
||
// ----------------------
|
||
/// Local SSA cache: ensure per-block materialization for critical operands (e.g., recv)
|
||
/// Key: (bb, original ValueId, kind) -> local ValueId
|
||
/// kind: 0=recv, 1+ reserved for future (args etc.)
|
||
pub(super) local_ssa_map: HashMap<(BasicBlockId, ValueId, u8), ValueId>,
|
||
/// BlockSchedule cache: deduplicate materialize copies per (bb, src)
|
||
pub(super) schedule_mat_map: HashMap<(BasicBlockId, ValueId), ValueId>,
|
||
/// Mapping from ValueId to its pin slot name (e.g., "__pin$3$@recv")
|
||
/// Used by LocalSSA to redirect old pinned values to the latest slot value.
|
||
pub(super) pin_slot_names: HashMap<ValueId, String>,
|
||
|
||
/// Guard flag to prevent re-entering emit_unified_call from BoxCall fallback.
|
||
/// Used when RouterPolicyBox in emit_unified_call has already decided to
|
||
/// route a given Method call to BoxCall; emit_box_or_plugin_call must not
|
||
/// bounce back into the unified path for the same call, otherwise an
|
||
/// infinite recursion (emit_unified_call → emit_box_or_plugin_call →
|
||
/// emit_unified_call …) can occur when routing decisions disagree.
|
||
pub(super) in_unified_boxcall_fallback: bool,
|
||
|
||
/// Recursion depth counter for debugging stack overflow
|
||
/// Tracks the depth of build_expression calls to detect infinite loops
|
||
pub(super) recursion_depth: usize,
|
||
|
||
/// Root lowering mode: how to treat top-level Program
|
||
/// - None: not decided yet (lower_root not called)
|
||
/// - Some(true): App mode (static box Main.main is entry)
|
||
/// - Some(false): Script/Test mode (top-level Program runs sequentially)
|
||
pub(super) root_is_app_mode: Option<bool>,
|
||
|
||
/// 🎯 Phase 21.7: Static box singleton instances for methodization
|
||
/// Maps BoxName → ValueId of singleton instance (created on demand)
|
||
/// Used when HAKO_MIR_BUILDER_METHODIZE=1 to convert Global("BoxName.method/arity")
|
||
/// to Method{receiver=singleton} calls
|
||
pub(super) static_box_singletons: HashMap<String, ValueId>,
|
||
|
||
/// Phase 288 P2: REPL mode flag - enables implicit local declarations
|
||
/// File mode: false (explicit local required)
|
||
/// REPL mode: true (暗黙 local 許可)
|
||
pub(crate) repl_mode: bool,
|
||
}
|
||
|
||
impl Default for MirBuilder {
|
||
fn default() -> Self {
|
||
Self::new()
|
||
}
|
||
}
|
||
|
||
// Phase 79: BindingMapProvider implementation
|
||
// Centralizes feature-gated binding_map access for promoters
|
||
use crate::mir::loop_pattern_detection::BindingMapProvider;
|
||
|
||
impl BindingMapProvider for MirBuilder {
|
||
#[cfg(feature = "normalized_dev")]
|
||
fn get_binding_map(
|
||
&self,
|
||
) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
|
||
// Phase 136 Step 4/7: Use binding_ctx (SSOT)
|
||
Some(self.binding_ctx.binding_map())
|
||
}
|
||
|
||
#[cfg(not(feature = "normalized_dev"))]
|
||
fn get_binding_map(
|
||
&self,
|
||
) -> Option<&std::collections::BTreeMap<String, crate::mir::BindingId>> {
|
||
None
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod binding_id_tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn test_binding_map_initialization() {
|
||
let builder = MirBuilder::new();
|
||
assert_eq!(builder.core_ctx.next_binding_id, 0);
|
||
// Phase 2-6: binding_ctx is now SSOT (legacy field removed)
|
||
assert!(builder.binding_ctx.binding_map.is_empty());
|
||
}
|
||
|
||
#[test]
|
||
fn test_binding_allocation_sequential() {
|
||
let mut builder = MirBuilder::new();
|
||
let bid0 = builder.allocate_binding_id();
|
||
let bid1 = builder.allocate_binding_id();
|
||
let bid2 = builder.allocate_binding_id();
|
||
|
||
assert_eq!(bid0.raw(), 0);
|
||
assert_eq!(bid1.raw(), 1);
|
||
assert_eq!(bid2.raw(), 2);
|
||
assert_eq!(builder.core_ctx.next_binding_id, 3);
|
||
}
|
||
|
||
#[test]
|
||
fn test_shadowing_binding_restore() {
|
||
let mut builder = MirBuilder::new();
|
||
|
||
// Simulate function entry scope
|
||
builder.push_lexical_scope();
|
||
|
||
// Declare outer x
|
||
// Phase 136 P0: Use SSOT allocator for function scope simulation
|
||
let outer_vid = builder.next_value_id();
|
||
builder
|
||
.declare_local_in_current_scope("x", outer_vid)
|
||
.unwrap();
|
||
// Phase 2-6: Check binding_ctx (SSOT)
|
||
let outer_bid = builder.binding_ctx.lookup("x").unwrap();
|
||
assert_eq!(outer_bid.raw(), 0);
|
||
|
||
// Enter inner scope and shadow x
|
||
builder.push_lexical_scope();
|
||
// Phase 136 P0: Use SSOT allocator for function scope simulation
|
||
let inner_vid = builder.next_value_id();
|
||
builder
|
||
.declare_local_in_current_scope("x", inner_vid)
|
||
.unwrap();
|
||
// Phase 2-6: Check binding_ctx (SSOT)
|
||
let inner_bid = builder.binding_ctx.lookup("x").unwrap();
|
||
assert_eq!(inner_bid.raw(), 1);
|
||
|
||
// Exit inner scope - should restore outer binding
|
||
builder.pop_lexical_scope();
|
||
// Phase 2-6: Check binding_ctx (SSOT)
|
||
let restored_bid = builder.binding_ctx.lookup("x").unwrap();
|
||
assert_eq!(restored_bid, outer_bid);
|
||
assert_eq!(restored_bid.raw(), 0);
|
||
|
||
// Cleanup
|
||
builder.pop_lexical_scope();
|
||
}
|
||
|
||
#[test]
|
||
fn test_valueid_binding_parallel_allocation() {
|
||
let mut builder = MirBuilder::new();
|
||
|
||
// Phase 136 P0: Use SSOT allocator (next_value_id)
|
||
// Note: Without current_function, next_value_id() falls back to value_gen.next()
|
||
// so this test still validates ValueId/BindingId independence
|
||
// Allocate ValueIds and BindingIds in parallel
|
||
let vid0 = builder.next_value_id();
|
||
let bid0 = builder.allocate_binding_id();
|
||
let vid1 = builder.next_value_id();
|
||
let bid1 = builder.allocate_binding_id();
|
||
|
||
// ValueId and BindingId should be independent
|
||
assert_eq!(vid0.0, 0);
|
||
assert_eq!(bid0.raw(), 0);
|
||
assert_eq!(vid1.0, 1);
|
||
assert_eq!(bid1.raw(), 1);
|
||
|
||
// Allocating more ValueIds should not affect BindingId counter
|
||
let _ = builder.next_value_id();
|
||
let _ = builder.next_value_id();
|
||
let bid2 = builder.allocate_binding_id();
|
||
assert_eq!(bid2.raw(), 2); // Still sequential
|
||
|
||
// Allocating more BindingIds should not affect ValueId counter
|
||
let _ = builder.allocate_binding_id();
|
||
let _ = builder.allocate_binding_id();
|
||
let vid2 = builder.next_value_id();
|
||
assert_eq!(vid2.0, 4); // Continues from where we left off
|
||
}
|
||
}
|