use core::fmt;
macro_rules! handle {
($(#[$doc:meta])* $name:ident, $prefix:literal, $example:literal) => {
$(#[$doc])*
#[doc = concat!("use ir_lang::", stringify!($name), ";")]
#[doc = concat!("let h = ", stringify!($name), "::from_u32(3);")]
#[doc = concat!("assert_eq!(h.to_string(), \"", $example, "\");")]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct $name(u32);
impl $name {
#[doc = concat!("use ir_lang::", stringify!($name), ";")]
#[doc = concat!("assert_eq!(", stringify!($name), "::from_u32(0).index(), 0);")]
#[inline]
#[must_use]
pub const fn from_u32(index: u32) -> Self {
Self(index)
}
#[doc = concat!("use ir_lang::", stringify!($name), ";")]
#[doc = concat!("assert_eq!(", stringify!($name), "::from_u32(7).index(), 7);")]
#[inline]
#[must_use]
pub const fn index(self) -> usize {
self.0 as usize
}
#[doc = concat!("use ir_lang::", stringify!($name), ";")]
#[doc = concat!("assert_eq!(", stringify!($name), "::from_u32(9).as_u32(), 9);")]
#[inline]
#[must_use]
pub const fn as_u32(self) -> u32 {
self.0
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!($prefix, "{}"), self.0)
}
}
};
}
handle!(
Value,
"v",
"v3"
);
handle!(
Block,
"b",
"b3"
);
handle!(
Inst,
"inst",
"inst3"
);
handle!(
StackSlot,
"ss",
"ss3"
);
handle!(
FuncId,
"f",
"f3"
);
handle!(
GlobalId,
"g",
"g3"
);
handle!(
SigId,
"s",
"s3"
);
handle!(
AggId,
"t",
"t3"
);
handle!(
Variable,
"var",
"var3"
);
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
pub struct ValueRange {
pub(crate) start: u32,
pub(crate) len: u32,
}
impl ValueRange {
#[inline]
#[must_use]
pub const fn len(self) -> usize {
self.len as usize
}
#[inline]
#[must_use]
pub const fn is_empty(self) -> bool {
self.len == 0
}
#[inline]
#[must_use]
pub const fn get(self, i: usize) -> Option<Value> {
if i < self.len as usize {
match self.start.checked_add(i as u32) {
Some(v) => Some(Value(v)),
None => None,
}
} else {
None
}
}
#[inline]
#[must_use]
pub const fn first(self) -> Option<Value> {
self.get(0)
}
#[inline]
#[must_use]
pub const fn contains(self, value: Value) -> bool {
value.0 >= self.start && value.0 - self.start < self.len
}
#[inline]
pub fn iter(self) -> impl ExactSizeIterator<Item = Value> + DoubleEndedIterator {
(self.start..self.start.saturating_add(self.len)).map(Value)
}
}
impl IntoIterator for ValueRange {
type Item = Value;
type IntoIter = core::iter::Map<core::ops::Range<u32>, fn(u32) -> Value>;
fn into_iter(self) -> Self::IntoIter {
(self.start..self.start.saturating_add(self.len)).map(Value as fn(u32) -> Value)
}
}
impl Value {
#[inline]
#[must_use]
pub const fn next(self) -> Value {
Value(self.0.saturating_add(1))
}
}
pub(crate) const NONE: u32 = u32::MAX;
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn test_handles_display_with_their_prefixes() {
assert_eq!(Value::from_u32(1).to_string(), "v1");
assert_eq!(Block::from_u32(2).to_string(), "b2");
assert_eq!(StackSlot::from_u32(0).to_string(), "ss0");
assert_eq!(AggId::from_u32(5).to_string(), "t5");
assert_eq!(SigId::from_u32(5).to_string(), "s5");
}
#[test]
fn test_value_range_contains_and_iterates() {
let r = ValueRange { start: 4, len: 2 };
assert!(r.contains(Value::from_u32(4)));
assert!(r.contains(Value::from_u32(5)));
assert!(!r.contains(Value::from_u32(6)));
assert!(!r.contains(Value::from_u32(3)));
assert_eq!(r.iter().collect::<alloc::vec::Vec<_>>().len(), 2);
assert_eq!(r.into_iter().next_back(), Some(Value::from_u32(5)));
}
}