/*! * Phase 136 Step 6/7: MetadataContext - Metadata/Span/Hint 管理の統一箱 * * 責務: * - current_span: 現在の AST span(命令アノテーション用) * - source_file: ソースファイルヒント(メタデータ用) * - hint_sink: 型推論ヒント(ゼロコストガイダンス) * - current_region_stack: Region 観測用スタック(NYASH_REGION_TRACE=1 デバッグ用) * * 設計: * - HintSink は no-op デフォルトだが、将来の型推論最適化に備える * - Span は命令単位で保持され、エラー報告・デバッグ情報生成に使用 * - source_file は関数メタデータに伝播 * - current_region_stack は開発用トレース(本番コストゼロ) */ use crate::ast::Span; use crate::mir::hints::HintSink; use crate::mir::region::RegionId; /// Phase 136 Step 6/7: Metadata/Span/Hint 管理を統一した構造体だよ #[derive(Debug, Clone)] pub struct MetadataContext { /// 現在の AST span(命令アノテーション用) pub(super) current_span: Span, /// ソースファイルヒント(関数メタデータに伝播) pub(super) source_file: Option, /// 型推論ヒント(ゼロコストガイダンス) pub(super) hint_sink: HintSink, /// Region 観測用のスタック(NYASH_REGION_TRACE=1 のデバッグ専用) /// - FunctionRegion がルート /// - 開発時のみ使用(本番コストゼロ) pub(super) current_region_stack: Vec, } impl MetadataContext { /// 新規 MetadataContext を生成(デフォルト状態) pub(crate) fn new() -> Self { Self { current_span: Span::unknown(), source_file: None, hint_sink: HintSink::new(), current_region_stack: Vec::new(), } } // ---- Span 管理 ---- /// 現在の span を取得 #[inline] pub(crate) fn current_span(&self) -> Span { self.current_span } /// 現在の span を設定 #[inline] pub(crate) fn set_current_span(&mut self, span: Span) { self.current_span = span; } // ---- Source File 管理 ---- /// ソースファイルヒントを設定 #[inline] pub(crate) fn set_source_file>(&mut self, source: S) { self.source_file = Some(source.into()); } /// ソースファイルヒントをクリア #[inline] pub(crate) fn clear_source_file(&mut self) { self.source_file = None; } /// 現在のソースファイルヒントを取得 #[inline] pub(crate) fn current_source_file(&self) -> Option { self.source_file.clone() } // ---- Hint Sink 管理(型推論ガイダンス)---- /// スコープ開始ヒント(no-op デフォルト) #[inline] pub(crate) fn hint_scope_enter(&mut self, id: u32) { self.hint_sink.scope_enter(id); } /// スコープ終了ヒント(no-op デフォルト) #[inline] pub(crate) fn hint_scope_leave(&mut self, id: u32) { self.hint_sink.scope_leave(id); } /// Join 結果ヒント(no-op デフォルト) #[inline] pub(crate) fn hint_join_result>(&mut self, var: S) { self.hint_sink.join_result(var.into()); } // ---- Region Stack 管理(デバッグ専用)---- /// Region スタックに push(NYASH_REGION_TRACE=1 専用) #[inline] pub(crate) fn push_region(&mut self, region_id: RegionId) { self.current_region_stack.push(region_id); } /// Region スタックから pop(NYASH_REGION_TRACE=1 専用) #[inline] pub(crate) fn pop_region(&mut self) -> Option { self.current_region_stack.pop() } /// 現在の Region スタックを取得(読み取り専用) #[inline] pub(crate) fn current_region_stack(&self) -> &[RegionId] { &self.current_region_stack } } impl Default for MetadataContext { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_metadata_context_creation() { let ctx = MetadataContext::new(); assert!(ctx.source_file.is_none()); assert_eq!(ctx.current_region_stack.len(), 0); } #[test] fn test_span_management() { let mut ctx = MetadataContext::new(); let span = Span::new(0, 10, 1, 1); ctx.set_current_span(span); assert_eq!(ctx.current_span().start, 0); assert_eq!(ctx.current_span().end, 10); } #[test] fn test_source_file_management() { let mut ctx = MetadataContext::new(); ctx.set_source_file("test.hako"); assert_eq!(ctx.current_source_file(), Some("test.hako".to_string())); ctx.clear_source_file(); assert!(ctx.current_source_file().is_none()); } #[test] fn test_region_stack() { let mut ctx = MetadataContext::new(); let region1 = RegionId(1); let region2 = RegionId(2); ctx.push_region(region1); ctx.push_region(region2); assert_eq!(ctx.current_region_stack().len(), 2); assert_eq!(ctx.pop_region(), Some(region2)); assert_eq!(ctx.pop_region(), Some(region1)); assert_eq!(ctx.pop_region(), None); } #[test] fn test_hint_operations_no_panic() { let mut ctx = MetadataContext::new(); // These should not panic (no-op by default) ctx.hint_scope_enter(1); ctx.hint_scope_leave(1); ctx.hint_join_result("test_var"); } }