Skip to main content

jugar_probar/coverage/
block.rs

1//! Type-Safe Block Identifiers (Poka-Yoke)
2//!
3//! Per spec ยง5.2: Type-safe block IDs prevent coverage gaps at compile time.
4//!
5//! These types are intentionally NOT interchangeable to catch errors at compile time.
6
7use std::cmp::Ordering;
8use std::hash::{Hash, Hasher};
9
10/// Type-safe block identifier (Poka-Yoke)
11///
12/// Represents a basic block in the CFG. Cannot be confused with FunctionId or EdgeId.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct BlockId(u32);
15
16impl BlockId {
17    /// Create a new block ID
18    #[inline]
19    #[must_use]
20    pub const fn new(id: u32) -> Self {
21        Self(id)
22    }
23
24    /// Get the inner value
25    #[inline]
26    #[must_use]
27    pub const fn as_u32(self) -> u32 {
28        self.0
29    }
30}
31
32impl Hash for BlockId {
33    fn hash<H: Hasher>(&self, state: &mut H) {
34        self.0.hash(state);
35    }
36}
37
38impl PartialOrd for BlockId {
39    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
40        Some(self.cmp(other))
41    }
42}
43
44impl Ord for BlockId {
45    fn cmp(&self, other: &Self) -> Ordering {
46        self.0.cmp(&other.0)
47    }
48}
49
50/// Type-safe function identifier (Poka-Yoke)
51///
52/// Represents a function in the WASM module. Cannot be confused with BlockId.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct FunctionId(u32);
55
56impl FunctionId {
57    /// Create a new function ID
58    #[inline]
59    #[must_use]
60    pub const fn new(id: u32) -> Self {
61        Self(id)
62    }
63
64    /// Get the inner value
65    #[inline]
66    #[must_use]
67    pub const fn as_u32(self) -> u32 {
68        self.0
69    }
70}
71
72impl Hash for FunctionId {
73    fn hash<H: Hasher>(&self, state: &mut H) {
74        self.0.hash(state);
75    }
76}
77
78impl PartialOrd for FunctionId {
79    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80        Some(self.cmp(other))
81    }
82}
83
84impl Ord for FunctionId {
85    fn cmp(&self, other: &Self) -> Ordering {
86        self.0.cmp(&other.0)
87    }
88}
89
90/// Type-safe edge identifier (Poka-Yoke)
91///
92/// Encodes both source and target block in a single u64:
93/// `EdgeId = (from << 32) | to`
94///
95/// # Panics
96///
97/// Debug assertion if block IDs exceed u32::MAX (Kaizen: overflow guard)
98#[derive(Debug, Clone, Copy, PartialEq, Eq)]
99pub struct EdgeId(u64);
100
101impl EdgeId {
102    /// Create edge ID from source and target (Poka-Yoke: can't mix up)
103    ///
104    /// The encoding `(from << 32) | to` is safe because BlockId is u32,
105    /// which always fits in the lower/upper 32 bits of u64.
106    #[inline]
107    #[must_use]
108    pub const fn new(from: BlockId, to: BlockId) -> Self {
109        // Poka-Yoke: BlockId is u32, so no overflow possible in u64 encoding
110        // The type system guarantees from.0 and to.0 are valid u32 values
111        Self((from.0 as u64) << 32 | to.0 as u64)
112    }
113
114    /// Get the source block
115    #[inline]
116    #[must_use]
117    pub const fn source(self) -> BlockId {
118        BlockId((self.0 >> 32) as u32)
119    }
120
121    /// Get the target block
122    #[inline]
123    #[must_use]
124    pub const fn target(self) -> BlockId {
125        BlockId(self.0 as u32)
126    }
127
128    /// Get the raw u64 value
129    #[inline]
130    #[must_use]
131    pub const fn as_u64(self) -> u64 {
132        self.0
133    }
134}
135
136impl Hash for EdgeId {
137    fn hash<H: Hasher>(&self, state: &mut H) {
138        self.0.hash(state);
139    }
140}
141
142impl PartialOrd for EdgeId {
143    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
144        Some(self.cmp(other))
145    }
146}
147
148impl Ord for EdgeId {
149    fn cmp(&self, other: &Self) -> Ordering {
150        self.0.cmp(&other.0)
151    }
152}