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 `Type` structs (176 bytes), we use this enum where:
5/// - Simple scalars are stored inline (1 byte discriminant)
6/// - Complex types are boxed (pointer to Type)
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::Type;
11use serde::{Deserialize, Serialize};
12use std::fmt;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash, 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<Type>),
26}
27
28impl SimpleType {
29    /// Convert a Type into a SimpleType, boxing complex types.
30    pub fn from_union(u: Type) -> Self {
31        // Simple scalar: single atomic, no flags.
32        if !u.possibly_undefined
33            && !u.from_docblock
34            && !u.falsy_stripped
35            && !u.possibly_absent_offset
36            && u.types.len() == 1
37        {
38            match &u.types[0] {
39                Atomic::TString => return Self::String,
40                Atomic::TInt => return Self::Int,
41                Atomic::TFloat | Atomic::TIntegralFloat => return Self::Float,
42                Atomic::TBool => return Self::Bool,
43                Atomic::TMixed => return Self::Mixed,
44                Atomic::TNull => return Self::Null,
45                Atomic::TVoid => return Self::Void,
46                Atomic::TNever => return Self::Never,
47                _ => {}
48            }
49        }
50        Self::Complex(Box::new(u))
51    }
52
53    /// Convert back to a Type.
54    pub fn to_union(&self) -> Type {
55        match self {
56            Self::String => Type::string(),
57            Self::Int => Type::int(),
58            Self::Float => Type::float(),
59            Self::Bool => Type::bool(),
60            Self::Mixed => Type::mixed(),
61            Self::Null => Type::null(),
62            Self::Void => Type::void(),
63            Self::Never => Type::never(),
64            Self::Complex(u) => *u.clone(),
65        }
66    }
67
68    /// Check if this is a simple scalar (not Complex).
69    pub fn is_simple(&self) -> bool {
70        !matches!(self, Self::Complex(_))
71    }
72
73    /// Get as a Type reference if Complex, or None if simple.
74    pub fn as_complex(&self) -> Option<&Type> {
75        match self {
76            Self::Complex(u) => Some(u),
77            _ => None,
78        }
79    }
80}
81
82impl fmt::Display for SimpleType {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        match self {
85            Self::String => write!(f, "string"),
86            Self::Int => write!(f, "int"),
87            Self::Float => write!(f, "float"),
88            Self::Bool => write!(f, "bool"),
89            Self::Mixed => write!(f, "mixed"),
90            Self::Null => write!(f, "null"),
91            Self::Void => write!(f, "void"),
92            Self::Never => write!(f, "never"),
93            Self::Complex(u) => write!(f, "{}", u),
94        }
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn simple_scalar_roundtrip() {
104        let u = Type::string();
105        let s = SimpleType::from_union(u.clone());
106        assert_eq!(s, SimpleType::String);
107        assert_eq!(s.to_union(), u);
108    }
109
110    #[test]
111    fn nullable_scalar_is_complex() {
112        let u = Type::nullable(Atomic::TString);
113        let s = SimpleType::from_union(u.clone());
114        assert_eq!(s, SimpleType::Complex(Box::new(u.clone())));
115        assert_eq!(s.to_union(), u);
116    }
117}