/home/noah/src/ruchy/src/middleend/mir/types.rs
Line | Count | Source |
1 | | //! MIR type definitions |
2 | | |
3 | | use std::collections::HashMap; |
4 | | use std::fmt; |
5 | | |
6 | | /// A MIR program consists of functions |
7 | | #[derive(Debug, Clone)] |
8 | | pub struct Program { |
9 | | /// Global functions in the program |
10 | | pub functions: HashMap<String, Function>, |
11 | | /// Entry point function name |
12 | | pub entry: String, |
13 | | } |
14 | | |
15 | | /// A function in MIR representation |
16 | | #[derive(Debug, Clone)] |
17 | | pub struct Function { |
18 | | /// Function name |
19 | | pub name: String, |
20 | | /// Parameters (as local variables) |
21 | | pub params: Vec<Local>, |
22 | | /// Return type |
23 | | pub return_ty: Type, |
24 | | /// Local variables (including parameters) |
25 | | pub locals: Vec<LocalDecl>, |
26 | | /// Basic blocks making up the function body |
27 | | pub blocks: Vec<BasicBlock>, |
28 | | /// Entry block index |
29 | | pub entry_block: BlockId, |
30 | | } |
31 | | |
32 | | /// A basic block is a sequence of statements with a single entry and exit |
33 | | #[derive(Debug, Clone)] |
34 | | pub struct BasicBlock { |
35 | | /// Block identifier |
36 | | pub id: BlockId, |
37 | | /// Statements in this block (no control flow) |
38 | | pub statements: Vec<Statement>, |
39 | | /// Terminator - how control leaves this block |
40 | | pub terminator: Terminator, |
41 | | } |
42 | | |
43 | | /// Block identifier |
44 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
45 | | pub struct BlockId(pub usize); |
46 | | |
47 | | /// Local variable identifier |
48 | | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
49 | | pub struct Local(pub usize); |
50 | | |
51 | | /// Local variable declaration |
52 | | #[derive(Debug, Clone)] |
53 | | pub struct LocalDecl { |
54 | | /// Variable identifier |
55 | | pub id: Local, |
56 | | /// Variable type |
57 | | pub ty: Type, |
58 | | /// Is this mutable? |
59 | | pub mutable: bool, |
60 | | /// Optional name for debugging |
61 | | pub name: Option<String>, |
62 | | } |
63 | | |
64 | | /// MIR types (simplified from AST types) |
65 | | #[derive(Debug, Clone, PartialEq, Eq)] |
66 | | pub enum Type { |
67 | | /// Unit type |
68 | | Unit, |
69 | | /// Boolean |
70 | | Bool, |
71 | | /// Signed integers |
72 | | I8, |
73 | | I16, |
74 | | I32, |
75 | | I64, |
76 | | I128, |
77 | | /// Unsigned integers |
78 | | U8, |
79 | | U16, |
80 | | U32, |
81 | | U64, |
82 | | U128, |
83 | | /// Floating point |
84 | | F32, |
85 | | F64, |
86 | | /// String |
87 | | String, |
88 | | /// Reference (borrowed) |
89 | | Ref(Box<Type>, Mutability), |
90 | | /// Array with known size |
91 | | Array(Box<Type>, usize), |
92 | | /// Dynamic vector |
93 | | Vec(Box<Type>), |
94 | | /// Tuple |
95 | | Tuple(Vec<Type>), |
96 | | /// Function pointer |
97 | | FnPtr(Vec<Type>, Box<Type>), |
98 | | /// User-defined type |
99 | | UserType(String), |
100 | | } |
101 | | |
102 | | /// Mutability of references |
103 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
104 | | pub enum Mutability { |
105 | | Immutable, |
106 | | Mutable, |
107 | | } |
108 | | |
109 | | /// A statement that doesn't affect control flow |
110 | | #[derive(Debug, Clone)] |
111 | | pub enum Statement { |
112 | | /// Assign an rvalue to a place |
113 | | Assign(Place, Rvalue), |
114 | | /// Mark a local as live (for storage) |
115 | | StorageLive(Local), |
116 | | /// Mark a local as dead (storage can be reclaimed) |
117 | | StorageDead(Local), |
118 | | /// No operation |
119 | | Nop, |
120 | | } |
121 | | |
122 | | /// A place where a value can be stored |
123 | | #[derive(Debug, Clone)] |
124 | | pub enum Place { |
125 | | /// Local variable |
126 | | Local(Local), |
127 | | /// Field projection (e.g., struct.field) |
128 | | Field(Box<Place>, FieldIdx), |
129 | | /// Array/slice index |
130 | | Index(Box<Place>, Box<Place>), |
131 | | /// Dereference |
132 | | Deref(Box<Place>), |
133 | | } |
134 | | |
135 | | /// Field index in a struct/tuple |
136 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
137 | | pub struct FieldIdx(pub usize); |
138 | | |
139 | | /// Right-hand side of an assignment |
140 | | #[derive(Debug, Clone)] |
141 | | pub enum Rvalue { |
142 | | /// Use a value from a place |
143 | | Use(Operand), |
144 | | /// Binary operation |
145 | | BinaryOp(BinOp, Operand, Operand), |
146 | | /// Unary operation |
147 | | UnaryOp(UnOp, Operand), |
148 | | /// Create a reference |
149 | | Ref(Mutability, Place), |
150 | | /// Create an aggregate (struct, tuple, array) |
151 | | Aggregate(AggregateKind, Vec<Operand>), |
152 | | /// Function call |
153 | | Call(Operand, Vec<Operand>), |
154 | | /// Cast between types |
155 | | Cast(CastKind, Operand, Type), |
156 | | } |
157 | | |
158 | | /// An operand (value that can be used) |
159 | | #[derive(Debug, Clone)] |
160 | | pub enum Operand { |
161 | | /// Copy value from a place |
162 | | Copy(Place), |
163 | | /// Move value from a place |
164 | | Move(Place), |
165 | | /// Constant value |
166 | | Constant(Constant), |
167 | | } |
168 | | |
169 | | /// Constant values |
170 | | #[derive(Debug, Clone)] |
171 | | pub enum Constant { |
172 | | /// Unit value |
173 | | Unit, |
174 | | /// Boolean |
175 | | Bool(bool), |
176 | | /// Integer |
177 | | Int(i128, Type), |
178 | | /// Unsigned integer |
179 | | Uint(u128, Type), |
180 | | /// Float |
181 | | Float(f64, Type), |
182 | | /// String literal |
183 | | String(String), |
184 | | /// Character literal |
185 | | Char(char), |
186 | | } |
187 | | |
188 | | /// Binary operations |
189 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
190 | | pub enum BinOp { |
191 | | // Arithmetic |
192 | | Add, |
193 | | Sub, |
194 | | Mul, |
195 | | Div, |
196 | | Rem, |
197 | | Pow, |
198 | | // Bitwise |
199 | | BitAnd, |
200 | | BitOr, |
201 | | BitXor, |
202 | | Shl, |
203 | | Shr, |
204 | | // Comparison |
205 | | Eq, |
206 | | Ne, |
207 | | Lt, |
208 | | Le, |
209 | | Gt, |
210 | | Ge, |
211 | | // Logical (short-circuiting is handled by control flow) |
212 | | And, |
213 | | Or, |
214 | | NullCoalesce, |
215 | | } |
216 | | |
217 | | /// Unary operations |
218 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
219 | | pub enum UnOp { |
220 | | /// Negation (arithmetic) |
221 | | Neg, |
222 | | /// Logical not |
223 | | Not, |
224 | | /// Bitwise not |
225 | | BitNot, |
226 | | /// Reference (borrow) |
227 | | Ref, |
228 | | } |
229 | | |
230 | | /// Aggregate kinds |
231 | | #[derive(Debug, Clone)] |
232 | | pub enum AggregateKind { |
233 | | /// Tuple |
234 | | Tuple, |
235 | | /// Array |
236 | | Array(Type), |
237 | | /// Struct |
238 | | Struct(String), |
239 | | } |
240 | | |
241 | | /// Cast kinds |
242 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
243 | | pub enum CastKind { |
244 | | /// Numeric cast (int to int, float to float, etc.) |
245 | | Numeric, |
246 | | /// Pointer to pointer |
247 | | Pointer, |
248 | | /// Unsizing (e.g., array to slice) |
249 | | Unsize, |
250 | | } |
251 | | |
252 | | /// How control flow leaves a basic block |
253 | | #[derive(Debug, Clone)] |
254 | | pub enum Terminator { |
255 | | /// Unconditional jump |
256 | | Goto(BlockId), |
257 | | /// Conditional branch |
258 | | If { |
259 | | condition: Operand, |
260 | | then_block: BlockId, |
261 | | else_block: BlockId, |
262 | | }, |
263 | | /// Switch/match on a value |
264 | | Switch { |
265 | | discriminant: Operand, |
266 | | targets: Vec<(Constant, BlockId)>, |
267 | | default: Option<BlockId>, |
268 | | }, |
269 | | /// Return from function |
270 | | Return(Option<Operand>), |
271 | | /// Call a function and continue |
272 | | Call { |
273 | | func: Operand, |
274 | | args: Vec<Operand>, |
275 | | destination: Option<(Place, BlockId)>, |
276 | | }, |
277 | | /// Unreachable code (for exhaustiveness) |
278 | | Unreachable, |
279 | | } |
280 | | |
281 | | // Display implementations for debugging |
282 | | impl fmt::Display for BlockId { |
283 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
284 | 0 | write!(f, "bb{}", self.0) |
285 | 0 | } |
286 | | } |
287 | | |
288 | | impl fmt::Display for Local { |
289 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
290 | 0 | write!(f, "__{}", self.0) |
291 | 0 | } |
292 | | } |
293 | | |
294 | | impl fmt::Display for Type { |
295 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
296 | 0 | match self { |
297 | 0 | Type::Unit => write!(f, "()"), |
298 | 0 | Type::Bool => write!(f, "bool"), |
299 | 0 | Type::I8 => write!(f, "i8"), |
300 | 0 | Type::I16 => write!(f, "i16"), |
301 | 0 | Type::I32 => write!(f, "i32"), |
302 | 0 | Type::I64 => write!(f, "i64"), |
303 | 0 | Type::I128 => write!(f, "i128"), |
304 | 0 | Type::U8 => write!(f, "u8"), |
305 | 0 | Type::U16 => write!(f, "u16"), |
306 | 0 | Type::U32 => write!(f, "u32"), |
307 | 0 | Type::U64 => write!(f, "u64"), |
308 | 0 | Type::U128 => write!(f, "u128"), |
309 | 0 | Type::F32 => write!(f, "f32"), |
310 | 0 | Type::F64 => write!(f, "f64"), |
311 | 0 | Type::String => write!(f, "String"), |
312 | 0 | Type::Ref(ty, Mutability::Immutable) => write!(f, "&{ty}"), |
313 | 0 | Type::Ref(ty, Mutability::Mutable) => write!(f, "&mut {ty}"), |
314 | 0 | Type::Array(ty, size) => write!(f, "[{ty}; {size}]"), |
315 | 0 | Type::Vec(ty) => write!(f, "Vec<{ty}>"), |
316 | 0 | Type::Tuple(tys) => { |
317 | 0 | write!(f, "(")?; |
318 | 0 | for (i, ty) in tys.iter().enumerate() { |
319 | 0 | if i > 0 { |
320 | 0 | write!(f, ", ")?; |
321 | 0 | } |
322 | 0 | write!(f, "{ty}")?; |
323 | | } |
324 | 0 | write!(f, ")") |
325 | | } |
326 | 0 | Type::FnPtr(params, ret) => { |
327 | 0 | write!(f, "fn(")?; |
328 | 0 | for (i, param) in params.iter().enumerate() { |
329 | 0 | if i > 0 { |
330 | 0 | write!(f, ", ")?; |
331 | 0 | } |
332 | 0 | write!(f, "{param}")?; |
333 | | } |
334 | 0 | write!(f, ") -> {ret}") |
335 | | } |
336 | 0 | Type::UserType(name) => write!(f, "{name}"), |
337 | | } |
338 | 0 | } |
339 | | } |
340 | | |
341 | | #[cfg(test)] |
342 | | mod tests { |
343 | | use super::*; |
344 | | |
345 | | #[test] |
346 | 1 | fn test_program_creation() { |
347 | 1 | let program = Program { |
348 | 1 | functions: HashMap::new(), |
349 | 1 | entry: "main".to_string(), |
350 | 1 | }; |
351 | | |
352 | 1 | assert_eq!(program.entry, "main"); |
353 | 1 | assert_eq!(program.functions.len(), 0); |
354 | 1 | } |
355 | | |
356 | | |
357 | | #[test] |
358 | 1 | fn test_block_id() { |
359 | 1 | let id1 = BlockId(0); |
360 | 1 | let id2 = BlockId(1); |
361 | 1 | let id3 = BlockId(0); |
362 | | |
363 | 1 | assert_eq!(id1, id3); |
364 | 1 | assert_ne!(id1, id2); |
365 | 1 | assert_eq!(format!("{id1:?}"), "BlockId(0)"); |
366 | 1 | } |
367 | | |
368 | | #[test] |
369 | 1 | fn test_local_variable() { |
370 | 1 | let local1 = Local(0); |
371 | 1 | let local2 = Local(1); |
372 | 1 | let local3 = Local(0); |
373 | | |
374 | 1 | assert_eq!(local1, local3); |
375 | 1 | assert_ne!(local1, local2); |
376 | 1 | assert_eq!(format!("{local1:?}"), "Local(0)"); |
377 | 1 | } |
378 | | |
379 | | #[test] |
380 | 1 | fn test_type_variants() { |
381 | 1 | let types = vec![ |
382 | 1 | Type::Unit, |
383 | 1 | Type::Bool, |
384 | 1 | Type::I32, |
385 | 1 | Type::I64, |
386 | 1 | Type::F32, |
387 | 1 | Type::F64, |
388 | 1 | Type::String, |
389 | 1 | Type::Array(Box::new(Type::I32), 10), |
390 | 1 | Type::Vec(Box::new(Type::F64)), |
391 | 1 | Type::Tuple(vec![Type::I32, Type::Bool]), |
392 | | ]; |
393 | | |
394 | 11 | for ty10 in types { |
395 | 10 | assert!(!format!("{ty:?}").is_empty()); |
396 | | } |
397 | 1 | } |
398 | | |
399 | | #[test] |
400 | 1 | fn test_type_equality() { |
401 | 1 | assert_eq!(Type::I32, Type::I32); |
402 | 1 | assert_ne!(Type::I32, Type::I64); |
403 | 1 | assert_eq!( |
404 | 1 | Type::Array(Box::new(Type::U8), 5), |
405 | 1 | Type::Array(Box::new(Type::U8), 5) |
406 | | ); |
407 | 1 | assert_ne!( |
408 | 1 | Type::Array(Box::new(Type::U8), 5), |
409 | 1 | Type::Array(Box::new(Type::U8), 10) |
410 | | ); |
411 | 1 | } |
412 | | } |