Files
hakorune/src/mir/builder/compilation_context.rs
nyash-codex e404746612 refactor(mir): Phase 139-P3-B - RoutingDecision を enum 対応 + レガシー削除
- RoutingDecision の missing_caps を Vec<CapabilityTag> に変更(型安全化)
- error_tags は to_tag() メソッドで自動生成
- 全 callsite を enum variant に修正
- capability_tags モジュール(文字列定数群)を完全削除
- 全テスト PASS(型安全性向上を確認)
- フォーマット適用
2025-12-16 07:02:14 +09:00

439 lines
15 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.

/*!
* CompilationContext - Compilation state management for MirBuilder
*
* Phase 136 follow-up (Step 7/7): Extract compilation-related fields from MirBuilder
* to consolidate box compilation state, type information, and analysis metadata.
*
* Consolidates:
* - compilation_context: Box compilation context (BoxCompilationContext)
* - current_static_box: Current static box being compiled
* - user_defined_boxes: User-defined box names registry
* - reserved_value_ids: Reserved ValueIds for PHI instructions
* - fn_body_ast: Function body AST for capture analysis
* - weak_fields_by_box: Weak field registry
* - property_getters_by_box: Property getter registry
* - field_origin_class: Field origin tracking
* - field_origin_by_box: Class-level field origin
* - static_method_index: Static method index
* - method_tail_index: Method tail index
* - method_tail_index_source_len: Source length snapshot
* - type_registry: Type registry box
* - current_slot_registry: Function scope slot registry
* - plugin_method_sigs: Plugin method signatures
*/
use crate::ast::ASTNode;
use crate::mir::region::function_slot_registry::FunctionSlotRegistry;
use crate::mir::{MirType, ValueId};
use std::collections::{BTreeMap, HashMap, HashSet};
use super::context::BoxCompilationContext;
use super::type_registry::TypeRegistry;
use super::PropertyKind;
/// Compilation state context for MIR builder
///
/// Consolidates all compilation-related state including box compilation context,
/// type information, analysis metadata, and method resolution indices.
#[derive(Debug)]
pub(crate) struct CompilationContext {
/// Box compilation context (for static box compilation isolation)
/// Some(ctx) during static box compilation, None for traditional mode
pub compilation_context: Option<BoxCompilationContext>,
/// Current static box name when lowering a static box body (e.g., "Main")
pub current_static_box: Option<String>,
/// Names of user-defined boxes declared in the current module
pub user_defined_boxes: HashSet<String>,
/// Phase 201-A: Reserved ValueIds that must not be allocated
/// These are PHI dst ValueIds created by LoopHeaderPhiBuilder.
/// When next_value_id() encounters a reserved ID, it skips to the next.
/// Cleared after JoinIR merge completes.
pub reserved_value_ids: HashSet<ValueId>,
/// Phase 200-C: Original function body AST for capture analysis
/// Stored temporarily during function lowering to support FunctionScopeCaptureAnalyzer.
/// None when not lowering a function, or when fn_body is not available.
pub fn_body_ast: Option<Vec<ASTNode>>,
/// Weak field registry: BoxName -> {weak field names}
pub weak_fields_by_box: HashMap<String, HashSet<String>>,
/// Unified members: BoxName -> {propName -> Kind}
pub property_getters_by_box: HashMap<String, HashMap<String, PropertyKind>>,
/// Remember class of object fields after assignments: (base_id, field) -> class_name
pub field_origin_class: HashMap<(ValueId, String), String>,
/// Class-level field origin (cross-function heuristic): (BaseBoxName, field) -> FieldBoxName
pub field_origin_by_box: HashMap<(String, String), String>,
/// Index of static methods seen during lowering: name -> [(BoxName, arity)]
pub static_method_index: HashMap<String, Vec<(String, usize)>>,
/// Fast lookup: method+arity tail → candidate function names (e.g., ".str/0" → ["JsonNode.str/0", ...])
pub method_tail_index: HashMap<String, Vec<String>>,
/// Source size snapshot to detect when to rebuild the tail index
pub method_tail_index_source_len: usize,
/// 🎯 箱理論: 型情報管理の一元化TypeRegistryBox
/// NYASH_USE_TYPE_REGISTRY=1 で有効化(段階的移行用)
pub type_registry: TypeRegistry,
/// 関数スコープの SlotRegistry観測専用
/// - current_function と同じライフサイクルを持つよ。
/// - 既存の variable_map/SSA には影響しない(メタデータのみ)。
pub current_slot_registry: Option<FunctionSlotRegistry>,
/// Plugin method return type signatures loaded from nyash_box.toml
pub plugin_method_sigs: HashMap<(String, String), MirType>,
}
impl CompilationContext {
/// Create a new CompilationContext with default-initialized state
pub fn new() -> Self {
Self {
compilation_context: None,
current_static_box: None,
user_defined_boxes: HashSet::new(),
reserved_value_ids: HashSet::new(),
fn_body_ast: None,
weak_fields_by_box: HashMap::new(),
property_getters_by_box: HashMap::new(),
field_origin_class: HashMap::new(),
field_origin_by_box: HashMap::new(),
static_method_index: HashMap::new(),
method_tail_index: HashMap::new(),
method_tail_index_source_len: 0,
type_registry: TypeRegistry::new(),
current_slot_registry: None,
plugin_method_sigs: HashMap::new(),
}
}
/// Create a new CompilationContext with plugin method signatures
pub fn with_plugin_sigs(plugin_method_sigs: HashMap<(String, String), MirType>) -> Self {
Self {
plugin_method_sigs,
..Self::new()
}
}
/// Check if a box is user-defined
pub fn is_user_defined_box(&self, name: &str) -> bool {
self.user_defined_boxes.contains(name)
}
/// Register a user-defined box
pub fn register_user_box(&mut self, name: String) {
self.user_defined_boxes.insert(name);
}
/// Check if a ValueId is reserved
pub fn is_reserved_value_id(&self, id: ValueId) -> bool {
self.reserved_value_ids.contains(&id)
}
/// Reserve a ValueId (for PHI instructions)
pub fn reserve_value_id(&mut self, id: ValueId) {
self.reserved_value_ids.insert(id);
}
/// Clear all reserved ValueIds (after JoinIR merge)
pub fn clear_reserved_value_ids(&mut self) {
self.reserved_value_ids.clear();
}
/// Enter static box compilation mode
pub fn enter_static_box(&mut self, name: String) {
self.current_static_box = Some(name);
}
/// Exit static box compilation mode
pub fn exit_static_box(&mut self) {
self.current_static_box = None;
}
/// Get current static box name
pub fn current_static_box(&self) -> Option<&str> {
self.current_static_box.as_deref()
}
/// Check if currently compiling a static box
pub fn is_in_static_box(&self) -> bool {
self.current_static_box.is_some()
}
/// Store function body AST for capture analysis
pub fn set_fn_body_ast(&mut self, ast: Vec<ASTNode>) {
self.fn_body_ast = Some(ast);
}
/// Take function body AST (consumes it)
pub fn take_fn_body_ast(&mut self) -> Option<Vec<ASTNode>> {
self.fn_body_ast.take()
}
/// Clear function body AST
pub fn clear_fn_body_ast(&mut self) {
self.fn_body_ast = None;
}
/// Check if a field is weak for a box
pub fn is_weak_field(&self, box_name: &str, field_name: &str) -> bool {
self.weak_fields_by_box
.get(box_name)
.map_or(false, |fields| fields.contains(field_name))
}
/// Register a weak field for a box
pub fn register_weak_field(&mut self, box_name: String, field_name: String) {
self.weak_fields_by_box
.entry(box_name)
.or_insert_with(HashSet::new)
.insert(field_name);
}
/// Get property kind for a box member
pub fn get_property_kind(&self, box_name: &str, prop_name: &str) -> Option<&PropertyKind> {
self.property_getters_by_box
.get(box_name)
.and_then(|props| props.get(prop_name))
}
/// Register a property getter for a box
pub fn register_property_getter(
&mut self,
box_name: String,
prop_name: String,
kind: PropertyKind,
) {
self.property_getters_by_box
.entry(box_name)
.or_insert_with(HashMap::new)
.insert(prop_name, kind);
}
/// Get field origin class for a value's field
pub fn get_field_origin_class(&self, base_id: ValueId, field: &str) -> Option<&str> {
self.field_origin_class
.get(&(base_id, field.to_string()))
.map(|s| s.as_str())
}
/// Set field origin class for a value's field
pub fn set_field_origin_class(&mut self, base_id: ValueId, field: String, class: String) {
self.field_origin_class.insert((base_id, field), class);
}
/// Get field origin by box (class-level)
pub fn get_field_origin_by_box(&self, base_box: &str, field: &str) -> Option<&str> {
self.field_origin_by_box
.get(&(base_box.to_string(), field.to_string()))
.map(|s| s.as_str())
}
/// Set field origin by box (class-level)
pub fn set_field_origin_by_box(&mut self, base_box: String, field: String, origin: String) {
self.field_origin_by_box.insert((base_box, field), origin);
}
/// Register a static method
pub fn register_static_method(&mut self, method_name: String, box_name: String, arity: usize) {
self.static_method_index
.entry(method_name)
.or_insert_with(Vec::new)
.push((box_name, arity));
}
/// Get static method candidates
pub fn get_static_method_candidates(&self, method_name: &str) -> Option<&[(String, usize)]> {
self.static_method_index
.get(method_name)
.map(|v| v.as_slice())
}
/// Get method tail index candidates
pub fn get_method_tail_candidates(&self, tail: &str) -> Option<&[String]> {
self.method_tail_index.get(tail).map(|v| v.as_slice())
}
/// Rebuild method tail index if needed
pub fn maybe_rebuild_method_tail_index(&mut self, current_source_len: usize) -> bool {
if self.method_tail_index_source_len != current_source_len {
self.method_tail_index_source_len = current_source_len;
true
} else {
false
}
}
/// Add method tail index entry
pub fn add_method_tail_entry(&mut self, tail: String, full_name: String) {
self.method_tail_index
.entry(tail)
.or_insert_with(Vec::new)
.push(full_name);
}
/// Clear method tail index
pub fn clear_method_tail_index(&mut self) {
self.method_tail_index.clear();
}
/// Get plugin method signature
pub fn get_plugin_method_sig(&self, box_name: &str, method_name: &str) -> Option<&MirType> {
self.plugin_method_sigs
.get(&(box_name.to_string(), method_name.to_string()))
}
/// Set current slot registry
pub fn set_slot_registry(&mut self, registry: FunctionSlotRegistry) {
self.current_slot_registry = Some(registry);
}
/// Take current slot registry (consumes it)
pub fn take_slot_registry(&mut self) -> Option<FunctionSlotRegistry> {
self.current_slot_registry.take()
}
/// Clear current slot registry
pub fn clear_slot_registry(&mut self) {
self.current_slot_registry = None;
}
}
impl Default for CompilationContext {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compilation_context_creation() {
let ctx = CompilationContext::new();
assert!(ctx.current_static_box.is_none());
assert!(ctx.user_defined_boxes.is_empty());
assert!(ctx.reserved_value_ids.is_empty());
}
#[test]
fn test_user_defined_box() {
let mut ctx = CompilationContext::new();
assert!(!ctx.is_user_defined_box("MyBox"));
ctx.register_user_box("MyBox".to_string());
assert!(ctx.is_user_defined_box("MyBox"));
}
#[test]
fn test_reserved_value_ids() {
let mut ctx = CompilationContext::new();
let id = ValueId::new(42);
assert!(!ctx.is_reserved_value_id(id));
ctx.reserve_value_id(id);
assert!(ctx.is_reserved_value_id(id));
ctx.clear_reserved_value_ids();
assert!(!ctx.is_reserved_value_id(id));
}
#[test]
fn test_static_box_mode() {
let mut ctx = CompilationContext::new();
assert!(!ctx.is_in_static_box());
ctx.enter_static_box("Main".to_string());
assert!(ctx.is_in_static_box());
assert_eq!(ctx.current_static_box(), Some("Main"));
ctx.exit_static_box();
assert!(!ctx.is_in_static_box());
assert_eq!(ctx.current_static_box(), None);
}
#[test]
fn test_weak_field_registry() {
let mut ctx = CompilationContext::new();
ctx.register_weak_field("MyBox".to_string(), "weakField".to_string());
assert!(ctx.is_weak_field("MyBox", "weakField"));
assert!(!ctx.is_weak_field("MyBox", "strongField"));
assert!(!ctx.is_weak_field("OtherBox", "weakField"));
}
#[test]
fn test_property_getter_registry() {
let mut ctx = CompilationContext::new();
ctx.register_property_getter(
"MyBox".to_string(),
"computed".to_string(),
PropertyKind::Computed,
);
assert_eq!(
ctx.get_property_kind("MyBox", "computed"),
Some(&PropertyKind::Computed)
);
assert_eq!(ctx.get_property_kind("MyBox", "other"), None);
}
#[test]
fn test_field_origin_tracking() {
let mut ctx = CompilationContext::new();
let base_id = ValueId::new(10);
ctx.set_field_origin_class(base_id, "name".to_string(), "StringBox".to_string());
assert_eq!(
ctx.get_field_origin_class(base_id, "name"),
Some("StringBox")
);
assert_eq!(ctx.get_field_origin_class(base_id, "other"), None);
}
#[test]
fn test_static_method_index() {
let mut ctx = CompilationContext::new();
ctx.register_static_method("parse".to_string(), "JsonBox".to_string(), 1);
ctx.register_static_method("parse".to_string(), "XmlBox".to_string(), 1);
let candidates = ctx.get_static_method_candidates("parse").unwrap();
assert_eq!(candidates.len(), 2);
assert!(candidates.contains(&("JsonBox".to_string(), 1)));
assert!(candidates.contains(&("XmlBox".to_string(), 1)));
}
#[test]
fn test_method_tail_index() {
let mut ctx = CompilationContext::new();
ctx.add_method_tail_entry(".str/0".to_string(), "JsonNode.str/0".to_string());
ctx.add_method_tail_entry(".str/0".to_string(), "XmlNode.str/0".to_string());
let candidates = ctx.get_method_tail_candidates(".str/0").unwrap();
assert_eq!(candidates.len(), 2);
assert!(candidates.contains(&"JsonNode.str/0".to_string()));
assert!(candidates.contains(&"XmlNode.str/0".to_string()));
}
#[test]
fn test_method_tail_index_rebuild() {
let mut ctx = CompilationContext::new();
assert!(ctx.maybe_rebuild_method_tail_index(100));
assert!(!ctx.maybe_rebuild_method_tail_index(100));
assert!(ctx.maybe_rebuild_method_tail_index(200));
}
}