Skip to main content

ir_lang/
ty.rs

1//! The machine-level value types an IR value can have.
2
3use core::fmt;
4
5/// The type of a value in the IR.
6///
7/// This is the IR's own *machine-level* type system, deliberately small and
8/// independent of any source language. A front-end lowering its program decides
9/// how its source types map onto these — a 32-bit and a 64-bit source integer both
10/// lower to [`Type::Int`] here, a source `bool` to [`Type::Bool`], and a value that
11/// produces nothing (a statement, a `void` call) to [`Type::Unit`]. The validator
12/// uses these types to reject operations applied to the wrong kind of value, so the
13/// set is kept to the four cases an arithmetic-and-control-flow core actually needs.
14///
15/// Wider machine types (sized integers, vectors, pointers) are a deliberate later
16/// addition: a new variant is an additive, non-breaking change.
17///
18/// # Examples
19///
20/// ```
21/// use ir_lang::Type;
22///
23/// // Types are small, `Copy`, and print as their lowercase name.
24/// assert_eq!(Type::Int.to_string(), "int");
25/// assert_eq!(Type::Bool.to_string(), "bool");
26/// assert_ne!(Type::Int, Type::Float);
27/// ```
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
30pub enum Type {
31    /// A signed integer value.
32    Int,
33    /// A floating-point value.
34    Float,
35    /// A boolean value, as produced by a comparison or a logical operation.
36    Bool,
37    /// The absence of a value — the result type of an operation that exists only
38    /// for its effect, and the return type of a function that returns nothing.
39    Unit,
40}
41
42impl Type {
43    /// Returns `true` for the numeric types ([`Int`](Type::Int) and
44    /// [`Float`](Type::Float)) that arithmetic and ordering operations accept.
45    ///
46    /// # Examples
47    ///
48    /// ```
49    /// use ir_lang::Type;
50    ///
51    /// assert!(Type::Int.is_numeric());
52    /// assert!(Type::Float.is_numeric());
53    /// assert!(!Type::Bool.is_numeric());
54    /// assert!(!Type::Unit.is_numeric());
55    /// ```
56    #[must_use]
57    pub const fn is_numeric(self) -> bool {
58        matches!(self, Type::Int | Type::Float)
59    }
60}
61
62impl fmt::Display for Type {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        let name = match self {
65            Type::Int => "int",
66            Type::Float => "float",
67            Type::Bool => "bool",
68            Type::Unit => "unit",
69        };
70        f.write_str(name)
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_type_is_numeric_classifies_each_variant() {
80        assert!(Type::Int.is_numeric());
81        assert!(Type::Float.is_numeric());
82        assert!(!Type::Bool.is_numeric());
83        assert!(!Type::Unit.is_numeric());
84    }
85
86    #[test]
87    fn test_type_display_matches_lowercase_name() {
88        assert_eq!(Type::Int.to_string(), "int");
89        assert_eq!(Type::Float.to_string(), "float");
90        assert_eq!(Type::Bool.to_string(), "bool");
91        assert_eq!(Type::Unit.to_string(), "unit");
92    }
93}