Skip to main content

mir_types/
compact.rs

1/// Compact representation of simple types for use in `FnParam` inside `TCallable`/`TClosure`.
2///
3/// Most parameters in vendor code are simple scalar types (string, int, bool, mixed, etc.).
4/// Instead of storing full `Union` structs (176 bytes), we use this enum where:
5/// - Simple scalars are stored inline (1 byte discriminant)
6/// - Complex types are boxed (pointer to Union)
7///
8/// This reduces the size of `mir_types::atomic::FnParam::ty` from ~176 bytes to ~8-16 bytes.
9use crate::atomic::Atomic;
10use crate::union::Union;
11use serde::{Deserialize, Serialize};
12use std::fmt;
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15pub enum SimpleType {
16    String,
17    Int,
18    Float,
19    Bool,
20    Mixed,
21    Null,
22    Void,
23    Never,
24    /// Complex types (multi-union, type args, etc.) are boxed.
25    Complex(Box<Union>),
26}
27
28impl SimpleType {
29    /// Convert a Union into a SimpleType, boxing complex types.
30    pub fn from_union(u: Union) -> Self {
31        // Simple scalar: single atomic, no flags.
32        if !u.possibly_undefined && !u.from_docblock && u.types.len() == 1 {
33            match &u.types[0] {
34                Atomic::TString => return Self::String,
35                Atomic::TInt => return Self::Int,
36                Atomic::TFloat => return Self::Float,
37                Atomic::TBool => return Self::Bool,
38                Atomic::TMixed => return Self::Mixed,
39                Atomic::TNull => return Self::Null,
40                Atomic::TVoid => return Self::Void,
41                Atomic::TNever => return Self::Never,
42                _ => {}
43            }
44        }
45        Self::Complex(Box::new(u))
46    }
47
48    /// Convert back to a Union.
49    pub fn to_union(&self) -> Union {
50        match self {
51            Self::String => Union::string(),
52            Self::Int => Union::int(),
53            Self::Float => Union::float(),
54            Self::Bool => Union::bool(),
55            Self::Mixed => Union::mixed(),
56            Self::Null => Union::null(),
57            Self::Void => Union::void(),
58            Self::Never => Union::never(),
59            Self::Complex(u) => *u.clone(),
60        }
61    }
62
63    /// Check if this is a simple scalar (not Complex).
64    pub fn is_simple(&self) -> bool {
65        !matches!(self, Self::Complex(_))
66    }
67
68    /// Get as a Union reference if Complex, or None if simple.
69    pub fn as_complex(&self) -> Option<&Union> {
70        match self {
71            Self::Complex(u) => Some(u),
72            _ => None,
73        }
74    }
75}
76
77impl fmt::Display for SimpleType {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        match self {
80            Self::String => write!(f, "string"),
81            Self::Int => write!(f, "int"),
82            Self::Float => write!(f, "float"),
83            Self::Bool => write!(f, "bool"),
84            Self::Mixed => write!(f, "mixed"),
85            Self::Null => write!(f, "null"),
86            Self::Void => write!(f, "void"),
87            Self::Never => write!(f, "never"),
88            Self::Complex(u) => write!(f, "{}", u),
89        }
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn simple_scalar_roundtrip() {
99        let u = Union::string();
100        let s = SimpleType::from_union(u.clone());
101        assert_eq!(s, SimpleType::String);
102        assert_eq!(s.to_union(), u);
103    }
104
105    #[test]
106    fn nullable_scalar_is_complex() {
107        let u = Union::nullable(Atomic::TString);
108        let s = SimpleType::from_union(u.clone());
109        assert_eq!(s, SimpleType::Complex(Box::new(u.clone())));
110        assert_eq!(s.to_union(), u);
111    }
112}