423 lines
12 KiB
Rust
423 lines
12 KiB
Rust
// value_kind.rs
|
||
// Phase 26-A: ValueId型安全化
|
||
//
|
||
// Purpose:
|
||
// - ValueIdの意味的分類(Parameter, Local, Constant等)を導入
|
||
// - GUARDバグのような「ValueId(0)の曖昧性」から生じるバグを根絶
|
||
|
||
use crate::mir::ValueId;
|
||
|
||
/// ValueIdの意味的分類(型安全性強化)
|
||
///
|
||
/// # 目的
|
||
///
|
||
/// ValueIdは単なる整数ラッパー `ValueId(u32)` であり、
|
||
/// その値が「パラメータ」「ローカル変数」「定数」のいずれを表すか区別できない。
|
||
/// これにより、以下のようなバグが発生していた:
|
||
///
|
||
/// ## GUARDバグの例(修正済み)
|
||
///
|
||
/// ```rust
|
||
/// // ❌ ValueId(0) を「常に未初期化」と誤判定
|
||
/// for (name, value) in ¤t_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 ¤t_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());
|
||
}
|
||
}
|