ruka_types/
lib.rs

1//! Shared semantic types used across compiler crates.
2
3mod coercion;
4mod ownership;
5
6pub use coercion::{
7    boundary_coercion_decision, normalized_coercion_decision, ty_coercion_decision, CheckPolicy,
8    CoercionDecision, MaterializationKind, MaterializationPolicy, RuntimeCheckKind,
9};
10pub use ownership::{
11    normalize_ty, normalize_ty_with_access, AccessMode, BaseTy, NormalizeError, NormalizedTy,
12};
13
14use std::fmt;
15
16/// Semantic type assigned by checking and consumed by lower IRs/backends.
17#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub enum Ty {
19    /// The unit type.
20    Unit,
21    /// Unsigned 8-bit integer type.
22    U8,
23    /// Unsigned 16-bit integer type.
24    U16,
25    /// Unsigned 32-bit integer type.
26    U32,
27    /// Unsigned 64-bit integer type.
28    U64,
29    /// Signed 8-bit integer type.
30    I8,
31    /// Signed 16-bit integer type.
32    I16,
33    /// Signed 32-bit integer type.
34    I32,
35    /// Signed 64-bit integer type.
36    I64,
37    /// 32-bit floating-point type.
38    F32,
39    /// 64-bit floating-point type.
40    F64,
41    /// The string value type.
42    String,
43    /// The boolean type.
44    Bool,
45    /// Nullable owned pointer to another type.
46    Pointer(Box<Ty>),
47    /// Optional value.
48    Option(Box<Ty>),
49    /// Fixed-size array of items.
50    Array {
51        /// Item type stored in the array.
52        item: Box<Ty>,
53        /// Number of items in the array.
54        len: usize,
55    },
56    /// Borrowed slice of items.
57    Slice(Box<Ty>),
58    /// Tuple of positional elements.
59    Tuple(Vec<Ty>),
60    /// Read-only reference to another type.
61    RefRo(Box<Ty>),
62    /// Mutable reference to another type.
63    RefMut(Box<Ty>),
64    /// Struct type with optional concrete type arguments.
65    Struct {
66        /// Struct constructor name.
67        name: String,
68        /// Concrete type arguments applied to the struct.
69        args: Vec<Ty>,
70    },
71    /// Enum type with optional concrete type arguments.
72    Enum {
73        /// Enum constructor name.
74        name: String,
75        /// Concrete type arguments applied to the enum.
76        args: Vec<Ty>,
77    },
78}
79
80impl Ty {
81    /// Return whether this is a scalar integer type.
82    pub fn is_integer(&self) -> bool {
83        matches!(
84            self,
85            Ty::U8 | Ty::U16 | Ty::U32 | Ty::U64 | Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64
86        )
87    }
88
89    /// Return whether this is a scalar floating-point type.
90    pub fn is_float(&self) -> bool {
91        matches!(self, Ty::F32 | Ty::F64)
92    }
93
94    /// Return whether this is any scalar numeric type.
95    pub fn is_numeric(&self) -> bool {
96        self.is_integer() || self.is_float()
97    }
98}
99
100/// Return whether numeric value `from` can be converted into `to` without loss.
101pub fn can_safely_coerce_numeric(from: &Ty, to: &Ty) -> bool {
102    if from == to {
103        return true;
104    }
105    match (from, to) {
106        (Ty::F32, Ty::F64) => true,
107        (Ty::F64, Ty::F32) => false,
108        (Ty::U8, Ty::F32)
109        | (Ty::U16, Ty::F32)
110        | (Ty::I8, Ty::F32)
111        | (Ty::I16, Ty::F32)
112        | (Ty::U8, Ty::F64)
113        | (Ty::U16, Ty::F64)
114        | (Ty::U32, Ty::F64)
115        | (Ty::I8, Ty::F64)
116        | (Ty::I16, Ty::F64)
117        | (Ty::I32, Ty::F64) => true,
118        _ => false,
119    }
120}
121
122/// Return whether `source` to `target` represents a narrowing truncation cast.
123pub fn is_truncation_cast(source: &Ty, target: &Ty) -> bool {
124    if source.is_integer() && target.is_integer() {
125        return integer_width(source)
126            .zip(integer_width(target))
127            .is_some_and(|(s, t)| s > t);
128    }
129    if source.is_float() && target.is_float() {
130        return float_width(source)
131            .zip(float_width(target))
132            .is_some_and(|(s, t)| s > t);
133    }
134    false
135}
136
137/// Return promoted numeric type for mixed arithmetic operands.
138pub fn promote_numeric_types(lhs: &Ty, rhs: &Ty) -> Option<Ty> {
139    if !lhs.is_numeric() || !rhs.is_numeric() {
140        return None;
141    }
142    if lhs == rhs {
143        return Some(lhs.clone());
144    }
145    if lhs.is_float() || rhs.is_float() {
146        if matches!(lhs, Ty::F64) || matches!(rhs, Ty::F64) {
147            return Some(Ty::F64);
148        }
149        return Some(Ty::F32);
150    }
151    let lhs_w = integer_width(lhs)?;
152    let rhs_w = integer_width(rhs)?;
153    if is_signed_integer(lhs) == is_signed_integer(rhs) {
154        if is_signed_integer(lhs) {
155            return signed_ty_for_width(lhs_w.max(rhs_w));
156        }
157        return unsigned_ty_for_width(lhs_w.max(rhs_w));
158    }
159    let signed_w = if is_signed_integer(lhs) { lhs_w } else { rhs_w };
160    let unsigned_w = if is_signed_integer(lhs) { rhs_w } else { lhs_w };
161    if signed_w > unsigned_w {
162        return signed_ty_for_width(signed_w);
163    }
164    signed_ty_for_width(match unsigned_w {
165        8 => 16,
166        16 => 32,
167        32 => 64,
168        _ => return Some(Ty::F64),
169    })
170}
171
172/// Return whether integer type is signed.
173fn is_signed_integer(ty: &Ty) -> bool {
174    matches!(ty, Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64)
175}
176
177/// Return signed integer type for width.
178fn signed_ty_for_width(width: u8) -> Option<Ty> {
179    match width {
180        8 => Some(Ty::I8),
181        16 => Some(Ty::I16),
182        32 => Some(Ty::I32),
183        64 => Some(Ty::I64),
184        _ => None,
185    }
186}
187
188/// Return unsigned integer type for width.
189fn unsigned_ty_for_width(width: u8) -> Option<Ty> {
190    match width {
191        8 => Some(Ty::U8),
192        16 => Some(Ty::U16),
193        32 => Some(Ty::U32),
194        64 => Some(Ty::U64),
195        _ => None,
196    }
197}
198
199fn integer_width(ty: &Ty) -> Option<u8> {
200    match ty {
201        Ty::U8 | Ty::I8 => Some(8),
202        Ty::U16 | Ty::I16 => Some(16),
203        Ty::U32 | Ty::I32 => Some(32),
204        Ty::U64 | Ty::I64 => Some(64),
205        _ => None,
206    }
207}
208
209fn float_width(ty: &Ty) -> Option<u8> {
210    match ty {
211        Ty::F32 => Some(32),
212        Ty::F64 => Some(64),
213        _ => None,
214    }
215}
216
217impl fmt::Display for Ty {
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        match self {
220            Ty::Unit => write!(f, "Unit"),
221            Ty::U8 => write!(f, "u8"),
222            Ty::U16 => write!(f, "u16"),
223            Ty::U32 => write!(f, "u32"),
224            Ty::U64 => write!(f, "u64"),
225            Ty::I8 => write!(f, "i8"),
226            Ty::I16 => write!(f, "i16"),
227            Ty::I32 => write!(f, "i32"),
228            Ty::I64 => write!(f, "i64"),
229            Ty::F32 => write!(f, "f32"),
230            Ty::F64 => write!(f, "f64"),
231            Ty::String => write!(f, "String"),
232            Ty::Bool => write!(f, "Bool"),
233            Ty::Pointer(item) => write!(f, "*{item}"),
234            Ty::Option(item) => write!(f, "Option[{item}]"),
235            Ty::Array { item, len } => write!(f, "[{item}; {len}]"),
236            Ty::Slice(item) => write!(f, "[{item}]"),
237            Ty::Tuple(items) => {
238                write!(f, "(")?;
239                for (index, item) in items.iter().enumerate() {
240                    if index > 0 {
241                        write!(f, ", ")?;
242                    }
243                    write!(f, "{item}")?;
244                }
245                if items.len() == 1 {
246                    write!(f, ",")?;
247                }
248                write!(f, ")")
249            }
250            Ty::RefRo(item) => write!(f, "RefRo({item})"),
251            Ty::RefMut(item) => write!(f, "RefMut({item})"),
252            Ty::Struct { name, args } => {
253                if args.is_empty() {
254                    write!(f, "{name}")
255                } else {
256                    let rendered = args
257                        .iter()
258                        .map(ToString::to_string)
259                        .collect::<Vec<_>>()
260                        .join(", ");
261                    write!(f, "{name}[{rendered}]")
262                }
263            }
264            Ty::Enum { name, args } => {
265                if args.is_empty() {
266                    write!(f, "{name}")
267                } else {
268                    let rendered = args
269                        .iter()
270                        .map(ToString::to_string)
271                        .collect::<Vec<_>>()
272                        .join(", ");
273                    write!(f, "{name}[{rendered}]")
274                }
275            }
276        }
277    }
278}