Files
hakorune/src/mir/value_kind.rs

423 lines
12 KiB
Rust
Raw Normal View History

2025-11-20 09:29:23 +09:00
// value_kind.rs
// Phase 26-A: ValueId型安全化
//
// Purpose:
// - ValueIdの意味的分類Parameter, Local, Constant等を導入
// - GUARDバグのような「ValueId(0)の曖昧性」から生じるバグを根絶
use crate::mir::ValueId;
2025-11-20 09:29:23 +09:00
/// ValueIdの意味的分類型安全性強化
///
/// # 目的
///
/// ValueIdは単なる整数ラッパー `ValueId(u32)` であり、
/// その値が「パラメータ」「ローカル変数」「定数」のいずれを表すか区別できない。
/// これにより、以下のようなバグが発生していた:
///
/// ## GUARDバグの例修正済み
///
/// ```rust
/// // ❌ ValueId(0) を「常に未初期化」と誤判定
/// for (name, value) in &current_vars {
/// if value.0 == 0 { // ← Parameter s=ValueId(0) も弾いてしまう!
/// return Ok(ValueId(0));
/// }
/// }
/// ```
///
/// ## 解決策
///
/// `MirValueKind` で値の種類を明示的に管理し、
/// `TypedValueId` で ValueId + 型情報をペアで保持することで、
/// コンパイル時・実行時の型安全性を確保する。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MirValueKind {
/// 関数パラメータ
///
/// - パラメータインデックス0-basedを保持
/// - 例: `fn skip_whitespace(s, idx)` → s=Parameter(0), idx=Parameter(1)
///
/// # 重要
///
/// ValueId(0) であってもParameterなら正当な値
/// GUARD checkバグはこれを見落としていた。
Parameter(u32),
/// ローカル変数
///
/// - スコープ内のローカル番号(関数ごとに独立)
/// - 例: `local i = 0` → Local(0)
///
/// # SSA形式との関係
///
/// SSA形式では再代入時に新しいValueIdが割り当てられるため、
/// 同じ名前でも複数のLocal(N)が存在しうる。
Local(u32),
/// 定数値
///
/// - コンパイル時に値が確定している
/// - 例: 42, "hello", true
///
/// # Note
///
/// ConstValueの実体はMirInstruction::Constに格納される。
/// MirValueKindはあくまで「定数である」というマーカー。
Constant,
/// 一時値
///
/// - 式評価・演算の中間結果
/// - 例: `a + b` の結果、copy, phi の結果
///
/// # デフォルト扱い
///
/// 明示的に分類されないValueIdはTemporaryとして扱われる。
Temporary,
/// Pinned変数
///
/// - ブロック跨ぎの一時変数SSA構築用
/// - 命名規則: `__pin$N$@<suffix>`
/// - 例: `__pin$42$@binop_lhs`
///
/// # 実装詳細
///
/// MIRビルダーがSSA形式を構築する際、
/// ブロック境界でのdef-use関係を追跡するために使用される。
Pinned,
/// LoopCarrier
///
/// - ループ内で再定義される変数
/// - PHI nodeで複数の値をマージ
/// - 例: loop内で更新される `i`
///
/// # PHI nodeとの関係
///
/// ```hako
/// local i = 0
/// loop(i < 10) {
/// i = i + 1 // ← i は LoopCarrier
/// }
/// ```
///
/// MIR表現:
/// ```
/// header:
/// %i_phi = phi [(%i_entry, preheader), (%i_next, latch)]
/// body:
/// %i_next = binop %i_phi + 1
/// ```
LoopCarrier,
}
impl MirValueKind {
/// パラメータか判定
pub fn is_parameter(&self) -> bool {
matches!(self, MirValueKind::Parameter(_))
}
/// ローカル変数か判定
pub fn is_local(&self) -> bool {
matches!(self, MirValueKind::Local(_))
}
/// 定数か判定
pub fn is_constant(&self) -> bool {
matches!(self, MirValueKind::Constant)
}
/// 一時値か判定
pub fn is_temporary(&self) -> bool {
matches!(self, MirValueKind::Temporary)
}
/// Pinned変数か判定
pub fn is_pinned(&self) -> bool {
matches!(self, MirValueKind::Pinned)
}
/// LoopCarrierか判定
pub fn is_loop_carrier(&self) -> bool {
matches!(self, MirValueKind::LoopCarrier)
}
/// パラメータインデックス取得Parameterのみ
pub fn parameter_index(&self) -> Option<u32> {
match self {
MirValueKind::Parameter(idx) => Some(*idx),
_ => None,
}
}
/// ローカル変数番号取得Localのみ
pub fn local_index(&self) -> Option<u32> {
match self {
MirValueKind::Local(idx) => Some(*idx),
_ => None,
}
}
}
/// 型付きValueId - ValueIdに意味情報を付与
///
/// # 設計思想
///
/// - ValueId: 実際の識別子(既存システムとの互換性)
/// - MirValueKind: 値の種類(新規導入の型情報)
///
/// # 使用例
///
/// ```rust
/// // パラメータ s=ValueId(0)
/// let s = TypedValueId::new(ValueId(0), MirValueKind::Parameter(0));
/// assert!(s.is_parameter());
/// assert_eq!(s.value_id(), ValueId(0));
///
/// // ローカル変数 i=ValueId(10)
/// let i = TypedValueId::new(ValueId(10), MirValueKind::Local(0));
/// assert!(i.is_local());
/// ```
#[derive(Debug, Clone, Copy)]
pub struct TypedValueId {
/// 実際のValueId既存システムとの互換性
pub id: ValueId,
/// 値の種類
pub kind: MirValueKind,
}
impl TypedValueId {
/// 新規作成
pub fn new(id: ValueId, kind: MirValueKind) -> Self {
Self { id, kind }
}
/// パラメータか判定(型安全)
///
/// # GUARDバグ予防
///
/// ```rust
/// let s = TypedValueId::new(ValueId(0), MirValueKind::Parameter(0));
/// assert!(s.is_parameter()); // ✅ ValueId(0) でも正しく判定!
/// ```
pub fn is_parameter(&self) -> bool {
self.kind.is_parameter()
}
/// ローカル変数か判定
pub fn is_local(&self) -> bool {
self.kind.is_local()
}
/// 定数か判定
pub fn is_constant(&self) -> bool {
self.kind.is_constant()
}
/// 一時値か判定
pub fn is_temporary(&self) -> bool {
self.kind.is_temporary()
}
/// Pinned変数か判定
pub fn is_pinned(&self) -> bool {
self.kind.is_pinned()
}
/// LoopCarrierか判定
pub fn is_loop_carrier(&self) -> bool {
self.kind.is_loop_carrier()
}
/// ValueIdを取得後方互換性
pub fn value_id(&self) -> ValueId {
self.id
}
/// MirValueKindを取得
pub fn kind(&self) -> MirValueKind {
self.kind
}
/// パラメータインデックス取得Parameterのみ
pub fn parameter_index(&self) -> Option<u32> {
self.kind.parameter_index()
}
/// ローカル変数番号取得Localのみ
pub fn local_index(&self) -> Option<u32> {
self.kind.local_index()
}
}
/// ValueIdへの自動変換後方互換性
///
/// # 使用例
///
/// ```rust
/// let typed = TypedValueId::new(ValueId(5), MirValueKind::Local(0));
/// let id: ValueId = typed.into(); // 自動変換
/// assert_eq!(id, ValueId(5));
/// ```
impl From<TypedValueId> for ValueId {
fn from(typed: TypedValueId) -> ValueId {
typed.id
}
}
/// PartialEq実装ValueIdベースで比較
impl PartialEq for TypedValueId {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for TypedValueId {}
/// Hash実装ValueIdベースでハッシュ
impl std::hash::Hash for TypedValueId {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
// ============================================================================
// ユニットテスト
// ============================================================================
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mir_value_kind_parameter() {
let kind = MirValueKind::Parameter(0);
assert!(kind.is_parameter());
assert!(!kind.is_local());
assert!(!kind.is_constant());
assert_eq!(kind.parameter_index(), Some(0));
}
#[test]
fn test_mir_value_kind_local() {
let kind = MirValueKind::Local(5);
assert!(kind.is_local());
assert!(!kind.is_parameter());
assert_eq!(kind.local_index(), Some(5));
}
#[test]
fn test_mir_value_kind_constant() {
let kind = MirValueKind::Constant;
assert!(kind.is_constant());
assert!(!kind.is_temporary());
}
#[test]
fn test_mir_value_kind_temporary() {
let kind = MirValueKind::Temporary;
assert!(kind.is_temporary());
assert!(!kind.is_constant());
}
#[test]
fn test_mir_value_kind_pinned() {
let kind = MirValueKind::Pinned;
assert!(kind.is_pinned());
assert!(!kind.is_temporary());
}
#[test]
fn test_mir_value_kind_loop_carrier() {
let kind = MirValueKind::LoopCarrier;
assert!(kind.is_loop_carrier());
assert!(!kind.is_local());
}
#[test]
fn test_typed_value_id_parameter() {
let typed = TypedValueId::new(ValueId(0), MirValueKind::Parameter(0));
assert!(typed.is_parameter());
assert!(!typed.is_local());
assert_eq!(typed.value_id(), ValueId(0));
assert_eq!(typed.parameter_index(), Some(0));
}
#[test]
fn test_typed_value_id_local() {
let typed = TypedValueId::new(ValueId(10), MirValueKind::Local(0));
assert!(typed.is_local());
assert!(!typed.is_parameter());
assert_eq!(typed.value_id(), ValueId(10));
assert_eq!(typed.local_index(), Some(0));
}
#[test]
fn test_typed_value_id_conversion_to_value_id() {
let typed = TypedValueId::new(ValueId(42), MirValueKind::Temporary);
let id: ValueId = typed.into();
assert_eq!(id, ValueId(42));
}
#[test]
fn test_typed_value_id_equality() {
let a = TypedValueId::new(ValueId(5), MirValueKind::Local(0));
let b = TypedValueId::new(ValueId(5), MirValueKind::Parameter(0));
// 同じValueIdなら種類が違っても等しいValueIdベース比較
assert_eq!(a, b);
}
/// GUARDバグ再現防止テスト
///
/// # 背景
///
/// loop_builder.rs で以下のバグがあった:
///
/// ```rust
/// // ❌ ValueId(0) を「常に未初期化」と誤判定
/// for (name, value) in &current_vars {
/// if value.0 == 0 { // ← Parameter s=ValueId(0) も弾く!
/// return Ok(ValueId(0));
/// }
/// }
/// ```
///
/// # 期待動作
///
/// TypedValueIdを使えば、ValueId(0)でも
/// Parameterとして正しく判定できる。
#[test]
fn test_guard_check_bug_prevention() {
// パラメータ s=ValueId(0), idx=ValueId(1)
let s = TypedValueId::new(ValueId(0), MirValueKind::Parameter(0));
let idx = TypedValueId::new(ValueId(1), MirValueKind::Parameter(1));
// ✅ ValueId(0) でもパラメータとして正しく判定される
assert!(s.is_parameter());
assert_eq!(s.value_id(), ValueId(0));
assert_eq!(s.parameter_index(), Some(0));
assert!(idx.is_parameter());
assert_eq!(idx.value_id(), ValueId(1));
assert_eq!(idx.parameter_index(), Some(1));
// ローカル変数 i=ValueId(2)
let i = TypedValueId::new(ValueId(2), MirValueKind::Local(0));
assert!(!i.is_parameter());
assert!(i.is_local());
// ❌ 旧実装(名前ベース判定)では不可能だった区別が可能に!
}
#[test]
fn test_loop_carrier_detection() {
let carrier = TypedValueId::new(ValueId(100), MirValueKind::LoopCarrier);
assert!(carrier.is_loop_carrier());
assert!(!carrier.is_parameter());
assert!(!carrier.is_local());
}
}