1mod 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#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
18pub enum Ty {
19 Unit,
21 U8,
23 U16,
25 U32,
27 U64,
29 I8,
31 I16,
33 I32,
35 I64,
37 F32,
39 F64,
41 String,
43 Bool,
45 Pointer(Box<Ty>),
47 Option(Box<Ty>),
49 Array {
51 item: Box<Ty>,
53 len: usize,
55 },
56 Slice(Box<Ty>),
58 Tuple(Vec<Ty>),
60 RefRo(Box<Ty>),
62 RefMut(Box<Ty>),
64 Struct {
66 name: String,
68 args: Vec<Ty>,
70 },
71 Enum {
73 name: String,
75 args: Vec<Ty>,
77 },
78}
79
80impl Ty {
81 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 pub fn is_float(&self) -> bool {
91 matches!(self, Ty::F32 | Ty::F64)
92 }
93
94 pub fn is_numeric(&self) -> bool {
96 self.is_integer() || self.is_float()
97 }
98}
99
100pub 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
122pub 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
137pub 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
172fn is_signed_integer(ty: &Ty) -> bool {
174 matches!(ty, Ty::I8 | Ty::I16 | Ty::I32 | Ty::I64)
175}
176
177fn 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
188fn 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}